diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b87ed4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,22 @@ +# 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 + +# Arbeitsdateien der Subagent-Ausführung +.superpowers/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..bae47fb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,125 @@ +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() + +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() +# 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() +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() +# 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 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) + 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) + +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 + 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 + src/drivers/nl_sm2135eh.c + src/sys/nl_console_cmd.c + src/sys/nl_console.c + src/sys/nl_ota.c + src/sys/sdk_stubs.c + src/net/nl_names.c + 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_recovery.c + src/net/nl_mdns.c + src/net/nl_http_server.c + 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") +target_compile_definitions(${NL_FW} PRIVATE + NINLIGHT_DEVICE_ID="${NINLIGHT_DEVICE}" + NINLIGHT_TOKEN_DEFAULT="${NINLIGHT_TOKEN}" + NINLIGHT_WIFI_SSID="${NINLIGHT_WIFI_SSID}" + NINLIGHT_WIFI_PASSWORD="${NINLIGHT_WIFI_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 + -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 for ${NINLIGHT_DEVICE} (version regenerated at every build, see ${NL_VERSION_HEADER})") diff --git a/README.md b/README.md new file mode 100644 index 0000000..a89d0e7 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# 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 + +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 + 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 (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, auf Hardware noch ungetestet). + +Serielle Konsole (115200 8N1, lokales Echo): + + tools/monitor.sh /dev/cu. + +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 + + 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}'` +- 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. + +## 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`). 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 endet bei `last_ota_error = download`. Die Firmware kann einen +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): + +| Wert | Bedeutung | +|---|---| +| `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` | 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. + +## 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 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 +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. + +## Hardwaretests + +Manuelle Checkliste: `docs/hardware-checklist.md`. diff --git a/bruno/ninlight.yml b/bruno/ninlight.yml new file mode 100644 index 0000000..16e7e79 --- /dev/null +++ b/bruno/ninlight.yml @@ -0,0 +1,433 @@ +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: ota_url + value: http://192.168.1.12:8000/ninlight_evk_ota.img + - name: Onboarding-AP + variables: + - name: device_address + value: 192.168.4.1 + - name: device_port + 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 + 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 + Transition + 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 + }, + "transition": 1500 + } + settings: + encodeUrl: true + timeout: 0 + followRedirects: true + maxRedirects: 5 + - info: + name: Color Temp 2700 + Transition + type: http + seq: 6 + http: + method: POST + url: '{{device_address}}:{{device_port}}/api/v1/device/state' + body: + type: json + data: |- + { + "color_temp": 2700, + "transition": 1500 + } + 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: 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 + 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 + docs: |- + Enthält `last_ota_error`: `null`, solange kein Update fehlgeschlagen ist, sonst einer von + `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 + seq: 2 + http: + method: GET + url: '{{device_address}}:{{device_port}}/api/v1/system/settings' + settings: + encodeUrl: true + 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 + 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": "{{ota_url}}" + } + } + settings: + encodeUrl: true + 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 + value: '{{device_token}}' +bundled: true +extensions: + bruno: + ignore: + - node_modules + - .git + presets: + requestType: http + requestUrl: '{{device_address}}:{{device_port}}/api/v1/' 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..8e20c0a --- /dev/null +++ b/cmake/image.cmake @@ -0,0 +1,24 @@ +# 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_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..821de76 --- /dev/null +++ b/cmake/run_mkimage.cmake @@ -0,0 +1,11 @@ +file(READ ${VERSION_HEADER} 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) + message(FATAL_ERROR "nl_mkimage failed (${rc})") +endif() diff --git a/cmake/sdk.cmake b/cmake/sdk.cmake new file mode 100644 index 0000000..31189ab --- /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 ${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) + 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..e73907e --- /dev/null +++ b/cmake/version.cmake @@ -0,0 +1,9 @@ +# 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") diff --git a/cmake/write_version.cmake b/cmake/write_version.cmake new file mode 100644 index 0000000..4f81854 --- /dev/null +++ b/cmake/write_version.cmake @@ -0,0 +1,17 @@ +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) +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() + set(OLD "") +endif() +if(NOT OLD STREQUAL CONTENT) + file(WRITE ${OUT} "${CONTENT}") +endif() diff --git a/config.local.cmake.example b/config.local.cmake.example new file mode 100644 index 0000000..db9207b --- /dev/null +++ b/config.local.cmake.example @@ -0,0 +1,11 @@ +# Copy to config.local.cmake (gitignored) and adjust. +# set(NINLIGHT_TOKEN "change-me-please") +# 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") +# 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/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/docs/hardware-checklist.md b/docs/hardware-checklist.md new file mode 100644 index 0000000..d905123 --- /dev/null +++ b/docs/hardware-checklist.md @@ -0,0 +1,135 @@ +# Hardware-Checkliste (EVK) + +Konsole: `tools/monitor.sh /dev/cu.` (115200 8N1). Flashen: `tools/flash.sh evk `. + +## Licht (Plan 1, Task 11) +- [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) +- [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`) +- [ ] 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) + +## Netzwerk & REST (Plan 2) +- [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 +- [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) + +## OTA und Recovery (Plan 3) +- [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) +- [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* +- [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) +- [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) +- [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 +- [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. + +## Später (Lampen) +- [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: 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 + +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. + +## 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 +- [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) +- [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 +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) + +## 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`. +- [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. + +**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 +dynamisch neu verhandelt. Mit einem eigenen Netzteil: `throttled=0x0` auch unter Schreiblast. +Die zerschossene erste SD-Karte war die Folge, nicht die Ursache. diff --git a/sdk/Bin/secboot.img b/sdk/Bin/secboot.img new file mode 100644 index 0000000..6bd3fe6 Binary files /dev/null and b/sdk/Bin/secboot.img differ 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 0000000..21b081d Binary files /dev/null and b/sdk/Lib/GNU/libwlan.a differ diff --git a/sdk/PATCHES.md b/sdk/PATCHES.md new file mode 100644 index 0000000..45d928e --- /dev/null +++ b/sdk/PATCHES.md @@ -0,0 +1,128 @@ +# Änderungen am WinnerMicro W600 SDK + +Quelle: `qcx-w600-firmware/qconnex_sdk/ext/WM_SDK_WM600` (QConnex-Stand 2025). Kopiert wurden nur +Include, Platform (ohne Boot/armcc), Src/OS, Src/Network, Src/App/{OTA,httpclient,dhcpserver,dnsserver}, +Lib/GNU, Bin/secboot.img, Tools/GNU/link_w600_2m.ld (→ ld/) und wm_tool.c (→ tools/). + +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. 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. 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 + 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. + + 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 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 + 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). + + 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. + **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): + minimale Stubs in `src/sys/sdk_stubs.c` angelegt (leere Funktionskörper). In der alten Firmware + (`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). + **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. +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 `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()`. diff --git a/sdk/Platform/Boot/gcc/core_cm3.h b/sdk/Platform/Boot/gcc/core_cm3.h new file mode 100644 index 0000000..f266821 --- /dev/null +++ b/sdk/Platform/Boot/gcc/core_cm3.h @@ -0,0 +1,1627 @@ +/**************************************************************************//** + * @file core_cm3.h + * @brief CMSIS Cortex-M3 Core Peripheral Access Layer Header File + * @version V3.20 + * @date 25. February 2013 + * + * @note + * + ******************************************************************************/ +/* Copyright (c) 2009 - 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. + ---------------------------------------------------------------------------*/ + + +#if defined ( __ICCARM__ ) + #pragma system_include /* treat file as system include file for MISRA check */ +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +#ifndef __CORE_CM3_H_GENERIC +#define __CORE_CM3_H_GENERIC + +/** \page CMSIS_MISRA_Exceptions MISRA-C:2004 Compliance Exceptions + CMSIS violates the following MISRA-C:2004 rules: + + \li Required Rule 8.5, object/function definition in header file.
+ 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..f876dba --- /dev/null +++ b/sdk/Platform/Common/fwup/wm_fwup.c @@ -0,0 +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; + + /* 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; + } +} + 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..02837b4 --- /dev/null +++ b/sdk/Platform/Drivers/internalflash/wm_internal_fls.c @@ -0,0 +1,1430 @@ +/** + * @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" + +/* 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); +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); + 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); + 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); + FLS2_UNLOCK(); + + 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); + 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); + 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); + FLS2_UNLOCK(); + + + 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)) + { + 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); + 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); + FLS2_UNLOCK(); + 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; + 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); + 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); + FLS2_UNLOCK(); + } + //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) + { + 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); + 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); + FLS2_UNLOCK(); + } + + 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); + /* 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) + printf("wm_internal_fls: s_fls2_lock create failed\r\n"); + + 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..41b6fba --- /dev/null +++ b/sdk/Platform/Sys/wm_main.c @@ -0,0 +1,404 @@ +/***************************************************************************** +* +* 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 ) +{ + /* 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"); +#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..a6da5de --- /dev/null +++ b/sdk/Src/App/OTA/wm_http_fwup.c @@ -0,0 +1,287 @@ +#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}; + +/* 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 token[32]; + char headRange[32] = {0}; + int nRetCode = 0; + u32 content_length=0, size=32; + u32 partLen; + u32 totalLen = 0; + u32 recvLen = 0; + u32 retryRounds = 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 + { + /* 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; + } + } + + if(nRetCode != HTTP_CLIENT_SUCCESS && nRetCode != HTTP_CLIENT_EOS){ + 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; + 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){ + /* 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; + } + 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); + 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; + + 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 0000000..0a4fc7b Binary files /dev/null and b/sdk/Src/Network/lwip2.0.3/apps/httpd/fs/img/sics.gif differ 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 0000000..fe6f345 Binary files /dev/null and b/sdk/Src/Network/lwip2.0.3/apps/snmp/LwipMibCompiler/SharpSnmpLib/sharpsnmplib.snk differ 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..7b2f96f --- /dev/null +++ b/src/app_main.c @@ -0,0 +1,303 @@ +#include +#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" +#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" +#include "sys/nl_console.h" +#include "sys/nl_ota.h" + +#include "nl_version.h" + +#define NL_LIGHT_PRIO 30 +#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; +static volatile bool s_counter_cleared; +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); } + +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); +} + +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(); + 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_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(); + 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"); + 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 */ + store_lock(); + nl_settings_erase(); + 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(); +} + +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) +{ + const nl_board_t *board = nl_board_get(); + uint32_t base = nl_flash_region_base(); + nl_engine_cfg_t cfg; + nl_light_state_t last, initial, pending; + 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(); + 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(); + 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(); + (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"); + + 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, + after_ota); + nl_app_lock(); + nl_engine_init(&cfg); + 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(); + + 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_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 */ + + { + 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); + } + + nl_http_server_start(nl_app_api_ctx()); + + light_seen = s_light_beat; + net_seen = nl_net_beat; + http_seen = nl_http_beat; + for (;;) { + uint32_t now; + bool due; + + tls_os_time_delay(HZ / 10); /* 100 ms */ + now = nl_app_now_ms(); + + 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 */ + + nl_app_lock(); + due = nl_ctrl_persist_due(now, &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(); + } + } + + /* 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(); + } + } +} + +void UserMain(void) +{ + printf("\r\nninlight %s for %s (built %s)\r\n", NINLIGHT_FW_VERSION, NINLIGHT_DEVICE_ID, NINLIGHT_BUILD_DATE); + if (nl_board_get() == NULL) { + printf("FATAL: unknown board %s\r\n", NINLIGHT_DEVICE_ID); + return; + } + if (tls_os_mutex_create(0, &s_lock) != TLS_OS_SUCCESS) { + 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/board/nl_board.c b/src/board/nl_board.c new file mode 100644 index 0000000..526cd2a --- /dev/null +++ b/src/board/nl_board.c @@ -0,0 +1,51 @@ +#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, 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, 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, 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, 2000, 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/src/config/nl_flash.h b/src/config/nl_flash.h new file mode 100644 index 0000000..0b9e324 --- /dev/null +++ b/src/config/nl_flash.h @@ -0,0 +1,19 @@ +#ifndef NL_FLASH_H +#define NL_FLASH_H + +#include + +#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. */ +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_flash_w600.c b/src/config/nl_flash_w600.c new file mode 100644 index 0000000..ef0a79e --- /dev/null +++ b/src/config/nl_flash_w600.c @@ -0,0 +1,85 @@ +#include "wm_include.h" +#include "wm_flash_map.h" +#include "wm_internal_flash.h" + +#include "config/nl_flash.h" + +/* + * 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 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 + * 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; + +void nl_flash_init(void) +{ + tls_os_mutex_create(0, &s_flash_lock); +} + +int nl_flash_read(uint32_t addr, void *buf, uint32_t len) +{ + int r; + + 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; + if (s_flash_lock) + tls_os_mutex_release(s_flash_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. */ + int r; + + 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; + if (s_flash_lock) + tls_os_mutex_release(s_flash_lock); + + return r; +} + +int nl_flash_erase_sector(uint32_t addr) +{ + int r; + + if (addr % NL_FLASH_SECTOR_SIZE) + return -1; + + 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; + if (s_flash_lock) + tls_os_mutex_release(s_flash_lock); + + return r; +} + +uint32_t nl_flash_region_base(void) +{ + /* 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; +} 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/src/config/nl_settings.c b/src/config/nl_settings.c new file mode 100644 index 0000000..ec97167 --- /dev/null +++ b/src/config/nl_settings.c @@ -0,0 +1,158 @@ +#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; /* 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) +{ + 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; + s->transition_ms = 300; + s->wake_on_change = 0; +} + +void nl_settings_init(uint32_t addr_a, uint32_t addr_b) +{ + s_addr[0] = addr_a; + s_addr[1] = addr_b; +} + +/* 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; + uint32_t crc, stored_crc; + + if (nl_flash_read(s_addr[slot], &hdr, sizeof hdr) != 0) + return false; + if (hdr.magic != NL_SETTINGS_MAGIC || hdr.version != NL_SETTINGS_VERSION || + hdr.length > sizeof(nl_settings_t)) + return false; + + 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] + NL_SETTINGS_CRC_OFF(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) +{ + 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, crc, crc_off; + 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; + /* 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, crc_off + sizeof crc); +} + +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..1941be8 --- /dev/null +++ b/src/config/nl_settings.h @@ -0,0 +1,40 @@ +#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; + 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); +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/src/config/nl_state_log.c b/src/config/nl_state_log.c new file mode 100644 index 0000000..b75adfd --- /dev/null +++ b/src/config/nl_state_log.c @@ -0,0 +1,157 @@ +#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]); + 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/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/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..50081d2 --- /dev/null +++ b/src/drivers/nl_sm2135eh.c @@ -0,0 +1,111 @@ +#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 */ +#define SM_CHANNEL_STBY 0x20u /* standby bit 5: powers the outputs down */ + +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) { + /* 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); + /* 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 + 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, 6); + sm_send(d, 3); + } +} diff --git a/src/light/nl_boot_state.c b/src/light/nl_boot_state.c new file mode 100644 index 0000000..1141cf9 --- /dev/null +++ b/src/light/nl_boot_state.c @@ -0,0 +1,51 @@ +#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, + bool after_ota) +{ + 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); + + /* 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) + 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..6e89019 --- /dev/null +++ b/src/light/nl_boot_state.h @@ -0,0 +1,21 @@ +#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. + * 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); + +#endif 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_control.c b/src/light/nl_control.c new file mode 100644 index 0000000..e88dc08 --- /dev/null +++ b/src/light/nl_control.c @@ -0,0 +1,111 @@ +#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 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, + 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_options(fade_in_ms, fade_out_ms, transition_ms, 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) +{ + 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) +{ + 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; + } + + /* 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) + dur = s_fade_in; + else if (s_state.on && !n.on) + dur = s_fade_out; + else + dur = s_transition; + + 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..50c202d --- /dev/null +++ b/src/light/nl_control.h @@ -0,0 +1,56 @@ +#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. + * + * 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); + +/* 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/src/light/nl_engine.c b/src/light/nl_engine.c new file mode 100644 index 0000000..039f89a --- /dev/null +++ b/src/light/nl_engine.c @@ -0,0 +1,182 @@ +#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) +{ + if (s_nseg >= sizeof s_seg / sizeof s_seg[0]) + return; + 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) +{ + if (count > NL_BLINK_MAX) + count = NL_BLINK_MAX; + 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..6a62cc4 --- /dev/null +++ b/src/light/nl_engine.h @@ -0,0 +1,32 @@ +#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_BLINK_MAX 100u +#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/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/src/net/nl_api.c b/src/net/nl_api.c new file mode 100644 index 0000000..a6ef00c --- /dev/null +++ b/src/net/nl_api.c @@ -0,0 +1,428 @@ +#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); + double d; + if (!v) + return 0; + if (!cJSON_IsNumber(v)) + return -1; + 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; +} + +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); + /* 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; + 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; + } + 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)) { + 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); + 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); + /* "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); +} + +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 (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"); + 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) { + /* fw_update() itself can genuinely fail now (busy, or a rejected URL) - 409 with the + * reason, not a blanket "not implemented". */ + /* 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) { + 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); + /* 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) + 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/src/net/nl_http.c b/src/net/nl_http.c new file mode 100644 index 0000000..af92669 --- /dev/null +++ b/src/net/nl_http.c @@ -0,0 +1,130 @@ +#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) { + 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'; + } + } + + 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..a58d523 --- /dev/null +++ b/src/net/nl_http.h @@ -0,0 +1,32 @@ +#ifndef NL_HTTP_H +#define NL_HTTP_H + +#include +#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 */ + 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; + +/* 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_http_server.c b/src/net/nl_http_server.c new file mode 100644 index 0000000..9eb7020 --- /dev/null +++ b/src/net/nl_http_server.c @@ -0,0 +1,140 @@ +#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; + +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) { + 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); +} + +#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_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) { + 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; + 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); + /* 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; + } + { + /* 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(); + } + } + } +} + +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..32b5f84 --- /dev/null +++ b/src/net/nl_http_server.h @@ -0,0 +1,18 @@ +#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); + +#endif diff --git a/src/net/nl_mdns.c b/src/net/nl_mdns.c new file mode 100644 index 0000000..73ae544 --- /dev/null +++ b/src/net/nl_mdns.c @@ -0,0 +1,94 @@ +#include +#include + +#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" + +#include "nl_version.h" + +#define NL_MDNS_TTL 120 + +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]; + +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(); + bool attempted = false; + + (void)arg; + if (!nif) + return; + + if (!s_inited) { + mdns_resp_init(); + 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) +{ + 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_service_added) + tcpip_callback(announce, NULL); +} 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_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/src/net/nl_recovery.c b/src/net/nl_recovery.c new file mode 100644 index 0000000..f691665 --- /dev/null +++ b/src/net/nl_recovery.c @@ -0,0 +1,175 @@ +#include "net/nl_recovery.h" + +#include +#include + +#include "wm_include.h" +#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 +/* 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: 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; + +/* 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; + int scan_rc; + + if (buf == NULL) + return false; + s_scan_done = false; + tls_wifi_scan_result_cb_register(scan_done_cb); + + /* 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; + } + + 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 + 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]; + + /* 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"); + return; + } + snprintf(url, sizeof url, "http://192.168.8.1/ninlight_%s_ota.img", NINLIGHT_DEVICE_ID); + printf("recovery: fetching %s\r\n", url); + /* 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; + } + /* 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) { + /* 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/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 new file mode 100644 index 0000000..7704815 --- /dev/null +++ b/src/net/nl_wifi.h @@ -0,0 +1,44 @@ +#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; + +/* 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); +/* 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 + * 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). */ +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_sm.c b/src/net/nl_wifi_sm.c new file mode 100644 index 0000000..373d80a --- /dev/null +++ b/src/net/nl_wifi_sm.c @@ -0,0 +1,202 @@ +#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 */ + sm->awaiting = true; + sm->connect_at = now; + } else { + sm->state = NL_WIFI_ST_AP_ONBOARDING; + a->disconnect = true; /* I1: no stored creds -> drop the stray station attempt */ + } +} + +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; + sm->awaiting = true; + sm->connect_at = now; + } 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: + sm->awaiting = false; + 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: + if (ev == NL_WIFI_EV_CONNECTED) + a.disconnect = true; /* I1: stray success of a timed-out/failed attempt */ + break; + 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->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); + 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.disconnect = true; /* M1: drop any stale station attempt so its events don't count */ + 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..5db82f1 --- /dev/null +++ b/src/net/nl_wifi_sm.h @@ -0,0 +1,63 @@ +#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 +#define NL_WIFI_CONNECT_TIMEOUT_MS 30000u /* no event after a connect -> treat as failed */ + +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 disconnect; /* drop a stray/failed station attempt (before any connect in the same set) */ + 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; + 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); +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/src/net/nl_wifi_w600.c b/src/net/nl_wifi_w600.c new file mode 100644 index 0000000..acf0493 --- /dev/null +++ b/src/net/nl_wifi_w600.c @@ -0,0 +1,402 @@ +#include +#include + +#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 */ + +#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 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); } + +static void copy(char *dst, const char *src, size_t size) +{ + strncpy(dst, src, size - 1); + 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, FALSE); + 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); + } +} + +/* 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; + 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"); + /* 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); + 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) +{ + 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(); + 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); +} + +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 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) + 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); + 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)) { + 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); + /* 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; + /* 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(); + } + + if (m & MSG_NETIF) + printf("wifi: netif event %u\r\n", (unsigned)(m & 0xFFu)); + execute(ev); + execute(tick); + 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(); + } + nl_net_beat++; + } +} + +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); + 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. */ + 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. */ + 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 */ + + 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) +{ + bool busy; + + if (!s_started) + return -1; + lock(); + 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; +} + +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); + /* 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); + 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); } + +/* --- 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; + + /* 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); + 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; + } + /* 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 new file mode 100644 index 0000000..df355e7 --- /dev/null +++ b/src/sys/nl_app.h @@ -0,0 +1,37 @@ +#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); +/* 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 + * 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); + +/* 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_app_api.c b/src/sys/nl_app_api.c new file mode 100644 index 0000000..1fdc7f9 --- /dev/null +++ b/src/sys/nl_app_api.c @@ -0,0 +1,130 @@ +#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" +#include "sys/nl_ota.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); } + +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_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) + 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 = nl_ota_last_error(); +} + +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) { return nl_ota_start(url); } + +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 diff --git a/src/sys/nl_console.c b/src/sys/nl_console.c new file mode 100644 index 0000000..c3fc874 --- /dev/null +++ b/src/sys/nl_console.c @@ -0,0 +1,167 @@ +#include +#include + +#include "wm_include.h" +#include "wm_watchdog.h" + +#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" + +#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_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(); + 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_request_factory_reset(); + return; + 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, + * so only the watchdog reset can recover the chip */ + 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 diff --git a/src/sys/nl_console_cmd.c b/src/sys/nl_console_cmd.c new file mode 100644 index 0000000..fcee502 --- /dev/null +++ b/src/sys/nl_console_cmd.c @@ -0,0 +1,121 @@ +#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 | net | factory | reboot | wdtest | 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}, {"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)) { + 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..0684d1c --- /dev/null +++ b/src/sys/nl_console_cmd.h @@ -0,0 +1,36 @@ +#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_WDTEST, + NL_CMD_NET, + 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/src/sys/nl_fwup_hook.h b/src/sys/nl_fwup_hook.h new file mode 100644 index 0000000..6d02275 --- /dev/null +++ b/src/sys/nl_fwup_hook.h @@ -0,0 +1,13 @@ +#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. + * 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..39b4851 --- /dev/null +++ b/src/sys/nl_ota.c @@ -0,0 +1,181 @@ +#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" + +#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); + +#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 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 */ + +void nl_ota_init(void) +{ + s_busy = false; + s_error = NULL; +} + +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; } + +/* 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. + 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) { + s_error = nl_trailer_err_name(e); + 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); + /* 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); + } + printf("ota: trailer ok\r\n"); + return true; +} + +/* 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) +{ + int rc; + + (void)arg; + log_url(s_url); + rc = t_http_fwup(s_url); + /* 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(); + 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); +} + +static int ota_start(const char *url, bool reject_running_version) +{ + size_t n; + + if (url == NULL || strncmp(url, "http://", 7) != 0) + return -1; + n = strlen(url); + if (n <= 7 || n >= sizeof s_url) /* n == 7 is bare "http://" with no host */ + return -1; + if (s_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; + s_error = NULL; + s_skipped = false; + 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. */ + 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) { + /* 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; + /* 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 new file mode 100644 index 0000000..603297e --- /dev/null +++ b/src/sys/nl_ota.h @@ -0,0 +1,22 @@ +#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); +/* 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); +/* 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); + +#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/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_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/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..1c4a5c8 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,51 @@ +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() + +# 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) + +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) +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) +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) +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) +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/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/fake_flash.c b/test/fake_flash.c new file mode 100644 index 0000000..a827f76 --- /dev/null +++ b/test/fake_flash.c @@ -0,0 +1,64 @@ +#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; +} + +void nl_flash_init(void) +{ +} + +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_api.c b/test/test_api.c new file mode 100644 index 0000000..ca25feb --- /dev/null +++ b/test/test_api.c @@ -0,0 +1,411 @@ +#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); + 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); + 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"}, + {"{\"a\":[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]}", "invalid json"}, /* C1: 20 levels > CJSON_NESTING_LIMIT */ + {"{\"on\":1}", "invalid on"}, + {"{\"brightness\":0}", "invalid brightness"}, + {"{\"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"}, + {"{\"transition\":60001}", "invalid transition"}, + {"{\"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); + 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_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; + 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); +} + +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) +{ + 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_NULL(cJSON_GetObjectItem(doc, "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_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; + call(NL_HTTP_GET, "/api/v1/system/settings", NULL, NULL); + 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\"}"); + 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()); + /* 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); + 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(409, resp.status); + 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\"}}"); + 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_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); + 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); + return UNITY_END(); +} diff --git a/test/test_board.c b/test/test_board.c new file mode 100644 index 0000000..939db9b --- /dev/null +++ b/test/test_board.c @@ -0,0 +1,119 @@ +#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; + /* 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_MESSAGE(g95 ? 2000 : 2700, b->ct_min, b->id); + TEST_ASSERT_EQUAL_UINT16_MESSAGE(g95 ? 5000 : 6500, b->ct_max, b->id); + } +} + +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(); +} diff --git a/test/test_boot_state.c b/test/test_boot_state.c new file mode 100644 index 0000000..264b61b --- /dev/null +++ b/test/test_boot_state.c @@ -0,0 +1,123 @@ +#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"), 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, false); + 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"), false); + 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"), false); + TEST_ASSERT_FALSE(s.on); + TEST_ASSERT_EQUAL_UINT8(40, s.brightness); +} + +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, 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 */ + /* 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 + * 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) +{ + 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, 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(b->ct_max, 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(); + 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); + 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(); +} 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(); +} diff --git a/test/test_console_cmd.c b/test/test_console_cmd.c new file mode 100644 index 0000000..d744ab8 --- /dev/null +++ b/test/test_console_cmd.c @@ -0,0 +1,102 @@ +#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_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) +{ + 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(); +} diff --git a/test/test_control.c b/test/test_control.c new file mode 100644 index 0000000..4bfbd28 --- /dev/null +++ b/test/test_control.c @@ -0,0 +1,325 @@ +#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_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, 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; + 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, 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(); + 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); + 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_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_engine.c b/test/test_engine.c new file mode 100644 index 0000000..84963a0 --- /dev/null +++ b/test/test_engine.c @@ -0,0 +1,201 @@ +#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()); +} + +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(); + 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); + RUN_TEST(test_blink_max_clamped); + return UNITY_END(); +} 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(); +} diff --git a/test/test_http.c b/test/test_http.c new file mode 100644 index 0000000..05f1863 --- /dev/null +++ b/test/test_http.c @@ -0,0 +1,135 @@ +#include +#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); + 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) +{ + 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_auth_too_long_sets_flag); + 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/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(); +} diff --git a/test/test_settings.c b/test/test_settings.c new file mode 100644 index 0000000..37d6cc6 --- /dev/null +++ b/test/test_settings.c @@ -0,0 +1,200 @@ +#include +#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" +#include "util/nl_crc.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); + TEST_ASSERT_EQUAL_UINT16(300, s.transition_ms); + TEST_ASSERT_EQUAL_UINT8(0, s.wake_on_change); +} + +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_load_accepts_shorter_record(void) +{ + nl_settings_t in, out; + uint32_t crc; + /* 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)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); + } + 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); + 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) +{ + nl_settings_t in = sample("x"), out; + uint16_t longer = (uint16_t)(sizeof(nl_settings_t) + 1); + 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 */ + 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); +} + +/* 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; + 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_transition_and_wake_roundtrip); + 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_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(); +} 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/test/test_state_log.c b/test/test_state_log.c new file mode 100644 index 0000000..9e3a57a --- /dev/null +++ b/test/test_state_log.c @@ -0,0 +1,157 @@ +#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)); +} + +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(); + 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); + RUN_TEST(test_erase_failure_rescans_flash); + return UNITY_END(); +} diff --git a/test/test_wifi_sm.c b/test/test_wifi_sm.c new file mode 100644 index 0000000..57a48d4 --- /dev/null +++ b/test/test_wifi_sm.c @@ -0,0 +1,222 @@ +#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_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)); + 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); + 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) +{ + 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_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)); + 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(); + 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_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(); +} 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 diff --git a/tools/build_all.sh b/tools/build_all.sh new file mode 100755 index 0000000..a74cf56 --- /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 "$OUT"/*/ninlight_*_ota.img 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/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..a7c8ec7 --- /dev/null +++ b/tools/flash.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# 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 [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 + mkdir -p "$(dirname "$TOOL")" + cc -O2 -w -o "$TOOL" "$ROOT/sdk/tools/wm_tool.c" -lpthread +fi +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 diff --git a/tools/mkimage/nl_mkimage.py b/tools/mkimage/nl_mkimage.py new file mode 100755 index 0000000..9a7583b --- /dev/null +++ b/tools/mkimage/nl_mkimage.py @@ -0,0 +1,117 @@ +#!/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. + +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 +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)") + 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.""" + 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 + 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 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}'", + 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", 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: + # 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") + + 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") + + 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/nl_sign.py b/tools/mkimage/nl_sign.py new file mode 100644 index 0000000..3a2681d --- /dev/null +++ b/tools/mkimage/nl_sign.py @@ -0,0 +1,227 @@ +#!/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: + 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}") + 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: + 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 + 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: + 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}") + digest = SHA256.new(data) + return pkcs1_15.new(key).sign(digest) + + def verify(public_der: bytes, data: bytes, signature: bytes) -> bool: + 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) + 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/pyproject.toml b/tools/mkimage/pyproject.toml new file mode 100644 index 0000000..4c3155f --- /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", "vermin>=1.6"] diff --git a/tools/mkimage/test_nl_mkimage.py b/tools/mkimage/test_nl_mkimage.py new file mode 100644 index 0000000..0d51b8a --- /dev/null +++ b/tools/mkimage/test_nl_mkimage.py @@ -0,0 +1,278 @@ +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 + + +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 + + +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 + + +# --- 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/tests/test_nl_sign.py b/tools/mkimage/tests/test_nl_sign.py new file mode 100644 index 0000000..4a441d8 --- /dev/null +++ b/tools/mkimage/tests/test_nl_sign.py @@ -0,0 +1,132 @@ +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) + + +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() diff --git a/tools/mkimage/tests/test_suite_coverage.py b/tools/mkimage/tests/test_suite_coverage.py new file mode 100644 index 0000000..3908551 --- /dev/null +++ b/tools/mkimage/tests/test_suite_coverage.py @@ -0,0 +1,142 @@ +"""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(suffix=".py"): + return sorted( + p.resolve() + 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 = [] + 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_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. + """ + 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 + ] + assert not 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) + ) + ) + + +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)" + ) + + +# 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/mkimage/uv.lock b/tools/mkimage/uv.lock new file mode 100644 index 0000000..8d0546a --- /dev/null +++ b/tools/mkimage/uv.lock @@ -0,0 +1,92 @@ +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" }, + { name = "vermin" }, +] + +[package.metadata] + +[package.metadata.requires-dev] +dev = [ + { name = "pytest", specifier = ">=8" }, + { name = "vermin", specifier = ">=1.6" }, +] + +[[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" }, +] + +[[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" }, +] 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 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/station/README.md b/tools/station/README.md new file mode 100644 index 0000000..5670bd8 --- /dev/null +++ b/tools/station/README.md @@ -0,0 +1,154 @@ +# 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. + +## 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 + 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**. +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. + +**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 +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`. `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. + +## 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. + +## 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/button/button.py b/tools/station/button/button.py new file mode 100644 index 0000000..6c67957 --- /dev/null +++ b/tools/station/button/button.py @@ -0,0 +1,673 @@ +#!/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: 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) - +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") +# 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. +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). + + `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, 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) + + +def action_shutdown() -> Action: + return Action("Herunterfahren", kind="shutdown", confirm=True, + 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_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.""" + 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") + imageset = state.get("imageset") or IMAGESET_DEFAULT + + if not running: + return [ + Action("Rettung starten", ("recovery", "on"), done_text="Rettung laeuft"), + # 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(), + # Der beste Zeitpunkt zum Umschalten ist VOR dem Einschalten - deshalb steht + # der Eintrag auch (und gerade) hier. + action_imageset(imageset), + 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")) + 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")) + 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 _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 + 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, + "imageset": _read_imageset(imageset_path)} + + +# --- 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: + # 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: + 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/deploy.sh b/tools/station/deploy.sh new file mode 100755 index 0000000..f7ca8cd --- /dev/null +++ b/tools/station/deploy.sh @@ -0,0 +1,539 @@ +#!/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" +# /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 + # 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 + # 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 + +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} + # 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" + 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). +# ${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 +# 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} + +# 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 + echo "Hinweis: $gz fehlt - $target fehlt im Bildsatz '$stage_sub'" >&2 + complete=0 + continue + fi + 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 +import sys +from datetime import datetime, timezone + +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, + "file": name, + "bytes": len(data), + "sha256": hashlib.sha256(data).hexdigest(), + "ninlight_version": version_map.get(target, ""), + }) + +manifest = { + "built": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "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") +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 +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 +# 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. +# -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" "$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 +# 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" "$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} +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 + # Vollabgleich: alle Images ausserhalb der aktuellen Auslieferung entfernen. + # 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 + # 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-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-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). +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/$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 + # 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/$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 + +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 = {} +# 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"), + "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") +MERGE + fi + fi +else + # 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 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" +sudo install -m 0755 /tmp/ninlight-station "$BIN_PATH" +sudo install -m 0644 /tmp/ninlight-station.service "$UNIT_PATH" +# 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" +# 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" +# 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-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 + 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 +# 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 +# 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/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..dd4f66c --- /dev/null +++ b/tools/station/display/display.py @@ -0,0 +1,689 @@ +#!/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 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 + +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") +# 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")) +# 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 +# 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 +# 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 + 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, 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: + 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 + if line.startswith("export ") or line.startswith("export\t"): + line = line[len("export"):].lstrip() + 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_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 + 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" + + +# 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], + 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. + 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 _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, + 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 + 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. + + 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", "imageset": imageset, "imageset_ok": imageset_ok} + + mode = _read_mode_file(run_dir) + status = _read_json(run_dir / "status.json") + 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") + else: # migration + state["ssid"] = conf.get("MIGRATE_SSID") + state["last_seen"] = status.get("last_seen") + 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") + + # 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 + + +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 + 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 + + +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), 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() (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 + 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 _full_init(self) -> None: + if self.revision == "V2": + self.epd.init(self.epd.FULL_UPDATE) + 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() + # 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: + buf = self.epd.getbuffer(image) + self._full_init() + self.epd.displayPartBaseImage(buf) + self._awake = True + + 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 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, + # 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 + + +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 | 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 + 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 + + @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.""" + 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) -> 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 + + +def _test_image(revision: str): + from PIL import Image, ImageDraw, ImageFont + + 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) + return 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_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() + panel.sleep() + log.info("Panel geleert (EPD_REVISION=%s)", revision) + + +def run_daemon(revision: str, clock=time, panel: "Panel | None" = None, + 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. + + 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: + 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") + + 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() + planner.record_sleep() + + clock.sleep(UI_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") + 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") + + try: + if args.test: + 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: + # 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: + 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..93f9ade --- /dev/null +++ b/tools/station/display/epdconfig.py @@ -0,0 +1,210 @@ +"""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 + +# 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 + 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 _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. + + 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") + + 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)?") + + hardware_errors = _hardware_error_types() + + 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 hardware_errors as e: + module_exit() + raise DisplayUnavailable( + f"GPIO/SPI-Initialisierung fehlgeschlagen: {e}{_busy_hint(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..6c1bdac --- /dev/null +++ b/tools/station/display/render.py @@ -0,0 +1,717 @@ +"""Textzerlegung und Bildaufbau fuer die E-Paper-Anzeige der ninlight-Station. + +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. + +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 +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 +/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 + +from typing import Callable + +PANEL_WIDTH_PX = 250 +PANEL_HEIGHT_PX = 122 +TEXT_MARGIN_PX = 2 + +# --- 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. +# 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). +# Rettung/aus kennen keine Phase - dort bleibt die Kopfzeile +# 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). +# 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 +# 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 + +# 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: ") +# 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). +# +# 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 +# (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 (es zieht zusaetzlich eine Beiwerkzeile mit dem +# Zielnamen nach sich), deshalb hier nicht gelistet. +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 +# 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: " + +# 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. +# 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": "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", +} + +# 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 = { + # "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."]), +} + +# 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 + aelteren Serverfassung ohne Zeitstempel) -> "--:--".""" + if isinstance(ts, str) and len(ts) >= 16 and ts[10:11] == "T": + return ts[11:16] + return "--:--" + + +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 {} + + # 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]] = [] + + 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. + footer = _imageset_blocks(state) + + 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). Genau deshalb ROLE_BIG: die Zeile ist der ganze Inhalt. + out.append(("Station aus", ROLE_BIG)) + 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 + footer + + out.append((MODE_LABELS[mode], ROLE_HEADER)) + + ssid = state.get("ssid") + + if mode == "recovery": + # 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: + # 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. + # + # 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") + armed_target = None + if phase == "armed": + # 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)) + + 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)) + + 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") + if target or variant: + 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)) + 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)) + + # 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)) + + 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 '?'}", ROLE_DETAIL)) + + 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_LABELS[name] + return [(label, ROLE_FOOTER)] + + +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 + 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. + + 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 + + +# 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. 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 + 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.""" + # 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: + font = image_font.truetype(path, size_px) + break + except (OSError, ImportError): + continue + 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, + 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) + 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((visual_x - bbox[0], y), text, font=font, fill=fill) + return ink_width + + +def compose(state: dict): + """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) + + # 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), + ROLE_FOOTER: (FOOTER_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: + # 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). 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 blocks() selbst kontrolliert - ein Zeilenumbruch dort waere ohnehin + # nur ein zerstoertes Layout, kein sinnvoller Inhalt. + text = raw_text.replace("\n", " ") + return _truncate_to_width( + text, measure=lambda t: draw.textlength(t, font=font), + 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 + + # 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) + + if role == ROLE_BIG: + # Einziger Block dieses Zustands (siehe blocks()) - er bekommt das ganze + # Panel. + text = fit(raw_text, font) + # 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 + + if role == ROLE_HEADER: + text = fit(raw_text, font) + draw.rectangle((0, 0, PANEL_WIDTH_PX - 1, HEADER_BAR_HEIGHT_PX - 1), fill=0) + 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 > 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. + break + draw.text((TEXT_MARGIN_PX, y), text, font=font, fill=0) + y += line_height + + return image 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/ninlight-display.service b/tools/station/ninlight-display.service new file mode 100644 index 0000000..d42a1ad --- /dev/null +++ b/tools/station/ninlight-display.service @@ -0,0 +1,109 @@ +[Unit] +Description=ninlight E-Paper-Anzeige (Status der Migrations-/Rettungsstation) +# 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 +# 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 +# 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 +# "-"-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 +# 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, +# 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 + +NoNewPrivileges=yes +ProtectSystem=strict +ProtectHome=yes +PrivateTmp=yes +# 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 + +# 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/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 new file mode 100755 index 0000000..1273c02 --- /dev/null +++ b/tools/station/ninlight-station @@ -0,0 +1,1194 @@ +#!/bin/sh +# 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). 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 + +# 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} + +# --- 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 +# 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" +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 \ + 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 + } +done +unset _key + +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 --escape no -f NAME con show | grep -Fqx "$1" || { + echo "Fehler: NetworkManager-Profil '$1' fehlt - tools/station/setup_pi.sh ausfuehren" >&2 + exit 1 + } +} + +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 "$(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) +# 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 + 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. + # --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 + fi + return 0 + fi + sudo nmcli con up "$_target" >/dev/null + # --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 + 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 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 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() { + 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 tee "$_tmp" >/dev/null && sudo chmod 0644 "$_tmp" || return 1 + 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. +# +# $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"). + sudo systemctl reset-failed ninlight-station 2>/dev/null || true + 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" >/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 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 + # 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" >/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 +} + +# 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 + ap_switch "$AP_CONNECTION" + write_mode "recovery" + 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 + # 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." + autooff_arm_if_requested +} + +migrate_on() { + # Argumente: "--listen" und/oder "--for ", beide optional und in beliebiger + # Reihenfolge. Beides wird VOLLSTAENDIG geprueft, bevor irgendein Profil hochkommt. + _phase=auto + _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" + _set=$(imageset_get) + _setdir=$(imageset_dir "$_set") + n=$(migrate_images | wc -l | tr -d ' ') + [ "$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" + 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" "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 +} + +# 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() { + # "$@", 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 +# 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". +# +# 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 + _was_active=1 + 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=$? + + trap - INT TERM HUP + display_service_resume + return "$_rc" +} + +# 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. +# +# 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 + # 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 +} + +# --- 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 + # 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 + # 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 --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 + 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 +} + +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" +} + +# "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 - + # 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. + _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/Erkannt unten und fuer + # Phase/control.json weiter 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(seen) +PY +) + _modus=$(printf '%s\n' "$_info" | sed -n '1p') + _erkannt=$(printf '%s\n' "$_info" | sed -n '2p') + elif [ -e "$_status_file" ]; then + _modus="unbekannt (Datei nicht lesbar)" + _erkannt="unbekannt (Datei nicht lesbar)" + else + _modus="-" + _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. + # + # 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 + +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" + imageset_status_lines + 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 --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 + 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("ascii", "replace") if t[:4] == b"NINL" else "kein Trailer") +PY +)" + done + 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 + # "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 +} + +display_cmd() { + case "${1:-}" in + 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 +} + +usage() { + 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 + 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 + 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 " 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 + 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:-} +[ $# -gt 0 ] && shift || true +case "$_cmd" in + on) + recovery_on "$@" + ;; + recovery) + _sub=${1:-} + [ $# -gt 0 ] && shift || true + case "$_sub" in + on) recovery_on "$@" ;; + off) full_off ;; + *) usage; exit 2 ;; + esac + ;; + migrate) + _sub=${1:-} + [ $# -gt 0 ] && shift || true + case "$_sub" in + on) migrate_on "$@" ;; + off) full_off ;; + *) usage; exit 2 ;; + esac + ;; + arm) + arm_cmd "${1:-}" + ;; + listen) + listen_cmd + ;; + bildsatz) + bildsatz_cmd "${1:-}" + ;; + 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 "${1:-}" "${2:-}" + ;; + off) + full_off + ;; + status) + status_cmd + ;; + *) + usage + exit 2 + ;; +esac diff --git a/tools/station/ninlight-station.service b/tools/station/ninlight-station.service new file mode 100644 index 0000000..6c32d19 --- /dev/null +++ b/tools/station/ninlight-station.service @@ -0,0 +1,52 @@ +[Unit] +Description=ninlight recovery station (HTTP image server) +# 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 +# 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 +EnvironmentFile=/etc/ninlight-station.conf +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 + +# 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 +# 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 + +[Install] +WantedBy=multi-user.target diff --git a/tools/station/products.py b/tools/station/products.py new file mode 100644 index 0000000..110d516 --- /dev/null +++ b/tools/station/products.py @@ -0,0 +1,112 @@ +"""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). + +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 name") +ParsedRequest = namedtuple( + "ParsedRequest", "product_id lamp_version product variant variant_source") + +# (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"), + ("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$") +# "-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): + """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: + 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", "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. .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 + variant, source = classify_variant(pid, version) + return ParsedRequest(pid, version, PRODUCTS.get(pid), variant, source) 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..2ec9fd1 --- /dev/null +++ b/tools/station/server.py @@ -0,0 +1,681 @@ +#!/usr/bin/env python3 +"""ninlight recovery/migration station. + +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. +""" +from __future__ import annotations + +import argparse +import contextlib +import json +import os +import re +import select +import socket +import sys +import tempfile +import threading +import time +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 +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") + + +# --- 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, + 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 + 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 + # 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 + # 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 + # 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 + # 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/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 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.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 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 + # 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): + data = {} + if not isinstance(data, dict): + data = {} + self._manifest_cache = data + self._manifest_mtime = mtime + 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" - + 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: + 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. + + 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": + 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 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, + 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 + 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, + "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, "variant_source": variant_source, + "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, "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 + 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-") + 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) + + +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_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 + + 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() + 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") + 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 + + 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, 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, + "unknown") + self.reject(404, "unknown product") + self.log(404, 0, started) + return + + # 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 + # zweite gleichzeitige Anfrage hindurchschluepfen, siehe die Methode selbst. + phase, armed_target, allowed = self.station.claim_delivery(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, + version, 0, "listen", variant_source) + self.reject(404, "listening only") + self.log(404, 0, started) + 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, + # 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", variant_source) + 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 == "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, variant_source) + + 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", "") + if rng.startswith("bytes="): + first = rng.split("=", 1)[1].split("-", 1)[0] + if first.isdigit() and int(first) < size: + start = int(first) + + 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" + remaining = size - start + client_ip = self.client_address[0] + try: + with path.open("rb") as f: + f.seek(start) + while sent < remaining: + read_size = min(CHUNK, remaining - sent) + if self.station.stall_after is not None: + # 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: + # 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) + except (BrokenPipeError, ConnectionResetError, TimeoutError): + status = "aborted" + self.log(206 if start else 200, sent, started) + 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/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("--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) + + if not a.image_dir.is_dir(): + print(f"error: image directory {a.image_dir} does not exist", file=sys.stderr) + 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, + 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: + httpd.serve_forever() + except KeyboardInterrupt: + pass + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/station/setup_pi.sh b/tools/station/setup_pi.sh new file mode 100755 index 0000000..b568be0 --- /dev/null +++ b/tools/station/setup_pi.sh @@ -0,0 +1,583 @@ +#!/usr/bin/env bash +# 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 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} +# 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' +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:$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=${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 + # 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 + # 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 + ADDED="$ADDED $KEY" + fi + fi + done + if [ -n "$ADDED" ]; then + echo " $CONF: fehlende Schluessel ergaenzt:$ADDED (vorhandene Werte unveraendert)" + else + echo " $CONF existiert bereits, alle Schluessel vorhanden" + fi +else + sudo install -m 0600 "$TEMPLATE" "$CONF" + echo " $CONF aus der Vorlage angelegt" +fi +rm -f "$TEMPLATE" +REMOTE + +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 +STAGE_DIR=${NINLIGHT_STAGE_DIR:-/tmp} +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 +# 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. +"$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} +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 +# 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" "$STAGE_DIR/ninlight-button.service" \ + "$STAGE_DIR/ninlight-button" "$STAGE_DIR/ninlight-poweroff.service" \ + "$STAGE_DIR/ninlight-reboot.service" +} +trap cleanup_stage EXIT + +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 +# 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 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" "$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" +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. +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 +# 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" +# 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" +# 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" +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. + # + # 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 + 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 "== 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 +# zeigt nur an, und "Station aus" ist genau das, was nach dem Einrichten auf dem Panel +# 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 - 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 - journalctl -u ninlight-display" >&2 +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 "== 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 +# 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_BIN" "$HOST" bash -s <<'REMOTE' +set -e +CONF=${NINLIGHT_STATION_CONF:-/etc/ninlight-station.conf} +NM_DIR=${NINLIGHT_NM_DIR:-/etc/NetworkManager/system-connections} +eval "$(sudo cat "$CONF")" + +# 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). +# +# 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 +# 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 +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). +# +# 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 + +# 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 + 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) + +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/station.conf.example b/tools/station/station.conf.example new file mode 100644 index 0000000..73e6e1e --- /dev/null +++ b/tools/station/station.conf.example @@ -0,0 +1,38 @@ +# /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. +# +# 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' + +# 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' + +# 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_button.py b/tools/station/tests/test_button.py new file mode 100644 index 0000000..98949fe --- /dev/null +++ b/tools/station/tests/test_button.py @@ -0,0 +1,553 @@ +"""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, + "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 --------------------------------------------- + + +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: horchen", + "Migration: AUTOMATIK", "Umschalten: mit WLAN", + "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(): + 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", + "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", "Umschalten: mit WLAN", + "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: 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), _short(5.0), + _short(6.0)], 8.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), _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() + + +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"]] + + +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(): + """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), _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" + + +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} + + +# --- 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 new file mode 100755 index 0000000..437f14e --- /dev/null +++ b/tools/station/tests/test_cli.sh @@ -0,0 +1,1493 @@ +#!/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. 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 + +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 + +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". 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 +# Leeren von $4 bei restart/stop entscheidet (wie beim echten systemd). +setup_stubbin() { + 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 + + cat >"$bindir/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 + 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 + +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 + emit GENERAL.CONNECTION "\${FAKE_NMCLI_DEVSHOW}" + exit 0 + fi + last=\$(tail -n1 "\$NMSTATE/active" 2>/dev/null) + emit 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 + # 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" + exit 0 +fi + +exit 0 +EOF + + cat >"$bindir/systemctl" <>"$log" +RUN_DIR="$rundir" +UNIT_FILE="$unitfile" +case "\$1" in + is-active) + # 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}" + # 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 + if [ "\$code" -eq 0 ]; then + echo active + exit 0 + else + echo inactive + 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 + # RuntimeDirectory bei JEDEM Stop/Restart, nicht nur beim allerersten Start, + # und legt es leer wieder an. + # + # 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 + 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 + exit 0 + ;; + *) exit 0 ;; +esac +EOF + + cat >"$bindir/sudo" <>"$log" +exec "\$@" +EOF + + # 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 --- +# $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 unitfile=${2:-$REAL_UNIT} + rm -rf "$sandbox" + 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. + # 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" <"))) + +# 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 + + cat >"$sandbox/station.conf" <"$sandbox/nmstate/profiles" + : >"$sandbox/nmstate/active" + + setup_stubbin "$sandbox/bin" "$sandbox/cmd.log" "$sandbox/nmstate" "$sandbox/run" "$unitfile" +} + +# 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="$sandbox/lib" \ + NINLIGHT_IMAGESET_FILE="$sandbox/var/imageset" \ + "$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-nocreds/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 || 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 + 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-nocreds/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" +# 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=$? +set -e +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 ===" +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")" +[ "$(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 ===" +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")" +[ "$(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 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>"$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 "$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 auf stderr, Rueckgabewert != 0 ===" +SBX="$TMPROOT/t9" +setup_sandbox "$SBX" +set +e +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 "$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 +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)" +[ "$(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 ===" +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" +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' +{"mode": "migration", + "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-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" +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 +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-nocreds/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-nocreds/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-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). +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 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" +setup_sandbox "$SBX" +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=$? +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 +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-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 +[ "$(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-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 +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 +echo "=== Fall D1 (Task 6b, Punkt 3): 'display test' ruft display.py mit --test und der konfigurierten Revision auf ===" +SBX="$TMPROOT/tD1" +setup_sandbox "$SBX" +: >"$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")" +# 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 ===" +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'" +# 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 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 +# 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 || 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 + fail "Reihenfolge falsch oder Aufruf fehlt (stop=$STOP_LINE, clear=$CLEAR_LINE): $(cat "$SBX/cmd.log")" +fi + +echo +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-nocreds/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" && 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 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 +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 +[ "$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 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" +: >"$SBX/cmd.log" +set +e +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 +[ "$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 ===" +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 +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-nocreds/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-nocreds/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 +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 +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." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" diff --git a/tools/station/tests/test_deploy.sh b/tools/station/tests/test_deploy.sh new file mode 100755 index 0000000..3b65822 --- /dev/null +++ b/tools/station/tests/test_deploy.sh @@ -0,0 +1,1428 @@ +#!/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) +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 + +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" + # 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" + 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" + 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 + # 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" + # 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 + # 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 + + # 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 + ;; + *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}" ;; + 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" + # 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_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" "$@" +} + +# --- 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" +} + +# 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" +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 +# 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 +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 +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 +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 +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=$(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 +GZ_SIZE=$(wc -c <"$SANDBOX/build/foo/ninlight_foo_gz.img" | tr -d ' ') +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 + 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=$(TEST_SIGN_DIR="$KEYDIR_FULL" run_deploy "$SANDBOX" pi-test foo 2>&1) +RC=$? +set -e +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 +set +e +# Form haelt sich an die Spec (docs/superpowers/spec Abschnitt 4) und an das, was +# 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: + data = json.load(fh) +entries = data.get('images', []) +by_variant = {e.get('variant'): e for e in entries if e.get('target') == 'foo'} +ok = ( + 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(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 (Bequemlichkeit) und target/variant/file/bytes/sha256/ninlight_version je Eintrag" +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-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" +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=$(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-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-wlan/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-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-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", + "ninlight_version": "0.9.0"}, + {"target": "bar", "variant": "dev", "file": "bar_dev.img", "bytes": 19, "sha256": "y", + "ninlight_version": "0.9.0"} + ]} +JSON +: >"$LOG" +set +e +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-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-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/" +fi +set +e +MANIFEST_CHECK=$(python3 -c " +import json +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} +expected = {('foo', 'prod'), ('foo', 'dev'), ('bar', 'prod'), ('bar', 'dev')} +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 mit je eigener Version, ohne falsches manifestweites Feld" +else + fail "manifest.json-Inhalt nach dem gezielten Deploy unerwartet: $MANIFEST_CHECK" +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=$(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 echo "$OUT" | grep -q -- "-----BEGIN"; then + fail "PEM-Header (Schluesselinhalt) taucht in der Ausgabe auf" +else + pass "kein PEM-Header in der Ausgabe" +fi +# 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 + fail "Schluesselinhalt gefunden - $LEAK" +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=$(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-wlan/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-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-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-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" +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-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-wlan/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-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" +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-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-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-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" +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.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" +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-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 +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'), + # 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) +RC2=$? +set -e +if [ "$RC2" -eq 0 ] && [ "$VERSION" = "1.0.0" ]; then + pass "Station.image_version() (der echte Server-Code) liest die Version korrekt, nicht 'unbekannt'" +else + 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-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 + # --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-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)" + 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 +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 +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 +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 +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." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" diff --git a/tools/station/tests/test_products.py b/tools/station/tests/test_products.py new file mode 100644 index 0000000..c6c7a28 --- /dev/null +++ b/tools/station/tests/test_products.py @@ -0,0 +1,125 @@ +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(): + """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_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 == "gu10_rgb" + assert p.name == "Klyqa GU10 RGB CW/WW" + + +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.variant, r.variant_source) == ("dev", "both") + + +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.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") + 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" diff --git a/tools/station/tests/test_render.py b/tools/station/tests/test_render.py new file mode 100644 index 0000000..2fad452 --- /dev/null +++ b/tools/station/tests/test_render.py @@ -0,0 +1,2144 @@ +import json +import sys +import time +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "display")) + +import display # noqa: E402 +import epdconfig # noqa: E402 +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_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": []}) + text = "\n".join(lines) + assert "RETTUNG" in text + assert "ninlight-recovery" in text + + +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 "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_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). + + 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", + "variant": "dev", "lamp_version": "V1"}} + + image = render.compose(state) + 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.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})" + + +HEADER_MIN_BLACK_SHARE = 0.55 + + +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. + + 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 + + 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 * 1.2, ( + f"grosse Zeile ({hero_height} px) hebt sich nicht vom Beiwerk " + 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(): + """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 + 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() + + # 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 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 - " + "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_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() + 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": {}}): + assert render.lines(state) + + +# --- "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"}) + text = "\n".join(lines) + assert render.ARMED_LABEL in text + 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", + imageset_path=tmp_path / "imageset") + + assert state["mode"] == "off" + assert "autooff_minutes" not in state + 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 +# 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", + imageset_path=tmp_path / "imageset") + + # "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): + """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" + + +# --- 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) + + +# --- "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 + 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 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") + image = render.compose({"mode": "off"}) + + panel.clear() + panel.draw_full(image) + panel.sleep() + 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). + 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 == [] + + +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) + + +# --- 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, 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 + + +class _StopDaemon(Exception): + """Bricht run_daemon()s Endlosschleife ab, sobald die simulierte Zeit abgelaufen ist.""" + + +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 + + +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 _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, + `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_ui=lambda: ui_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 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.""" + panel = _drive_daemon(lambda t: {"mode": "recovery", "served": 0, "recent": []}, + SIX_HOURS_S) + + 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(): + panel = _drive_daemon(_changing_every(3 * 60), SIX_HOURS_S) + + 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), ( + 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.""" + panel = _drive_daemon(_changing_every(9 * 60), SIX_HOURS_S) + + 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) + + +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). + + 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(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(panel, "full") == [0.0], ( + f"unerwartete Vollaufbauten: {_times(panel, '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 state_at(t): + # 30 Aenderungen im 5-Sekunden-Takt (t = 5 .. 150), danach unveraendert. + step = min(int(t // display.POLL_INTERVAL_S), 30) + return {"mode": "migration", "served": step, "recent": []} + + panel = _drive_daemon(state_at, SIX_HOURS_S) + + assert len(_times(panel, "partial")) == 30, ( + f"das Buendel muss aus Teilaufbauten bestehen: {_times(panel, 'partial')}") + + 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(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(): + """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" + + +# --- 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] + + +# --- 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 + + +# --- 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. 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 "trennen erlaubt" 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 + + +# --- 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() + + +# --- 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) + + +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 + + assert width_px * FOOTER_WIDTH_SAFETY <= 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)) diff --git a/tools/station/tests/test_server.py b/tools/station/tests/test_server.py new file mode 100644 index 0000000..73cb481 --- /dev/null +++ b/tools/station/tests/test_server.py @@ -0,0 +1,1101 @@ +import hashlib +import http.client +import json +import os +import select +import socket +import stat +import subprocess +import sys +import threading +import time +from pathlib import Path + +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: + """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, 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: + last = json.loads(self.state.read_text()) + if predicate is None or predicate(last): + return last + except json.JSONDecodeError: + pass + time.sleep(0.05) + 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() + 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): + 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_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() + 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(lambda s: s["served"] >= 1) + 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(lambda s: len(s["recent"]) >= 1) + assert state["served"] == 0 + assert state["recent"][-1]["status"] == "notfound" + 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): + c = station.conn() + c.request("GET", "/ninlight_evk_ota.img") + c.getresponse().read() + state = station.read_state(lambda s: s["served"] >= 12) + assert state["served"] == 12 + assert len(state["recent"]) == RECENT_MAX + + +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 + 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_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. + + 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 + + +# --- 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. + + 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, *extra_args): + self.image_dir = tmp_path / "images" + self.image_dir.mkdir() + self.state_path = tmp_path / "status.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" + # 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), + "--imageset-file", str(self.imageset_path), *extra_args], + 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() + return _Response(r.status, dict(r.getheaders()), body) + finally: + conn.close() + + 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: + 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) + 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, predicate=None): + return self._poll_json(self.state_path, predicate) + + 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") + 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 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) + + +@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-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 + 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_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-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 + 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-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 + 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-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 + 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"][-1]["status"] == "listen" + + +def test_arm_serves_exactly_once_then_falls_back_to_listen(migration_server): + srv, imgdir = migration_server + (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 + assert srv.read_control(lambda c: c.get("phase") == "listen")["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-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 + 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") + assert r.status == 404 + 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, 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) + assert st["recent"][-1]["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-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 + + +def test_corrupt_control_file_means_auto(migration_server): + srv, imgdir = migration_server + (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 + + +def test_range_resume_on_migration_image(migration_server): + srv, imgdir = migration_server + (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 + 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): + """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-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") + st = srv.status(lambda s: len(s["recent"]) >= 1) + 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-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", + "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 + 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-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(): + 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-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" + + 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-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" + + 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" + + +# --- 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 new file mode 100755 index 0000000..cd9e109 --- /dev/null +++ b/tools/station/tests/test_setup_pi.sh @@ -0,0 +1,1142 @@ +#!/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; } + +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" + + cat >"$bindir/ssh" <>"$log" +host=\$1; shift +"\$@" +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 ;; + *ninlight-button) [ -n "\${FAKE_SCP_SKIP_BUTTON_DIR:-}" ] && exit 0 ;; + esac +done +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" +# 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 + # 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 + # 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" +# 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 + 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" +# Testschalter: laesst "enable"/"restart" scheitern (beides darf setup_pi.sh nur +# 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. +# 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 + 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}" ;; + 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 \ + "$bindir"/apt-get "$bindir"/python3 +} + +# $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" "$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 +# 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 + # 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" \ + 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" \ + 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" \ + NINLIGHT_SPIDEV_PATH="$state/dev/spidev0.0" \ + "$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=$(file_mode "$CONF_OUT") +[ "$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 +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 +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 +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" +# 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 ===" +# 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 +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" +# 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") +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 +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 "=== 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 +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 +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 +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 +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." +else + echo "Mindestens ein Test ist fehlgeschlagen." >&2 +fi +exit "$FAILED" 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" }, +] diff --git a/tools/test_host.sh b/tools/test_host.sh new file mode 100755 index 0000000..1c1852a --- /dev/null +++ b/tools/test_host.sh @@ -0,0 +1,45 @@ +#!/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 "$@" + +if python3 -c 'import pytest' 2>/dev/null; then + # 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 + +# 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" + "$ROOT/tools/tests/test_release.sh" +) +for suite in "${SHELL_SUITES[@]}"; do + "$suite" +done 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"