From 304c80592bfa74d50c692d2f14c4de1516fc35af Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Mon, 29 Jun 2020 18:23:24 +0200 Subject: [PATCH 01/28] Drafting ideas about app on host and some parts on the device. --- .../Host/Components/sming-arch/component.mk | 7 +- Sming/Arch/Host/app.mk | 5 + Sming/Components/Hosted-Lib/component.mk | 2 + .../Hosted-Lib/src/DigitalHosted.cpp | 22 + .../Components/Hosted-Lib/src/InitClient.cpp | 8 + Sming/Components/Hosted/README.rst | 34 + Sming/Components/Hosted/app/Makefile | 9 + Sming/Components/Hosted/app/README.rst | 4 + .../Components/Hosted/app/app/application.cpp | 48 + Sming/Components/Hosted/app/component.mk | 6 + Sming/Components/Hosted/component.mk | 24 + Sming/Components/Hosted/proto/hosted.pb.c | 26 + Sming/Components/Hosted/proto/hosted.pb.h | 156 ++ Sming/Components/Hosted/proto/hosted.proto | 43 + Sming/Components/Hosted/src/HostedClient.h | 59 + .../Hosted/src/HostedClientStream.h | 49 + Sming/Components/Hosted/src/HostedServer.h | 70 + Sming/Components/nanopb/component.mk | 2 + Sming/Components/nanopb/inc/pb.h | 875 +++++++++ Sming/Components/nanopb/inc/pb_common.h | 49 + Sming/Components/nanopb/inc/pb_decode.h | 196 ++ Sming/Components/nanopb/inc/pb_encode.h | 185 ++ Sming/Components/nanopb/src/pb_common.c | 388 ++++ Sming/Components/nanopb/src/pb_decode.c | 1685 +++++++++++++++++ Sming/Components/nanopb/src/pb_encode.c | 978 ++++++++++ 25 files changed, 4929 insertions(+), 1 deletion(-) create mode 100644 Sming/Components/Hosted-Lib/component.mk create mode 100644 Sming/Components/Hosted-Lib/src/DigitalHosted.cpp create mode 100644 Sming/Components/Hosted-Lib/src/InitClient.cpp create mode 100644 Sming/Components/Hosted/README.rst create mode 100644 Sming/Components/Hosted/app/Makefile create mode 100644 Sming/Components/Hosted/app/README.rst create mode 100644 Sming/Components/Hosted/app/app/application.cpp create mode 100644 Sming/Components/Hosted/app/component.mk create mode 100644 Sming/Components/Hosted/component.mk create mode 100644 Sming/Components/Hosted/proto/hosted.pb.c create mode 100644 Sming/Components/Hosted/proto/hosted.pb.h create mode 100644 Sming/Components/Hosted/proto/hosted.proto create mode 100644 Sming/Components/Hosted/src/HostedClient.h create mode 100644 Sming/Components/Hosted/src/HostedClientStream.h create mode 100644 Sming/Components/Hosted/src/HostedServer.h create mode 100644 Sming/Components/nanopb/component.mk create mode 100644 Sming/Components/nanopb/inc/pb.h create mode 100644 Sming/Components/nanopb/inc/pb_common.h create mode 100644 Sming/Components/nanopb/inc/pb_decode.h create mode 100644 Sming/Components/nanopb/inc/pb_encode.h create mode 100644 Sming/Components/nanopb/src/pb_common.c create mode 100644 Sming/Components/nanopb/src/pb_decode.c create mode 100644 Sming/Components/nanopb/src/pb_encode.c diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index 2fe62df117..1132de90e4 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -36,7 +36,12 @@ COMPONENT_DEPENDS := \ lwip \ spi_flash \ vflash \ - rboot + rboot \ + + +ifneq ($(ENABLE_HOSTED),) + COMPONENT_DEPENDS += Hosted-Lib +endif # => Platform WiFi COMPONENT_VARS := \ diff --git a/Sming/Arch/Host/app.mk b/Sming/Arch/Host/app.mk index 3d895d08ff..0a26dbdf46 100644 --- a/Sming/Arch/Host/app.mk +++ b/Sming/Arch/Host/app.mk @@ -12,6 +12,11 @@ LDFLAGS += \ # Executable TARGET_OUT_0 := $(FW_BASE)/$(APP_NAME)$(TOOL_EXT) +# Hosted Settings +ifneq ($(ENABLE_HOSTED),) + COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted.a $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib.a $(COMPONENTS_AR) +endif + # Target definitions .PHONY: application diff --git a/Sming/Components/Hosted-Lib/component.mk b/Sming/Components/Hosted-Lib/component.mk new file mode 100644 index 0000000000..9d2f99535c --- /dev/null +++ b/Sming/Components/Hosted-Lib/component.mk @@ -0,0 +1,2 @@ +COMPONENT_SRCDIRS := src +COMPONENT_DEPENDS := Hosted \ No newline at end of file diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp new file mode 100644 index 0000000000..526b5b6355 --- /dev/null +++ b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp @@ -0,0 +1,22 @@ +#include +#include + +extern HostedClient hostedClient; + +void pinMode(uint16_t pin, uint8_t mode) +{ + INIT_HD_COMMAND(PinMode); + command->pin = pin; + command->mode = (PinMode)mode; + + hostedClient.send(message); +} + +void digitalWrite(uint16_t pin, uint8_t val) +{ + INIT_HD_COMMAND(DigitalWrite); + command->pin = pin; + command->value = val; + + hostedClient.send(message); +} diff --git a/Sming/Components/Hosted-Lib/src/InitClient.cpp b/Sming/Components/Hosted-Lib/src/InitClient.cpp new file mode 100644 index 0000000000..e64508374a --- /dev/null +++ b/Sming/Components/Hosted-Lib/src/InitClient.cpp @@ -0,0 +1,8 @@ +#include +#include +#include + + +// TODO: Initialize the code transport and the client + +HostedClient hostedClient(new HostedTcpStream("192.168.13.3", 4031)); diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst new file mode 100644 index 0000000000..ab70a45279 --- /dev/null +++ b/Sming/Components/Hosted/README.rst @@ -0,0 +1,34 @@ +HostEd +============ + +.. highlight:: bash + +The hosted component allows Sming's host emulator to run parts of the code on an actual microcontroller. +TBD... + +Configuration variables +----------------------- + +Serial Communications +~~~~~~~~~~~~~~~~~~~~~ + +.. envvar:: COM_SPEED + + Default baud rate for serial port. + + This will recompile your application to use the revised baud rate. + Note that this will change the default speed used for both flashing and serial comms. + See also :component-esp8266:`esptool` and :component:`terminal` for further details. + + +Development +----------- +If you are interested in ... +TDB + +Protobuf with nanopb:: + + cd $SMING_HOME/Components/Hosted/src + python ~/dev/nanopb/generator/nanopb_generator.py -I../proto hosted.proto + +TBD... \ No newline at end of file diff --git a/Sming/Components/Hosted/app/Makefile b/Sming/Components/Hosted/app/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/Sming/Components/Hosted/app/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/Sming/Components/Hosted/app/README.rst b/Sming/Components/Hosted/app/README.rst new file mode 100644 index 0000000000..515dff9e8e --- /dev/null +++ b/Sming/Components/Hosted/app/README.rst @@ -0,0 +1,4 @@ +Hosted Server Application +========================= + +TBD... diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp new file mode 100644 index 0000000000..1eb90d42ce --- /dev/null +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +// If you want, you can define WiFi settings globally in Eclipse Environment Variables +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +HostedServer hostedServer; +TcpServer* tcpServer; + +// Will be called when WiFi station was connected to AP +void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + // Start the TcpServer + tcpServer = new TcpServer([](TcpClient& client, char* data, int size) -> bool { + hostedServer.process((const uint8_t*)data, size); + // TODO: check if the processed bytes are equal to the provided bytes + return true; + }); + + tcpServer->listen(4031); + tcpServer->setTimeOut(USHRT_MAX); // disable connection timeout +} + +void init() +{ + // Register Command Handlers + hostedServer.registerCommand(HostedMessageType_TypeRequestPinMode, [](HostedCommand *request, HostedCommand *response)-> int { + pinMode((uint16_t)request->payload.requestPinMode.pin, (uint8_t)request->payload.requestPinMode.mode); + return 0; + }); + + hostedServer.registerCommand(HostedMessageType_TypeRequestDigitalWrite, [](HostedCommand *request, HostedCommand *response)-> int { + digitalWrite((uint16_t)request->payload.requestDigitalWrite.pin, (uint8_t)request->payload.requestDigitalWrite.value); + return 0; + }); + + // Connect to same AP as the client application + WifiStation.enable(true); + WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); + + // Set callback that should be triggered when we have assigned IP + WifiEvents.onStationGotIP(connectOk); +} + diff --git a/Sming/Components/Hosted/app/component.mk b/Sming/Components/Hosted/app/component.mk new file mode 100644 index 0000000000..ab2b059deb --- /dev/null +++ b/Sming/Components/Hosted/app/component.mk @@ -0,0 +1,6 @@ +## List the names of any additional Components required for this project +COMPONENT_DEPENDS := Hosted + +## SPIFFS options +DISABLE_SPIFFS := 1 +# SPIFF_FILES = files diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk new file mode 100644 index 0000000000..822216eb8d --- /dev/null +++ b/Sming/Components/Hosted/component.mk @@ -0,0 +1,24 @@ +COMPONENT_SRCDIRS := src proto +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) +COMPONENT_DEPENDS := nanopb + +# Architecture of the device where the hosted service will be flashed +HOSTED_ARCH ?= Esp8266 + +HOSTED_APP_DIR := $(COMPONENT_PATH)/app + +RELINK_VARS = ENABLE_HOSTED + +##@Building + +hostedservice: ##Builds the hosted service firmware + $(MAKE) -C $(HOSTED_APP_DIR) SMING_ARCH=$(HOSTED_ARCH) + +##@Flashing + +flashhostedservice: ##Flashes the hosted service firmware to an actual device + $(MAKE) -C $(HOSTED_APP_DIR) flash SMING_ARCH=$(HOSTED_ARCH) + +flashhostedserviceapp: ##Flashes only the hosted service app + $(MAKE) -C $(HOSTED_APP_DIR) flashapp SMING_ARCH=$(HOSTED_ARCH) + diff --git a/Sming/Components/Hosted/proto/hosted.pb.c b/Sming/Components/Hosted/proto/hosted.pb.c new file mode 100644 index 0000000000..7bb50c07db --- /dev/null +++ b/Sming/Components/Hosted/proto/hosted.pb.c @@ -0,0 +1,26 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.3-dev */ + +#include "hosted.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(RequestDigitalWrite, RequestDigitalWrite, AUTO) + + +PB_BIND(RequestPinMode, RequestPinMode, AUTO) + + +PB_BIND(RequestDigitalRead, RequestDigitalRead, AUTO) + + +PB_BIND(ResponseDigitalRead, ResponseDigitalRead, AUTO) + + +PB_BIND(HostedCommand, HostedCommand, AUTO) + + + + + diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h new file mode 100644 index 0000000000..6afd920d08 --- /dev/null +++ b/Sming/Components/Hosted/proto/hosted.pb.h @@ -0,0 +1,156 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.3-dev */ + +#ifndef PB_HOSTED_PB_H_INCLUDED +#define PB_HOSTED_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Enum definitions */ +typedef enum _PinMode { + PinMode_INPUT = 0, + PinMode_OUTPUT = 1, + PinMode_INPUT_PULLUP = 2 +} PinMode; + +typedef enum _HostedMessageType { + HostedMessageType_TypeRequestDigitalWrite = 0, + HostedMessageType_TypeRequestPinMode = 1, + HostedMessageType_TypeRequestDigitalRead = 2, + HostedMessageType_TypeResponseDigitalRead = 3 +} HostedMessageType; + +/* Struct definitions */ +typedef struct _RequestDigitalRead { + uint32_t pin; +} RequestDigitalRead; + +typedef struct _RequestDigitalWrite { + uint32_t pin; + uint32_t value; +} RequestDigitalWrite; + +typedef struct _RequestPinMode { + uint32_t pin; + PinMode mode; +} RequestPinMode; + +typedef struct _ResponseDigitalRead { + uint32_t value; +} ResponseDigitalRead; + +typedef struct _HostedCommand { + HostedMessageType type; + pb_size_t which_payload; + union { + RequestDigitalWrite requestDigitalWrite; + RequestPinMode requestPinMode; + RequestDigitalRead requestDigitalRead; + ResponseDigitalRead responseDigitalRead; + } payload; +} HostedCommand; + + +/* Helper constants for enums */ +#define _PinMode_MIN PinMode_INPUT +#define _PinMode_MAX PinMode_INPUT_PULLUP +#define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP+1)) + +#define _HostedMessageType_MIN HostedMessageType_TypeRequestDigitalWrite +#define _HostedMessageType_MAX HostedMessageType_TypeResponseDigitalRead +#define _HostedMessageType_ARRAYSIZE ((HostedMessageType)(HostedMessageType_TypeResponseDigitalRead+1)) + + +/* Initializer values for message structs */ +#define RequestDigitalWrite_init_default {0, 0} +#define RequestPinMode_init_default {0, _PinMode_MIN} +#define RequestDigitalRead_init_default {0} +#define ResponseDigitalRead_init_default {0} +#define HostedCommand_init_default {_HostedMessageType_MIN, 0, {RequestDigitalWrite_init_default}} +#define RequestDigitalWrite_init_zero {0, 0} +#define RequestPinMode_init_zero {0, _PinMode_MIN} +#define RequestDigitalRead_init_zero {0} +#define ResponseDigitalRead_init_zero {0} +#define HostedCommand_init_zero {_HostedMessageType_MIN, 0, {RequestDigitalWrite_init_zero}} + +/* Field tags (for use in manual encoding/decoding) */ +#define RequestDigitalRead_pin_tag 1 +#define RequestDigitalWrite_pin_tag 1 +#define RequestDigitalWrite_value_tag 2 +#define RequestPinMode_pin_tag 1 +#define RequestPinMode_mode_tag 2 +#define ResponseDigitalRead_value_tag 1 +#define HostedCommand_type_tag 1 +#define HostedCommand_requestDigitalWrite_tag 10 +#define HostedCommand_requestPinMode_tag 11 +#define HostedCommand_requestDigitalRead_tag 12 +#define HostedCommand_responseDigitalRead_tag 13 + +/* Struct field encoding specification for nanopb */ +#define RequestDigitalWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, pin, 1) \ +X(a, STATIC, SINGULAR, UINT32, value, 2) +#define RequestDigitalWrite_CALLBACK NULL +#define RequestDigitalWrite_DEFAULT NULL + +#define RequestPinMode_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, pin, 1) \ +X(a, STATIC, SINGULAR, UENUM, mode, 2) +#define RequestPinMode_CALLBACK NULL +#define RequestPinMode_DEFAULT NULL + +#define RequestDigitalRead_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, pin, 1) +#define RequestDigitalRead_CALLBACK NULL +#define RequestDigitalRead_DEFAULT NULL + +#define ResponseDigitalRead_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, value, 1) +#define ResponseDigitalRead_CALLBACK NULL +#define ResponseDigitalRead_DEFAULT NULL + +#define HostedCommand_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, type, 1) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalWrite,payload.requestDigitalWrite), 10) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestPinMode,payload.requestPinMode), 11) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalRead,payload.requestDigitalRead), 12) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,responseDigitalRead,payload.responseDigitalRead), 13) +#define HostedCommand_CALLBACK NULL +#define HostedCommand_DEFAULT NULL +#define HostedCommand_payload_requestDigitalWrite_MSGTYPE RequestDigitalWrite +#define HostedCommand_payload_requestPinMode_MSGTYPE RequestPinMode +#define HostedCommand_payload_requestDigitalRead_MSGTYPE RequestDigitalRead +#define HostedCommand_payload_responseDigitalRead_MSGTYPE ResponseDigitalRead + +extern const pb_msgdesc_t RequestDigitalWrite_msg; +extern const pb_msgdesc_t RequestPinMode_msg; +extern const pb_msgdesc_t RequestDigitalRead_msg; +extern const pb_msgdesc_t ResponseDigitalRead_msg; +extern const pb_msgdesc_t HostedCommand_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define RequestDigitalWrite_fields &RequestDigitalWrite_msg +#define RequestPinMode_fields &RequestPinMode_msg +#define RequestDigitalRead_fields &RequestDigitalRead_msg +#define ResponseDigitalRead_fields &ResponseDigitalRead_msg +#define HostedCommand_fields &HostedCommand_msg + +/* Maximum encoded size of messages (where known) */ +#define RequestDigitalWrite_size 12 +#define RequestPinMode_size 8 +#define RequestDigitalRead_size 6 +#define ResponseDigitalRead_size 6 +#define HostedCommand_size 16 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Sming/Components/Hosted/proto/hosted.proto b/Sming/Components/Hosted/proto/hosted.proto new file mode 100644 index 0000000000..20aa29bbf7 --- /dev/null +++ b/Sming/Components/Hosted/proto/hosted.proto @@ -0,0 +1,43 @@ +syntax = "proto3"; + +message RequestDigitalWrite { + uint32 pin = 1; + uint32 value = 2; +} + +// See: https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/ +enum PinMode { + INPUT = 0; + OUTPUT = 1; + INPUT_PULLUP = 2; +} + +message RequestPinMode { + uint32 pin = 1; + PinMode mode = 2; +} + +message RequestDigitalRead { + uint32 pin = 1; +} + +message ResponseDigitalRead { + uint32 value = 1; +} + +enum HostedMessageType { + TypeRequestDigitalWrite = 0; + TypeRequestPinMode = 1; + TypeRequestDigitalRead = 2; + TypeResponseDigitalRead = 3; +} + +message HostedCommand { + HostedMessageType type = 1; + oneof payload { + RequestDigitalWrite requestDigitalWrite = 10; + RequestPinMode requestPinMode = 11; + RequestDigitalRead requestDigitalRead = 12; + ResponseDigitalRead responseDigitalRead = 13; + } +} diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src/HostedClient.h new file mode 100644 index 0000000000..b413ddcab8 --- /dev/null +++ b/Sming/Components/Hosted/src/HostedClient.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include +#include "hosted.pb.h" + +// Initializes hosted command +#define INIT_HD_COMMAND(XX) \ + HostedCommand message = HostedCommand_init_zero; \ + message.type = HostedMessageType_TypeRequest ## XX; \ + message.which_payload = HostedCommand_request ## XX ##_tag; \ + auto command = &message.payload.request ## XX; + +class HostedClient +{ +public: + + HostedClient(ReadWriteStream* stream): stream(stream) + { + } + + void setStream(ReadWriteStream* stream) + { + if(this->stream != nullptr) { + debug_w("Discarding current stream!"); + } + + this->stream = stream; + } + + bool send(const HostedCommand& message) + { + bool status; + + pb_ostream_t ouput = pb_ostream_from_buffer(buffer, sizeof(buffer)); + status = pb_encode(&ouput, HostedCommand_fields, &message); + + if (!status) { + debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); + return false; + } + + if(ouput.bytes_written > 0) { + size_t written = stream->write(buffer, ouput.bytes_written); + if(written != ouput.bytes_written) { + debug_e("Unable to queue message in transport stream"); + return false; + } + } + + return true; + } +private: + ReadWriteStream* stream = nullptr; + + uint8_t buffer[128]; /* This is the buffer where we will store our message. */ +}; diff --git a/Sming/Components/Hosted/src/HostedClientStream.h b/Sming/Components/Hosted/src/HostedClientStream.h new file mode 100644 index 0000000000..b0a075363e --- /dev/null +++ b/Sming/Components/Hosted/src/HostedClientStream.h @@ -0,0 +1,49 @@ +#include + +class HostedTcpStream: public ReadWriteStream +{ +public: + HostedTcpStream(const String& host, uint16_t port): host(host), port(port) + { + auto onCompleted = [](TcpClient& client, bool successful) { + // onCompleted; + + }; + + auto onReadyToSend = [](TcpClient& client, TcpConnectionEvent sourceEvent) { + + }; + + auto onReceive = [](TcpClient& client, char* data, int size)->bool { + return true; + }; + + client = new TcpClient(onCompleted, onReadyToSend, onReceive); + } + + size_t write(const uint8_t* buffer, size_t size) override + { + if(client->getConnectionState() == eTCS_Ready) { + client->connect(host, (int)port); + } + client->send((const char *)buffer, size); + return size; + } + + uint16_t readMemoryBlock(char* data, int bufSize) override + { + return 0; + } + + + bool isFinished() override + { + return false; + } + + +private: + TcpClient* client =nullptr; + String host; + uint16_t port = 0; +}; diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h new file mode 100644 index 0000000000..82c731b7b9 --- /dev/null +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -0,0 +1,70 @@ +/* + * HostedServer.h + * + * Created on: Jun 29, 2020 + * Author: slavey + */ + +#pragma once + +#include +#include +#include "hosted.pb.h" + +constexpr int HOSTED_OK = 0; +constexpr int HOSTED_FAIL = -1; +constexpr int HOSTED_NO_MEM = -2; + +typedef Delegate HostedCommandDelegate; + +class HostedServer +{ +public: + void registerCommand(HostedMessageType type, HostedCommandDelegate callback) + { + commands[type] = callback; + } + + /** + * @brief Process incoming commands + * @return number of processed bytes + */ + int process(const uint8_t *at, size_t length) + { + HostedCommand request = HostedCommand_init_zero; + HostedCommand response = HostedCommand_init_zero; + + if (at == nullptr || length < 0) { + debug_e("empty buffer"); + return HOSTED_FAIL; + } + + int result = HOSTED_OK; + bool status; + + // extract the request message + pb_istream_t input = pb_istream_from_buffer(at, length); + + status = pb_decode(&input, HostedCommand_fields, &request); + if (!status) { + debug_e("Decoding failed: %s\n", PB_GET_ERROR(&input)); + return HOSTED_FAIL; + } + + // dispatch the command + if(commands.contains(request.type) && commands[request.type] != nullptr) { + result = commands[request.type](&request, &response); + } + else { + debug_w("No command registered for type: %d", request.type); + } + + // TODO: cleanup + + return result; + + } + +private: + HashMap commands; +}; diff --git a/Sming/Components/nanopb/component.mk b/Sming/Components/nanopb/component.mk new file mode 100644 index 0000000000..1a6f6c0e7b --- /dev/null +++ b/Sming/Components/nanopb/component.mk @@ -0,0 +1,2 @@ +COMPONENT_SRCDIRS := src +COMPONENT_INCDIRS := inc \ No newline at end of file diff --git a/Sming/Components/nanopb/inc/pb.h b/Sming/Components/nanopb/inc/pb.h new file mode 100644 index 0000000000..3cc130f73b --- /dev/null +++ b/Sming/Components/nanopb/inc/pb.h @@ -0,0 +1,875 @@ +/* Common parts of the nanopb library. Most of these are quite low-level + * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. + */ + +#ifndef PB_H_INCLUDED +#define PB_H_INCLUDED + +/***************************************************************** + * Nanopb compilation time options. You can change these here by * + * uncommenting the lines, or on the compiler command line. * + *****************************************************************/ + +/* Enable support for dynamically allocated fields */ +/* #define PB_ENABLE_MALLOC 1 */ + +/* Define this if your CPU / compiler combination does not support + * unaligned memory access to packed structures. */ +/* #define PB_NO_PACKED_STRUCTS 1 */ + +/* Increase the number of required fields that are tracked. + * A compiler warning will tell if you need this. */ +/* #define PB_MAX_REQUIRED_FIELDS 256 */ + +/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ +/* #define PB_FIELD_32BIT 1 */ + +/* Disable support for error messages in order to save some code space. */ +/* #define PB_NO_ERRMSG 1 */ + +/* Disable support for custom streams (support only memory buffers). */ +/* #define PB_BUFFER_ONLY 1 */ + +/* Disable support for 64-bit datatypes, for compilers without int64_t + or to save some code space. */ +/* #define PB_WITHOUT_64BIT 1 */ + +/* Don't encode scalar arrays as packed. This is only to be used when + * the decoder on the receiving side cannot process packed scalar arrays. + * Such example is older protobuf.js. */ +/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ + +/* Enable conversion of doubles to floats for platforms that do not + * support 64-bit doubles. Most commonly AVR. */ +/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ + +/* Check whether incoming strings are valid UTF-8 sequences. Slows down + * the string processing slightly and slightly increases code size. */ +/* #define PB_VALIDATE_UTF8 1 */ + +/****************************************************************** + * You usually don't need to change anything below this line. * + * Feel free to look around and use the defined macros, though. * + ******************************************************************/ + + +/* Version of the nanopb library. Just in case you want to check it in + * your own program. */ +#define NANOPB_VERSION nanopb-0.4.3-dev + +/* Include all the system headers needed by nanopb. You will need the + * definitions of the following: + * - strlen, memcpy, memset functions + * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t + * - size_t + * - bool + * + * If you don't have the standard header files, you can instead provide + * a custom header that defines or includes all this. In that case, + * define PB_SYSTEM_HEADER to the path of this file. + */ +#ifdef PB_SYSTEM_HEADER +#include PB_SYSTEM_HEADER +#else +#include +#include +#include +#include +#include + +#ifdef PB_ENABLE_MALLOC +#include +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macro for defining packed structures (compiler dependent). + * This just reduces memory requirements, but is not required. + */ +#if defined(PB_NO_PACKED_STRUCTS) + /* Disable struct packing */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#elif defined(__GNUC__) || defined(__clang__) + /* For GCC and clang */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed __attribute__((packed)) +#elif defined(__ICCARM__) || defined(__CC_ARM) + /* For IAR ARM and Keil MDK-ARM compilers */ +# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") +# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") +# define pb_packed +#elif defined(_MSC_VER) && (_MSC_VER >= 1500) + /* For Microsoft Visual C++ */ +# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) +# define PB_PACKED_STRUCT_END __pragma(pack(pop)) +# define pb_packed +#else + /* Unknown compiler */ +# define PB_PACKED_STRUCT_START +# define PB_PACKED_STRUCT_END +# define pb_packed +#endif + +/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ +#ifndef PB_UNUSED +#define PB_UNUSED(x) (void)(x) +#endif + +/* Harvard-architecture processors may need special attributes for storing + * field information in program memory. */ +#ifndef PB_PROGMEM +#ifdef __AVR__ +#include +#define PB_PROGMEM PROGMEM +#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) +#else +#define PB_PROGMEM +#define PB_PROGMEM_READU32(x) (x) +#endif +#endif + +/* Compile-time assertion, used for checking compatible compilation options. + * If this does not work properly on your compiler, use + * #define PB_NO_STATIC_ASSERT to disable it. + * + * But before doing that, check carefully the error message / place where it + * comes from to see if the error has a real cause. Unfortunately the error + * message is not always very clear to read, but you can see the reason better + * in the place where the PB_STATIC_ASSERT macro was called. + */ +#ifndef PB_NO_STATIC_ASSERT +# ifndef PB_STATIC_ASSERT +# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + /* C11 standard _Static_assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); +# else + /* Classic negative-size-array static assert mechanism */ +# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; +# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) +# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER +# endif +# endif +#else + /* Static asserts disabled by PB_NO_STATIC_ASSERT */ +# define PB_STATIC_ASSERT(COND,MSG) +#endif + +/* Number of required fields to keep track of. */ +#ifndef PB_MAX_REQUIRED_FIELDS +#define PB_MAX_REQUIRED_FIELDS 64 +#endif + +#if PB_MAX_REQUIRED_FIELDS < 64 +#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). +#endif + +#ifdef PB_WITHOUT_64BIT +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Cannot use doubles without 64-bit types */ +#undef PB_CONVERT_DOUBLE_FLOAT +#endif +#endif + +/* List of possible field types. These are used in the autogenerated code. + * Least-significant 4 bits tell the scalar type + * Most-significant 4 bits specify repeated/required/packed etc. + */ + +typedef uint_least8_t pb_type_t; + +/**** Field data types ****/ + +/* Numeric types */ +#define PB_LTYPE_BOOL 0x00U /* bool */ +#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ +#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ +#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ +#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ +#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ + +/* Marker for last packable field type. */ +#define PB_LTYPE_LAST_PACKABLE 0x05U + +/* Byte array with pre-allocated buffer. + * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ +#define PB_LTYPE_BYTES 0x06U + +/* String with pre-allocated buffer. + * data_size is the maximum length. */ +#define PB_LTYPE_STRING 0x07U + +/* Submessage + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMESSAGE 0x08U + +/* Submessage with pre-decoding callback + * The pre-decoding callback is stored as pb_callback_t right before pSize. + * submsg_fields is pointer to field descriptions */ +#define PB_LTYPE_SUBMSG_W_CB 0x09U + +/* Extension pseudo-field + * The field contains a pointer to pb_extension_t */ +#define PB_LTYPE_EXTENSION 0x0AU + +/* Byte array with inline, pre-allocated byffer. + * data_size is the length of the inline, allocated buffer. + * This differs from PB_LTYPE_BYTES by defining the element as + * pb_byte_t[data_size] rather than pb_bytes_array_t. */ +#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU + +/* Number of declared LTYPES */ +#define PB_LTYPES_COUNT 0x0CU +#define PB_LTYPE_MASK 0x0FU + +/**** Field repetition rules ****/ + +#define PB_HTYPE_REQUIRED 0x00U +#define PB_HTYPE_OPTIONAL 0x10U +#define PB_HTYPE_SINGULAR 0x10U +#define PB_HTYPE_REPEATED 0x20U +#define PB_HTYPE_FIXARRAY 0x20U +#define PB_HTYPE_ONEOF 0x30U +#define PB_HTYPE_MASK 0x30U + +/**** Field allocation types ****/ + +#define PB_ATYPE_STATIC 0x00U +#define PB_ATYPE_POINTER 0x80U +#define PB_ATYPE_CALLBACK 0x40U +#define PB_ATYPE_MASK 0xC0U + +#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) +#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) +#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) +#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ + PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) + +/* Data type used for storing sizes of struct fields + * and array counts. + */ +#if defined(PB_FIELD_32BIT) + typedef uint32_t pb_size_t; + typedef int32_t pb_ssize_t; +#else + typedef uint_least16_t pb_size_t; + typedef int_least16_t pb_ssize_t; +#endif +#define PB_SIZE_MAX ((pb_size_t)-1) + +/* Data type for storing encoded data and other byte streams. + * This typedef exists to support platforms where uint8_t does not exist. + * You can regard it as equivalent on uint8_t on other platforms. + */ +typedef uint_least8_t pb_byte_t; + +/* Forward declaration of struct types */ +typedef struct pb_istream_s pb_istream_t; +typedef struct pb_ostream_s pb_ostream_t; +typedef struct pb_field_iter_s pb_field_iter_t; + +/* This structure is used in auto-generated constants + * to specify struct fields. + */ +typedef struct pb_msgdesc_s pb_msgdesc_t; +struct pb_msgdesc_s { + const uint32_t *field_info; + const pb_msgdesc_t * const * submsg_info; + const pb_byte_t *default_value; + + bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); + + pb_size_t field_count; + pb_size_t required_field_count; + pb_size_t largest_tag; +}; + +/* Iterator for message descriptor */ +struct pb_field_iter_s { + const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ + void *message; /* Pointer to start of the structure */ + + pb_size_t index; /* Index of the field */ + pb_size_t field_info_index; /* Index to descriptor->field_info array */ + pb_size_t required_field_index; /* Index that counts only the required fields */ + pb_size_t submessage_index; /* Index that counts only submessages */ + + pb_size_t tag; /* Tag of current field */ + pb_size_t data_size; /* sizeof() of a single item */ + pb_size_t array_size; /* Number of array entries */ + pb_type_t type; /* Type of current field */ + + void *pField; /* Pointer to current field in struct */ + void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ + void *pSize; /* Pointer to count/has field */ + + const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ +}; + +/* For compatibility with legacy code */ +typedef pb_field_iter_t pb_field_t; + +/* Make sure that the standard integer types are of the expected sizes. + * Otherwise fixed32/fixed64 fields can break. + * + * If you get errors here, it probably means that your stdint.h is not + * correct for your platform. + */ +#ifndef PB_WITHOUT_64BIT +PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) +PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) +#endif + +/* This structure is used for 'bytes' arrays. + * It has the number of bytes in the beginning, and after that an array. + * Note that actual structs used will have a different length of bytes array. + */ +#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } +#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) + +struct pb_bytes_array_s { + pb_size_t size; + pb_byte_t bytes[1]; +}; +typedef struct pb_bytes_array_s pb_bytes_array_t; + +/* This structure is used for giving the callback function. + * It is stored in the message structure and filled in by the method that + * calls pb_decode. + * + * The decoding callback will be given a limited-length stream + * If the wire type was string, the length is the length of the string. + * If the wire type was a varint/fixed32/fixed64, the length is the length + * of the actual value. + * The function may be called multiple times (especially for repeated types, + * but also otherwise if the message happens to contain the field multiple + * times.) + * + * The encoding callback will receive the actual output stream. + * It should write all the data in one call, including the field tag and + * wire type. It can write multiple fields. + * + * The callback can be null if you want to skip a field. + */ +typedef struct pb_callback_s pb_callback_t; +struct pb_callback_s { + /* Callback functions receive a pointer to the arg field. + * You can access the value of the field as *arg, and modify it if needed. + */ + union { + bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); + bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); + } funcs; + + /* Free arg for use by callback */ + void *arg; +}; + +extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); + +/* Wire types. Library user needs these only in encoder callbacks. */ +typedef enum { + PB_WT_VARINT = 0, + PB_WT_64BIT = 1, + PB_WT_STRING = 2, + PB_WT_32BIT = 5 +} pb_wire_type_t; + +/* Structure for defining the handling of unknown/extension fields. + * Usually the pb_extension_type_t structure is automatically generated, + * while the pb_extension_t structure is created by the user. However, + * if you want to catch all unknown fields, you can also create a custom + * pb_extension_type_t with your own callback. + */ +typedef struct pb_extension_type_s pb_extension_type_t; +typedef struct pb_extension_s pb_extension_t; +struct pb_extension_type_s { + /* Called for each unknown field in the message. + * If you handle the field, read off all of its data and return true. + * If you do not handle the field, do not read anything and return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, + uint32_t tag, pb_wire_type_t wire_type); + + /* Called once after all regular fields have been encoded. + * If you have something to write, do so and return true. + * If you do not have anything to write, just return true. + * If you run into an error, return false. + * Set to NULL for default handler. + */ + bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); + + /* Free field for use by the callback. */ + const void *arg; +}; + +struct pb_extension_s { + /* Type describing the extension field. Usually you'll initialize + * this to a pointer to the automatically generated structure. */ + const pb_extension_type_t *type; + + /* Destination for the decoded data. This must match the datatype + * of the extension field. */ + void *dest; + + /* Pointer to the next extension handler, or NULL. + * If this extension does not match a field, the next handler is + * automatically called. */ + pb_extension_t *next; + + /* The decoder sets this to true if the extension was found. + * Ignored for encoding. */ + bool found; +}; + +#define pb_extension_init_zero {NULL,NULL,NULL,false} + +/* Memory allocation functions to use. You can define pb_realloc and + * pb_free to custom functions if you want. */ +#ifdef PB_ENABLE_MALLOC +# ifndef pb_realloc +# define pb_realloc(ptr, size) realloc(ptr, size) +# endif +# ifndef pb_free +# define pb_free(ptr) free(ptr) +# endif +#endif + +/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ +#define PB_PROTO_HEADER_VERSION 40 + +/* These macros are used to declare pb_field_t's in the constant array. */ +/* Size of a structure member, in bytes. */ +#define pb_membersize(st, m) (sizeof ((st*)0)->m) +/* Number of entries in an array. */ +#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) +/* Delta from start of one member to the start of another member. */ +#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) + +/* Force expansion of macro value */ +#define PB_EXPAND(x) x + +/* Binding of a message field set into a specific structure */ +#define PB_BIND(msgname, structname, width) \ + const uint32_t structname ## _field_info[] PB_PROGMEM = \ + { \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ + 0 \ + }; \ + const pb_msgdesc_t* const structname ## _submsg_info[] = \ + { \ + msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ + NULL \ + }; \ + const pb_msgdesc_t structname ## _msg = \ + { \ + structname ## _field_info, \ + structname ## _submsg_info, \ + msgname ## _DEFAULT, \ + msgname ## _CALLBACK, \ + 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ + 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ + }; \ + msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) + +#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 +#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ + + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) +#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ + * 0 + tag + +/* X-macro for generating the entries in struct_field_info[] array. */ +#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +/* X-macro for generating asserts that entries fit in struct_field_info[] array. + * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), + * but it is not easily reused because of how macro substitutions work. */ +#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ + PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ + tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ + PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ + PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) + +#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ + PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) + +#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) +#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) +#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) + +#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) +#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) +#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) +#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) +#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) +#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) +#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) +#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) +#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) +#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 +#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 + +#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) +#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) +#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 +#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) + +#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) +#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) +#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) +#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) +#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) +#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) +#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) +#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) +#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) + +#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) +#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname +#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername +#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname + +#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ + PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) + +#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) +#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) +#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) +#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) +#define PB_SI_PB_LTYPE_BOOL(t) +#define PB_SI_PB_LTYPE_BYTES(t) +#define PB_SI_PB_LTYPE_DOUBLE(t) +#define PB_SI_PB_LTYPE_ENUM(t) +#define PB_SI_PB_LTYPE_UENUM(t) +#define PB_SI_PB_LTYPE_FIXED32(t) +#define PB_SI_PB_LTYPE_FIXED64(t) +#define PB_SI_PB_LTYPE_FLOAT(t) +#define PB_SI_PB_LTYPE_INT32(t) +#define PB_SI_PB_LTYPE_INT64(t) +#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) +#define PB_SI_PB_LTYPE_SFIXED32(t) +#define PB_SI_PB_LTYPE_SFIXED64(t) +#define PB_SI_PB_LTYPE_SINT32(t) +#define PB_SI_PB_LTYPE_SINT64(t) +#define PB_SI_PB_LTYPE_STRING(t) +#define PB_SI_PB_LTYPE_UINT32(t) +#define PB_SI_PB_LTYPE_UINT64(t) +#define PB_SI_PB_LTYPE_EXTENSION(t) +#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) +#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), + +/* The field descriptors use a variable width format, with width of either + * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always + * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits + * of the field type. + * + * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. + * + * Formats, listed starting with the least significant bit of the first word. + * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] + * + * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] + * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] + * + * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * + * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] + * [8-bit size_offset] [24-bit tag>>6] + * [32-bit data_offset] + * [32-bit data_size] + * [32-bit array_size] + * [32-bit reserved] + * [32-bit reserved] + * [32-bit reserved] + */ + +#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ + (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ + (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), + +#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ + (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ + (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), + +#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ + (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), + +#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ + (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ + ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ + (data_offset), (data_size), (array_size), 0, 0, 0, + +/* These assertions verify that the field information fits in the allocated space. + * The generator tries to automatically determine the correct width that can fit all + * data associated with a message. These asserts will fail only if there has been a + * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, + * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting + * descriptorsize option in .options file. + */ +#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. + */ +#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) + +#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ + PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) +#endif + + +/* Automatic picking of FIELDINFO width: + * Uses width 1 when possible, otherwise resorts to width 2. + * This is used when PB_BIND() is called with "AUTO" as the argument. + * The generator will give explicit size argument when it knows that a message + * structure grows beyond 1-word format limits. + */ +#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) +#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) +#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype +#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 +#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 +#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 +#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 +#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 +#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 +#define PB_FI_WIDTH_PB_LTYPE_INT32 1 +#define PB_FI_WIDTH_PB_LTYPE_INT64 1 +#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 +#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 +#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_STRING 2 +#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 +#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 +#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 +#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 + +/* The mapping from protobuf types to LTYPEs is done using these macros. */ +#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL +#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES +#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT +#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT +#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE +#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB +#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 +#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 +#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT +#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING +#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT +#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION +#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES + +/* These macros are used for giving out error messages. + * They are mostly a debugging aid; the main error information + * is the true/false return value from functions. + * Some code space can be saved by disabling the error + * messages if not used. + * + * PB_SET_ERROR() sets the error message if none has been set yet. + * msg must be a constant string literal. + * PB_GET_ERROR() always returns a pointer to a string. + * PB_RETURN_ERROR() sets the error and returns false from current + * function. + */ +#ifdef PB_NO_ERRMSG +#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) +#define PB_GET_ERROR(stream) "(errmsg disabled)" +#else +#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) +#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") +#endif + +#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#ifdef __cplusplus +#if __cplusplus >= 201103L +#define PB_CONSTEXPR constexpr +#else // __cplusplus >= 201103L +#define PB_CONSTEXPR +#endif // __cplusplus >= 201103L + +#if __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR inline constexpr +#else // __cplusplus >= 201703L +#define PB_INLINE_CONSTEXPR PB_CONSTEXPR +#endif // __cplusplus >= 201703L + +namespace nanopb { +// Each type will be partially specialized by the generator. +template struct MessageDescriptor; +} // namespace nanopb +#endif /* __cplusplus */ + +#endif + diff --git a/Sming/Components/nanopb/inc/pb_common.h b/Sming/Components/nanopb/inc/pb_common.h new file mode 100644 index 0000000000..58aa90f76d --- /dev/null +++ b/Sming/Components/nanopb/inc/pb_common.h @@ -0,0 +1,49 @@ +/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. + * These functions are rarely needed by applications directly. + */ + +#ifndef PB_COMMON_H_INCLUDED +#define PB_COMMON_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Initialize the field iterator structure to beginning. + * Returns false if the message type is empty. */ +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); + +/* Get a field iterator for extension field. */ +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); + +/* Same as pb_field_iter_begin(), but for const message pointer. + * Note that the pointers in pb_field_iter_t will be non-const but shouldn't + * be written to when using these functions. */ +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); + +/* Advance the iterator to the next field. + * Returns false when the iterator wraps back to the first field. */ +bool pb_field_iter_next(pb_field_iter_t *iter); + +/* Advance the iterator until it points at a field with the given tag. + * Returns false if no such field exists. */ +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); + +/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. + * There can be only one extension range field per message. */ +bool pb_field_iter_find_extension(pb_field_iter_t *iter); + +#ifdef PB_VALIDATE_UTF8 +/* Validate UTF-8 text string */ +bool pb_validate_utf8(const char *s); +#endif + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif + diff --git a/Sming/Components/nanopb/inc/pb_decode.h b/Sming/Components/nanopb/inc/pb_decode.h new file mode 100644 index 0000000000..9b70c8b345 --- /dev/null +++ b/Sming/Components/nanopb/inc/pb_decode.h @@ -0,0 +1,196 @@ +/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. + * The main function is pb_decode. You also need an input stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_DECODE_H_INCLUDED +#define PB_DECODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom input streams. You will need to provide + * a callback function to read the bytes from your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause decoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer), + * and rely on pb_read to verify that no-body reads past bytes_left. + * 3) Your callback may be used with substreams, in which case bytes_left + * is different than from the main stream. Don't use bytes_left to compute + * any pointers. + */ +struct pb_istream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + */ + int *callback; +#else + bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); +#endif + + void *state; /* Free field for use by callback implementation */ + size_t bytes_left; + +#ifndef PB_NO_ERRMSG + const char *errmsg; +#endif +}; + +#ifndef PB_NO_ERRMSG +#define PB_ISTREAM_EMPTY {0,0,0,0} +#else +#define PB_ISTREAM_EMPTY {0,0,0} +#endif + +/*************************** + * Main decoding functions * + ***************************/ + +/* Decode a single protocol buffers message from input stream into a C structure. + * Returns true on success, false on any failure. + * The actual struct pointed to by dest must match the description in fields. + * Callback fields of the destination structure must be initialized by caller. + * All other fields will be initialized by this function. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_istream_t stream; + * + * // ... read some data into buffer ... + * + * stream = pb_istream_from_buffer(buffer, count); + * pb_decode(&stream, MyMessage_fields, &msg); + */ +bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); + +/* Extended version of pb_decode, with several options to control + * the decoding process: + * + * PB_DECODE_NOINIT: Do not initialize the fields to default values. + * This is slightly faster if you do not need the default + * values and instead initialize the structure to 0 using + * e.g. memset(). This can also be used for merging two + * messages, i.e. combine already existing data with new + * values. + * + * PB_DECODE_DELIMITED: Input message starts with the message size as varint. + * Corresponds to parseDelimitedFrom() in Google's + * protobuf API. + * + * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows + * reading null terminated messages. + * NOTE: Until nanopb-0.4.0, pb_decode() also allows + * null-termination. This behaviour is not supported in + * most other protobuf implementations, so PB_DECODE_DELIMITED + * is a better option for compatibility. + * + * Multiple flags can be combined with bitwise or (| operator) + */ +#define PB_DECODE_NOINIT 0x01U +#define PB_DECODE_DELIMITED 0x02U +#define PB_DECODE_NULLTERMINATED 0x04U +bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) +#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) +#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) +#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) + +#ifdef PB_ENABLE_MALLOC +/* Release any allocated pointer fields. If you use dynamic allocation, you should + * call this for any successfully decoded message when you are done with it. If + * pb_decode() returns with an error, the message is already released. + */ +void pb_release(const pb_msgdesc_t *fields, void *dest_struct); +#endif + + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an input stream for reading from a memory buffer. + * + * msglen should be the actual length of the message, not the full size of + * allocated buffer. + * + * Alternatively, you can use a custom stream that reads directly from e.g. + * a file or a network socket. + */ +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); + +/* Function to read from a pb_istream_t. You can use this if you need to + * read some custom header data, or to read data in field callbacks. + */ +bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Decode the tag for the next field in the stream. Gives the wire type and + * field tag. At end of the message, returns false and sets eof to true. */ +bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); + +/* Skip the field payload data, given the wire type. */ +bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); + +/* Decode an integer in the varint format. This works for enum, int32, + * int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); +#else +#define pb_decode_varint pb_decode_varint32 +#endif + +/* Decode an integer in the varint format. This works for enum, int32, + * and uint32 field types. */ +bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); + +/* Decode a bool value in varint format. */ +bool pb_decode_bool(pb_istream_t *stream, bool *dest); + +/* Decode an integer in the zig-zagged svarint format. This works for sint32 + * and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); +#else +bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); +#endif + +/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to + * a 4-byte wide C variable. */ +bool pb_decode_fixed32(pb_istream_t *stream, void *dest); + +#ifndef PB_WITHOUT_64BIT +/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to + * a 8-byte wide C variable. */ +bool pb_decode_fixed64(pb_istream_t *stream, void *dest); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Decode a double value into float variable. */ +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); +#endif + +/* Make a limited-length substream for reading a PB_WT_STRING field. */ +bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); +bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Sming/Components/nanopb/inc/pb_encode.h b/Sming/Components/nanopb/inc/pb_encode.h new file mode 100644 index 0000000000..88e246a2d9 --- /dev/null +++ b/Sming/Components/nanopb/inc/pb_encode.h @@ -0,0 +1,185 @@ +/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. + * The main function is pb_encode. You also need an output stream, and the + * field descriptions created by nanopb_generator.py. + */ + +#ifndef PB_ENCODE_H_INCLUDED +#define PB_ENCODE_H_INCLUDED + +#include "pb.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Structure for defining custom output streams. You will need to provide + * a callback function to write the bytes to your storage, which can be + * for example a file or a network socket. + * + * The callback must conform to these rules: + * + * 1) Return false on IO errors. This will cause encoding to abort. + * 2) You can use state to store your own data (e.g. buffer pointer). + * 3) pb_write will update bytes_written after your callback runs. + * 4) Substreams will modify max_size and bytes_written. Don't use them + * to calculate any pointers. + */ +struct pb_ostream_s +{ +#ifdef PB_BUFFER_ONLY + /* Callback pointer is not used in buffer-only configuration. + * Having an int pointer here allows binary compatibility but + * gives an error if someone tries to assign callback function. + * Also, NULL pointer marks a 'sizing stream' that does not + * write anything. + */ + int *callback; +#else + bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +#endif + void *state; /* Free field for use by callback implementation. */ + size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ + size_t bytes_written; /* Number of bytes written so far. */ + +#ifndef PB_NO_ERRMSG + const char *errmsg; +#endif +}; + +/*************************** + * Main encoding functions * + ***************************/ + +/* Encode a single protocol buffers message from C structure into a stream. + * Returns true on success, false on any failure. + * The actual struct pointed to by src_struct must match the description in fields. + * All required fields in the struct are assumed to have been filled in. + * + * Example usage: + * MyMessage msg = {}; + * uint8_t buffer[64]; + * pb_ostream_t stream; + * + * msg.field1 = 42; + * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); + * pb_encode(&stream, MyMessage_fields, &msg); + */ +bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +/* Extended version of pb_encode, with several options to control the + * encoding process: + * + * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. + * Corresponds to writeDelimitedTo() in Google's + * protobuf API. + * + * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. + * NOTE: This behaviour is not supported in most other + * protobuf implementations, so PB_ENCODE_DELIMITED + * is a better option for compatibility. + */ +#define PB_ENCODE_DELIMITED 0x02U +#define PB_ENCODE_NULLTERMINATED 0x04U +bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) +#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) + +/* Encode the message to get the size of the encoded data, but do not store + * the data. */ +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); + +/************************************** + * Functions for manipulating streams * + **************************************/ + +/* Create an output stream for writing into a memory buffer. + * The number of bytes written can be found in stream.bytes_written after + * encoding the message. + * + * Alternatively, you can use a custom stream that writes directly to e.g. + * a file or a network socket. + */ +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); + +/* Pseudo-stream for measuring the size of a message without actually storing + * the encoded data. + * + * Example usage: + * MyMessage msg = {}; + * pb_ostream_t stream = PB_OSTREAM_SIZING; + * pb_encode(&stream, MyMessage_fields, &msg); + * printf("Message size is %d\n", stream.bytes_written); + */ +#ifndef PB_NO_ERRMSG +#define PB_OSTREAM_SIZING {0,0,0,0,0} +#else +#define PB_OSTREAM_SIZING {0,0,0,0} +#endif + +/* Function to write into a pb_ostream_t stream. You can use this if you need + * to append or prepend some custom headers to the message. + */ +bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); + + +/************************************************ + * Helper functions for writing field callbacks * + ************************************************/ + +/* Encode field header based on type and field number defined in the field + * structure. Call this from the callback before writing out field contents. */ +bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); + +/* Encode field header by manually specifing wire type. You need to use this + * if you want to write out packed arrays from a callback field. */ +bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); + +/* Encode an integer in the varint format. + * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); +#else +bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); +#endif + +/* Encode an integer in the zig-zagged svarint format. + * This works for sint32 and sint64. */ +#ifndef PB_WITHOUT_64BIT +bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); +#else +bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); +#endif + +/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ +bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); + +/* Encode a fixed32, sfixed32 or float value. + * You need to pass a pointer to a 4-byte wide C variable. */ +bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); + +#ifndef PB_WITHOUT_64BIT +/* Encode a fixed64, sfixed64 or double value. + * You need to pass a pointer to a 8-byte wide C variable. */ +bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); +#endif + +#ifdef PB_CONVERT_DOUBLE_FLOAT +/* Encode a float value so that it appears like a double in the encoded + * message. */ +bool pb_encode_float_as_double(pb_ostream_t *stream, float value); +#endif + +/* Encode a submessage field. + * You need to pass the pb_field_t array and pointer to struct, just like + * with pb_encode(). This internally encodes the submessage twice, first to + * calculate message size and then to actually write it out. + */ +bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/Sming/Components/nanopb/src/pb_common.c b/Sming/Components/nanopb/src/pb_common.c new file mode 100644 index 0000000000..6aee76b1ef --- /dev/null +++ b/Sming/Components/nanopb/src/pb_common.c @@ -0,0 +1,388 @@ +/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. + * + * 2014 Petteri Aimonen + */ + +#include "pb_common.h" + +static bool load_descriptor_values(pb_field_iter_t *iter) +{ + uint32_t word0; + uint32_t data_offset; + int_least8_t size_offset; + + if (iter->index >= iter->descriptor->field_count) + return false; + + word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + iter->type = (pb_type_t)((word0 >> 8) & 0xFF); + + switch(word0 & 3) + { + case 0: { + /* 1-word format */ + iter->array_size = 1; + iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); + size_offset = (int_least8_t)((word0 >> 24) & 0x0F); + data_offset = (word0 >> 16) & 0xFF; + iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); + break; + } + + case 1: { + /* 2-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + + iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); + size_offset = (int_least8_t)((word0 >> 28) & 0x0F); + data_offset = word1 & 0xFFFF; + iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); + break; + } + + case 2: { + /* 4-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + + iter->array_size = (pb_size_t)(word0 >> 16); + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + + default: { + /* 8-word format */ + uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); + uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); + uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); + uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); + + iter->array_size = (pb_size_t)word4; + iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); + size_offset = (int_least8_t)(word1 & 0xFF); + data_offset = word2; + iter->data_size = (pb_size_t)word3; + break; + } + } + + if (!iter->message) + { + /* Avoid doing arithmetic on null pointers, it is undefined */ + iter->pField = NULL; + iter->pSize = NULL; + } + else + { + iter->pField = (char*)iter->message + data_offset; + + if (size_offset) + { + iter->pSize = (char*)iter->pField - size_offset; + } + else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && + (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || + PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) + { + /* Fixed count array */ + iter->pSize = &iter->array_size; + } + else + { + iter->pSize = NULL; + } + + if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) + { + iter->pData = *(void**)iter->pField; + } + else + { + iter->pData = iter->pField; + } + } + + if (PB_LTYPE_IS_SUBMSG(iter->type)) + { + iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; + } + else + { + iter->submsg_desc = NULL; + } + + return true; +} + +static void advance_iterator(pb_field_iter_t *iter) +{ + iter->index++; + + if (iter->index >= iter->descriptor->field_count) + { + /* Restart */ + iter->index = 0; + iter->field_info_index = 0; + iter->submessage_index = 0; + iter->required_field_index = 0; + } + else + { + /* Increment indexes based on previous field type. + * All field info formats have the following fields: + * - lowest 2 bits tell the amount of words in the descriptor (2^n words) + * - bits 2..7 give the lowest bits of tag number. + * - bits 8..15 give the field type. + */ + uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; + pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); + + /* Add to fields. + * The cast to pb_size_t is needed to avoid -Wconversion warning. + * Because the data is is constants from generator, there is no danger of overflow. + */ + iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); + iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); + iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); + } +} + +bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) +{ + memset(iter, 0, sizeof(*iter)); + + iter->descriptor = desc; + iter->message = message; + + return load_descriptor_values(iter); +} + +bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) +{ + const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; + bool status; + + uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); + if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) + { + /* For pointer extensions, the pointer is stored directly + * in the extension structure. This avoids having an extra + * indirection. */ + status = pb_field_iter_begin(iter, msg, &extension->dest); + } + else + { + status = pb_field_iter_begin(iter, msg, extension->dest); + } + + iter->pSize = &extension->found; + return status; +} + +bool pb_field_iter_next(pb_field_iter_t *iter) +{ + advance_iterator(iter); + (void)load_descriptor_values(iter); + return iter->index != 0; +} + +bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) +{ + if (iter->tag == tag) + { + return true; /* Nothing to do, correct field already. */ + } + else if (tag > iter->descriptor->largest_tag) + { + return false; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + if (tag < iter->tag) + { + /* Fields are in tag number order, so we know that tag is between + * 0 and our start position. Setting index to end forces + * advance_iterator() call below to restart from beginning. */ + iter->index = iter->descriptor->field_count; + } + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for tag number match */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) + { + /* Good candidate, check further */ + (void)load_descriptor_values(iter); + + if (iter->tag == tag && + PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) + { + /* Found it */ + return true; + } + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +bool pb_field_iter_find_extension(pb_field_iter_t *iter) +{ + if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) + { + return true; + } + else + { + pb_size_t start = iter->index; + uint32_t fieldinfo; + + do + { + /* Advance iterator but don't load values yet */ + advance_iterator(iter); + + /* Do fast check for field type */ + fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); + + if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) + { + return load_descriptor_values(iter); + } + } while (iter->index != start); + + /* Searched all the way back to start, and found nothing. */ + (void)load_descriptor_values(iter); + return false; + } +} + +static void *pb_const_cast(const void *p) +{ + /* Note: this casts away const, in order to use the common field iterator + * logic for both encoding and decoding. The cast is done using union + * to avoid spurious compiler warnings. */ + union { + void *p1; + const void *p2; + } t; + t.p2 = p; + return t.p1; +} + +bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) +{ + return pb_field_iter_begin(iter, desc, pb_const_cast(message)); +} + +bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) +{ + return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); +} + +bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) +{ + if (field->data_size == sizeof(pb_callback_t)) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + + if (pCallback != NULL) + { + if (istream != NULL && pCallback->funcs.decode != NULL) + { + return pCallback->funcs.decode(istream, field, &pCallback->arg); + } + + if (ostream != NULL && pCallback->funcs.encode != NULL) + { + return pCallback->funcs.encode(ostream, field, &pCallback->arg); + } + } + } + + return true; /* Success, but didn't do anything */ + +} + +#ifdef PB_VALIDATE_UTF8 + +/* This function checks whether a string is valid UTF-8 text. + * + * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c + * Original copyright: Markus Kuhn 2005-03-30 + * Licensed under "Short code license", which allows use under MIT license or + * any compatible with it. + */ + +bool pb_validate_utf8(const char *str) +{ + const pb_byte_t *s = (const pb_byte_t*)str; + while (*s) + { + if (*s < 0x80) + { + /* 0xxxxxxx */ + s++; + } + else if ((s[0] & 0xe0) == 0xc0) + { + /* 110XXXXx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[0] & 0xfe) == 0xc0) /* overlong? */ + return false; + else + s += 2; + } + else if ((s[0] & 0xf0) == 0xe0) + { + /* 1110XXXX 10Xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ + (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ + (s[0] == 0xef && s[1] == 0xbf && + (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ + return false; + else + s += 3; + } + else if ((s[0] & 0xf8) == 0xf0) + { + /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ + if ((s[1] & 0xc0) != 0x80 || + (s[2] & 0xc0) != 0x80 || + (s[3] & 0xc0) != 0x80 || + (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ + (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ + return false; + else + s += 4; + } + else + { + return false; + } + } + + return true; +} + +#endif + diff --git a/Sming/Components/nanopb/src/pb_decode.c b/Sming/Components/nanopb/src/pb_decode.c new file mode 100644 index 0000000000..2dab0e307e --- /dev/null +++ b/Sming/Components/nanopb/src/pb_decode.c @@ -0,0 +1,1685 @@ +/* pb_decode.c -- decode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers and gcc before 3.4.0 just + * ignore the annotation. + */ +#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) + #define checkreturn +#else + #define checkreturn __attribute__((warn_unused_result)) +#endif + +#include "pb.h" +#include "pb_decode.h" +#include "pb_common.h" + +/************************************** + * Declarations internal to this file * + **************************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); +static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); +static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); +static bool pb_message_set_to_defaults(pb_field_iter_t *iter); +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_skip_varint(pb_istream_t *stream); +static bool checkreturn pb_skip_string(pb_istream_t *stream); + +#ifdef PB_ENABLE_MALLOC +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); +static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); +static void pb_release_single_field(pb_field_iter_t *field); +#endif + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +#define PB_WT_PACKED ((pb_wire_type_t)0xFF) + +typedef struct { + uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; +} pb_fields_seen_t; + +/******************************* + * pb_istream_t implementation * + *******************************/ + +static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + size_t i; + const pb_byte_t *source = (const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + count; + + if (buf != NULL) + { + for (i = 0; i < count; i++) + buf[i] = source[i]; + } + + return true; +} + +bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) +{ + if (count == 0) + return true; + +#ifndef PB_BUFFER_ONLY + if (buf == NULL && stream->callback != buf_read) + { + /* Skip input bytes */ + pb_byte_t tmp[16]; + while (count > 16) + { + if (!pb_read(stream, tmp, 16)) + return false; + + count -= 16; + } + + return pb_read(stream, tmp, count); + } +#endif + + if (stream->bytes_left < count) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!buf_read(stream, buf, count)) + return false; +#endif + + stream->bytes_left -= count; + return true; +} + +/* Read a single byte from input stream. buf may not be NULL. + * This is an optimization for the varint decoding. */ +static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) +{ + if (stream->bytes_left == 0) + PB_RETURN_ERROR(stream, "end-of-stream"); + +#ifndef PB_BUFFER_ONLY + if (!stream->callback(stream, buf, 1)) + PB_RETURN_ERROR(stream, "io error"); +#else + *buf = *(const pb_byte_t*)stream->state; + stream->state = (pb_byte_t*)stream->state + 1; +#endif + + stream->bytes_left--; + + return true; +} + +pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) +{ + pb_istream_t stream; + /* Cast away the const from buf without a compiler error. We are + * careful to use it only in a const manner in the callbacks. + */ + union { + void *state; + const void *c_state; + } state; +#ifdef PB_BUFFER_ONLY + stream.callback = NULL; +#else + stream.callback = &buf_read; +#endif + state.c_state = buf; + stream.state = state.state; + stream.bytes_left = msglen; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +/******************** + * Helper functions * + ********************/ + +static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) +{ + pb_byte_t byte; + uint32_t result; + + if (!pb_readbyte(stream, &byte)) + { + if (stream->bytes_left == 0) + { + if (eof) + { + *eof = true; + } + } + + return false; + } + + if ((byte & 0x80) == 0) + { + /* Quick case, 1 byte value */ + result = byte; + } + else + { + /* Multibyte case */ + uint_fast8_t bitpos = 7; + result = byte & 0x7F; + + do + { + if (!pb_readbyte(stream, &byte)) + return false; + + if (bitpos >= 32) + { + /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ + pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; + + if ((byte & 0x7F) != 0x00 && ((result >> 31) == 0 || byte != sign_extension)) + { + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + else + { + result |= (uint32_t)(byte & 0x7F) << bitpos; + } + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + if (bitpos == 35 && (byte & 0x70) != 0) + { + /* The last byte was at bitpos=28, so only bottom 4 bits fit. */ + PB_RETURN_ERROR(stream, "varint overflow"); + } + } + + *dest = result; + return true; +} + +bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) +{ + return pb_decode_varint32_eof(stream, dest, NULL); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) +{ + pb_byte_t byte; + uint_fast8_t bitpos = 0; + uint64_t result = 0; + + do + { + if (bitpos >= 64) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_readbyte(stream, &byte)) + return false; + + result |= (uint64_t)(byte & 0x7F) << bitpos; + bitpos = (uint_fast8_t)(bitpos + 7); + } while (byte & 0x80); + + *dest = result; + return true; +} +#endif + +bool checkreturn pb_skip_varint(pb_istream_t *stream) +{ + pb_byte_t byte; + do + { + if (!pb_read(stream, &byte, 1)) + return false; + } while (byte & 0x80); + return true; +} + +bool checkreturn pb_skip_string(pb_istream_t *stream) +{ + uint32_t length; + if (!pb_decode_varint32(stream, &length)) + return false; + + if ((size_t)length != length) + { + PB_RETURN_ERROR(stream, "size too large"); + } + + return pb_read(stream, NULL, (size_t)length); +} + +bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) +{ + uint32_t temp; + *eof = false; + *wire_type = (pb_wire_type_t) 0; + *tag = 0; + + if (!pb_decode_varint32_eof(stream, &temp, eof)) + { + return false; + } + + *tag = temp >> 3; + *wire_type = (pb_wire_type_t)(temp & 7); + return true; +} + +bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) +{ + switch (wire_type) + { + case PB_WT_VARINT: return pb_skip_varint(stream); + case PB_WT_64BIT: return pb_read(stream, NULL, 8); + case PB_WT_STRING: return pb_skip_string(stream); + case PB_WT_32BIT: return pb_read(stream, NULL, 4); + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Read a raw value to buffer, for the purpose of passing it to callback as + * a substream. Size is maximum size on call, and actual size on return. + */ +static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) +{ + size_t max_size = *size; + switch (wire_type) + { + case PB_WT_VARINT: + *size = 0; + do + { + (*size)++; + if (*size > max_size) + PB_RETURN_ERROR(stream, "varint overflow"); + + if (!pb_read(stream, buf, 1)) + return false; + } while (*buf++ & 0x80); + return true; + + case PB_WT_64BIT: + *size = 8; + return pb_read(stream, buf, 8); + + case PB_WT_32BIT: + *size = 4; + return pb_read(stream, buf, 4); + + case PB_WT_STRING: + /* Calling read_raw_value with a PB_WT_STRING is an error. + * Explicitly handle this case and fallthrough to default to avoid + * compiler warnings. + */ + + default: PB_RETURN_ERROR(stream, "invalid wire_type"); + } +} + +/* Decode string length from stream and return a substream with limited length. + * Remember to close the substream using pb_close_string_substream(). + */ +bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + uint32_t size; + if (!pb_decode_varint32(stream, &size)) + return false; + + *substream = *stream; + if (substream->bytes_left < size) + PB_RETURN_ERROR(stream, "parent stream too short"); + + substream->bytes_left = (size_t)size; + stream->bytes_left -= (size_t)size; + return true; +} + +bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) +{ + if (substream->bytes_left) { + if (!pb_read(substream, NULL, substream->bytes_left)) + return false; + } + + stream->state = substream->state; + +#ifndef PB_NO_ERRMSG + stream->errmsg = substream->errmsg; +#endif + return true; +} + +/************************* + * Decode a single field * + *************************/ + +static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_varint(stream, field); + + case PB_LTYPE_FIXED32: + if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_decode_fixed32(stream, field->pData); + + case PB_LTYPE_FIXED64: + if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) + PB_RETURN_ERROR(stream, "wrong wire type"); + +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float)) + { + return pb_decode_double_as_float(stream, (float*)field->pData); + } +#endif + +#ifdef PB_WITHOUT_64BIT + PB_RETURN_ERROR(stream, "invalid data_size"); +#else + return pb_decode_fixed64(stream, field->pData); +#endif + + case PB_LTYPE_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_bytes(stream, field); + + case PB_LTYPE_STRING: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + if (wire_type != PB_WT_STRING) + PB_RETURN_ERROR(stream, "wrong wire type"); + + return pb_dec_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_OPTIONAL: + if (field->pSize != NULL) + *(bool*)field->pSize = true; + return decode_basic_field(stream, wire_type, field); + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array */ + bool status = true; + pb_istream_t substream; + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left > 0 && *size < field->array_size) + { + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + (*size)++; + field->pData = (char*)field->pData + field->data_size; + } + + if (substream.bytes_left != 0) + PB_RETURN_ERROR(stream, "array overflow"); + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Repeated field */ + pb_size_t *size = (pb_size_t*)field->pSize; + field->pData = (char*)field->pField + field->data_size * (*size); + + if ((*size)++ >= field->array_size) + PB_RETURN_ERROR(stream, "array overflow"); + + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_ONEOF: + *(pb_size_t*)field->pSize = field->tag; + if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * This is because the callbacks might otherwise have values + * from some other union field. + * If callbacks are needed inside oneof field, use .proto + * option submsg_callback to have a separate callback function + * that can set the fields before submessage is decoded. + * pb_dec_submessage() will set any default values. */ + memset(field->pData, 0, (size_t)field->data_size); + } + + return decode_basic_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +#ifdef PB_ENABLE_MALLOC +/* Allocate storage for the field and store the pointer at iter->pData. + * array_size is the number of entries to reserve in an array. + * Zero size is not allowed, use pb_free() for releasing. + */ +static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) +{ + void *ptr = *(void**)pData; + + if (data_size == 0 || array_size == 0) + PB_RETURN_ERROR(stream, "invalid size"); + +#ifdef __AVR__ + /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 + * Realloc to size of 1 byte can cause corruption of the malloc structures. + */ + if (data_size == 1 && array_size == 1) + { + data_size = 2; + } +#endif + + /* Check for multiplication overflows. + * This code avoids the costly division if the sizes are small enough. + * Multiplication is safe as long as only half of bits are set + * in either multiplicand. + */ + { + const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); + if (data_size >= check_limit || array_size >= check_limit) + { + const size_t size_max = (size_t)-1; + if (size_max / array_size < data_size) + { + PB_RETURN_ERROR(stream, "size too large"); + } + } + } + + /* Allocate new or expand previous allocation */ + /* Note: on failure the old pointer will remain in the structure, + * the message must be freed by caller also on error return. */ + ptr = pb_realloc(ptr, array_size * data_size); + if (ptr == NULL) + PB_RETURN_ERROR(stream, "realloc failed"); + + *(void**)pData = ptr; + return true; +} + +/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ +static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + *(void**)pItem = NULL; + } + else if (PB_LTYPE_IS_SUBMSG(field->type)) + { + /* We memset to zero so that any callbacks are set to NULL. + * Default values will be set by pb_dec_submessage(). */ + memset(pItem, 0, field->data_size); + } +} +#endif + +static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifndef PB_ENABLE_MALLOC + PB_UNUSED(wire_type); + PB_UNUSED(field); + PB_RETURN_ERROR(stream, "no malloc support"); +#else + switch (PB_HTYPE(field->type)) + { + case PB_HTYPE_REQUIRED: + case PB_HTYPE_OPTIONAL: + case PB_HTYPE_ONEOF: + if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) + { + /* Duplicate field, have to release the old allocation first. */ + /* FIXME: Does this work correctly for oneofs? */ + pb_release_single_field(field); + } + + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = field->tag; + } + + if (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES) + { + /* pb_dec_string and pb_dec_bytes handle allocation themselves */ + field->pData = field->pField; + return decode_basic_field(stream, wire_type, field); + } + else + { + if (!allocate_field(stream, field->pField, field->data_size, 1)) + return false; + + field->pData = *(void**)field->pField; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + case PB_HTYPE_REPEATED: + if (wire_type == PB_WT_STRING + && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Packed array, multiple items come in at once. */ + bool status = true; + pb_size_t *size = (pb_size_t*)field->pSize; + size_t allocated_size = *size; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + while (substream.bytes_left) + { + if (*size == PB_SIZE_MAX) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = "too many array entries"; +#endif + status = false; + break; + } + + if ((size_t)*size + 1 > allocated_size) + { + /* Allocate more storage. This tries to guess the + * number of remaining entries. Round the division + * upwards. */ + size_t remain = (substream.bytes_left - 1) / field->data_size + 1; + if (remain < PB_SIZE_MAX - allocated_size) + allocated_size += remain; + else + allocated_size += 1; + + if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) + { + status = false; + break; + } + } + + /* Decode the array entry */ + field->pData = *(char**)field->pField + field->data_size * (*size); + initialize_pointer_field(field->pData, field); + if (!decode_basic_field(&substream, PB_WT_PACKED, field)) + { + status = false; + break; + } + + (*size)++; + } + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; + } + else + { + /* Normal repeated field, i.e. only one item at a time. */ + pb_size_t *size = (pb_size_t*)field->pSize; + + if (*size == PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "too many array entries"); + + if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) + return false; + + field->pData = *(char**)field->pField + field->data_size * (*size); + (*size)++; + initialize_pointer_field(field->pData, field); + return decode_basic_field(stream, wire_type, field); + } + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +#endif +} + +static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ + if (!field->descriptor->field_callback) + return pb_skip_field(stream, wire_type); + + if (wire_type == PB_WT_STRING) + { + pb_istream_t substream; + size_t prev_bytes_left; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + do + { + prev_bytes_left = substream.bytes_left; + if (!field->descriptor->field_callback(&substream, NULL, field)) + PB_RETURN_ERROR(stream, "callback failed"); + } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return true; + } + else + { + /* Copy the single scalar value to stack. + * This is required so that we can limit the stream length, + * which in turn allows to use same callback for packed and + * not-packed fields. */ + pb_istream_t substream; + pb_byte_t buffer[10]; + size_t size = sizeof(buffer); + + if (!read_raw_value(stream, wire_type, buffer, &size)) + return false; + substream = pb_istream_from_buffer(buffer, size); + + return field->descriptor->field_callback(&substream, NULL, field); + } +} + +static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) +{ +#ifdef PB_ENABLE_MALLOC + /* When decoding an oneof field, check if there is old data that must be + * released first. */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (!pb_release_union_field(stream, field)) + return false; + } +#endif + + switch (PB_ATYPE(field->type)) + { + case PB_ATYPE_STATIC: + return decode_static_field(stream, wire_type, field); + + case PB_ATYPE_POINTER: + return decode_pointer_field(stream, wire_type, field); + + case PB_ATYPE_CALLBACK: + return decode_callback_field(stream, wire_type, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_decoder(pb_istream_t *stream, + pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + if (iter.tag != tag || !iter.message) + return true; + + extension->found = true; + return decode_field(stream, wire_type, &iter); +} + +/* Try to decode an unknown field as an extension field. Tries each extension + * decoder in turn, until one of them handles the field or loop ends. */ +static bool checkreturn decode_extension(pb_istream_t *stream, + uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) +{ + size_t pos = stream->bytes_left; + + while (extension != NULL && pos == stream->bytes_left) + { + bool status; + if (extension->type->decode) + status = extension->type->decode(stream, extension, tag, wire_type); + else + status = default_extension_decoder(stream, extension, tag, wire_type); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/* Initialize message fields to default values, recursively */ +static bool pb_field_set_to_default(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + pb_extension_t *ext = *(pb_extension_t* const *)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + ext->found = false; + if (!pb_message_set_to_defaults(&ext_iter)) + return false; + } + ext = ext->next; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + bool init_data = true; + if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Set has_field to false. Still initialize the optional field + * itself also. */ + *(bool*)field->pSize = false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* REPEATED: Set array count to 0, no need to initialize contents. + ONEOF: Set which_field to 0. */ + *(pb_size_t*)field->pSize = 0; + init_data = false; + } + + if (init_data) + { + if (PB_LTYPE_IS_SUBMSG(field->type) && + (field->submsg_desc->default_value != NULL || + field->submsg_desc->field_callback != NULL || + field->submsg_desc->submsg_info[0] != NULL)) + { + /* Initialize submessage to defaults. + * Only needed if it has default values + * or callback/submessage fields. */ + pb_field_iter_t submsg_iter; + if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) + { + if (!pb_message_set_to_defaults(&submsg_iter)) + return false; + } + } + else + { + /* Initialize to zeros */ + memset(field->pData, 0, (size_t)field->data_size); + } + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + /* Initialize the pointer to NULL. */ + *(void**)field->pField = NULL; + + /* Initialize array count to 0. */ + if (PB_HTYPE(type) == PB_HTYPE_REPEATED || + PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + *(pb_size_t*)field->pSize = 0; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + /* Don't overwrite callback */ + } + + return true; +} + +static bool pb_message_set_to_defaults(pb_field_iter_t *iter) +{ + pb_istream_t defstream = PB_ISTREAM_EMPTY; + uint32_t tag = 0; + pb_wire_type_t wire_type = PB_WT_VARINT; + bool eof; + + if (iter->descriptor->default_value) + { + defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + } + + do + { + if (!pb_field_set_to_default(iter)) + return false; + + if (tag != 0 && iter->tag == tag) + { + /* We have a default value for this field in the defstream */ + if (!decode_field(&defstream, wire_type, iter)) + return false; + if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) + return false; + + if (iter->pSize) + *(bool*)iter->pSize = false; + } + } while (pb_field_iter_next(iter)); + + return true; +} + +/********************* + * Decode all fields * + *********************/ + +static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + uint32_t extension_range_start = 0; + pb_extension_t *extensions = NULL; + + /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed + * count field. This can only handle _one_ repeated fixed count field that + * is unpacked and unordered among other (non repeated fixed count) fields. + */ + pb_size_t fixed_count_field = PB_SIZE_MAX; + pb_size_t fixed_count_size = 0; + pb_size_t fixed_count_total_size = 0; + + pb_fields_seen_t fields_seen = {{0, 0}}; + const uint32_t allbits = ~(uint32_t)0; + pb_field_iter_t iter; + + if (pb_field_iter_begin(&iter, fields, dest_struct)) + { + if ((flags & PB_DECODE_NOINIT) == 0) + { + if (!pb_message_set_to_defaults(&iter)) + PB_RETURN_ERROR(stream, "failed to set defaults"); + } + } + + while (stream->bytes_left) + { + uint32_t tag; + pb_wire_type_t wire_type; + bool eof; + + if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) + { + if (eof) + break; + else + return false; + } + + if (tag == 0) + { + if (flags & PB_DECODE_NULLTERMINATED) + { + break; + } + else + { + PB_RETURN_ERROR(stream, "zero tag"); + } + } + + if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* No match found, check if it matches an extension. */ + if (extension_range_start == 0) + { + if (pb_field_iter_find_extension(&iter)) + { + extensions = *(pb_extension_t* const *)iter.pData; + extension_range_start = iter.tag; + } + + if (!extensions) + { + extension_range_start = (uint32_t)-1; + } + } + + if (tag >= extension_range_start) + { + size_t pos = stream->bytes_left; + + if (!decode_extension(stream, tag, wire_type, extensions)) + return false; + + if (pos != stream->bytes_left) + { + /* The field was handled */ + continue; + } + } + + /* No match found, skip data */ + if (!pb_skip_field(stream, wire_type)) + return false; + continue; + } + + /* If a repeated fixed count field was found, get size from + * 'fixed_count_field' as there is no counter contained in the struct. + */ + if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) + { + if (fixed_count_field != iter.index) { + /* If the new fixed count field does not match the previous one, + * check that the previous one is NULL or that it finished + * receiving all the expected data. + */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + fixed_count_field = iter.index; + fixed_count_size = 0; + fixed_count_total_size = iter.array_size; + } + + iter.pSize = &fixed_count_size; + } + + if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED + && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) + { + uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); + fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; + } + + if (!decode_field(stream, wire_type, &iter)) + return false; + } + + /* Check that all elements of the last decoded fixed count field were present. */ + if (fixed_count_field != PB_SIZE_MAX && + fixed_count_size != fixed_count_total_size) + { + PB_RETURN_ERROR(stream, "wrong size for fixed count field"); + } + + /* Check that all required fields were present. */ + { + pb_size_t req_field_count = iter.descriptor->required_field_count; + + if (req_field_count > 0) + { + pb_size_t i; + + if (req_field_count > PB_MAX_REQUIRED_FIELDS) + req_field_count = PB_MAX_REQUIRED_FIELDS; + + /* Check the whole words */ + for (i = 0; i < (req_field_count >> 5); i++) + { + if (fields_seen.bitfield[i] != allbits) + PB_RETURN_ERROR(stream, "missing required field"); + } + + /* Check the remaining bits (if any) */ + if ((req_field_count & 31) != 0) + { + if (fields_seen.bitfield[req_field_count >> 5] != + (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) + { + PB_RETURN_ERROR(stream, "missing required field"); + } + } + } + } + + return true; +} + +bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) +{ + bool status; + + if ((flags & PB_DECODE_DELIMITED) == 0) + { + status = pb_decode_inner(stream, fields, dest_struct, flags); + } + else + { + pb_istream_t substream; + if (!pb_make_string_substream(stream, &substream)) + return false; + + status = pb_decode_inner(&substream, fields, dest_struct, flags); + + if (!pb_close_string_substream(stream, &substream)) + return false; + } + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) +{ + bool status; + + status = pb_decode_inner(stream, fields, dest_struct, 0); + +#ifdef PB_ENABLE_MALLOC + if (!status) + pb_release(fields, dest_struct); +#endif + + return status; +} + +#ifdef PB_ENABLE_MALLOC +/* Given an oneof field, if there has already been a field inside this oneof, + * release it before overwriting with a different one. */ +static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) +{ + pb_field_iter_t old_field = *field; + pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ + pb_size_t new_tag = field->tag; /* New which_ value */ + + if (old_tag == 0) + return true; /* Ok, no old data in union */ + + if (old_tag == new_tag) + return true; /* Ok, old data is of same type => merge */ + + /* Release old data. The find can fail if the message struct contains + * invalid data. */ + if (!pb_field_iter_find(&old_field, old_tag)) + PB_RETURN_ERROR(stream, "invalid union tag"); + + pb_release_single_field(&old_field); + + return true; +} + +static void pb_release_single_field(pb_field_iter_t *field) +{ + pb_type_t type; + type = field->type; + + if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + if (*(pb_size_t*)field->pSize != field->tag) + return; /* This is not the current field in the union */ + } + + /* Release anything contained inside an extension or submsg. + * This has to be done even if the submsg itself is statically + * allocated. */ + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + /* Release fields from all extensions in the linked list */ + pb_extension_t *ext = *(pb_extension_t**)field->pData; + while (ext != NULL) + { + pb_field_iter_t ext_iter; + if (pb_field_iter_begin_extension(&ext_iter, ext)) + { + pb_release_single_field(&ext_iter); + } + ext = ext->next; + } + } + else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) + { + /* Release fields in submessage or submsg array */ + pb_size_t count = 1; + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + field->pData = *(void**)field->pField; + } + else + { + field->pData = field->pField; + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + count = *(pb_size_t*)field->pSize; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) + { + /* Protect against corrupted _count fields */ + count = field->array_size; + } + } + + if (field->pData) + { + for (; count > 0; count--) + { + pb_release(field->submsg_desc, field->pData); + field->pData = (char*)field->pData + field->data_size; + } + } + } + + if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + if (PB_HTYPE(type) == PB_HTYPE_REPEATED && + (PB_LTYPE(type) == PB_LTYPE_STRING || + PB_LTYPE(type) == PB_LTYPE_BYTES)) + { + /* Release entries in repeated string or bytes array */ + void **pItem = *(void***)field->pField; + pb_size_t count = *(pb_size_t*)field->pSize; + for (; count > 0; count--) + { + pb_free(*pItem); + *pItem++ = NULL; + } + } + + if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* We are going to release the array, so set the size to 0 */ + *(pb_size_t*)field->pSize = 0; + } + + /* Release main pointer */ + pb_free(*(void**)field->pField); + *(void**)field->pField = NULL; + } +} + +void pb_release(const pb_msgdesc_t *fields, void *dest_struct) +{ + pb_field_iter_t iter; + + if (!dest_struct) + return; /* Ignore NULL pointers, similar to free() */ + + if (!pb_field_iter_begin(&iter, fields, dest_struct)) + return; /* Empty message type */ + + do + { + pb_release_single_field(&iter); + } while (pb_field_iter_next(&iter)); +} +#endif + +/* Field decoders */ + +bool pb_decode_bool(pb_istream_t *stream, bool *dest) +{ + uint32_t value; + if (!pb_decode_varint32(stream, &value)) + return false; + + *(bool*)dest = (value != 0); + return true; +} + +bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) +{ + pb_uint64_t value; + if (!pb_decode_varint(stream, &value)) + return false; + + if (value & 1) + *dest = (pb_int64_t)(~(value >> 1)); + else + *dest = (pb_int64_t)(value >> 1); + + return true; +} + +bool pb_decode_fixed32(pb_istream_t *stream, void *dest) +{ + union { + uint32_t fixed32; + pb_byte_t bytes[4]; + } u; + + if (!pb_read(stream, u.bytes, 4)) + return false; + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint32_t*)dest = u.fixed32; +#else + *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | + ((uint32_t)u.bytes[1] << 8) | + ((uint32_t)u.bytes[2] << 16) | + ((uint32_t)u.bytes[3] << 24); +#endif + return true; +} + +#ifndef PB_WITHOUT_64BIT +bool pb_decode_fixed64(pb_istream_t *stream, void *dest) +{ + union { + uint64_t fixed64; + pb_byte_t bytes[8]; + } u; + + if (!pb_read(stream, u.bytes, 8)) + return false; + +#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 + /* fast path - if we know that we're on little endian, assign directly */ + *(uint64_t*)dest = u.fixed64; +#else + *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | + ((uint64_t)u.bytes[1] << 8) | + ((uint64_t)u.bytes[2] << 16) | + ((uint64_t)u.bytes[3] << 24) | + ((uint64_t)u.bytes[4] << 32) | + ((uint64_t)u.bytes[5] << 40) | + ((uint64_t)u.bytes[6] << 48) | + ((uint64_t)u.bytes[7] << 56); +#endif + return true; +} +#endif + +static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) +{ + return pb_decode_bool(stream, (bool*)field->pData); +} + +static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + pb_uint64_t value, clamped; + if (!pb_decode_varint(stream, &value)) + return false; + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_uint64_t)) + clamped = *(pb_uint64_t*)field->pData = value; + else if (field->data_size == sizeof(uint32_t)) + clamped = *(uint32_t*)field->pData = (uint32_t)value; + else if (field->data_size == sizeof(uint_least16_t)) + clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; + else if (field->data_size == sizeof(uint_least8_t)) + clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != value) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } + else + { + pb_uint64_t value; + pb_int64_t svalue; + pb_int64_t clamped; + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + { + if (!pb_decode_svarint(stream, &svalue)) + return false; + } + else + { + if (!pb_decode_varint(stream, &value)) + return false; + + /* See issue 97: Google's C++ protobuf allows negative varint values to + * be cast as int32_t, instead of the int64_t that should be used when + * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to + * not break decoding of such messages, we cast <=32 bit fields to + * int32_t first to get the sign correct. + */ + if (field->data_size == sizeof(pb_int64_t)) + svalue = (pb_int64_t)value; + else + svalue = (int32_t)value; + } + + /* Cast to the proper field size, while checking for overflows */ + if (field->data_size == sizeof(pb_int64_t)) + clamped = *(pb_int64_t*)field->pData = svalue; + else if (field->data_size == sizeof(int32_t)) + clamped = *(int32_t*)field->pData = (int32_t)svalue; + else if (field->data_size == sizeof(int_least16_t)) + clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; + else if (field->data_size == sizeof(int_least8_t)) + clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (clamped != svalue) + PB_RETURN_ERROR(stream, "integer too large"); + + return true; + } +} + +static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_bytes_array_t *dest; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); + if (size > alloc_size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_bytes_array_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "bytes overflow"); + dest = (pb_bytes_array_t*)field->pData; + } + + dest->size = (pb_size_t)size; + return pb_read(stream, dest->bytes, (size_t)size); +} + +static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + size_t alloc_size; + pb_byte_t *dest = (pb_byte_t*)field->pData; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size == (uint32_t)-1) + PB_RETURN_ERROR(stream, "size too large"); + + /* Space for null terminator */ + alloc_size = (size_t)(size + 1); + + if (alloc_size < size) + PB_RETURN_ERROR(stream, "size too large"); + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { +#ifndef PB_ENABLE_MALLOC + PB_RETURN_ERROR(stream, "no malloc support"); +#else + if (stream->bytes_left < size) + PB_RETURN_ERROR(stream, "end-of-stream"); + + if (!allocate_field(stream, field->pData, alloc_size, 1)) + return false; + dest = *(pb_byte_t**)field->pData; +#endif + } + else + { + if (alloc_size > field->data_size) + PB_RETURN_ERROR(stream, "string overflow"); + } + + dest[size] = 0; + + if (!pb_read(stream, dest, (size_t)size)) + return false; + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8((const char*)dest)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return true; +} + +static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) +{ + bool status = true; + bool submsg_consumed = false; + pb_istream_t substream; + + if (!pb_make_string_substream(stream, &substream)) + return false; + + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + /* Submessages can have a separate message-level callback that is called + * before decoding the message. Typically it is used to set callback fields + * inside oneofs. */ + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.decode) + { + status = callback->funcs.decode(&substream, field, &callback->arg); + + if (substream.bytes_left == 0) + { + submsg_consumed = true; + } + } + } + + /* Now decode the submessage contents */ + if (status && !submsg_consumed) + { + unsigned int flags = 0; + + /* Static required/optional fields are already initialized by top-level + * pb_decode(), no need to initialize them again. */ + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + PB_HTYPE(field->type) != PB_HTYPE_REPEATED && + PB_HTYPE(field->type) != PB_HTYPE_ONEOF) + { + flags = PB_DECODE_NOINIT; + } + + status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); + } + + if (!pb_close_string_substream(stream, &substream)) + return false; + + return status; +} + +static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) +{ + uint32_t size; + + if (!pb_decode_varint32(stream, &size)) + return false; + + if (size > PB_SIZE_MAX) + PB_RETURN_ERROR(stream, "bytes overflow"); + + if (size == 0) + { + /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ + memset(field->pData, 0, (size_t)field->data_size); + return true; + } + + if (size != field->data_size) + PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); + + return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) +{ + uint_least8_t sign; + int exponent; + uint32_t mantissa; + uint64_t value; + union { float f; uint32_t i; } out; + + if (!pb_decode_fixed64(stream, &value)) + return false; + + /* Decompose input value */ + sign = (uint_least8_t)((value >> 63) & 1); + exponent = (int)((value >> 52) & 0x7FF) - 1023; + mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ + + /* Figure if value is in range representable by floats. */ + if (exponent == 1024) + { + /* Special value */ + exponent = 128; + mantissa >>= 1; + } + else + { + if (exponent > 127) + { + /* Too large, convert to infinity */ + exponent = 128; + mantissa = 0; + } + else if (exponent < -150) + { + /* Too small, convert to zero */ + exponent = -127; + mantissa = 0; + } + else if (exponent < -126) + { + /* Denormalized */ + mantissa |= 0x1000000; + mantissa >>= (-126 - exponent); + exponent = -127; + } + + /* Round off mantissa */ + mantissa = (mantissa + 1) >> 1; + + /* Check if mantissa went over 2.0 */ + if (mantissa & 0x800000) + { + exponent += 1; + mantissa &= 0x7FFFFF; + mantissa >>= 1; + } + } + + /* Combine fields */ + out.i = mantissa; + out.i |= (uint32_t)(exponent + 127) << 23; + out.i |= (uint32_t)sign << 31; + + *dest = out.f; + return true; +} +#endif diff --git a/Sming/Components/nanopb/src/pb_encode.c b/Sming/Components/nanopb/src/pb_encode.c new file mode 100644 index 0000000000..54cd5bae2d --- /dev/null +++ b/Sming/Components/nanopb/src/pb_encode.c @@ -0,0 +1,978 @@ +/* pb_encode.c -- encode a protobuf using minimal resources + * + * 2011 Petteri Aimonen + */ + +#include "pb.h" +#include "pb_encode.h" +#include "pb_common.h" + +/* Use the GCC warn_unused_result attribute to check that all return values + * are propagated correctly. On other compilers and gcc before 3.4.0 just + * ignore the annotation. + */ +#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) + #define checkreturn +#else + #define checkreturn __attribute__((warn_unused_result)) +#endif + +/************************************** + * Declarations internal to this file * + **************************************/ +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); + +#ifdef PB_WITHOUT_64BIT +#define pb_int64_t int32_t +#define pb_uint64_t uint32_t +#else +#define pb_int64_t int64_t +#define pb_uint64_t uint64_t +#endif + +/******************************* + * pb_ostream_t implementation * + *******************************/ + +static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + size_t i; + pb_byte_t *dest = (pb_byte_t*)stream->state; + stream->state = dest + count; + + for (i = 0; i < count; i++) + dest[i] = buf[i]; + + return true; +} + +pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) +{ + pb_ostream_t stream; +#ifdef PB_BUFFER_ONLY + stream.callback = (void*)1; /* Just a marker value */ +#else + stream.callback = &buf_write; +#endif + stream.state = buf; + stream.max_size = bufsize; + stream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + stream.errmsg = NULL; +#endif + return stream; +} + +bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) +{ + if (count > 0 && stream->callback != NULL) + { + if (stream->bytes_written + count < stream->bytes_written || + stream->bytes_written + count > stream->max_size) + { + PB_RETURN_ERROR(stream, "stream full"); + } + +#ifdef PB_BUFFER_ONLY + if (!buf_write(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#else + if (!stream->callback(stream, buf, count)) + PB_RETURN_ERROR(stream, "io error"); +#endif + } + + stream->bytes_written += count; + return true; +} + +/************************* + * Encode a single field * + *************************/ + +/* Read a bool value without causing undefined behavior even if the value + * is invalid. See issue #434 and + * https://stackoverflow.com/questions/27661768/weird-results-for-conditional + */ +static bool safe_read_bool(const void *pSize) +{ + const char *p = (const char *)pSize; + size_t i; + for (i = 0; i < sizeof(bool); i++) + { + if (p[i] != 0) + return true; + } + return false; +} + +/* Encode a static array. Handles the size calculations and possible packing. */ +static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) +{ + pb_size_t i; + pb_size_t count; +#ifndef PB_ENCODE_ARRAYS_UNPACKED + size_t size; +#endif + + count = *(pb_size_t*)field->pSize; + + if (count == 0) + return true; + + if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) + PB_RETURN_ERROR(stream, "array max size exceeded"); + +#ifndef PB_ENCODE_ARRAYS_UNPACKED + /* We always pack arrays if the datatype allows it. */ + if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) + { + if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) + return false; + + /* Determine the total size of packed array. */ + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) + { + size = 4 * (size_t)count; + } + else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + size = 8 * (size_t)count; + } + else + { + pb_ostream_t sizestream = PB_OSTREAM_SIZING; + void *pData_orig = field->pData; + for (i = 0; i < count; i++) + { + if (!pb_enc_varint(&sizestream, field)) + PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); + field->pData = (char*)field->pData + field->data_size; + } + field->pData = pData_orig; + size = sizestream.bytes_written; + } + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing.. */ + + /* Write the data */ + for (i = 0; i < count; i++) + { + if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + if (!pb_enc_fixed(stream, field)) + return false; + } + else + { + if (!pb_enc_varint(stream, field)) + return false; + } + + field->pData = (char*)field->pData + field->data_size; + } + } + else /* Unpacked fields */ +#endif + { + for (i = 0; i < count; i++) + { + /* Normally the data is stored directly in the array entries, but + * for pointer-type string and bytes fields, the array entries are + * actually pointers themselves also. So we have to dereference once + * more to get to the actual data. */ + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && + (PB_LTYPE(field->type) == PB_LTYPE_STRING || + PB_LTYPE(field->type) == PB_LTYPE_BYTES)) + { + bool status; + void *pData_orig = field->pData; + field->pData = *(void* const*)field->pData; + + if (!field->pData) + { + /* Null pointer in array is treated as empty string / bytes */ + status = pb_encode_tag_for_field(stream, field) && + pb_encode_varint(stream, 0); + } + else + { + status = encode_basic_field(stream, field); + } + + field->pData = pData_orig; + + if (!status) + return false; + } + else + { + if (!encode_basic_field(stream, field)) + return false; + } + field->pData = (char*)field->pData + field->data_size; + } + } + + return true; +} + +/* In proto3, all fields are optional and are only encoded if their value is "non-zero". + * This function implements the check for the zero value. */ +static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) +{ + pb_type_t type = field->type; + + if (PB_ATYPE(type) == PB_ATYPE_STATIC) + { + if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) + { + /* Required proto2 fields inside proto3 submessage, pretty rare case */ + return false; + } + else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) + { + /* Repeated fields inside proto3 submessage: present if count != 0 */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) + { + /* Oneof fields */ + return *(const pb_size_t*)field->pSize == 0; + } + else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) + { + /* Proto2 optional fields inside proto3 message, or proto3 + * submessage fields. */ + return safe_read_bool(field->pSize) == false; + } + + /* Rest is proto3 singular fields */ + if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) + { + /* Simple integer / float fields */ + pb_size_t i; + const char *p = (const char*)field->pData; + for (i = 0; i < field->data_size; i++) + { + if (p[i] != 0) + { + return false; + } + } + + return true; + } + else if (PB_LTYPE(type) == PB_LTYPE_BYTES) + { + const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; + return bytes->size == 0; + } + else if (PB_LTYPE(type) == PB_LTYPE_STRING) + { + return *(const char*)field->pData == '\0'; + } + else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) + { + /* Fixed length bytes is only empty if its length is fixed + * as 0. Which would be pretty strange, but we can check + * it anyway. */ + return field->data_size == 0; + } + else if (PB_LTYPE_IS_SUBMSG(type)) + { + /* Check all fields in the submessage to find if any of them + * are non-zero. The comparison cannot be done byte-per-byte + * because the C struct may contain padding bytes that must + * be skipped. Note that usually proto3 submessages have + * a separate has_field that is checked earlier in this if. + */ + pb_field_iter_t iter; + if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) + { + do + { + if (!pb_check_proto3_default_value(&iter)) + { + return false; + } + } while (pb_field_iter_next(&iter)); + } + return true; + } + } + else if (PB_ATYPE(type) == PB_ATYPE_POINTER) + { + return field->pData == NULL; + } + else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) + { + if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) + { + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + return extension == NULL; + } + else if (field->descriptor->field_callback == pb_default_field_callback) + { + pb_callback_t *pCallback = (pb_callback_t*)field->pData; + return pCallback->funcs.encode == NULL; + } + else + { + return field->descriptor->field_callback == NULL; + } + } + + return false; /* Not typically reached, safe default for weird special cases. */ +} + +/* Encode a field with static or pointer allocation, i.e. one whose data + * is available to the encoder directly. */ +static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (!field->pData) + { + /* Missing pointer field */ + return true; + } + + if (!pb_encode_tag_for_field(stream, field)) + return false; + + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + return pb_enc_bool(stream, field); + + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + return pb_enc_varint(stream, field); + + case PB_LTYPE_FIXED32: + case PB_LTYPE_FIXED64: + return pb_enc_fixed(stream, field); + + case PB_LTYPE_BYTES: + return pb_enc_bytes(stream, field); + + case PB_LTYPE_STRING: + return pb_enc_string(stream, field); + + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + return pb_enc_submessage(stream, field); + + case PB_LTYPE_FIXED_LENGTH_BYTES: + return pb_enc_fixed_length_bytes(stream, field); + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } +} + +/* Encode a field with callback semantics. This means that a user function is + * called to provide and encode the actual data. */ +static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->descriptor->field_callback != NULL) + { + if (!field->descriptor->field_callback(NULL, stream, field)) + PB_RETURN_ERROR(stream, "callback error"); + } + return true; +} + +/* Encode a single field of any callback, pointer or static type. */ +static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) +{ + /* Check field presence */ + if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) + { + if (*(const pb_size_t*)field->pSize != field->tag) + { + /* Different type oneof field */ + return true; + } + } + else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) + { + if (field->pSize) + { + if (safe_read_bool(field->pSize) == false) + { + /* Missing optional field */ + return true; + } + } + else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) + { + /* Proto3 singular field */ + if (pb_check_proto3_default_value(field)) + return true; + } + } + + if (!field->pData) + { + if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) + PB_RETURN_ERROR(stream, "missing required field"); + + /* Pointer field set to NULL */ + return true; + } + + /* Then encode field contents */ + if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) + { + return encode_callback_field(stream, field); + } + else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) + { + return encode_array(stream, field); + } + else + { + return encode_basic_field(stream, field); + } +} + +/* Default handler for extension fields. Expects to have a pb_msgdesc_t + * pointer in the extension->type->arg field, pointing to a message with + * only one field in it. */ +static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) +{ + pb_field_iter_t iter; + + if (!pb_field_iter_begin_extension_const(&iter, extension)) + PB_RETURN_ERROR(stream, "invalid extension"); + + return encode_field(stream, &iter); +} + + +/* Walk through all the registered extensions and give them a chance + * to encode themselves. */ +static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; + + while (extension) + { + bool status; + if (extension->type->encode) + status = extension->type->encode(stream, extension); + else + status = default_extension_encoder(stream, extension); + + if (!status) + return false; + + extension = extension->next; + } + + return true; +} + +/********************* + * Encode all fields * + *********************/ + +bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_field_iter_t iter; + if (!pb_field_iter_begin_const(&iter, fields, src_struct)) + return true; /* Empty message type */ + + do { + if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) + { + /* Special case for the extension field placeholder */ + if (!encode_extension_field(stream, &iter)) + return false; + } + else + { + /* Regular field */ + if (!encode_field(stream, &iter)) + return false; + } + } while (pb_field_iter_next(&iter)); + + return true; +} + +bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) +{ + if ((flags & PB_ENCODE_DELIMITED) != 0) + { + return pb_encode_submessage(stream, fields, src_struct); + } + else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) + { + const pb_byte_t zero = 0; + + if (!pb_encode(stream, fields, src_struct)) + return false; + + return pb_write(stream, &zero, 1); + } + else + { + return pb_encode(stream, fields, src_struct); + } +} + +bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) +{ + pb_ostream_t stream = PB_OSTREAM_SIZING; + + if (!pb_encode(&stream, fields, src_struct)) + return false; + + *size = stream.bytes_written; + return true; +} + +/******************** + * Helper functions * + ********************/ + +/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ +static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) +{ + size_t i = 0; + pb_byte_t buffer[10]; + pb_byte_t byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + + while (i < 4 && (low != 0 || high != 0)) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(low & 0x7F); + low >>= 7; + } + + if (high) + { + byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); + high >>= 3; + + while (high) + { + byte |= 0x80; + buffer[i++] = byte; + byte = (pb_byte_t)(high & 0x7F); + high >>= 7; + } + } + + buffer[i++] = byte; + + return pb_write(stream, buffer, i); +} + +bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) +{ + if (value <= 0x7F) + { + /* Fast path: single byte */ + pb_byte_t byte = (pb_byte_t)value; + return pb_write(stream, &byte, 1); + } + else + { +#ifdef PB_WITHOUT_64BIT + return pb_encode_varint_32(stream, value, 0); +#else + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); +#endif + } +} + +bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) +{ + pb_uint64_t zigzagged; + if (value < 0) + zigzagged = ~((pb_uint64_t)value << 1); + else + zigzagged = (pb_uint64_t)value << 1; + + return pb_encode_varint(stream, zigzagged); +} + +bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) +{ + uint32_t val = *(const uint32_t*)value; + pb_byte_t bytes[4]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + return pb_write(stream, bytes, 4); +} + +#ifndef PB_WITHOUT_64BIT +bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) +{ + uint64_t val = *(const uint64_t*)value; + pb_byte_t bytes[8]; + bytes[0] = (pb_byte_t)(val & 0xFF); + bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); + bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); + bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); + bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); + bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); + bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); + bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); + return pb_write(stream, bytes, 8); +} +#endif + +bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) +{ + pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; + return pb_encode_varint(stream, tag); +} + +bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) +{ + pb_wire_type_t wiretype; + switch (PB_LTYPE(field->type)) + { + case PB_LTYPE_BOOL: + case PB_LTYPE_VARINT: + case PB_LTYPE_UVARINT: + case PB_LTYPE_SVARINT: + wiretype = PB_WT_VARINT; + break; + + case PB_LTYPE_FIXED32: + wiretype = PB_WT_32BIT; + break; + + case PB_LTYPE_FIXED64: + wiretype = PB_WT_64BIT; + break; + + case PB_LTYPE_BYTES: + case PB_LTYPE_STRING: + case PB_LTYPE_SUBMESSAGE: + case PB_LTYPE_SUBMSG_W_CB: + case PB_LTYPE_FIXED_LENGTH_BYTES: + wiretype = PB_WT_STRING; + break; + + default: + PB_RETURN_ERROR(stream, "invalid field type"); + } + + return pb_encode_tag(stream, wiretype, field->tag); +} + +bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) +{ + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + return pb_write(stream, buffer, size); +} + +bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) +{ + /* First calculate the message size using a non-writing substream. */ + pb_ostream_t substream = PB_OSTREAM_SIZING; + size_t size; + bool status; + + if (!pb_encode(&substream, fields, src_struct)) + { +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + return false; + } + + size = substream.bytes_written; + + if (!pb_encode_varint(stream, (pb_uint64_t)size)) + return false; + + if (stream->callback == NULL) + return pb_write(stream, NULL, size); /* Just sizing */ + + if (stream->bytes_written + size > stream->max_size) + PB_RETURN_ERROR(stream, "stream full"); + + /* Use a substream to verify that a callback doesn't write more than + * what it did the first time. */ + substream.callback = stream->callback; + substream.state = stream->state; + substream.max_size = size; + substream.bytes_written = 0; +#ifndef PB_NO_ERRMSG + substream.errmsg = NULL; +#endif + + status = pb_encode(&substream, fields, src_struct); + + stream->bytes_written += substream.bytes_written; + stream->state = substream.state; +#ifndef PB_NO_ERRMSG + stream->errmsg = substream.errmsg; +#endif + + if (substream.bytes_written != size) + PB_RETURN_ERROR(stream, "submsg size changed"); + + return status; +} + +/* Field encoders */ + +static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + uint32_t value = safe_read_bool(field->pData) ? 1 : 0; + PB_UNUSED(field); + return pb_encode_varint(stream, value); +} + +static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) + { + /* Perform unsigned integer extension */ + pb_uint64_t value = 0; + + if (field->data_size == sizeof(uint_least8_t)) + value = *(const uint_least8_t*)field->pData; + else if (field->data_size == sizeof(uint_least16_t)) + value = *(const uint_least16_t*)field->pData; + else if (field->data_size == sizeof(uint32_t)) + value = *(const uint32_t*)field->pData; + else if (field->data_size == sizeof(pb_uint64_t)) + value = *(const pb_uint64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + return pb_encode_varint(stream, value); + } + else + { + /* Perform signed integer extension */ + pb_int64_t value = 0; + + if (field->data_size == sizeof(int_least8_t)) + value = *(const int_least8_t*)field->pData; + else if (field->data_size == sizeof(int_least16_t)) + value = *(const int_least16_t*)field->pData; + else if (field->data_size == sizeof(int32_t)) + value = *(const int32_t*)field->pData; + else if (field->data_size == sizeof(pb_int64_t)) + value = *(const pb_int64_t*)field->pData; + else + PB_RETURN_ERROR(stream, "invalid data_size"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) + return pb_encode_svarint(stream, value); +#ifdef PB_WITHOUT_64BIT + else if (value < 0) + return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); +#endif + else + return pb_encode_varint(stream, (pb_uint64_t)value); + + } +} + +static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) +{ +#ifdef PB_CONVERT_DOUBLE_FLOAT + if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) + { + return pb_encode_float_as_double(stream, *(float*)field->pData); + } +#endif + + if (field->data_size == sizeof(uint32_t)) + { + return pb_encode_fixed32(stream, field->pData); + } +#ifndef PB_WITHOUT_64BIT + else if (field->data_size == sizeof(uint64_t)) + { + return pb_encode_fixed64(stream, field->pData); + } +#endif + else + { + PB_RETURN_ERROR(stream, "invalid data_size"); + } +} + +static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + const pb_bytes_array_t *bytes = NULL; + + bytes = (const pb_bytes_array_t*)field->pData; + + if (bytes == NULL) + { + /* Treat null pointer as an empty bytes field */ + return pb_encode_string(stream, NULL, 0); + } + + if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && + bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) + { + PB_RETURN_ERROR(stream, "bytes size exceeded"); + } + + return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); +} + +static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + size_t size = 0; + size_t max_size = (size_t)field->data_size; + const char *str = (const char*)field->pData; + + if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) + { + max_size = (size_t)-1; + } + else + { + /* pb_dec_string() assumes string fields end with a null + * terminator when the type isn't PB_ATYPE_POINTER, so we + * shouldn't allow more than max-1 bytes to be written to + * allow space for the null terminator. + */ + if (max_size == 0) + PB_RETURN_ERROR(stream, "zero-length string"); + + max_size -= 1; + } + + + if (str == NULL) + { + size = 0; /* Treat null pointer as an empty string */ + } + else + { + const char *p = str; + + /* strnlen() is not always available, so just use a loop */ + while (size < max_size && *p != '\0') + { + size++; + p++; + } + + if (*p != '\0') + { + PB_RETURN_ERROR(stream, "unterminated string"); + } + } + +#ifdef PB_VALIDATE_UTF8 + if (!pb_validate_utf8(str)) + PB_RETURN_ERROR(stream, "invalid utf8"); +#endif + + return pb_encode_string(stream, (const pb_byte_t*)str, size); +} + +static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + if (field->submsg_desc == NULL) + PB_RETURN_ERROR(stream, "invalid field descriptor"); + + if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) + { + /* Message callback is stored right before pSize. */ + pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; + if (callback->funcs.encode) + { + if (!callback->funcs.encode(stream, field, &callback->arg)) + return false; + } + } + + return pb_encode_submessage(stream, field->submsg_desc, field->pData); +} + +static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) +{ + return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); +} + +#ifdef PB_CONVERT_DOUBLE_FLOAT +bool pb_encode_float_as_double(pb_ostream_t *stream, float value) +{ + union { float f; uint32_t i; } in; + uint_least8_t sign; + int exponent; + uint64_t mantissa; + + in.f = value; + + /* Decompose input value */ + sign = (uint_least8_t)((in.i >> 31) & 1); + exponent = (int)((in.i >> 23) & 0xFF) - 127; + mantissa = in.i & 0x7FFFFF; + + if (exponent == 128) + { + /* Special value (NaN etc.) */ + exponent = 1024; + } + else if (exponent == -127) + { + if (!mantissa) + { + /* Zero */ + exponent = -1023; + } + else + { + /* Denormalized */ + mantissa <<= 1; + while (!(mantissa & 0x800000)) + { + mantissa <<= 1; + exponent--; + } + mantissa &= 0x7FFFFF; + } + } + + /* Combine fields */ + mantissa <<= 29; + mantissa |= (uint64_t)(exponent + 1023) << 52; + mantissa |= (uint64_t)sign << 63; + + return pb_encode_fixed64(stream, &mantissa); +} +#endif From 7334e0b80ef2967cbdb1f4f859ccc2acac0ed14e Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 2 Jul 2020 14:50:45 +0200 Subject: [PATCH 02/28] further research ... --- .../Hosted-Lib/src/DigitalHosted.cpp | 18 ++-- Sming/Components/Hosted/README.rst | 4 +- .../Components/Hosted/app/app/application.cpp | 4 +- Sming/Components/Hosted/component.mk | 6 +- Sming/Components/Hosted/proto/hosted.pb.h | 30 +++---- Sming/Components/Hosted/proto/hosted.proto | 14 ++-- Sming/Components/Hosted/src/HostedClient.h | 62 ++++++++------ Sming/Components/Hosted/src/HostedCommon.h | 29 +++++++ Sming/Components/Hosted/src/HostedServer.h | 83 +++++++++++++------ 9 files changed, 162 insertions(+), 88 deletions(-) create mode 100644 Sming/Components/Hosted/src/HostedCommon.h diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp index 526b5b6355..1ce8a206f1 100644 --- a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp @@ -5,18 +5,20 @@ extern HostedClient hostedClient; void pinMode(uint16_t pin, uint8_t mode) { - INIT_HD_COMMAND(PinMode); - command->pin = pin; - command->mode = (PinMode)mode; + NEW_HD_COMMAND(message, PinMode, { + command->pin = pin; + command->mode = (PinMode)mode; + }); - hostedClient.send(message); + hostedClient.send(&message); } void digitalWrite(uint16_t pin, uint8_t val) { - INIT_HD_COMMAND(DigitalWrite); - command->pin = pin; - command->value = val; + NEW_HD_COMMAND(message, DigitalWrite, { + command->pin = pin; + command->value = val; + }); - hostedClient.send(message); + hostedClient.send(&message); } diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index ab70a45279..8c734392ad 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -28,7 +28,7 @@ TDB Protobuf with nanopb:: - cd $SMING_HOME/Components/Hosted/src - python ~/dev/nanopb/generator/nanopb_generator.py -I../proto hosted.proto + cd $SMING_HOME/Components/Hosted/proto + python ~/dev/nanopb/generator/nanopb_generator.py hosted.proto TBD... \ No newline at end of file diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp index 1eb90d42ce..ae2ffb1d42 100644 --- a/Sming/Components/Hosted/app/app/application.cpp +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -28,12 +28,12 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) void init() { // Register Command Handlers - hostedServer.registerCommand(HostedMessageType_TypeRequestPinMode, [](HostedCommand *request, HostedCommand *response)-> int { + hostedServer.registerCommand(HostedCommand_requestPinMode_tag, [](HostedCommand *request, HostedCommand *response)-> int { pinMode((uint16_t)request->payload.requestPinMode.pin, (uint8_t)request->payload.requestPinMode.mode); return 0; }); - hostedServer.registerCommand(HostedMessageType_TypeRequestDigitalWrite, [](HostedCommand *request, HostedCommand *response)-> int { + hostedServer.registerCommand(HostedCommand_requestDigitalWrite_tag, [](HostedCommand *request, HostedCommand *response)-> int { digitalWrite((uint16_t)request->payload.requestDigitalWrite.pin, (uint8_t)request->payload.requestDigitalWrite.value); return 0; }); diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 822216eb8d..0ad1f68c73 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -11,14 +11,14 @@ RELINK_VARS = ENABLE_HOSTED ##@Building -hostedservice: ##Builds the hosted service firmware +hosted-app: ##Builds the hosted service firmware $(MAKE) -C $(HOSTED_APP_DIR) SMING_ARCH=$(HOSTED_ARCH) ##@Flashing -flashhostedservice: ##Flashes the hosted service firmware to an actual device +hosted-flash: ##Flashes the hosted service firmware to an actual device $(MAKE) -C $(HOSTED_APP_DIR) flash SMING_ARCH=$(HOSTED_ARCH) -flashhostedserviceapp: ##Flashes only the hosted service app +hosted-flashapp: ##Flashes only the hosted service app $(MAKE) -C $(HOSTED_APP_DIR) flashapp SMING_ARCH=$(HOSTED_ARCH) diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h index 6afd920d08..eef81ebf86 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.h +++ b/Sming/Components/Hosted/proto/hosted.pb.h @@ -20,12 +20,9 @@ typedef enum _PinMode { PinMode_INPUT_PULLUP = 2 } PinMode; -typedef enum _HostedMessageType { - HostedMessageType_TypeRequestDigitalWrite = 0, - HostedMessageType_TypeRequestPinMode = 1, - HostedMessageType_TypeRequestDigitalRead = 2, - HostedMessageType_TypeResponseDigitalRead = 3 -} HostedMessageType; +typedef enum _HostedCommand_Version { + HostedCommand_Version_HOSTED_V_1_0 = 0 +} HostedCommand_Version; /* Struct definitions */ typedef struct _RequestDigitalRead { @@ -47,7 +44,8 @@ typedef struct _ResponseDigitalRead { } ResponseDigitalRead; typedef struct _HostedCommand { - HostedMessageType type; + HostedCommand_Version version; + uint32_t id; pb_size_t which_payload; union { RequestDigitalWrite requestDigitalWrite; @@ -63,9 +61,9 @@ typedef struct _HostedCommand { #define _PinMode_MAX PinMode_INPUT_PULLUP #define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP+1)) -#define _HostedMessageType_MIN HostedMessageType_TypeRequestDigitalWrite -#define _HostedMessageType_MAX HostedMessageType_TypeResponseDigitalRead -#define _HostedMessageType_ARRAYSIZE ((HostedMessageType)(HostedMessageType_TypeResponseDigitalRead+1)) +#define _HostedCommand_Version_MIN HostedCommand_Version_HOSTED_V_1_0 +#define _HostedCommand_Version_MAX HostedCommand_Version_HOSTED_V_1_0 +#define _HostedCommand_Version_ARRAYSIZE ((HostedCommand_Version)(HostedCommand_Version_HOSTED_V_1_0+1)) /* Initializer values for message structs */ @@ -73,12 +71,12 @@ typedef struct _HostedCommand { #define RequestPinMode_init_default {0, _PinMode_MIN} #define RequestDigitalRead_init_default {0} #define ResponseDigitalRead_init_default {0} -#define HostedCommand_init_default {_HostedMessageType_MIN, 0, {RequestDigitalWrite_init_default}} +#define HostedCommand_init_default {_HostedCommand_Version_MIN, 0, 0, {RequestDigitalWrite_init_default}} #define RequestDigitalWrite_init_zero {0, 0} #define RequestPinMode_init_zero {0, _PinMode_MIN} #define RequestDigitalRead_init_zero {0} #define ResponseDigitalRead_init_zero {0} -#define HostedCommand_init_zero {_HostedMessageType_MIN, 0, {RequestDigitalWrite_init_zero}} +#define HostedCommand_init_zero {_HostedCommand_Version_MIN, 0, 0, {RequestDigitalWrite_init_zero}} /* Field tags (for use in manual encoding/decoding) */ #define RequestDigitalRead_pin_tag 1 @@ -87,7 +85,8 @@ typedef struct _HostedCommand { #define RequestPinMode_pin_tag 1 #define RequestPinMode_mode_tag 2 #define ResponseDigitalRead_value_tag 1 -#define HostedCommand_type_tag 1 +#define HostedCommand_version_tag 1 +#define HostedCommand_id_tag 2 #define HostedCommand_requestDigitalWrite_tag 10 #define HostedCommand_requestPinMode_tag 11 #define HostedCommand_requestDigitalRead_tag 12 @@ -117,7 +116,8 @@ X(a, STATIC, SINGULAR, UINT32, value, 1) #define ResponseDigitalRead_DEFAULT NULL #define HostedCommand_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, type, 1) \ +X(a, STATIC, SINGULAR, UENUM, version, 1) \ +X(a, STATIC, SINGULAR, UINT32, id, 2) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalWrite,payload.requestDigitalWrite), 10) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestPinMode,payload.requestPinMode), 11) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalRead,payload.requestDigitalRead), 12) \ @@ -147,7 +147,7 @@ extern const pb_msgdesc_t HostedCommand_msg; #define RequestPinMode_size 8 #define RequestDigitalRead_size 6 #define ResponseDigitalRead_size 6 -#define HostedCommand_size 16 +#define HostedCommand_size 22 #ifdef __cplusplus } /* extern "C" */ diff --git a/Sming/Components/Hosted/proto/hosted.proto b/Sming/Components/Hosted/proto/hosted.proto index 20aa29bbf7..1b1c5fc71a 100644 --- a/Sming/Components/Hosted/proto/hosted.proto +++ b/Sming/Components/Hosted/proto/hosted.proto @@ -25,15 +25,13 @@ message ResponseDigitalRead { uint32 value = 1; } -enum HostedMessageType { - TypeRequestDigitalWrite = 0; - TypeRequestPinMode = 1; - TypeRequestDigitalRead = 2; - TypeResponseDigitalRead = 3; -} - message HostedCommand { - HostedMessageType type = 1; + // Version of the protocol for future compatibility + enum Version { + HOSTED_V_1_0 = 0; + } + Version version = 1; + uint32 id = 2; // unique message id, > 0 oneof payload { RequestDigitalWrite requestDigitalWrite = 10; RequestPinMode requestPinMode = 11; diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src/HostedClient.h index b413ddcab8..6e2e00bb04 100644 --- a/Sming/Components/Hosted/src/HostedClient.h +++ b/Sming/Components/Hosted/src/HostedClient.h @@ -2,16 +2,7 @@ #include #include - -#include -#include "hosted.pb.h" - -// Initializes hosted command -#define INIT_HD_COMMAND(XX) \ - HostedCommand message = HostedCommand_init_zero; \ - message.type = HostedMessageType_TypeRequest ## XX; \ - message.which_payload = HostedCommand_request ## XX ##_tag; \ - auto command = &message.payload.request ## XX; +#include "HostedCommon.h" class HostedClient { @@ -30,30 +21,53 @@ class HostedClient this->stream = stream; } - bool send(const HostedCommand& message) + bool send(HostedCommand* message, HostedCommandDelegate callback = nullptr) { - bool status; - - pb_ostream_t ouput = pb_ostream_from_buffer(buffer, sizeof(buffer)); - status = pb_encode(&ouput, HostedCommand_fields, &message); + if(++messageId == 0) { + messageId = 1; // messages with id 0 will be discarded... + } + message->id = messageId; - if (!status) { + pb_ostream_t ouput = newOutputStream(); + bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); + if (!success) { debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); return false; } - if(ouput.bytes_written > 0) { - size_t written = stream->write(buffer, ouput.bytes_written); - if(written != ouput.bytes_written) { - debug_e("Unable to queue message in transport stream"); - return false; - } + if(callback != nullptr) { + responseCallbacks[message->id] = callback; } return true; } + + /** + * @brief This method handles incoming data + */ + bool onData(const char* at, size_t length) + { + return true; + } private: - ReadWriteStream* stream = nullptr; + pb_ostream_t newOutputStream() + { + pb_ostream_t outputStream; + outputStream.callback = [](pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -> bool { + ReadWriteStream* destination = (ReadWriteStream* )stream->state; + size_t written = destination->write((const uint8_t *)buf, count); + + return (written == count); + }; + outputStream.state = (void*)this->stream; + outputStream.max_size = SIZE_MAX; + outputStream.bytes_written = 0; + outputStream.errmsg = nullptr; - uint8_t buffer[128]; /* This is the buffer where we will store our message. */ + return outputStream; + } +private: + HashMap responseCallbacks; + uint32_t messageId = 0; + ReadWriteStream* stream = nullptr; }; diff --git a/Sming/Components/Hosted/src/HostedCommon.h b/Sming/Components/Hosted/src/HostedCommon.h new file mode 100644 index 0000000000..363fb72bec --- /dev/null +++ b/Sming/Components/Hosted/src/HostedCommon.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include +#include "hosted.pb.h" + +constexpr int HOSTED_OK = 0; +constexpr int HOSTED_FAIL = -1; +constexpr int HOSTED_NO_MEM = -2; + +typedef Delegate HostedCommandDelegate; + +// Creates new hosted command +#define NEW_HD_COMMAND(NAME, XX, YY) \ + HostedCommand NAME = HostedCommand_init_zero; \ + NAME.which_payload = HostedCommand_request ## XX ##_tag; \ + { \ + auto command = &NAME.payload.request ## XX; \ + YY \ + } + +class HostedCommon +{ +public: + virtual bool onData(const char* at, size_t length) = 0; + +private: +}; diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h index 82c731b7b9..cde1e0a5b5 100644 --- a/Sming/Components/Hosted/src/HostedServer.h +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -8,19 +8,18 @@ #pragma once #include -#include -#include "hosted.pb.h" - -constexpr int HOSTED_OK = 0; -constexpr int HOSTED_FAIL = -1; -constexpr int HOSTED_NO_MEM = -2; - -typedef Delegate HostedCommandDelegate; +#include +#include class HostedServer { public: - void registerCommand(HostedMessageType type, HostedCommandDelegate callback) + HostedServer(size_t storageSize = 1024): storage(new CircularBuffer(storageSize)) + { + + } + + void registerCommand(uint32_t type, HostedCommandDelegate callback) { commands[type] = callback; } @@ -39,32 +38,64 @@ class HostedServer return HOSTED_FAIL; } + size_t written = storage->write(at, length); + if(written != length) { + // Not enough space to store the message... + return HOSTED_NO_MEM; + } + int result = HOSTED_OK; - bool status; + bool success; - // extract the request message - pb_istream_t input = pb_istream_from_buffer(at, length); + pb_istream_t input = newInputStream(); + size_t leftBytes = input.bytes_left; + do { + success = pb_decode_ex(&input, HostedCommand_fields, &request, PB_DECODE_DELIMITED); + if (!(success && request.id)) { + Serial.printf("Decoding failed: %s\n", PB_GET_ERROR(&input)); + storage->seek(storage->available()- leftBytes); + break; + } - status = pb_decode(&input, HostedCommand_fields, &request); - if (!status) { - debug_e("Decoding failed: %s\n", PB_GET_ERROR(&input)); - return HOSTED_FAIL; - } + // dispatch the command + if(!(commands.contains(request.which_payload) && commands[request.which_payload] != nullptr)) { + debug_w("No command registered for type: %d", request.which_payload); + continue; + } - // dispatch the command - if(commands.contains(request.type) && commands[request.type] != nullptr) { - result = commands[request.type](&request, &response); - } - else { - debug_w("No command registered for type: %d", request.type); - } + result = commands[request.which_payload](&request, &response); - // TODO: cleanup + // TODO: process the response + + // and send it back... + leftBytes = input.bytes_left; + + } while(input.bytes_left && success); return result; } private: - HashMap commands; + pb_istream_t newInputStream() + { + pb_istream_t stream; + stream.callback = [](pb_istream_t *stream, pb_byte_t *buf, size_t count) -> bool { + CircularBuffer* source = (CircularBuffer* )stream->state; + int read = source->readBytes((char *)buf, count); + stream->bytes_left = source->available() - read; + + return true; + }; + stream.state = (void*)storage; + stream.bytes_left = storage->available(); + stream.errmsg = nullptr; + + return stream; + } + + +private: + CircularBuffer* storage = nullptr; + HashMap commands; }; From 60a6960cb838b4eeabbfc2120c49f558a114338a Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 2 Jul 2020 18:28:14 +0200 Subject: [PATCH 03/28] And we have a running minimal HostedServer using Tcp transport. --- Sming/Components/Hosted/app/README.rst | 17 ++++ .../Components/Hosted/app/app/application.cpp | 25 +++++- Sming/Components/Hosted/app/component.mk | 1 + Sming/Components/Hosted/app/test/data.pb | 1 + Sming/Components/Hosted/src/HostedServer.h | 81 +++++++++++++++---- 5 files changed, 109 insertions(+), 16 deletions(-) create mode 100644 Sming/Components/Hosted/app/test/data.pb diff --git a/Sming/Components/Hosted/app/README.rst b/Sming/Components/Hosted/app/README.rst index 515dff9e8e..8d91a5e57d 100644 --- a/Sming/Components/Hosted/app/README.rst +++ b/Sming/Components/Hosted/app/README.rst @@ -2,3 +2,20 @@ Hosted Server Application ========================= TBD... + + +Testing +------- + +You can compile the Hosted App to run also under the Host system. This can be done with the following command:: + + cd $SMING_HOME/Components/Hosted/app + make run SMING_ARCH=Host ENABLE_GDB=1 + +Once the HostedServer is up and running you can send protobuffer encoded commands to it. A sample test client can be +run with the following command:: + + nc 192.168.13.10 4031 < test/data.pb + + + diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp index ae2ffb1d42..538f8df703 100644 --- a/Sming/Components/Hosted/app/app/application.cpp +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -15,9 +15,21 @@ TcpServer* tcpServer; void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) { // Start the TcpServer + if(tcpServer != nullptr) { + return; + } + tcpServer = new TcpServer([](TcpClient& client, char* data, int size) -> bool { - hostedServer.process((const uint8_t*)data, size); - // TODO: check if the processed bytes are equal to the provided bytes + // clientReceiveDataHandler + int result = hostedServer.process((const uint8_t*)data, size); + if(result != HOSTED_OK) { + return result == HOSTED_NO_MEM ? false: true; + } + + hostedServer.transfer([&client](const uint8_t* data, size_t size) -> bool { + return client.send((const char*)data, size); + }); + return true; }); @@ -38,6 +50,15 @@ void init() return 0; }); + hostedServer.registerCommand(HostedCommand_requestDigitalRead_tag, [](HostedCommand *request, HostedCommand *response)-> int { + uint8_t result = digitalRead((uint16_t)request->payload.requestDigitalRead.pin); + response->id = request->id; + response->which_payload = HostedCommand_responseDigitalRead_tag; + response->payload.responseDigitalRead.value = result; + + return 0; + }); + // Connect to same AP as the client application WifiStation.enable(true); WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); diff --git a/Sming/Components/Hosted/app/component.mk b/Sming/Components/Hosted/app/component.mk index ab2b059deb..9f991745a3 100644 --- a/Sming/Components/Hosted/app/component.mk +++ b/Sming/Components/Hosted/app/component.mk @@ -1,5 +1,6 @@ ## List the names of any additional Components required for this project COMPONENT_DEPENDS := Hosted +ENABLE_HOSTED := ## SPIFFS options DISABLE_SPIFFS := 1 diff --git a/Sming/Components/Hosted/app/test/data.pb b/Sming/Components/Hosted/app/test/data.pb new file mode 100644 index 0000000000..092a9bac0b --- /dev/null +++ b/Sming/Components/Hosted/app/test/data.pb @@ -0,0 +1 @@ + RÒR \ No newline at end of file diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h index cde1e0a5b5..62beecbfc4 100644 --- a/Sming/Components/Hosted/src/HostedServer.h +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -11,10 +11,12 @@ #include #include +typedef Delegate HostedTransferDelegate; + class HostedServer { public: - HostedServer(size_t storageSize = 1024): storage(new CircularBuffer(storageSize)) + HostedServer(size_t storageSize = 1024): inputBuffer(new CircularBuffer(storageSize)) { } @@ -38,7 +40,7 @@ class HostedServer return HOSTED_FAIL; } - size_t written = storage->write(at, length); + size_t written = inputBuffer->write(at, length); if(written != length) { // Not enough space to store the message... return HOSTED_NO_MEM; @@ -46,17 +48,19 @@ class HostedServer int result = HOSTED_OK; bool success; - pb_istream_t input = newInputStream(); size_t leftBytes = input.bytes_left; do { success = pb_decode_ex(&input, HostedCommand_fields, &request, PB_DECODE_DELIMITED); if (!(success && request.id)) { Serial.printf("Decoding failed: %s\n", PB_GET_ERROR(&input)); - storage->seek(storage->available()- leftBytes); + inputBuffer->seek(inputBuffer->available()- leftBytes); break; } + // and send it back... + leftBytes = input.bytes_left; + // dispatch the command if(!(commands.contains(request.which_payload) && commands[request.which_payload] != nullptr)) { debug_w("No command registered for type: %d", request.which_payload); @@ -64,16 +68,47 @@ class HostedServer } result = commands[request.which_payload](&request, &response); + if(result != HOSTED_OK) { + break; + } - // TODO: process the response - - // and send it back... - leftBytes = input.bytes_left; - + // process the response + if(response.id && !send(&response)) { + result = HOSTED_FAIL; + break; + } } while(input.bytes_left && success); return result; + } + bool send(HostedCommand *message) + { + pb_ostream_t ouput = newOutputStream(); + bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); + if (!success) { + debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); + return false; + } + + return true; + } + + bool transfer(HostedTransferDelegate callback) + { + uint8_t buf[512]; + while(outputBuffer.available() > 0) { + int read = outputBuffer.readMemoryBlock((char *)buf, 512); + outputBuffer.seek(read); + if(!callback(buf, read)) { + return false; + } + + if(read < 1024) { + break; + } + } + return true; } private: @@ -82,20 +117,38 @@ class HostedServer pb_istream_t stream; stream.callback = [](pb_istream_t *stream, pb_byte_t *buf, size_t count) -> bool { CircularBuffer* source = (CircularBuffer* )stream->state; - int read = source->readBytes((char *)buf, count); - stream->bytes_left = source->available() - read; + size_t read = source->readMemoryBlock((char *)buf, count); + source->seek(read); return true; }; - stream.state = (void*)storage; - stream.bytes_left = storage->available(); + stream.state = (void*)inputBuffer; + stream.bytes_left = inputBuffer->available(); stream.errmsg = nullptr; return stream; } + pb_ostream_t newOutputStream() + { + pb_ostream_t outputStream; + outputStream.callback = [](pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -> bool { + CircularBuffer* destination = (CircularBuffer* )stream->state; + size_t written = destination->write((const uint8_t *)buf, count); + + return (written == count); + }; + outputStream.state = (void*)&this->outputBuffer; + outputStream.max_size = SIZE_MAX; + outputStream.bytes_written = 0; + outputStream.errmsg = nullptr; + + return outputStream; + } + private: - CircularBuffer* storage = nullptr; + CircularBuffer* inputBuffer = nullptr; + CircularBuffer outputBuffer = CircularBuffer(1024); HashMap commands; }; From b0bd00ad0be7f83a50f28a54b6fdb17afde1d8af Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 3 Jul 2020 12:39:24 +0200 Subject: [PATCH 04/28] Replaced nanopb code with submodule. --- .gitmodules | 6 +- Sming/Components/Hosted/README.rst | 2 +- Sming/Components/nanopb/component.mk | 6 +- Sming/Components/nanopb/inc/pb.h | 875 ------------ Sming/Components/nanopb/inc/pb_common.h | 49 - Sming/Components/nanopb/inc/pb_decode.h | 196 --- Sming/Components/nanopb/inc/pb_encode.h | 185 --- Sming/Components/nanopb/nanopb | 1 + Sming/Components/nanopb/src/pb_common.c | 388 ------ Sming/Components/nanopb/src/pb_decode.c | 1685 ----------------------- Sming/Components/nanopb/src/pb_encode.c | 978 ------------- 11 files changed, 9 insertions(+), 4362 deletions(-) delete mode 100644 Sming/Components/nanopb/inc/pb.h delete mode 100644 Sming/Components/nanopb/inc/pb_common.h delete mode 100644 Sming/Components/nanopb/inc/pb_decode.h delete mode 100644 Sming/Components/nanopb/inc/pb_encode.h create mode 160000 Sming/Components/nanopb/nanopb delete mode 100644 Sming/Components/nanopb/src/pb_common.c delete mode 100644 Sming/Components/nanopb/src/pb_decode.c delete mode 100644 Sming/Components/nanopb/src/pb_encode.c diff --git a/.gitmodules b/.gitmodules index 85003018ee..3066e1bef4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -57,9 +57,9 @@ path = Sming/Components/libyuarel url = https://github.com/jacketizer/libyuarel.git ignore = dirty -[submodule "mqtt-codec"] - path = Sming/Components/mqtt-codec - url = https://github.com/slaff/mqtt-codec.git +[submodule "nanopb"] + path = Sming/Components/nanopb/nanopb + url = https://github.com/nanopb/nanopb.git ignore = dirty [submodule "rboot"] path = Sming/Components/rboot/rboot diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index 8c734392ad..83036449ef 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -29,6 +29,6 @@ TDB Protobuf with nanopb:: cd $SMING_HOME/Components/Hosted/proto - python ~/dev/nanopb/generator/nanopb_generator.py hosted.proto + python $SMING_HOME/Components/nanopb/nanopb/generator/nanopb_generator.py hosted.proto TBD... \ No newline at end of file diff --git a/Sming/Components/nanopb/component.mk b/Sming/Components/nanopb/component.mk index 1a6f6c0e7b..9f5bf56b87 100644 --- a/Sming/Components/nanopb/component.mk +++ b/Sming/Components/nanopb/component.mk @@ -1,2 +1,4 @@ -COMPONENT_SRCDIRS := src -COMPONENT_INCDIRS := inc \ No newline at end of file +COMPONENT_SRCDIRS := nanopb +COMPONENT_INCDIRS := nanopb + +COMPONENT_SUBMODULES += nanopb \ No newline at end of file diff --git a/Sming/Components/nanopb/inc/pb.h b/Sming/Components/nanopb/inc/pb.h deleted file mode 100644 index 3cc130f73b..0000000000 --- a/Sming/Components/nanopb/inc/pb.h +++ /dev/null @@ -1,875 +0,0 @@ -/* Common parts of the nanopb library. Most of these are quite low-level - * stuff. For the high-level interface, see pb_encode.h and pb_decode.h. - */ - -#ifndef PB_H_INCLUDED -#define PB_H_INCLUDED - -/***************************************************************** - * Nanopb compilation time options. You can change these here by * - * uncommenting the lines, or on the compiler command line. * - *****************************************************************/ - -/* Enable support for dynamically allocated fields */ -/* #define PB_ENABLE_MALLOC 1 */ - -/* Define this if your CPU / compiler combination does not support - * unaligned memory access to packed structures. */ -/* #define PB_NO_PACKED_STRUCTS 1 */ - -/* Increase the number of required fields that are tracked. - * A compiler warning will tell if you need this. */ -/* #define PB_MAX_REQUIRED_FIELDS 256 */ - -/* Add support for tag numbers > 65536 and fields larger than 65536 bytes. */ -/* #define PB_FIELD_32BIT 1 */ - -/* Disable support for error messages in order to save some code space. */ -/* #define PB_NO_ERRMSG 1 */ - -/* Disable support for custom streams (support only memory buffers). */ -/* #define PB_BUFFER_ONLY 1 */ - -/* Disable support for 64-bit datatypes, for compilers without int64_t - or to save some code space. */ -/* #define PB_WITHOUT_64BIT 1 */ - -/* Don't encode scalar arrays as packed. This is only to be used when - * the decoder on the receiving side cannot process packed scalar arrays. - * Such example is older protobuf.js. */ -/* #define PB_ENCODE_ARRAYS_UNPACKED 1 */ - -/* Enable conversion of doubles to floats for platforms that do not - * support 64-bit doubles. Most commonly AVR. */ -/* #define PB_CONVERT_DOUBLE_FLOAT 1 */ - -/* Check whether incoming strings are valid UTF-8 sequences. Slows down - * the string processing slightly and slightly increases code size. */ -/* #define PB_VALIDATE_UTF8 1 */ - -/****************************************************************** - * You usually don't need to change anything below this line. * - * Feel free to look around and use the defined macros, though. * - ******************************************************************/ - - -/* Version of the nanopb library. Just in case you want to check it in - * your own program. */ -#define NANOPB_VERSION nanopb-0.4.3-dev - -/* Include all the system headers needed by nanopb. You will need the - * definitions of the following: - * - strlen, memcpy, memset functions - * - [u]int_least8_t, uint_fast8_t, [u]int_least16_t, [u]int32_t, [u]int64_t - * - size_t - * - bool - * - * If you don't have the standard header files, you can instead provide - * a custom header that defines or includes all this. In that case, - * define PB_SYSTEM_HEADER to the path of this file. - */ -#ifdef PB_SYSTEM_HEADER -#include PB_SYSTEM_HEADER -#else -#include -#include -#include -#include -#include - -#ifdef PB_ENABLE_MALLOC -#include -#endif -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Macro for defining packed structures (compiler dependent). - * This just reduces memory requirements, but is not required. - */ -#if defined(PB_NO_PACKED_STRUCTS) - /* Disable struct packing */ -# define PB_PACKED_STRUCT_START -# define PB_PACKED_STRUCT_END -# define pb_packed -#elif defined(__GNUC__) || defined(__clang__) - /* For GCC and clang */ -# define PB_PACKED_STRUCT_START -# define PB_PACKED_STRUCT_END -# define pb_packed __attribute__((packed)) -#elif defined(__ICCARM__) || defined(__CC_ARM) - /* For IAR ARM and Keil MDK-ARM compilers */ -# define PB_PACKED_STRUCT_START _Pragma("pack(push, 1)") -# define PB_PACKED_STRUCT_END _Pragma("pack(pop)") -# define pb_packed -#elif defined(_MSC_VER) && (_MSC_VER >= 1500) - /* For Microsoft Visual C++ */ -# define PB_PACKED_STRUCT_START __pragma(pack(push, 1)) -# define PB_PACKED_STRUCT_END __pragma(pack(pop)) -# define pb_packed -#else - /* Unknown compiler */ -# define PB_PACKED_STRUCT_START -# define PB_PACKED_STRUCT_END -# define pb_packed -#endif - -/* Handly macro for suppressing unreferenced-parameter compiler warnings. */ -#ifndef PB_UNUSED -#define PB_UNUSED(x) (void)(x) -#endif - -/* Harvard-architecture processors may need special attributes for storing - * field information in program memory. */ -#ifndef PB_PROGMEM -#ifdef __AVR__ -#include -#define PB_PROGMEM PROGMEM -#define PB_PROGMEM_READU32(x) pgm_read_dword(&x) -#else -#define PB_PROGMEM -#define PB_PROGMEM_READU32(x) (x) -#endif -#endif - -/* Compile-time assertion, used for checking compatible compilation options. - * If this does not work properly on your compiler, use - * #define PB_NO_STATIC_ASSERT to disable it. - * - * But before doing that, check carefully the error message / place where it - * comes from to see if the error has a real cause. Unfortunately the error - * message is not always very clear to read, but you can see the reason better - * in the place where the PB_STATIC_ASSERT macro was called. - */ -#ifndef PB_NO_STATIC_ASSERT -# ifndef PB_STATIC_ASSERT -# if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - /* C11 standard _Static_assert mechanism */ -# define PB_STATIC_ASSERT(COND,MSG) _Static_assert(COND,#MSG); -# else - /* Classic negative-size-array static assert mechanism */ -# define PB_STATIC_ASSERT(COND,MSG) typedef char PB_STATIC_ASSERT_MSG(MSG, __LINE__, __COUNTER__)[(COND)?1:-1]; -# define PB_STATIC_ASSERT_MSG(MSG, LINE, COUNTER) PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) -# define PB_STATIC_ASSERT_MSG_(MSG, LINE, COUNTER) pb_static_assertion_##MSG##_##LINE##_##COUNTER -# endif -# endif -#else - /* Static asserts disabled by PB_NO_STATIC_ASSERT */ -# define PB_STATIC_ASSERT(COND,MSG) -#endif - -/* Number of required fields to keep track of. */ -#ifndef PB_MAX_REQUIRED_FIELDS -#define PB_MAX_REQUIRED_FIELDS 64 -#endif - -#if PB_MAX_REQUIRED_FIELDS < 64 -#error You should not lower PB_MAX_REQUIRED_FIELDS from the default value (64). -#endif - -#ifdef PB_WITHOUT_64BIT -#ifdef PB_CONVERT_DOUBLE_FLOAT -/* Cannot use doubles without 64-bit types */ -#undef PB_CONVERT_DOUBLE_FLOAT -#endif -#endif - -/* List of possible field types. These are used in the autogenerated code. - * Least-significant 4 bits tell the scalar type - * Most-significant 4 bits specify repeated/required/packed etc. - */ - -typedef uint_least8_t pb_type_t; - -/**** Field data types ****/ - -/* Numeric types */ -#define PB_LTYPE_BOOL 0x00U /* bool */ -#define PB_LTYPE_VARINT 0x01U /* int32, int64, enum, bool */ -#define PB_LTYPE_UVARINT 0x02U /* uint32, uint64 */ -#define PB_LTYPE_SVARINT 0x03U /* sint32, sint64 */ -#define PB_LTYPE_FIXED32 0x04U /* fixed32, sfixed32, float */ -#define PB_LTYPE_FIXED64 0x05U /* fixed64, sfixed64, double */ - -/* Marker for last packable field type. */ -#define PB_LTYPE_LAST_PACKABLE 0x05U - -/* Byte array with pre-allocated buffer. - * data_size is the length of the allocated PB_BYTES_ARRAY structure. */ -#define PB_LTYPE_BYTES 0x06U - -/* String with pre-allocated buffer. - * data_size is the maximum length. */ -#define PB_LTYPE_STRING 0x07U - -/* Submessage - * submsg_fields is pointer to field descriptions */ -#define PB_LTYPE_SUBMESSAGE 0x08U - -/* Submessage with pre-decoding callback - * The pre-decoding callback is stored as pb_callback_t right before pSize. - * submsg_fields is pointer to field descriptions */ -#define PB_LTYPE_SUBMSG_W_CB 0x09U - -/* Extension pseudo-field - * The field contains a pointer to pb_extension_t */ -#define PB_LTYPE_EXTENSION 0x0AU - -/* Byte array with inline, pre-allocated byffer. - * data_size is the length of the inline, allocated buffer. - * This differs from PB_LTYPE_BYTES by defining the element as - * pb_byte_t[data_size] rather than pb_bytes_array_t. */ -#define PB_LTYPE_FIXED_LENGTH_BYTES 0x0BU - -/* Number of declared LTYPES */ -#define PB_LTYPES_COUNT 0x0CU -#define PB_LTYPE_MASK 0x0FU - -/**** Field repetition rules ****/ - -#define PB_HTYPE_REQUIRED 0x00U -#define PB_HTYPE_OPTIONAL 0x10U -#define PB_HTYPE_SINGULAR 0x10U -#define PB_HTYPE_REPEATED 0x20U -#define PB_HTYPE_FIXARRAY 0x20U -#define PB_HTYPE_ONEOF 0x30U -#define PB_HTYPE_MASK 0x30U - -/**** Field allocation types ****/ - -#define PB_ATYPE_STATIC 0x00U -#define PB_ATYPE_POINTER 0x80U -#define PB_ATYPE_CALLBACK 0x40U -#define PB_ATYPE_MASK 0xC0U - -#define PB_ATYPE(x) ((x) & PB_ATYPE_MASK) -#define PB_HTYPE(x) ((x) & PB_HTYPE_MASK) -#define PB_LTYPE(x) ((x) & PB_LTYPE_MASK) -#define PB_LTYPE_IS_SUBMSG(x) (PB_LTYPE(x) == PB_LTYPE_SUBMESSAGE || \ - PB_LTYPE(x) == PB_LTYPE_SUBMSG_W_CB) - -/* Data type used for storing sizes of struct fields - * and array counts. - */ -#if defined(PB_FIELD_32BIT) - typedef uint32_t pb_size_t; - typedef int32_t pb_ssize_t; -#else - typedef uint_least16_t pb_size_t; - typedef int_least16_t pb_ssize_t; -#endif -#define PB_SIZE_MAX ((pb_size_t)-1) - -/* Data type for storing encoded data and other byte streams. - * This typedef exists to support platforms where uint8_t does not exist. - * You can regard it as equivalent on uint8_t on other platforms. - */ -typedef uint_least8_t pb_byte_t; - -/* Forward declaration of struct types */ -typedef struct pb_istream_s pb_istream_t; -typedef struct pb_ostream_s pb_ostream_t; -typedef struct pb_field_iter_s pb_field_iter_t; - -/* This structure is used in auto-generated constants - * to specify struct fields. - */ -typedef struct pb_msgdesc_s pb_msgdesc_t; -struct pb_msgdesc_s { - const uint32_t *field_info; - const pb_msgdesc_t * const * submsg_info; - const pb_byte_t *default_value; - - bool (*field_callback)(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_iter_t *field); - - pb_size_t field_count; - pb_size_t required_field_count; - pb_size_t largest_tag; -}; - -/* Iterator for message descriptor */ -struct pb_field_iter_s { - const pb_msgdesc_t *descriptor; /* Pointer to message descriptor constant */ - void *message; /* Pointer to start of the structure */ - - pb_size_t index; /* Index of the field */ - pb_size_t field_info_index; /* Index to descriptor->field_info array */ - pb_size_t required_field_index; /* Index that counts only the required fields */ - pb_size_t submessage_index; /* Index that counts only submessages */ - - pb_size_t tag; /* Tag of current field */ - pb_size_t data_size; /* sizeof() of a single item */ - pb_size_t array_size; /* Number of array entries */ - pb_type_t type; /* Type of current field */ - - void *pField; /* Pointer to current field in struct */ - void *pData; /* Pointer to current data contents. Different than pField for arrays and pointers. */ - void *pSize; /* Pointer to count/has field */ - - const pb_msgdesc_t *submsg_desc; /* For submessage fields, pointer to field descriptor for the submessage. */ -}; - -/* For compatibility with legacy code */ -typedef pb_field_iter_t pb_field_t; - -/* Make sure that the standard integer types are of the expected sizes. - * Otherwise fixed32/fixed64 fields can break. - * - * If you get errors here, it probably means that your stdint.h is not - * correct for your platform. - */ -#ifndef PB_WITHOUT_64BIT -PB_STATIC_ASSERT(sizeof(int64_t) == 2 * sizeof(int32_t), INT64_T_WRONG_SIZE) -PB_STATIC_ASSERT(sizeof(uint64_t) == 2 * sizeof(uint32_t), UINT64_T_WRONG_SIZE) -#endif - -/* This structure is used for 'bytes' arrays. - * It has the number of bytes in the beginning, and after that an array. - * Note that actual structs used will have a different length of bytes array. - */ -#define PB_BYTES_ARRAY_T(n) struct { pb_size_t size; pb_byte_t bytes[n]; } -#define PB_BYTES_ARRAY_T_ALLOCSIZE(n) ((size_t)n + offsetof(pb_bytes_array_t, bytes)) - -struct pb_bytes_array_s { - pb_size_t size; - pb_byte_t bytes[1]; -}; -typedef struct pb_bytes_array_s pb_bytes_array_t; - -/* This structure is used for giving the callback function. - * It is stored in the message structure and filled in by the method that - * calls pb_decode. - * - * The decoding callback will be given a limited-length stream - * If the wire type was string, the length is the length of the string. - * If the wire type was a varint/fixed32/fixed64, the length is the length - * of the actual value. - * The function may be called multiple times (especially for repeated types, - * but also otherwise if the message happens to contain the field multiple - * times.) - * - * The encoding callback will receive the actual output stream. - * It should write all the data in one call, including the field tag and - * wire type. It can write multiple fields. - * - * The callback can be null if you want to skip a field. - */ -typedef struct pb_callback_s pb_callback_t; -struct pb_callback_s { - /* Callback functions receive a pointer to the arg field. - * You can access the value of the field as *arg, and modify it if needed. - */ - union { - bool (*decode)(pb_istream_t *stream, const pb_field_t *field, void **arg); - bool (*encode)(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); - } funcs; - - /* Free arg for use by callback */ - void *arg; -}; - -extern bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field); - -/* Wire types. Library user needs these only in encoder callbacks. */ -typedef enum { - PB_WT_VARINT = 0, - PB_WT_64BIT = 1, - PB_WT_STRING = 2, - PB_WT_32BIT = 5 -} pb_wire_type_t; - -/* Structure for defining the handling of unknown/extension fields. - * Usually the pb_extension_type_t structure is automatically generated, - * while the pb_extension_t structure is created by the user. However, - * if you want to catch all unknown fields, you can also create a custom - * pb_extension_type_t with your own callback. - */ -typedef struct pb_extension_type_s pb_extension_type_t; -typedef struct pb_extension_s pb_extension_t; -struct pb_extension_type_s { - /* Called for each unknown field in the message. - * If you handle the field, read off all of its data and return true. - * If you do not handle the field, do not read anything and return true. - * If you run into an error, return false. - * Set to NULL for default handler. - */ - bool (*decode)(pb_istream_t *stream, pb_extension_t *extension, - uint32_t tag, pb_wire_type_t wire_type); - - /* Called once after all regular fields have been encoded. - * If you have something to write, do so and return true. - * If you do not have anything to write, just return true. - * If you run into an error, return false. - * Set to NULL for default handler. - */ - bool (*encode)(pb_ostream_t *stream, const pb_extension_t *extension); - - /* Free field for use by the callback. */ - const void *arg; -}; - -struct pb_extension_s { - /* Type describing the extension field. Usually you'll initialize - * this to a pointer to the automatically generated structure. */ - const pb_extension_type_t *type; - - /* Destination for the decoded data. This must match the datatype - * of the extension field. */ - void *dest; - - /* Pointer to the next extension handler, or NULL. - * If this extension does not match a field, the next handler is - * automatically called. */ - pb_extension_t *next; - - /* The decoder sets this to true if the extension was found. - * Ignored for encoding. */ - bool found; -}; - -#define pb_extension_init_zero {NULL,NULL,NULL,false} - -/* Memory allocation functions to use. You can define pb_realloc and - * pb_free to custom functions if you want. */ -#ifdef PB_ENABLE_MALLOC -# ifndef pb_realloc -# define pb_realloc(ptr, size) realloc(ptr, size) -# endif -# ifndef pb_free -# define pb_free(ptr) free(ptr) -# endif -#endif - -/* This is used to inform about need to regenerate .pb.h/.pb.c files. */ -#define PB_PROTO_HEADER_VERSION 40 - -/* These macros are used to declare pb_field_t's in the constant array. */ -/* Size of a structure member, in bytes. */ -#define pb_membersize(st, m) (sizeof ((st*)0)->m) -/* Number of entries in an array. */ -#define pb_arraysize(st, m) (pb_membersize(st, m) / pb_membersize(st, m[0])) -/* Delta from start of one member to the start of another member. */ -#define pb_delta(st, m1, m2) ((int)offsetof(st, m1) - (int)offsetof(st, m2)) - -/* Force expansion of macro value */ -#define PB_EXPAND(x) x - -/* Binding of a message field set into a specific structure */ -#define PB_BIND(msgname, structname, width) \ - const uint32_t structname ## _field_info[] PB_PROGMEM = \ - { \ - msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ ## width, structname) \ - 0 \ - }; \ - const pb_msgdesc_t* const structname ## _submsg_info[] = \ - { \ - msgname ## _FIELDLIST(PB_GEN_SUBMSG_INFO, structname) \ - NULL \ - }; \ - const pb_msgdesc_t structname ## _msg = \ - { \ - structname ## _field_info, \ - structname ## _submsg_info, \ - msgname ## _DEFAULT, \ - msgname ## _CALLBACK, \ - 0 msgname ## _FIELDLIST(PB_GEN_FIELD_COUNT, structname), \ - 0 msgname ## _FIELDLIST(PB_GEN_REQ_FIELD_COUNT, structname), \ - 0 msgname ## _FIELDLIST(PB_GEN_LARGEST_TAG, structname), \ - }; \ - msgname ## _FIELDLIST(PB_GEN_FIELD_INFO_ASSERT_ ## width, structname) - -#define PB_GEN_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) +1 -#define PB_GEN_REQ_FIELD_COUNT(structname, atype, htype, ltype, fieldname, tag) \ - + (PB_HTYPE_ ## htype == PB_HTYPE_REQUIRED) -#define PB_GEN_LARGEST_TAG(structname, atype, htype, ltype, fieldname, tag) \ - * 0 + tag - -/* X-macro for generating the entries in struct_field_info[] array. */ -#define PB_GEN_FIELD_INFO_1(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_2(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_4(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_8(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_AUTO(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ - tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_FIELDINFO_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ - PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) - -#define PB_FIELDINFO_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ - PB_FIELDINFO_ ## width(tag, type, data_offset, data_size, size_offset, array_size) - -/* X-macro for generating asserts that entries fit in struct_field_info[] array. - * The structure of macros here must match the structure above in PB_GEN_FIELD_INFO_x(), - * but it is not easily reused because of how macro substitutions work. */ -#define PB_GEN_FIELD_INFO_ASSERT_1(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_ASSERT_1(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_ASSERT_2(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_ASSERT_2(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_ASSERT_4(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_ASSERT_4(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_ASSERT_8(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_ASSERT_8(tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_GEN_FIELD_INFO_ASSERT_AUTO(structname, atype, htype, ltype, fieldname, tag) \ - PB_FIELDINFO_ASSERT_AUTO2(PB_FIELDINFO_WIDTH_AUTO(_PB_ATYPE_ ## atype, _PB_HTYPE_ ## htype, _PB_LTYPE_ ## ltype), \ - tag, PB_ATYPE_ ## atype | PB_HTYPE_ ## htype | PB_LTYPE_MAP_ ## ltype, \ - PB_DATA_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_DATA_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_SIZE_OFFSET_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname), \ - PB_ARRAY_SIZE_ ## atype(_PB_HTYPE_ ## htype, structname, fieldname)) - -#define PB_FIELDINFO_ASSERT_AUTO2(width, tag, type, data_offset, data_size, size_offset, array_size) \ - PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) - -#define PB_FIELDINFO_ASSERT_AUTO3(width, tag, type, data_offset, data_size, size_offset, array_size) \ - PB_FIELDINFO_ASSERT_ ## width(tag, type, data_offset, data_size, size_offset, array_size) - -#define PB_DATA_OFFSET_STATIC(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) -#define PB_DATA_OFFSET_POINTER(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) -#define PB_DATA_OFFSET_CALLBACK(htype, structname, fieldname) PB_DO ## htype(structname, fieldname) -#define PB_DO_PB_HTYPE_REQUIRED(structname, fieldname) offsetof(structname, fieldname) -#define PB_DO_PB_HTYPE_SINGULAR(structname, fieldname) offsetof(structname, fieldname) -#define PB_DO_PB_HTYPE_ONEOF(structname, fieldname) offsetof(structname, PB_ONEOF_NAME(FULL, fieldname)) -#define PB_DO_PB_HTYPE_OPTIONAL(structname, fieldname) offsetof(structname, fieldname) -#define PB_DO_PB_HTYPE_REPEATED(structname, fieldname) offsetof(structname, fieldname) -#define PB_DO_PB_HTYPE_FIXARRAY(structname, fieldname) offsetof(structname, fieldname) - -#define PB_SIZE_OFFSET_STATIC(htype, structname, fieldname) PB_SO ## htype(structname, fieldname) -#define PB_SIZE_OFFSET_POINTER(htype, structname, fieldname) PB_SO_PTR ## htype(structname, fieldname) -#define PB_SIZE_OFFSET_CALLBACK(htype, structname, fieldname) PB_SO_CB ## htype(structname, fieldname) -#define PB_SO_PB_HTYPE_REQUIRED(structname, fieldname) 0 -#define PB_SO_PB_HTYPE_SINGULAR(structname, fieldname) 0 -#define PB_SO_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF2(structname, PB_ONEOF_NAME(FULL, fieldname), PB_ONEOF_NAME(UNION, fieldname)) -#define PB_SO_PB_HTYPE_ONEOF2(structname, fullname, unionname) PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) -#define PB_SO_PB_HTYPE_ONEOF3(structname, fullname, unionname) pb_delta(structname, fullname, which_ ## unionname) -#define PB_SO_PB_HTYPE_OPTIONAL(structname, fieldname) pb_delta(structname, fieldname, has_ ## fieldname) -#define PB_SO_PB_HTYPE_REPEATED(structname, fieldname) pb_delta(structname, fieldname, fieldname ## _count) -#define PB_SO_PB_HTYPE_FIXARRAY(structname, fieldname) 0 -#define PB_SO_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 0 -#define PB_SO_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 0 -#define PB_SO_PTR_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) -#define PB_SO_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 0 -#define PB_SO_PTR_PB_HTYPE_REPEATED(structname, fieldname) PB_SO_PB_HTYPE_REPEATED(structname, fieldname) -#define PB_SO_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) 0 -#define PB_SO_CB_PB_HTYPE_REQUIRED(structname, fieldname) 0 -#define PB_SO_CB_PB_HTYPE_SINGULAR(structname, fieldname) 0 -#define PB_SO_CB_PB_HTYPE_ONEOF(structname, fieldname) PB_SO_PB_HTYPE_ONEOF(structname, fieldname) -#define PB_SO_CB_PB_HTYPE_OPTIONAL(structname, fieldname) 0 -#define PB_SO_CB_PB_HTYPE_REPEATED(structname, fieldname) 0 -#define PB_SO_CB_PB_HTYPE_FIXARRAY(structname, fieldname) 0 - -#define PB_ARRAY_SIZE_STATIC(htype, structname, fieldname) PB_AS ## htype(structname, fieldname) -#define PB_ARRAY_SIZE_POINTER(htype, structname, fieldname) PB_AS_PTR ## htype(structname, fieldname) -#define PB_ARRAY_SIZE_CALLBACK(htype, structname, fieldname) 1 -#define PB_AS_PB_HTYPE_REQUIRED(structname, fieldname) 1 -#define PB_AS_PB_HTYPE_SINGULAR(structname, fieldname) 1 -#define PB_AS_PB_HTYPE_OPTIONAL(structname, fieldname) 1 -#define PB_AS_PB_HTYPE_ONEOF(structname, fieldname) 1 -#define PB_AS_PB_HTYPE_REPEATED(structname, fieldname) pb_arraysize(structname, fieldname) -#define PB_AS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname) -#define PB_AS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) 1 -#define PB_AS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) 1 -#define PB_AS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) 1 -#define PB_AS_PTR_PB_HTYPE_ONEOF(structname, fieldname) 1 -#define PB_AS_PTR_PB_HTYPE_REPEATED(structname, fieldname) 1 -#define PB_AS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_arraysize(structname, fieldname[0]) - -#define PB_DATA_SIZE_STATIC(htype, structname, fieldname) PB_DS ## htype(structname, fieldname) -#define PB_DATA_SIZE_POINTER(htype, structname, fieldname) PB_DS_PTR ## htype(structname, fieldname) -#define PB_DATA_SIZE_CALLBACK(htype, structname, fieldname) PB_DS_CB ## htype(structname, fieldname) -#define PB_DS_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) -#define PB_DS_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PTR_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PTR_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PTR_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PTR_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)[0]) -#define PB_DS_PTR_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname[0]) -#define PB_DS_PTR_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname[0][0]) -#define PB_DS_CB_PB_HTYPE_REQUIRED(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_CB_PB_HTYPE_SINGULAR(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_CB_PB_HTYPE_OPTIONAL(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_CB_PB_HTYPE_ONEOF(structname, fieldname) pb_membersize(structname, PB_ONEOF_NAME(FULL, fieldname)) -#define PB_DS_CB_PB_HTYPE_REPEATED(structname, fieldname) pb_membersize(structname, fieldname) -#define PB_DS_CB_PB_HTYPE_FIXARRAY(structname, fieldname) pb_membersize(structname, fieldname) - -#define PB_ONEOF_NAME(type, tuple) PB_EXPAND(PB_ONEOF_NAME_ ## type tuple) -#define PB_ONEOF_NAME_UNION(unionname,membername,fullname) unionname -#define PB_ONEOF_NAME_MEMBER(unionname,membername,fullname) membername -#define PB_ONEOF_NAME_FULL(unionname,membername,fullname) fullname - -#define PB_GEN_SUBMSG_INFO(structname, atype, htype, ltype, fieldname, tag) \ - PB_SUBMSG_INFO_ ## htype(_PB_LTYPE_ ## ltype, structname, fieldname) - -#define PB_SUBMSG_INFO_REQUIRED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) -#define PB_SUBMSG_INFO_SINGULAR(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) -#define PB_SUBMSG_INFO_OPTIONAL(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) -#define PB_SUBMSG_INFO_ONEOF(ltype, structname, fieldname) PB_SUBMSG_INFO_ONEOF2(ltype, structname, PB_ONEOF_NAME(UNION, fieldname), PB_ONEOF_NAME(MEMBER, fieldname)) -#define PB_SUBMSG_INFO_ONEOF2(ltype, structname, unionname, membername) PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) -#define PB_SUBMSG_INFO_ONEOF3(ltype, structname, unionname, membername) PB_SI ## ltype(structname ## _ ## unionname ## _ ## membername ## _MSGTYPE) -#define PB_SUBMSG_INFO_REPEATED(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) -#define PB_SUBMSG_INFO_FIXARRAY(ltype, structname, fieldname) PB_SI ## ltype(structname ## _ ## fieldname ## _MSGTYPE) -#define PB_SI_PB_LTYPE_BOOL(t) -#define PB_SI_PB_LTYPE_BYTES(t) -#define PB_SI_PB_LTYPE_DOUBLE(t) -#define PB_SI_PB_LTYPE_ENUM(t) -#define PB_SI_PB_LTYPE_UENUM(t) -#define PB_SI_PB_LTYPE_FIXED32(t) -#define PB_SI_PB_LTYPE_FIXED64(t) -#define PB_SI_PB_LTYPE_FLOAT(t) -#define PB_SI_PB_LTYPE_INT32(t) -#define PB_SI_PB_LTYPE_INT64(t) -#define PB_SI_PB_LTYPE_MESSAGE(t) PB_SUBMSG_DESCRIPTOR(t) -#define PB_SI_PB_LTYPE_MSG_W_CB(t) PB_SUBMSG_DESCRIPTOR(t) -#define PB_SI_PB_LTYPE_SFIXED32(t) -#define PB_SI_PB_LTYPE_SFIXED64(t) -#define PB_SI_PB_LTYPE_SINT32(t) -#define PB_SI_PB_LTYPE_SINT64(t) -#define PB_SI_PB_LTYPE_STRING(t) -#define PB_SI_PB_LTYPE_UINT32(t) -#define PB_SI_PB_LTYPE_UINT64(t) -#define PB_SI_PB_LTYPE_EXTENSION(t) -#define PB_SI_PB_LTYPE_FIXED_LENGTH_BYTES(t) -#define PB_SUBMSG_DESCRIPTOR(t) &(t ## _msg), - -/* The field descriptors use a variable width format, with width of either - * 1, 2, 4 or 8 of 32-bit words. The two lowest bytes of the first byte always - * encode the descriptor size, 6 lowest bits of field tag number, and 8 bits - * of the field type. - * - * Descriptor size is encoded as 0 = 1 word, 1 = 2 words, 2 = 4 words, 3 = 8 words. - * - * Formats, listed starting with the least significant bit of the first word. - * 1 word: [2-bit len] [6-bit tag] [8-bit type] [8-bit data_offset] [4-bit size_offset] [4-bit data_size] - * - * 2 words: [2-bit len] [6-bit tag] [8-bit type] [12-bit array_size] [4-bit size_offset] - * [16-bit data_offset] [12-bit data_size] [4-bit tag>>6] - * - * 4 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit array_size] - * [8-bit size_offset] [24-bit tag>>6] - * [32-bit data_offset] - * [32-bit data_size] - * - * 8 words: [2-bit len] [6-bit tag] [8-bit type] [16-bit reserved] - * [8-bit size_offset] [24-bit tag>>6] - * [32-bit data_offset] - * [32-bit data_size] - * [32-bit array_size] - * [32-bit reserved] - * [32-bit reserved] - * [32-bit reserved] - */ - -#define PB_FIELDINFO_1(tag, type, data_offset, data_size, size_offset, array_size) \ - (0 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(data_offset) & 0xFF) << 16) | \ - (((uint32_t)(size_offset) & 0x0F) << 24) | (((uint32_t)(data_size) & 0x0F) << 28)), - -#define PB_FIELDINFO_2(tag, type, data_offset, data_size, size_offset, array_size) \ - (1 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFF) << 16) | (((uint32_t)(size_offset) & 0x0F) << 28)), \ - (((uint32_t)(data_offset) & 0xFFFF) | (((uint32_t)(data_size) & 0xFFF) << 16) | (((uint32_t)(tag) & 0x3c0) << 22)), - -#define PB_FIELDINFO_4(tag, type, data_offset, data_size, size_offset, array_size) \ - (2 | (((tag) << 2) & 0xFF) | ((type) << 8) | (((uint32_t)(array_size) & 0xFFFF) << 16)), \ - ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ - (data_offset), (data_size), - -#define PB_FIELDINFO_8(tag, type, data_offset, data_size, size_offset, array_size) \ - (3 | (((tag) << 2) & 0xFF) | ((type) << 8)), \ - ((uint32_t)(int_least8_t)(size_offset) | (((uint32_t)(tag) << 2) & 0xFFFFFF00)), \ - (data_offset), (data_size), (array_size), 0, 0, 0, - -/* These assertions verify that the field information fits in the allocated space. - * The generator tries to automatically determine the correct width that can fit all - * data associated with a message. These asserts will fail only if there has been a - * problem in the automatic logic - this may be worth reporting as a bug. As a workaround, - * you can increase the descriptor width by defining PB_FIELDINFO_WIDTH or by setting - * descriptorsize option in .options file. - */ -#define PB_FITS(value,bits) ((uint32_t)(value) < ((uint32_t)1<2GB messages with nanopb anyway. - */ -#define PB_FIELDINFO_ASSERT_4(tag, type, data_offset, data_size, size_offset, array_size) \ - PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,16), FIELDINFO_DOES_NOT_FIT_width4_field ## tag) - -#define PB_FIELDINFO_ASSERT_8(tag, type, data_offset, data_size, size_offset, array_size) \ - PB_STATIC_ASSERT(PB_FITS(tag,30) && PB_FITS(data_offset,31) && PB_FITS(size_offset,8) && PB_FITS(data_size,31) && PB_FITS(array_size,31), FIELDINFO_DOES_NOT_FIT_width8_field ## tag) -#endif - - -/* Automatic picking of FIELDINFO width: - * Uses width 1 when possible, otherwise resorts to width 2. - * This is used when PB_BIND() is called with "AUTO" as the argument. - * The generator will give explicit size argument when it knows that a message - * structure grows beyond 1-word format limits. - */ -#define PB_FIELDINFO_WIDTH_AUTO(atype, htype, ltype) PB_FI_WIDTH ## atype(htype, ltype) -#define PB_FI_WIDTH_PB_ATYPE_STATIC(htype, ltype) PB_FI_WIDTH ## htype(ltype) -#define PB_FI_WIDTH_PB_ATYPE_POINTER(htype, ltype) PB_FI_WIDTH ## htype(ltype) -#define PB_FI_WIDTH_PB_ATYPE_CALLBACK(htype, ltype) 2 -#define PB_FI_WIDTH_PB_HTYPE_REQUIRED(ltype) PB_FI_WIDTH ## ltype -#define PB_FI_WIDTH_PB_HTYPE_SINGULAR(ltype) PB_FI_WIDTH ## ltype -#define PB_FI_WIDTH_PB_HTYPE_OPTIONAL(ltype) PB_FI_WIDTH ## ltype -#define PB_FI_WIDTH_PB_HTYPE_ONEOF(ltype) PB_FI_WIDTH ## ltype -#define PB_FI_WIDTH_PB_HTYPE_REPEATED(ltype) 2 -#define PB_FI_WIDTH_PB_HTYPE_FIXARRAY(ltype) 2 -#define PB_FI_WIDTH_PB_LTYPE_BOOL 1 -#define PB_FI_WIDTH_PB_LTYPE_BYTES 2 -#define PB_FI_WIDTH_PB_LTYPE_DOUBLE 1 -#define PB_FI_WIDTH_PB_LTYPE_ENUM 1 -#define PB_FI_WIDTH_PB_LTYPE_UENUM 1 -#define PB_FI_WIDTH_PB_LTYPE_FIXED32 1 -#define PB_FI_WIDTH_PB_LTYPE_FIXED64 1 -#define PB_FI_WIDTH_PB_LTYPE_FLOAT 1 -#define PB_FI_WIDTH_PB_LTYPE_INT32 1 -#define PB_FI_WIDTH_PB_LTYPE_INT64 1 -#define PB_FI_WIDTH_PB_LTYPE_MESSAGE 2 -#define PB_FI_WIDTH_PB_LTYPE_MSG_W_CB 2 -#define PB_FI_WIDTH_PB_LTYPE_SFIXED32 1 -#define PB_FI_WIDTH_PB_LTYPE_SFIXED64 1 -#define PB_FI_WIDTH_PB_LTYPE_SINT32 1 -#define PB_FI_WIDTH_PB_LTYPE_SINT64 1 -#define PB_FI_WIDTH_PB_LTYPE_STRING 2 -#define PB_FI_WIDTH_PB_LTYPE_UINT32 1 -#define PB_FI_WIDTH_PB_LTYPE_UINT64 1 -#define PB_FI_WIDTH_PB_LTYPE_EXTENSION 1 -#define PB_FI_WIDTH_PB_LTYPE_FIXED_LENGTH_BYTES 2 - -/* The mapping from protobuf types to LTYPEs is done using these macros. */ -#define PB_LTYPE_MAP_BOOL PB_LTYPE_BOOL -#define PB_LTYPE_MAP_BYTES PB_LTYPE_BYTES -#define PB_LTYPE_MAP_DOUBLE PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_ENUM PB_LTYPE_VARINT -#define PB_LTYPE_MAP_UENUM PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_FIXED32 PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_FIXED64 PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_FLOAT PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_INT32 PB_LTYPE_VARINT -#define PB_LTYPE_MAP_INT64 PB_LTYPE_VARINT -#define PB_LTYPE_MAP_MESSAGE PB_LTYPE_SUBMESSAGE -#define PB_LTYPE_MAP_MSG_W_CB PB_LTYPE_SUBMSG_W_CB -#define PB_LTYPE_MAP_SFIXED32 PB_LTYPE_FIXED32 -#define PB_LTYPE_MAP_SFIXED64 PB_LTYPE_FIXED64 -#define PB_LTYPE_MAP_SINT32 PB_LTYPE_SVARINT -#define PB_LTYPE_MAP_SINT64 PB_LTYPE_SVARINT -#define PB_LTYPE_MAP_STRING PB_LTYPE_STRING -#define PB_LTYPE_MAP_UINT32 PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_UINT64 PB_LTYPE_UVARINT -#define PB_LTYPE_MAP_EXTENSION PB_LTYPE_EXTENSION -#define PB_LTYPE_MAP_FIXED_LENGTH_BYTES PB_LTYPE_FIXED_LENGTH_BYTES - -/* These macros are used for giving out error messages. - * They are mostly a debugging aid; the main error information - * is the true/false return value from functions. - * Some code space can be saved by disabling the error - * messages if not used. - * - * PB_SET_ERROR() sets the error message if none has been set yet. - * msg must be a constant string literal. - * PB_GET_ERROR() always returns a pointer to a string. - * PB_RETURN_ERROR() sets the error and returns false from current - * function. - */ -#ifdef PB_NO_ERRMSG -#define PB_SET_ERROR(stream, msg) PB_UNUSED(stream) -#define PB_GET_ERROR(stream) "(errmsg disabled)" -#else -#define PB_SET_ERROR(stream, msg) (stream->errmsg = (stream)->errmsg ? (stream)->errmsg : (msg)) -#define PB_GET_ERROR(stream) ((stream)->errmsg ? (stream)->errmsg : "(none)") -#endif - -#define PB_RETURN_ERROR(stream, msg) return PB_SET_ERROR(stream, msg), false - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#ifdef __cplusplus -#if __cplusplus >= 201103L -#define PB_CONSTEXPR constexpr -#else // __cplusplus >= 201103L -#define PB_CONSTEXPR -#endif // __cplusplus >= 201103L - -#if __cplusplus >= 201703L -#define PB_INLINE_CONSTEXPR inline constexpr -#else // __cplusplus >= 201703L -#define PB_INLINE_CONSTEXPR PB_CONSTEXPR -#endif // __cplusplus >= 201703L - -namespace nanopb { -// Each type will be partially specialized by the generator. -template struct MessageDescriptor; -} // namespace nanopb -#endif /* __cplusplus */ - -#endif - diff --git a/Sming/Components/nanopb/inc/pb_common.h b/Sming/Components/nanopb/inc/pb_common.h deleted file mode 100644 index 58aa90f76d..0000000000 --- a/Sming/Components/nanopb/inc/pb_common.h +++ /dev/null @@ -1,49 +0,0 @@ -/* pb_common.h: Common support functions for pb_encode.c and pb_decode.c. - * These functions are rarely needed by applications directly. - */ - -#ifndef PB_COMMON_H_INCLUDED -#define PB_COMMON_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Initialize the field iterator structure to beginning. - * Returns false if the message type is empty. */ -bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message); - -/* Get a field iterator for extension field. */ -bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension); - -/* Same as pb_field_iter_begin(), but for const message pointer. - * Note that the pointers in pb_field_iter_t will be non-const but shouldn't - * be written to when using these functions. */ -bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message); -bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension); - -/* Advance the iterator to the next field. - * Returns false when the iterator wraps back to the first field. */ -bool pb_field_iter_next(pb_field_iter_t *iter); - -/* Advance the iterator until it points at a field with the given tag. - * Returns false if no such field exists. */ -bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag); - -/* Find a field with type PB_LTYPE_EXTENSION, or return false if not found. - * There can be only one extension range field per message. */ -bool pb_field_iter_find_extension(pb_field_iter_t *iter); - -#ifdef PB_VALIDATE_UTF8 -/* Validate UTF-8 text string */ -bool pb_validate_utf8(const char *s); -#endif - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif - diff --git a/Sming/Components/nanopb/inc/pb_decode.h b/Sming/Components/nanopb/inc/pb_decode.h deleted file mode 100644 index 9b70c8b345..0000000000 --- a/Sming/Components/nanopb/inc/pb_decode.h +++ /dev/null @@ -1,196 +0,0 @@ -/* pb_decode.h: Functions to decode protocol buffers. Depends on pb_decode.c. - * The main function is pb_decode. You also need an input stream, and the - * field descriptions created by nanopb_generator.py. - */ - -#ifndef PB_DECODE_H_INCLUDED -#define PB_DECODE_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure for defining custom input streams. You will need to provide - * a callback function to read the bytes from your storage, which can be - * for example a file or a network socket. - * - * The callback must conform to these rules: - * - * 1) Return false on IO errors. This will cause decoding to abort. - * 2) You can use state to store your own data (e.g. buffer pointer), - * and rely on pb_read to verify that no-body reads past bytes_left. - * 3) Your callback may be used with substreams, in which case bytes_left - * is different than from the main stream. Don't use bytes_left to compute - * any pointers. - */ -struct pb_istream_s -{ -#ifdef PB_BUFFER_ONLY - /* Callback pointer is not used in buffer-only configuration. - * Having an int pointer here allows binary compatibility but - * gives an error if someone tries to assign callback function. - */ - int *callback; -#else - bool (*callback)(pb_istream_t *stream, pb_byte_t *buf, size_t count); -#endif - - void *state; /* Free field for use by callback implementation */ - size_t bytes_left; - -#ifndef PB_NO_ERRMSG - const char *errmsg; -#endif -}; - -#ifndef PB_NO_ERRMSG -#define PB_ISTREAM_EMPTY {0,0,0,0} -#else -#define PB_ISTREAM_EMPTY {0,0,0} -#endif - -/*************************** - * Main decoding functions * - ***************************/ - -/* Decode a single protocol buffers message from input stream into a C structure. - * Returns true on success, false on any failure. - * The actual struct pointed to by dest must match the description in fields. - * Callback fields of the destination structure must be initialized by caller. - * All other fields will be initialized by this function. - * - * Example usage: - * MyMessage msg = {}; - * uint8_t buffer[64]; - * pb_istream_t stream; - * - * // ... read some data into buffer ... - * - * stream = pb_istream_from_buffer(buffer, count); - * pb_decode(&stream, MyMessage_fields, &msg); - */ -bool pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct); - -/* Extended version of pb_decode, with several options to control - * the decoding process: - * - * PB_DECODE_NOINIT: Do not initialize the fields to default values. - * This is slightly faster if you do not need the default - * values and instead initialize the structure to 0 using - * e.g. memset(). This can also be used for merging two - * messages, i.e. combine already existing data with new - * values. - * - * PB_DECODE_DELIMITED: Input message starts with the message size as varint. - * Corresponds to parseDelimitedFrom() in Google's - * protobuf API. - * - * PB_DECODE_NULLTERMINATED: Stop reading when field tag is read as 0. This allows - * reading null terminated messages. - * NOTE: Until nanopb-0.4.0, pb_decode() also allows - * null-termination. This behaviour is not supported in - * most other protobuf implementations, so PB_DECODE_DELIMITED - * is a better option for compatibility. - * - * Multiple flags can be combined with bitwise or (| operator) - */ -#define PB_DECODE_NOINIT 0x01U -#define PB_DECODE_DELIMITED 0x02U -#define PB_DECODE_NULLTERMINATED 0x04U -bool pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags); - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define pb_decode_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NOINIT) -#define pb_decode_delimited(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED) -#define pb_decode_delimited_noinit(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_DELIMITED | PB_DECODE_NOINIT) -#define pb_decode_nullterminated(s,f,d) pb_decode_ex(s,f,d, PB_DECODE_NULLTERMINATED) - -#ifdef PB_ENABLE_MALLOC -/* Release any allocated pointer fields. If you use dynamic allocation, you should - * call this for any successfully decoded message when you are done with it. If - * pb_decode() returns with an error, the message is already released. - */ -void pb_release(const pb_msgdesc_t *fields, void *dest_struct); -#endif - - -/************************************** - * Functions for manipulating streams * - **************************************/ - -/* Create an input stream for reading from a memory buffer. - * - * msglen should be the actual length of the message, not the full size of - * allocated buffer. - * - * Alternatively, you can use a custom stream that reads directly from e.g. - * a file or a network socket. - */ -pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen); - -/* Function to read from a pb_istream_t. You can use this if you need to - * read some custom header data, or to read data in field callbacks. - */ -bool pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); - - -/************************************************ - * Helper functions for writing field callbacks * - ************************************************/ - -/* Decode the tag for the next field in the stream. Gives the wire type and - * field tag. At end of the message, returns false and sets eof to true. */ -bool pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof); - -/* Skip the field payload data, given the wire type. */ -bool pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type); - -/* Decode an integer in the varint format. This works for enum, int32, - * int64, uint32 and uint64 field types. */ -#ifndef PB_WITHOUT_64BIT -bool pb_decode_varint(pb_istream_t *stream, uint64_t *dest); -#else -#define pb_decode_varint pb_decode_varint32 -#endif - -/* Decode an integer in the varint format. This works for enum, int32, - * and uint32 field types. */ -bool pb_decode_varint32(pb_istream_t *stream, uint32_t *dest); - -/* Decode a bool value in varint format. */ -bool pb_decode_bool(pb_istream_t *stream, bool *dest); - -/* Decode an integer in the zig-zagged svarint format. This works for sint32 - * and sint64. */ -#ifndef PB_WITHOUT_64BIT -bool pb_decode_svarint(pb_istream_t *stream, int64_t *dest); -#else -bool pb_decode_svarint(pb_istream_t *stream, int32_t *dest); -#endif - -/* Decode a fixed32, sfixed32 or float value. You need to pass a pointer to - * a 4-byte wide C variable. */ -bool pb_decode_fixed32(pb_istream_t *stream, void *dest); - -#ifndef PB_WITHOUT_64BIT -/* Decode a fixed64, sfixed64 or double value. You need to pass a pointer to - * a 8-byte wide C variable. */ -bool pb_decode_fixed64(pb_istream_t *stream, void *dest); -#endif - -#ifdef PB_CONVERT_DOUBLE_FLOAT -/* Decode a double value into float variable. */ -bool pb_decode_double_as_float(pb_istream_t *stream, float *dest); -#endif - -/* Make a limited-length substream for reading a PB_WT_STRING field. */ -bool pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream); -bool pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/Sming/Components/nanopb/inc/pb_encode.h b/Sming/Components/nanopb/inc/pb_encode.h deleted file mode 100644 index 88e246a2d9..0000000000 --- a/Sming/Components/nanopb/inc/pb_encode.h +++ /dev/null @@ -1,185 +0,0 @@ -/* pb_encode.h: Functions to encode protocol buffers. Depends on pb_encode.c. - * The main function is pb_encode. You also need an output stream, and the - * field descriptions created by nanopb_generator.py. - */ - -#ifndef PB_ENCODE_H_INCLUDED -#define PB_ENCODE_H_INCLUDED - -#include "pb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Structure for defining custom output streams. You will need to provide - * a callback function to write the bytes to your storage, which can be - * for example a file or a network socket. - * - * The callback must conform to these rules: - * - * 1) Return false on IO errors. This will cause encoding to abort. - * 2) You can use state to store your own data (e.g. buffer pointer). - * 3) pb_write will update bytes_written after your callback runs. - * 4) Substreams will modify max_size and bytes_written. Don't use them - * to calculate any pointers. - */ -struct pb_ostream_s -{ -#ifdef PB_BUFFER_ONLY - /* Callback pointer is not used in buffer-only configuration. - * Having an int pointer here allows binary compatibility but - * gives an error if someone tries to assign callback function. - * Also, NULL pointer marks a 'sizing stream' that does not - * write anything. - */ - int *callback; -#else - bool (*callback)(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); -#endif - void *state; /* Free field for use by callback implementation. */ - size_t max_size; /* Limit number of output bytes written (or use SIZE_MAX). */ - size_t bytes_written; /* Number of bytes written so far. */ - -#ifndef PB_NO_ERRMSG - const char *errmsg; -#endif -}; - -/*************************** - * Main encoding functions * - ***************************/ - -/* Encode a single protocol buffers message from C structure into a stream. - * Returns true on success, false on any failure. - * The actual struct pointed to by src_struct must match the description in fields. - * All required fields in the struct are assumed to have been filled in. - * - * Example usage: - * MyMessage msg = {}; - * uint8_t buffer[64]; - * pb_ostream_t stream; - * - * msg.field1 = 42; - * stream = pb_ostream_from_buffer(buffer, sizeof(buffer)); - * pb_encode(&stream, MyMessage_fields, &msg); - */ -bool pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); - -/* Extended version of pb_encode, with several options to control the - * encoding process: - * - * PB_ENCODE_DELIMITED: Prepend the length of message as a varint. - * Corresponds to writeDelimitedTo() in Google's - * protobuf API. - * - * PB_ENCODE_NULLTERMINATED: Append a null byte to the message for termination. - * NOTE: This behaviour is not supported in most other - * protobuf implementations, so PB_ENCODE_DELIMITED - * is a better option for compatibility. - */ -#define PB_ENCODE_DELIMITED 0x02U -#define PB_ENCODE_NULLTERMINATED 0x04U -bool pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags); - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define pb_encode_delimited(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_DELIMITED) -#define pb_encode_nullterminated(s,f,d) pb_encode_ex(s,f,d, PB_ENCODE_NULLTERMINATED) - -/* Encode the message to get the size of the encoded data, but do not store - * the data. */ -bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct); - -/************************************** - * Functions for manipulating streams * - **************************************/ - -/* Create an output stream for writing into a memory buffer. - * The number of bytes written can be found in stream.bytes_written after - * encoding the message. - * - * Alternatively, you can use a custom stream that writes directly to e.g. - * a file or a network socket. - */ -pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize); - -/* Pseudo-stream for measuring the size of a message without actually storing - * the encoded data. - * - * Example usage: - * MyMessage msg = {}; - * pb_ostream_t stream = PB_OSTREAM_SIZING; - * pb_encode(&stream, MyMessage_fields, &msg); - * printf("Message size is %d\n", stream.bytes_written); - */ -#ifndef PB_NO_ERRMSG -#define PB_OSTREAM_SIZING {0,0,0,0,0} -#else -#define PB_OSTREAM_SIZING {0,0,0,0} -#endif - -/* Function to write into a pb_ostream_t stream. You can use this if you need - * to append or prepend some custom headers to the message. - */ -bool pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); - - -/************************************************ - * Helper functions for writing field callbacks * - ************************************************/ - -/* Encode field header based on type and field number defined in the field - * structure. Call this from the callback before writing out field contents. */ -bool pb_encode_tag_for_field(pb_ostream_t *stream, const pb_field_iter_t *field); - -/* Encode field header by manually specifing wire type. You need to use this - * if you want to write out packed arrays from a callback field. */ -bool pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number); - -/* Encode an integer in the varint format. - * This works for bool, enum, int32, int64, uint32 and uint64 field types. */ -#ifndef PB_WITHOUT_64BIT -bool pb_encode_varint(pb_ostream_t *stream, uint64_t value); -#else -bool pb_encode_varint(pb_ostream_t *stream, uint32_t value); -#endif - -/* Encode an integer in the zig-zagged svarint format. - * This works for sint32 and sint64. */ -#ifndef PB_WITHOUT_64BIT -bool pb_encode_svarint(pb_ostream_t *stream, int64_t value); -#else -bool pb_encode_svarint(pb_ostream_t *stream, int32_t value); -#endif - -/* Encode a string or bytes type field. For strings, pass strlen(s) as size. */ -bool pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size); - -/* Encode a fixed32, sfixed32 or float value. - * You need to pass a pointer to a 4-byte wide C variable. */ -bool pb_encode_fixed32(pb_ostream_t *stream, const void *value); - -#ifndef PB_WITHOUT_64BIT -/* Encode a fixed64, sfixed64 or double value. - * You need to pass a pointer to a 8-byte wide C variable. */ -bool pb_encode_fixed64(pb_ostream_t *stream, const void *value); -#endif - -#ifdef PB_CONVERT_DOUBLE_FLOAT -/* Encode a float value so that it appears like a double in the encoded - * message. */ -bool pb_encode_float_as_double(pb_ostream_t *stream, float value); -#endif - -/* Encode a submessage field. - * You need to pass the pb_field_t array and pointer to struct, just like - * with pb_encode(). This internally encodes the submessage twice, first to - * calculate message size and then to actually write it out. - */ -bool pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct); - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/Sming/Components/nanopb/nanopb b/Sming/Components/nanopb/nanopb new file mode 160000 index 0000000000..b4bc585963 --- /dev/null +++ b/Sming/Components/nanopb/nanopb @@ -0,0 +1 @@ +Subproject commit b4bc585963801338fcb8db8eb650c075f492da24 diff --git a/Sming/Components/nanopb/src/pb_common.c b/Sming/Components/nanopb/src/pb_common.c deleted file mode 100644 index 6aee76b1ef..0000000000 --- a/Sming/Components/nanopb/src/pb_common.c +++ /dev/null @@ -1,388 +0,0 @@ -/* pb_common.c: Common support functions for pb_encode.c and pb_decode.c. - * - * 2014 Petteri Aimonen - */ - -#include "pb_common.h" - -static bool load_descriptor_values(pb_field_iter_t *iter) -{ - uint32_t word0; - uint32_t data_offset; - int_least8_t size_offset; - - if (iter->index >= iter->descriptor->field_count) - return false; - - word0 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); - iter->type = (pb_type_t)((word0 >> 8) & 0xFF); - - switch(word0 & 3) - { - case 0: { - /* 1-word format */ - iter->array_size = 1; - iter->tag = (pb_size_t)((word0 >> 2) & 0x3F); - size_offset = (int_least8_t)((word0 >> 24) & 0x0F); - data_offset = (word0 >> 16) & 0xFF; - iter->data_size = (pb_size_t)((word0 >> 28) & 0x0F); - break; - } - - case 1: { - /* 2-word format */ - uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); - - iter->array_size = (pb_size_t)((word0 >> 16) & 0x0FFF); - iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 28) << 6)); - size_offset = (int_least8_t)((word0 >> 28) & 0x0F); - data_offset = word1 & 0xFFFF; - iter->data_size = (pb_size_t)((word1 >> 16) & 0x0FFF); - break; - } - - case 2: { - /* 4-word format */ - uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); - uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); - uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); - - iter->array_size = (pb_size_t)(word0 >> 16); - iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); - size_offset = (int_least8_t)(word1 & 0xFF); - data_offset = word2; - iter->data_size = (pb_size_t)word3; - break; - } - - default: { - /* 8-word format */ - uint32_t word1 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 1]); - uint32_t word2 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 2]); - uint32_t word3 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 3]); - uint32_t word4 = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index + 4]); - - iter->array_size = (pb_size_t)word4; - iter->tag = (pb_size_t)(((word0 >> 2) & 0x3F) | ((word1 >> 8) << 6)); - size_offset = (int_least8_t)(word1 & 0xFF); - data_offset = word2; - iter->data_size = (pb_size_t)word3; - break; - } - } - - if (!iter->message) - { - /* Avoid doing arithmetic on null pointers, it is undefined */ - iter->pField = NULL; - iter->pSize = NULL; - } - else - { - iter->pField = (char*)iter->message + data_offset; - - if (size_offset) - { - iter->pSize = (char*)iter->pField - size_offset; - } - else if (PB_HTYPE(iter->type) == PB_HTYPE_REPEATED && - (PB_ATYPE(iter->type) == PB_ATYPE_STATIC || - PB_ATYPE(iter->type) == PB_ATYPE_POINTER)) - { - /* Fixed count array */ - iter->pSize = &iter->array_size; - } - else - { - iter->pSize = NULL; - } - - if (PB_ATYPE(iter->type) == PB_ATYPE_POINTER && iter->pField != NULL) - { - iter->pData = *(void**)iter->pField; - } - else - { - iter->pData = iter->pField; - } - } - - if (PB_LTYPE_IS_SUBMSG(iter->type)) - { - iter->submsg_desc = iter->descriptor->submsg_info[iter->submessage_index]; - } - else - { - iter->submsg_desc = NULL; - } - - return true; -} - -static void advance_iterator(pb_field_iter_t *iter) -{ - iter->index++; - - if (iter->index >= iter->descriptor->field_count) - { - /* Restart */ - iter->index = 0; - iter->field_info_index = 0; - iter->submessage_index = 0; - iter->required_field_index = 0; - } - else - { - /* Increment indexes based on previous field type. - * All field info formats have the following fields: - * - lowest 2 bits tell the amount of words in the descriptor (2^n words) - * - bits 2..7 give the lowest bits of tag number. - * - bits 8..15 give the field type. - */ - uint32_t prev_descriptor = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); - pb_type_t prev_type = (prev_descriptor >> 8) & 0xFF; - pb_size_t descriptor_len = (pb_size_t)(1 << (prev_descriptor & 3)); - - /* Add to fields. - * The cast to pb_size_t is needed to avoid -Wconversion warning. - * Because the data is is constants from generator, there is no danger of overflow. - */ - iter->field_info_index = (pb_size_t)(iter->field_info_index + descriptor_len); - iter->required_field_index = (pb_size_t)(iter->required_field_index + (PB_HTYPE(prev_type) == PB_HTYPE_REQUIRED)); - iter->submessage_index = (pb_size_t)(iter->submessage_index + PB_LTYPE_IS_SUBMSG(prev_type)); - } -} - -bool pb_field_iter_begin(pb_field_iter_t *iter, const pb_msgdesc_t *desc, void *message) -{ - memset(iter, 0, sizeof(*iter)); - - iter->descriptor = desc; - iter->message = message; - - return load_descriptor_values(iter); -} - -bool pb_field_iter_begin_extension(pb_field_iter_t *iter, pb_extension_t *extension) -{ - const pb_msgdesc_t *msg = (const pb_msgdesc_t*)extension->type->arg; - bool status; - - uint32_t word0 = PB_PROGMEM_READU32(msg->field_info[0]); - if (PB_ATYPE(word0 >> 8) == PB_ATYPE_POINTER) - { - /* For pointer extensions, the pointer is stored directly - * in the extension structure. This avoids having an extra - * indirection. */ - status = pb_field_iter_begin(iter, msg, &extension->dest); - } - else - { - status = pb_field_iter_begin(iter, msg, extension->dest); - } - - iter->pSize = &extension->found; - return status; -} - -bool pb_field_iter_next(pb_field_iter_t *iter) -{ - advance_iterator(iter); - (void)load_descriptor_values(iter); - return iter->index != 0; -} - -bool pb_field_iter_find(pb_field_iter_t *iter, uint32_t tag) -{ - if (iter->tag == tag) - { - return true; /* Nothing to do, correct field already. */ - } - else if (tag > iter->descriptor->largest_tag) - { - return false; - } - else - { - pb_size_t start = iter->index; - uint32_t fieldinfo; - - if (tag < iter->tag) - { - /* Fields are in tag number order, so we know that tag is between - * 0 and our start position. Setting index to end forces - * advance_iterator() call below to restart from beginning. */ - iter->index = iter->descriptor->field_count; - } - - do - { - /* Advance iterator but don't load values yet */ - advance_iterator(iter); - - /* Do fast check for tag number match */ - fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); - - if (((fieldinfo >> 2) & 0x3F) == (tag & 0x3F)) - { - /* Good candidate, check further */ - (void)load_descriptor_values(iter); - - if (iter->tag == tag && - PB_LTYPE(iter->type) != PB_LTYPE_EXTENSION) - { - /* Found it */ - return true; - } - } - } while (iter->index != start); - - /* Searched all the way back to start, and found nothing. */ - (void)load_descriptor_values(iter); - return false; - } -} - -bool pb_field_iter_find_extension(pb_field_iter_t *iter) -{ - if (PB_LTYPE(iter->type) == PB_LTYPE_EXTENSION) - { - return true; - } - else - { - pb_size_t start = iter->index; - uint32_t fieldinfo; - - do - { - /* Advance iterator but don't load values yet */ - advance_iterator(iter); - - /* Do fast check for field type */ - fieldinfo = PB_PROGMEM_READU32(iter->descriptor->field_info[iter->field_info_index]); - - if (PB_LTYPE((fieldinfo >> 8) & 0xFF) == PB_LTYPE_EXTENSION) - { - return load_descriptor_values(iter); - } - } while (iter->index != start); - - /* Searched all the way back to start, and found nothing. */ - (void)load_descriptor_values(iter); - return false; - } -} - -static void *pb_const_cast(const void *p) -{ - /* Note: this casts away const, in order to use the common field iterator - * logic for both encoding and decoding. The cast is done using union - * to avoid spurious compiler warnings. */ - union { - void *p1; - const void *p2; - } t; - t.p2 = p; - return t.p1; -} - -bool pb_field_iter_begin_const(pb_field_iter_t *iter, const pb_msgdesc_t *desc, const void *message) -{ - return pb_field_iter_begin(iter, desc, pb_const_cast(message)); -} - -bool pb_field_iter_begin_extension_const(pb_field_iter_t *iter, const pb_extension_t *extension) -{ - return pb_field_iter_begin_extension(iter, (pb_extension_t*)pb_const_cast(extension)); -} - -bool pb_default_field_callback(pb_istream_t *istream, pb_ostream_t *ostream, const pb_field_t *field) -{ - if (field->data_size == sizeof(pb_callback_t)) - { - pb_callback_t *pCallback = (pb_callback_t*)field->pData; - - if (pCallback != NULL) - { - if (istream != NULL && pCallback->funcs.decode != NULL) - { - return pCallback->funcs.decode(istream, field, &pCallback->arg); - } - - if (ostream != NULL && pCallback->funcs.encode != NULL) - { - return pCallback->funcs.encode(ostream, field, &pCallback->arg); - } - } - } - - return true; /* Success, but didn't do anything */ - -} - -#ifdef PB_VALIDATE_UTF8 - -/* This function checks whether a string is valid UTF-8 text. - * - * Algorithm is adapted from https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c - * Original copyright: Markus Kuhn 2005-03-30 - * Licensed under "Short code license", which allows use under MIT license or - * any compatible with it. - */ - -bool pb_validate_utf8(const char *str) -{ - const pb_byte_t *s = (const pb_byte_t*)str; - while (*s) - { - if (*s < 0x80) - { - /* 0xxxxxxx */ - s++; - } - else if ((s[0] & 0xe0) == 0xc0) - { - /* 110XXXXx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - (s[0] & 0xfe) == 0xc0) /* overlong? */ - return false; - else - s += 2; - } - else if ((s[0] & 0xf0) == 0xe0) - { - /* 1110XXXX 10Xxxxxx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - (s[2] & 0xc0) != 0x80 || - (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || /* overlong? */ - (s[0] == 0xed && (s[1] & 0xe0) == 0xa0) || /* surrogate? */ - (s[0] == 0xef && s[1] == 0xbf && - (s[2] & 0xfe) == 0xbe)) /* U+FFFE or U+FFFF? */ - return false; - else - s += 3; - } - else if ((s[0] & 0xf8) == 0xf0) - { - /* 11110XXX 10XXxxxx 10xxxxxx 10xxxxxx */ - if ((s[1] & 0xc0) != 0x80 || - (s[2] & 0xc0) != 0x80 || - (s[3] & 0xc0) != 0x80 || - (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || /* overlong? */ - (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) /* > U+10FFFF? */ - return false; - else - s += 4; - } - else - { - return false; - } - } - - return true; -} - -#endif - diff --git a/Sming/Components/nanopb/src/pb_decode.c b/Sming/Components/nanopb/src/pb_decode.c deleted file mode 100644 index 2dab0e307e..0000000000 --- a/Sming/Components/nanopb/src/pb_decode.c +++ /dev/null @@ -1,1685 +0,0 @@ -/* pb_decode.c -- decode a protobuf using minimal resources - * - * 2011 Petteri Aimonen - */ - -/* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. - */ -#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) - #define checkreturn -#else - #define checkreturn __attribute__((warn_unused_result)) -#endif - -#include "pb.h" -#include "pb_decode.h" -#include "pb_common.h" - -/************************************** - * Declarations internal to this file * - **************************************/ - -static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count); -static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof); -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size); -static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); -static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); -static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); -static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); -static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field); -static bool checkreturn default_extension_decoder(pb_istream_t *stream, pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type); -static bool checkreturn decode_extension(pb_istream_t *stream, uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension); -static bool pb_message_set_to_defaults(pb_field_iter_t *iter); -static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_skip_varint(pb_istream_t *stream); -static bool checkreturn pb_skip_string(pb_istream_t *stream); - -#ifdef PB_ENABLE_MALLOC -static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size); -static void initialize_pointer_field(void *pItem, pb_field_iter_t *field); -static bool checkreturn pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field); -static void pb_release_single_field(pb_field_iter_t *field); -#endif - -#ifdef PB_WITHOUT_64BIT -#define pb_int64_t int32_t -#define pb_uint64_t uint32_t -#else -#define pb_int64_t int64_t -#define pb_uint64_t uint64_t -#endif - -#define PB_WT_PACKED ((pb_wire_type_t)0xFF) - -typedef struct { - uint32_t bitfield[(PB_MAX_REQUIRED_FIELDS + 31) / 32]; -} pb_fields_seen_t; - -/******************************* - * pb_istream_t implementation * - *******************************/ - -static bool checkreturn buf_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) -{ - size_t i; - const pb_byte_t *source = (const pb_byte_t*)stream->state; - stream->state = (pb_byte_t*)stream->state + count; - - if (buf != NULL) - { - for (i = 0; i < count; i++) - buf[i] = source[i]; - } - - return true; -} - -bool checkreturn pb_read(pb_istream_t *stream, pb_byte_t *buf, size_t count) -{ - if (count == 0) - return true; - -#ifndef PB_BUFFER_ONLY - if (buf == NULL && stream->callback != buf_read) - { - /* Skip input bytes */ - pb_byte_t tmp[16]; - while (count > 16) - { - if (!pb_read(stream, tmp, 16)) - return false; - - count -= 16; - } - - return pb_read(stream, tmp, count); - } -#endif - - if (stream->bytes_left < count) - PB_RETURN_ERROR(stream, "end-of-stream"); - -#ifndef PB_BUFFER_ONLY - if (!stream->callback(stream, buf, count)) - PB_RETURN_ERROR(stream, "io error"); -#else - if (!buf_read(stream, buf, count)) - return false; -#endif - - stream->bytes_left -= count; - return true; -} - -/* Read a single byte from input stream. buf may not be NULL. - * This is an optimization for the varint decoding. */ -static bool checkreturn pb_readbyte(pb_istream_t *stream, pb_byte_t *buf) -{ - if (stream->bytes_left == 0) - PB_RETURN_ERROR(stream, "end-of-stream"); - -#ifndef PB_BUFFER_ONLY - if (!stream->callback(stream, buf, 1)) - PB_RETURN_ERROR(stream, "io error"); -#else - *buf = *(const pb_byte_t*)stream->state; - stream->state = (pb_byte_t*)stream->state + 1; -#endif - - stream->bytes_left--; - - return true; -} - -pb_istream_t pb_istream_from_buffer(const pb_byte_t *buf, size_t msglen) -{ - pb_istream_t stream; - /* Cast away the const from buf without a compiler error. We are - * careful to use it only in a const manner in the callbacks. - */ - union { - void *state; - const void *c_state; - } state; -#ifdef PB_BUFFER_ONLY - stream.callback = NULL; -#else - stream.callback = &buf_read; -#endif - state.c_state = buf; - stream.state = state.state; - stream.bytes_left = msglen; -#ifndef PB_NO_ERRMSG - stream.errmsg = NULL; -#endif - return stream; -} - -/******************** - * Helper functions * - ********************/ - -static bool checkreturn pb_decode_varint32_eof(pb_istream_t *stream, uint32_t *dest, bool *eof) -{ - pb_byte_t byte; - uint32_t result; - - if (!pb_readbyte(stream, &byte)) - { - if (stream->bytes_left == 0) - { - if (eof) - { - *eof = true; - } - } - - return false; - } - - if ((byte & 0x80) == 0) - { - /* Quick case, 1 byte value */ - result = byte; - } - else - { - /* Multibyte case */ - uint_fast8_t bitpos = 7; - result = byte & 0x7F; - - do - { - if (!pb_readbyte(stream, &byte)) - return false; - - if (bitpos >= 32) - { - /* Note: The varint could have trailing 0x80 bytes, or 0xFF for negative. */ - pb_byte_t sign_extension = (bitpos < 63) ? 0xFF : 0x01; - - if ((byte & 0x7F) != 0x00 && ((result >> 31) == 0 || byte != sign_extension)) - { - PB_RETURN_ERROR(stream, "varint overflow"); - } - } - else - { - result |= (uint32_t)(byte & 0x7F) << bitpos; - } - bitpos = (uint_fast8_t)(bitpos + 7); - } while (byte & 0x80); - - if (bitpos == 35 && (byte & 0x70) != 0) - { - /* The last byte was at bitpos=28, so only bottom 4 bits fit. */ - PB_RETURN_ERROR(stream, "varint overflow"); - } - } - - *dest = result; - return true; -} - -bool checkreturn pb_decode_varint32(pb_istream_t *stream, uint32_t *dest) -{ - return pb_decode_varint32_eof(stream, dest, NULL); -} - -#ifndef PB_WITHOUT_64BIT -bool checkreturn pb_decode_varint(pb_istream_t *stream, uint64_t *dest) -{ - pb_byte_t byte; - uint_fast8_t bitpos = 0; - uint64_t result = 0; - - do - { - if (bitpos >= 64) - PB_RETURN_ERROR(stream, "varint overflow"); - - if (!pb_readbyte(stream, &byte)) - return false; - - result |= (uint64_t)(byte & 0x7F) << bitpos; - bitpos = (uint_fast8_t)(bitpos + 7); - } while (byte & 0x80); - - *dest = result; - return true; -} -#endif - -bool checkreturn pb_skip_varint(pb_istream_t *stream) -{ - pb_byte_t byte; - do - { - if (!pb_read(stream, &byte, 1)) - return false; - } while (byte & 0x80); - return true; -} - -bool checkreturn pb_skip_string(pb_istream_t *stream) -{ - uint32_t length; - if (!pb_decode_varint32(stream, &length)) - return false; - - if ((size_t)length != length) - { - PB_RETURN_ERROR(stream, "size too large"); - } - - return pb_read(stream, NULL, (size_t)length); -} - -bool checkreturn pb_decode_tag(pb_istream_t *stream, pb_wire_type_t *wire_type, uint32_t *tag, bool *eof) -{ - uint32_t temp; - *eof = false; - *wire_type = (pb_wire_type_t) 0; - *tag = 0; - - if (!pb_decode_varint32_eof(stream, &temp, eof)) - { - return false; - } - - *tag = temp >> 3; - *wire_type = (pb_wire_type_t)(temp & 7); - return true; -} - -bool checkreturn pb_skip_field(pb_istream_t *stream, pb_wire_type_t wire_type) -{ - switch (wire_type) - { - case PB_WT_VARINT: return pb_skip_varint(stream); - case PB_WT_64BIT: return pb_read(stream, NULL, 8); - case PB_WT_STRING: return pb_skip_string(stream); - case PB_WT_32BIT: return pb_read(stream, NULL, 4); - default: PB_RETURN_ERROR(stream, "invalid wire_type"); - } -} - -/* Read a raw value to buffer, for the purpose of passing it to callback as - * a substream. Size is maximum size on call, and actual size on return. - */ -static bool checkreturn read_raw_value(pb_istream_t *stream, pb_wire_type_t wire_type, pb_byte_t *buf, size_t *size) -{ - size_t max_size = *size; - switch (wire_type) - { - case PB_WT_VARINT: - *size = 0; - do - { - (*size)++; - if (*size > max_size) - PB_RETURN_ERROR(stream, "varint overflow"); - - if (!pb_read(stream, buf, 1)) - return false; - } while (*buf++ & 0x80); - return true; - - case PB_WT_64BIT: - *size = 8; - return pb_read(stream, buf, 8); - - case PB_WT_32BIT: - *size = 4; - return pb_read(stream, buf, 4); - - case PB_WT_STRING: - /* Calling read_raw_value with a PB_WT_STRING is an error. - * Explicitly handle this case and fallthrough to default to avoid - * compiler warnings. - */ - - default: PB_RETURN_ERROR(stream, "invalid wire_type"); - } -} - -/* Decode string length from stream and return a substream with limited length. - * Remember to close the substream using pb_close_string_substream(). - */ -bool checkreturn pb_make_string_substream(pb_istream_t *stream, pb_istream_t *substream) -{ - uint32_t size; - if (!pb_decode_varint32(stream, &size)) - return false; - - *substream = *stream; - if (substream->bytes_left < size) - PB_RETURN_ERROR(stream, "parent stream too short"); - - substream->bytes_left = (size_t)size; - stream->bytes_left -= (size_t)size; - return true; -} - -bool checkreturn pb_close_string_substream(pb_istream_t *stream, pb_istream_t *substream) -{ - if (substream->bytes_left) { - if (!pb_read(substream, NULL, substream->bytes_left)) - return false; - } - - stream->state = substream->state; - -#ifndef PB_NO_ERRMSG - stream->errmsg = substream->errmsg; -#endif - return true; -} - -/************************* - * Decode a single field * - *************************/ - -static bool checkreturn decode_basic_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) -{ - switch (PB_LTYPE(field->type)) - { - case PB_LTYPE_BOOL: - if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_bool(stream, field); - - case PB_LTYPE_VARINT: - case PB_LTYPE_UVARINT: - case PB_LTYPE_SVARINT: - if (wire_type != PB_WT_VARINT && wire_type != PB_WT_PACKED) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_varint(stream, field); - - case PB_LTYPE_FIXED32: - if (wire_type != PB_WT_32BIT && wire_type != PB_WT_PACKED) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_decode_fixed32(stream, field->pData); - - case PB_LTYPE_FIXED64: - if (wire_type != PB_WT_64BIT && wire_type != PB_WT_PACKED) - PB_RETURN_ERROR(stream, "wrong wire type"); - -#ifdef PB_CONVERT_DOUBLE_FLOAT - if (field->data_size == sizeof(float)) - { - return pb_decode_double_as_float(stream, (float*)field->pData); - } -#endif - -#ifdef PB_WITHOUT_64BIT - PB_RETURN_ERROR(stream, "invalid data_size"); -#else - return pb_decode_fixed64(stream, field->pData); -#endif - - case PB_LTYPE_BYTES: - if (wire_type != PB_WT_STRING) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_bytes(stream, field); - - case PB_LTYPE_STRING: - if (wire_type != PB_WT_STRING) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_string(stream, field); - - case PB_LTYPE_SUBMESSAGE: - case PB_LTYPE_SUBMSG_W_CB: - if (wire_type != PB_WT_STRING) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_submessage(stream, field); - - case PB_LTYPE_FIXED_LENGTH_BYTES: - if (wire_type != PB_WT_STRING) - PB_RETURN_ERROR(stream, "wrong wire type"); - - return pb_dec_fixed_length_bytes(stream, field); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -static bool checkreturn decode_static_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) -{ - switch (PB_HTYPE(field->type)) - { - case PB_HTYPE_REQUIRED: - return decode_basic_field(stream, wire_type, field); - - case PB_HTYPE_OPTIONAL: - if (field->pSize != NULL) - *(bool*)field->pSize = true; - return decode_basic_field(stream, wire_type, field); - - case PB_HTYPE_REPEATED: - if (wire_type == PB_WT_STRING - && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) - { - /* Packed array */ - bool status = true; - pb_istream_t substream; - pb_size_t *size = (pb_size_t*)field->pSize; - field->pData = (char*)field->pField + field->data_size * (*size); - - if (!pb_make_string_substream(stream, &substream)) - return false; - - while (substream.bytes_left > 0 && *size < field->array_size) - { - if (!decode_basic_field(&substream, PB_WT_PACKED, field)) - { - status = false; - break; - } - (*size)++; - field->pData = (char*)field->pData + field->data_size; - } - - if (substream.bytes_left != 0) - PB_RETURN_ERROR(stream, "array overflow"); - if (!pb_close_string_substream(stream, &substream)) - return false; - - return status; - } - else - { - /* Repeated field */ - pb_size_t *size = (pb_size_t*)field->pSize; - field->pData = (char*)field->pField + field->data_size * (*size); - - if ((*size)++ >= field->array_size) - PB_RETURN_ERROR(stream, "array overflow"); - - return decode_basic_field(stream, wire_type, field); - } - - case PB_HTYPE_ONEOF: - *(pb_size_t*)field->pSize = field->tag; - if (PB_LTYPE_IS_SUBMSG(field->type)) - { - /* We memset to zero so that any callbacks are set to NULL. - * This is because the callbacks might otherwise have values - * from some other union field. - * If callbacks are needed inside oneof field, use .proto - * option submsg_callback to have a separate callback function - * that can set the fields before submessage is decoded. - * pb_dec_submessage() will set any default values. */ - memset(field->pData, 0, (size_t)field->data_size); - } - - return decode_basic_field(stream, wire_type, field); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -#ifdef PB_ENABLE_MALLOC -/* Allocate storage for the field and store the pointer at iter->pData. - * array_size is the number of entries to reserve in an array. - * Zero size is not allowed, use pb_free() for releasing. - */ -static bool checkreturn allocate_field(pb_istream_t *stream, void *pData, size_t data_size, size_t array_size) -{ - void *ptr = *(void**)pData; - - if (data_size == 0 || array_size == 0) - PB_RETURN_ERROR(stream, "invalid size"); - -#ifdef __AVR__ - /* Workaround for AVR libc bug 53284: http://savannah.nongnu.org/bugs/?53284 - * Realloc to size of 1 byte can cause corruption of the malloc structures. - */ - if (data_size == 1 && array_size == 1) - { - data_size = 2; - } -#endif - - /* Check for multiplication overflows. - * This code avoids the costly division if the sizes are small enough. - * Multiplication is safe as long as only half of bits are set - * in either multiplicand. - */ - { - const size_t check_limit = (size_t)1 << (sizeof(size_t) * 4); - if (data_size >= check_limit || array_size >= check_limit) - { - const size_t size_max = (size_t)-1; - if (size_max / array_size < data_size) - { - PB_RETURN_ERROR(stream, "size too large"); - } - } - } - - /* Allocate new or expand previous allocation */ - /* Note: on failure the old pointer will remain in the structure, - * the message must be freed by caller also on error return. */ - ptr = pb_realloc(ptr, array_size * data_size); - if (ptr == NULL) - PB_RETURN_ERROR(stream, "realloc failed"); - - *(void**)pData = ptr; - return true; -} - -/* Clear a newly allocated item in case it contains a pointer, or is a submessage. */ -static void initialize_pointer_field(void *pItem, pb_field_iter_t *field) -{ - if (PB_LTYPE(field->type) == PB_LTYPE_STRING || - PB_LTYPE(field->type) == PB_LTYPE_BYTES) - { - *(void**)pItem = NULL; - } - else if (PB_LTYPE_IS_SUBMSG(field->type)) - { - /* We memset to zero so that any callbacks are set to NULL. - * Default values will be set by pb_dec_submessage(). */ - memset(pItem, 0, field->data_size); - } -} -#endif - -static bool checkreturn decode_pointer_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) -{ -#ifndef PB_ENABLE_MALLOC - PB_UNUSED(wire_type); - PB_UNUSED(field); - PB_RETURN_ERROR(stream, "no malloc support"); -#else - switch (PB_HTYPE(field->type)) - { - case PB_HTYPE_REQUIRED: - case PB_HTYPE_OPTIONAL: - case PB_HTYPE_ONEOF: - if (PB_LTYPE_IS_SUBMSG(field->type) && *(void**)field->pField != NULL) - { - /* Duplicate field, have to release the old allocation first. */ - /* FIXME: Does this work correctly for oneofs? */ - pb_release_single_field(field); - } - - if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) - { - *(pb_size_t*)field->pSize = field->tag; - } - - if (PB_LTYPE(field->type) == PB_LTYPE_STRING || - PB_LTYPE(field->type) == PB_LTYPE_BYTES) - { - /* pb_dec_string and pb_dec_bytes handle allocation themselves */ - field->pData = field->pField; - return decode_basic_field(stream, wire_type, field); - } - else - { - if (!allocate_field(stream, field->pField, field->data_size, 1)) - return false; - - field->pData = *(void**)field->pField; - initialize_pointer_field(field->pData, field); - return decode_basic_field(stream, wire_type, field); - } - - case PB_HTYPE_REPEATED: - if (wire_type == PB_WT_STRING - && PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) - { - /* Packed array, multiple items come in at once. */ - bool status = true; - pb_size_t *size = (pb_size_t*)field->pSize; - size_t allocated_size = *size; - pb_istream_t substream; - - if (!pb_make_string_substream(stream, &substream)) - return false; - - while (substream.bytes_left) - { - if (*size == PB_SIZE_MAX) - { -#ifndef PB_NO_ERRMSG - stream->errmsg = "too many array entries"; -#endif - status = false; - break; - } - - if ((size_t)*size + 1 > allocated_size) - { - /* Allocate more storage. This tries to guess the - * number of remaining entries. Round the division - * upwards. */ - size_t remain = (substream.bytes_left - 1) / field->data_size + 1; - if (remain < PB_SIZE_MAX - allocated_size) - allocated_size += remain; - else - allocated_size += 1; - - if (!allocate_field(&substream, field->pField, field->data_size, allocated_size)) - { - status = false; - break; - } - } - - /* Decode the array entry */ - field->pData = *(char**)field->pField + field->data_size * (*size); - initialize_pointer_field(field->pData, field); - if (!decode_basic_field(&substream, PB_WT_PACKED, field)) - { - status = false; - break; - } - - (*size)++; - } - if (!pb_close_string_substream(stream, &substream)) - return false; - - return status; - } - else - { - /* Normal repeated field, i.e. only one item at a time. */ - pb_size_t *size = (pb_size_t*)field->pSize; - - if (*size == PB_SIZE_MAX) - PB_RETURN_ERROR(stream, "too many array entries"); - - if (!allocate_field(stream, field->pField, field->data_size, (size_t)(*size + 1))) - return false; - - field->pData = *(char**)field->pField + field->data_size * (*size); - (*size)++; - initialize_pointer_field(field->pData, field); - return decode_basic_field(stream, wire_type, field); - } - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -#endif -} - -static bool checkreturn decode_callback_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) -{ - if (!field->descriptor->field_callback) - return pb_skip_field(stream, wire_type); - - if (wire_type == PB_WT_STRING) - { - pb_istream_t substream; - size_t prev_bytes_left; - - if (!pb_make_string_substream(stream, &substream)) - return false; - - do - { - prev_bytes_left = substream.bytes_left; - if (!field->descriptor->field_callback(&substream, NULL, field)) - PB_RETURN_ERROR(stream, "callback failed"); - } while (substream.bytes_left > 0 && substream.bytes_left < prev_bytes_left); - - if (!pb_close_string_substream(stream, &substream)) - return false; - - return true; - } - else - { - /* Copy the single scalar value to stack. - * This is required so that we can limit the stream length, - * which in turn allows to use same callback for packed and - * not-packed fields. */ - pb_istream_t substream; - pb_byte_t buffer[10]; - size_t size = sizeof(buffer); - - if (!read_raw_value(stream, wire_type, buffer, &size)) - return false; - substream = pb_istream_from_buffer(buffer, size); - - return field->descriptor->field_callback(&substream, NULL, field); - } -} - -static bool checkreturn decode_field(pb_istream_t *stream, pb_wire_type_t wire_type, pb_field_iter_t *field) -{ -#ifdef PB_ENABLE_MALLOC - /* When decoding an oneof field, check if there is old data that must be - * released first. */ - if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) - { - if (!pb_release_union_field(stream, field)) - return false; - } -#endif - - switch (PB_ATYPE(field->type)) - { - case PB_ATYPE_STATIC: - return decode_static_field(stream, wire_type, field); - - case PB_ATYPE_POINTER: - return decode_pointer_field(stream, wire_type, field); - - case PB_ATYPE_CALLBACK: - return decode_callback_field(stream, wire_type, field); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -/* Default handler for extension fields. Expects to have a pb_msgdesc_t - * pointer in the extension->type->arg field, pointing to a message with - * only one field in it. */ -static bool checkreturn default_extension_decoder(pb_istream_t *stream, - pb_extension_t *extension, uint32_t tag, pb_wire_type_t wire_type) -{ - pb_field_iter_t iter; - - if (!pb_field_iter_begin_extension(&iter, extension)) - PB_RETURN_ERROR(stream, "invalid extension"); - - if (iter.tag != tag || !iter.message) - return true; - - extension->found = true; - return decode_field(stream, wire_type, &iter); -} - -/* Try to decode an unknown field as an extension field. Tries each extension - * decoder in turn, until one of them handles the field or loop ends. */ -static bool checkreturn decode_extension(pb_istream_t *stream, - uint32_t tag, pb_wire_type_t wire_type, pb_extension_t *extension) -{ - size_t pos = stream->bytes_left; - - while (extension != NULL && pos == stream->bytes_left) - { - bool status; - if (extension->type->decode) - status = extension->type->decode(stream, extension, tag, wire_type); - else - status = default_extension_decoder(stream, extension, tag, wire_type); - - if (!status) - return false; - - extension = extension->next; - } - - return true; -} - -/* Initialize message fields to default values, recursively */ -static bool pb_field_set_to_default(pb_field_iter_t *field) -{ - pb_type_t type; - type = field->type; - - if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) - { - pb_extension_t *ext = *(pb_extension_t* const *)field->pData; - while (ext != NULL) - { - pb_field_iter_t ext_iter; - if (pb_field_iter_begin_extension(&ext_iter, ext)) - { - ext->found = false; - if (!pb_message_set_to_defaults(&ext_iter)) - return false; - } - ext = ext->next; - } - } - else if (PB_ATYPE(type) == PB_ATYPE_STATIC) - { - bool init_data = true; - if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) - { - /* Set has_field to false. Still initialize the optional field - * itself also. */ - *(bool*)field->pSize = false; - } - else if (PB_HTYPE(type) == PB_HTYPE_REPEATED || - PB_HTYPE(type) == PB_HTYPE_ONEOF) - { - /* REPEATED: Set array count to 0, no need to initialize contents. - ONEOF: Set which_field to 0. */ - *(pb_size_t*)field->pSize = 0; - init_data = false; - } - - if (init_data) - { - if (PB_LTYPE_IS_SUBMSG(field->type) && - (field->submsg_desc->default_value != NULL || - field->submsg_desc->field_callback != NULL || - field->submsg_desc->submsg_info[0] != NULL)) - { - /* Initialize submessage to defaults. - * Only needed if it has default values - * or callback/submessage fields. */ - pb_field_iter_t submsg_iter; - if (pb_field_iter_begin(&submsg_iter, field->submsg_desc, field->pData)) - { - if (!pb_message_set_to_defaults(&submsg_iter)) - return false; - } - } - else - { - /* Initialize to zeros */ - memset(field->pData, 0, (size_t)field->data_size); - } - } - } - else if (PB_ATYPE(type) == PB_ATYPE_POINTER) - { - /* Initialize the pointer to NULL. */ - *(void**)field->pField = NULL; - - /* Initialize array count to 0. */ - if (PB_HTYPE(type) == PB_HTYPE_REPEATED || - PB_HTYPE(type) == PB_HTYPE_ONEOF) - { - *(pb_size_t*)field->pSize = 0; - } - } - else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) - { - /* Don't overwrite callback */ - } - - return true; -} - -static bool pb_message_set_to_defaults(pb_field_iter_t *iter) -{ - pb_istream_t defstream = PB_ISTREAM_EMPTY; - uint32_t tag = 0; - pb_wire_type_t wire_type = PB_WT_VARINT; - bool eof; - - if (iter->descriptor->default_value) - { - defstream = pb_istream_from_buffer(iter->descriptor->default_value, (size_t)-1); - if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) - return false; - } - - do - { - if (!pb_field_set_to_default(iter)) - return false; - - if (tag != 0 && iter->tag == tag) - { - /* We have a default value for this field in the defstream */ - if (!decode_field(&defstream, wire_type, iter)) - return false; - if (!pb_decode_tag(&defstream, &wire_type, &tag, &eof)) - return false; - - if (iter->pSize) - *(bool*)iter->pSize = false; - } - } while (pb_field_iter_next(iter)); - - return true; -} - -/********************* - * Decode all fields * - *********************/ - -static bool checkreturn pb_decode_inner(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) -{ - uint32_t extension_range_start = 0; - pb_extension_t *extensions = NULL; - - /* 'fixed_count_field' and 'fixed_count_size' track position of a repeated fixed - * count field. This can only handle _one_ repeated fixed count field that - * is unpacked and unordered among other (non repeated fixed count) fields. - */ - pb_size_t fixed_count_field = PB_SIZE_MAX; - pb_size_t fixed_count_size = 0; - pb_size_t fixed_count_total_size = 0; - - pb_fields_seen_t fields_seen = {{0, 0}}; - const uint32_t allbits = ~(uint32_t)0; - pb_field_iter_t iter; - - if (pb_field_iter_begin(&iter, fields, dest_struct)) - { - if ((flags & PB_DECODE_NOINIT) == 0) - { - if (!pb_message_set_to_defaults(&iter)) - PB_RETURN_ERROR(stream, "failed to set defaults"); - } - } - - while (stream->bytes_left) - { - uint32_t tag; - pb_wire_type_t wire_type; - bool eof; - - if (!pb_decode_tag(stream, &wire_type, &tag, &eof)) - { - if (eof) - break; - else - return false; - } - - if (tag == 0) - { - if (flags & PB_DECODE_NULLTERMINATED) - { - break; - } - else - { - PB_RETURN_ERROR(stream, "zero tag"); - } - } - - if (!pb_field_iter_find(&iter, tag) || PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) - { - /* No match found, check if it matches an extension. */ - if (extension_range_start == 0) - { - if (pb_field_iter_find_extension(&iter)) - { - extensions = *(pb_extension_t* const *)iter.pData; - extension_range_start = iter.tag; - } - - if (!extensions) - { - extension_range_start = (uint32_t)-1; - } - } - - if (tag >= extension_range_start) - { - size_t pos = stream->bytes_left; - - if (!decode_extension(stream, tag, wire_type, extensions)) - return false; - - if (pos != stream->bytes_left) - { - /* The field was handled */ - continue; - } - } - - /* No match found, skip data */ - if (!pb_skip_field(stream, wire_type)) - return false; - continue; - } - - /* If a repeated fixed count field was found, get size from - * 'fixed_count_field' as there is no counter contained in the struct. - */ - if (PB_HTYPE(iter.type) == PB_HTYPE_REPEATED && iter.pSize == &iter.array_size) - { - if (fixed_count_field != iter.index) { - /* If the new fixed count field does not match the previous one, - * check that the previous one is NULL or that it finished - * receiving all the expected data. - */ - if (fixed_count_field != PB_SIZE_MAX && - fixed_count_size != fixed_count_total_size) - { - PB_RETURN_ERROR(stream, "wrong size for fixed count field"); - } - - fixed_count_field = iter.index; - fixed_count_size = 0; - fixed_count_total_size = iter.array_size; - } - - iter.pSize = &fixed_count_size; - } - - if (PB_HTYPE(iter.type) == PB_HTYPE_REQUIRED - && iter.required_field_index < PB_MAX_REQUIRED_FIELDS) - { - uint32_t tmp = ((uint32_t)1 << (iter.required_field_index & 31)); - fields_seen.bitfield[iter.required_field_index >> 5] |= tmp; - } - - if (!decode_field(stream, wire_type, &iter)) - return false; - } - - /* Check that all elements of the last decoded fixed count field were present. */ - if (fixed_count_field != PB_SIZE_MAX && - fixed_count_size != fixed_count_total_size) - { - PB_RETURN_ERROR(stream, "wrong size for fixed count field"); - } - - /* Check that all required fields were present. */ - { - pb_size_t req_field_count = iter.descriptor->required_field_count; - - if (req_field_count > 0) - { - pb_size_t i; - - if (req_field_count > PB_MAX_REQUIRED_FIELDS) - req_field_count = PB_MAX_REQUIRED_FIELDS; - - /* Check the whole words */ - for (i = 0; i < (req_field_count >> 5); i++) - { - if (fields_seen.bitfield[i] != allbits) - PB_RETURN_ERROR(stream, "missing required field"); - } - - /* Check the remaining bits (if any) */ - if ((req_field_count & 31) != 0) - { - if (fields_seen.bitfield[req_field_count >> 5] != - (allbits >> (uint_least8_t)(32 - (req_field_count & 31)))) - { - PB_RETURN_ERROR(stream, "missing required field"); - } - } - } - } - - return true; -} - -bool checkreturn pb_decode_ex(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct, unsigned int flags) -{ - bool status; - - if ((flags & PB_DECODE_DELIMITED) == 0) - { - status = pb_decode_inner(stream, fields, dest_struct, flags); - } - else - { - pb_istream_t substream; - if (!pb_make_string_substream(stream, &substream)) - return false; - - status = pb_decode_inner(&substream, fields, dest_struct, flags); - - if (!pb_close_string_substream(stream, &substream)) - return false; - } - -#ifdef PB_ENABLE_MALLOC - if (!status) - pb_release(fields, dest_struct); -#endif - - return status; -} - -bool checkreturn pb_decode(pb_istream_t *stream, const pb_msgdesc_t *fields, void *dest_struct) -{ - bool status; - - status = pb_decode_inner(stream, fields, dest_struct, 0); - -#ifdef PB_ENABLE_MALLOC - if (!status) - pb_release(fields, dest_struct); -#endif - - return status; -} - -#ifdef PB_ENABLE_MALLOC -/* Given an oneof field, if there has already been a field inside this oneof, - * release it before overwriting with a different one. */ -static bool pb_release_union_field(pb_istream_t *stream, pb_field_iter_t *field) -{ - pb_field_iter_t old_field = *field; - pb_size_t old_tag = *(pb_size_t*)field->pSize; /* Previous which_ value */ - pb_size_t new_tag = field->tag; /* New which_ value */ - - if (old_tag == 0) - return true; /* Ok, no old data in union */ - - if (old_tag == new_tag) - return true; /* Ok, old data is of same type => merge */ - - /* Release old data. The find can fail if the message struct contains - * invalid data. */ - if (!pb_field_iter_find(&old_field, old_tag)) - PB_RETURN_ERROR(stream, "invalid union tag"); - - pb_release_single_field(&old_field); - - return true; -} - -static void pb_release_single_field(pb_field_iter_t *field) -{ - pb_type_t type; - type = field->type; - - if (PB_HTYPE(type) == PB_HTYPE_ONEOF) - { - if (*(pb_size_t*)field->pSize != field->tag) - return; /* This is not the current field in the union */ - } - - /* Release anything contained inside an extension or submsg. - * This has to be done even if the submsg itself is statically - * allocated. */ - if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) - { - /* Release fields from all extensions in the linked list */ - pb_extension_t *ext = *(pb_extension_t**)field->pData; - while (ext != NULL) - { - pb_field_iter_t ext_iter; - if (pb_field_iter_begin_extension(&ext_iter, ext)) - { - pb_release_single_field(&ext_iter); - } - ext = ext->next; - } - } - else if (PB_LTYPE_IS_SUBMSG(type) && PB_ATYPE(type) != PB_ATYPE_CALLBACK) - { - /* Release fields in submessage or submsg array */ - pb_size_t count = 1; - - if (PB_ATYPE(type) == PB_ATYPE_POINTER) - { - field->pData = *(void**)field->pField; - } - else - { - field->pData = field->pField; - } - - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - count = *(pb_size_t*)field->pSize; - - if (PB_ATYPE(type) == PB_ATYPE_STATIC && count > field->array_size) - { - /* Protect against corrupted _count fields */ - count = field->array_size; - } - } - - if (field->pData) - { - for (; count > 0; count--) - { - pb_release(field->submsg_desc, field->pData); - field->pData = (char*)field->pData + field->data_size; - } - } - } - - if (PB_ATYPE(type) == PB_ATYPE_POINTER) - { - if (PB_HTYPE(type) == PB_HTYPE_REPEATED && - (PB_LTYPE(type) == PB_LTYPE_STRING || - PB_LTYPE(type) == PB_LTYPE_BYTES)) - { - /* Release entries in repeated string or bytes array */ - void **pItem = *(void***)field->pField; - pb_size_t count = *(pb_size_t*)field->pSize; - for (; count > 0; count--) - { - pb_free(*pItem); - *pItem++ = NULL; - } - } - - if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - /* We are going to release the array, so set the size to 0 */ - *(pb_size_t*)field->pSize = 0; - } - - /* Release main pointer */ - pb_free(*(void**)field->pField); - *(void**)field->pField = NULL; - } -} - -void pb_release(const pb_msgdesc_t *fields, void *dest_struct) -{ - pb_field_iter_t iter; - - if (!dest_struct) - return; /* Ignore NULL pointers, similar to free() */ - - if (!pb_field_iter_begin(&iter, fields, dest_struct)) - return; /* Empty message type */ - - do - { - pb_release_single_field(&iter); - } while (pb_field_iter_next(&iter)); -} -#endif - -/* Field decoders */ - -bool pb_decode_bool(pb_istream_t *stream, bool *dest) -{ - uint32_t value; - if (!pb_decode_varint32(stream, &value)) - return false; - - *(bool*)dest = (value != 0); - return true; -} - -bool pb_decode_svarint(pb_istream_t *stream, pb_int64_t *dest) -{ - pb_uint64_t value; - if (!pb_decode_varint(stream, &value)) - return false; - - if (value & 1) - *dest = (pb_int64_t)(~(value >> 1)); - else - *dest = (pb_int64_t)(value >> 1); - - return true; -} - -bool pb_decode_fixed32(pb_istream_t *stream, void *dest) -{ - union { - uint32_t fixed32; - pb_byte_t bytes[4]; - } u; - - if (!pb_read(stream, u.bytes, 4)) - return false; - -#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 - /* fast path - if we know that we're on little endian, assign directly */ - *(uint32_t*)dest = u.fixed32; -#else - *(uint32_t*)dest = ((uint32_t)u.bytes[0] << 0) | - ((uint32_t)u.bytes[1] << 8) | - ((uint32_t)u.bytes[2] << 16) | - ((uint32_t)u.bytes[3] << 24); -#endif - return true; -} - -#ifndef PB_WITHOUT_64BIT -bool pb_decode_fixed64(pb_istream_t *stream, void *dest) -{ - union { - uint64_t fixed64; - pb_byte_t bytes[8]; - } u; - - if (!pb_read(stream, u.bytes, 8)) - return false; - -#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && CHAR_BIT == 8 - /* fast path - if we know that we're on little endian, assign directly */ - *(uint64_t*)dest = u.fixed64; -#else - *(uint64_t*)dest = ((uint64_t)u.bytes[0] << 0) | - ((uint64_t)u.bytes[1] << 8) | - ((uint64_t)u.bytes[2] << 16) | - ((uint64_t)u.bytes[3] << 24) | - ((uint64_t)u.bytes[4] << 32) | - ((uint64_t)u.bytes[5] << 40) | - ((uint64_t)u.bytes[6] << 48) | - ((uint64_t)u.bytes[7] << 56); -#endif - return true; -} -#endif - -static bool checkreturn pb_dec_bool(pb_istream_t *stream, const pb_field_iter_t *field) -{ - return pb_decode_bool(stream, (bool*)field->pData); -} - -static bool checkreturn pb_dec_varint(pb_istream_t *stream, const pb_field_iter_t *field) -{ - if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) - { - pb_uint64_t value, clamped; - if (!pb_decode_varint(stream, &value)) - return false; - - /* Cast to the proper field size, while checking for overflows */ - if (field->data_size == sizeof(pb_uint64_t)) - clamped = *(pb_uint64_t*)field->pData = value; - else if (field->data_size == sizeof(uint32_t)) - clamped = *(uint32_t*)field->pData = (uint32_t)value; - else if (field->data_size == sizeof(uint_least16_t)) - clamped = *(uint_least16_t*)field->pData = (uint_least16_t)value; - else if (field->data_size == sizeof(uint_least8_t)) - clamped = *(uint_least8_t*)field->pData = (uint_least8_t)value; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (clamped != value) - PB_RETURN_ERROR(stream, "integer too large"); - - return true; - } - else - { - pb_uint64_t value; - pb_int64_t svalue; - pb_int64_t clamped; - - if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) - { - if (!pb_decode_svarint(stream, &svalue)) - return false; - } - else - { - if (!pb_decode_varint(stream, &value)) - return false; - - /* See issue 97: Google's C++ protobuf allows negative varint values to - * be cast as int32_t, instead of the int64_t that should be used when - * encoding. Nanopb versions before 0.2.5 had a bug in encoding. In order to - * not break decoding of such messages, we cast <=32 bit fields to - * int32_t first to get the sign correct. - */ - if (field->data_size == sizeof(pb_int64_t)) - svalue = (pb_int64_t)value; - else - svalue = (int32_t)value; - } - - /* Cast to the proper field size, while checking for overflows */ - if (field->data_size == sizeof(pb_int64_t)) - clamped = *(pb_int64_t*)field->pData = svalue; - else if (field->data_size == sizeof(int32_t)) - clamped = *(int32_t*)field->pData = (int32_t)svalue; - else if (field->data_size == sizeof(int_least16_t)) - clamped = *(int_least16_t*)field->pData = (int_least16_t)svalue; - else if (field->data_size == sizeof(int_least8_t)) - clamped = *(int_least8_t*)field->pData = (int_least8_t)svalue; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (clamped != svalue) - PB_RETURN_ERROR(stream, "integer too large"); - - return true; - } -} - -static bool checkreturn pb_dec_bytes(pb_istream_t *stream, const pb_field_iter_t *field) -{ - uint32_t size; - size_t alloc_size; - pb_bytes_array_t *dest; - - if (!pb_decode_varint32(stream, &size)) - return false; - - if (size > PB_SIZE_MAX) - PB_RETURN_ERROR(stream, "bytes overflow"); - - alloc_size = PB_BYTES_ARRAY_T_ALLOCSIZE(size); - if (size > alloc_size) - PB_RETURN_ERROR(stream, "size too large"); - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) - { -#ifndef PB_ENABLE_MALLOC - PB_RETURN_ERROR(stream, "no malloc support"); -#else - if (stream->bytes_left < size) - PB_RETURN_ERROR(stream, "end-of-stream"); - - if (!allocate_field(stream, field->pData, alloc_size, 1)) - return false; - dest = *(pb_bytes_array_t**)field->pData; -#endif - } - else - { - if (alloc_size > field->data_size) - PB_RETURN_ERROR(stream, "bytes overflow"); - dest = (pb_bytes_array_t*)field->pData; - } - - dest->size = (pb_size_t)size; - return pb_read(stream, dest->bytes, (size_t)size); -} - -static bool checkreturn pb_dec_string(pb_istream_t *stream, const pb_field_iter_t *field) -{ - uint32_t size; - size_t alloc_size; - pb_byte_t *dest = (pb_byte_t*)field->pData; - - if (!pb_decode_varint32(stream, &size)) - return false; - - if (size == (uint32_t)-1) - PB_RETURN_ERROR(stream, "size too large"); - - /* Space for null terminator */ - alloc_size = (size_t)(size + 1); - - if (alloc_size < size) - PB_RETURN_ERROR(stream, "size too large"); - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) - { -#ifndef PB_ENABLE_MALLOC - PB_RETURN_ERROR(stream, "no malloc support"); -#else - if (stream->bytes_left < size) - PB_RETURN_ERROR(stream, "end-of-stream"); - - if (!allocate_field(stream, field->pData, alloc_size, 1)) - return false; - dest = *(pb_byte_t**)field->pData; -#endif - } - else - { - if (alloc_size > field->data_size) - PB_RETURN_ERROR(stream, "string overflow"); - } - - dest[size] = 0; - - if (!pb_read(stream, dest, (size_t)size)) - return false; - -#ifdef PB_VALIDATE_UTF8 - if (!pb_validate_utf8((const char*)dest)) - PB_RETURN_ERROR(stream, "invalid utf8"); -#endif - - return true; -} - -static bool checkreturn pb_dec_submessage(pb_istream_t *stream, const pb_field_iter_t *field) -{ - bool status = true; - bool submsg_consumed = false; - pb_istream_t substream; - - if (!pb_make_string_substream(stream, &substream)) - return false; - - if (field->submsg_desc == NULL) - PB_RETURN_ERROR(stream, "invalid field descriptor"); - - /* Submessages can have a separate message-level callback that is called - * before decoding the message. Typically it is used to set callback fields - * inside oneofs. */ - if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) - { - /* Message callback is stored right before pSize. */ - pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; - if (callback->funcs.decode) - { - status = callback->funcs.decode(&substream, field, &callback->arg); - - if (substream.bytes_left == 0) - { - submsg_consumed = true; - } - } - } - - /* Now decode the submessage contents */ - if (status && !submsg_consumed) - { - unsigned int flags = 0; - - /* Static required/optional fields are already initialized by top-level - * pb_decode(), no need to initialize them again. */ - if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && - PB_HTYPE(field->type) != PB_HTYPE_REPEATED && - PB_HTYPE(field->type) != PB_HTYPE_ONEOF) - { - flags = PB_DECODE_NOINIT; - } - - status = pb_decode_inner(&substream, field->submsg_desc, field->pData, flags); - } - - if (!pb_close_string_substream(stream, &substream)) - return false; - - return status; -} - -static bool checkreturn pb_dec_fixed_length_bytes(pb_istream_t *stream, const pb_field_iter_t *field) -{ - uint32_t size; - - if (!pb_decode_varint32(stream, &size)) - return false; - - if (size > PB_SIZE_MAX) - PB_RETURN_ERROR(stream, "bytes overflow"); - - if (size == 0) - { - /* As a special case, treat empty bytes string as all zeros for fixed_length_bytes. */ - memset(field->pData, 0, (size_t)field->data_size); - return true; - } - - if (size != field->data_size) - PB_RETURN_ERROR(stream, "incorrect fixed length bytes size"); - - return pb_read(stream, (pb_byte_t*)field->pData, (size_t)field->data_size); -} - -#ifdef PB_CONVERT_DOUBLE_FLOAT -bool pb_decode_double_as_float(pb_istream_t *stream, float *dest) -{ - uint_least8_t sign; - int exponent; - uint32_t mantissa; - uint64_t value; - union { float f; uint32_t i; } out; - - if (!pb_decode_fixed64(stream, &value)) - return false; - - /* Decompose input value */ - sign = (uint_least8_t)((value >> 63) & 1); - exponent = (int)((value >> 52) & 0x7FF) - 1023; - mantissa = (value >> 28) & 0xFFFFFF; /* Highest 24 bits */ - - /* Figure if value is in range representable by floats. */ - if (exponent == 1024) - { - /* Special value */ - exponent = 128; - mantissa >>= 1; - } - else - { - if (exponent > 127) - { - /* Too large, convert to infinity */ - exponent = 128; - mantissa = 0; - } - else if (exponent < -150) - { - /* Too small, convert to zero */ - exponent = -127; - mantissa = 0; - } - else if (exponent < -126) - { - /* Denormalized */ - mantissa |= 0x1000000; - mantissa >>= (-126 - exponent); - exponent = -127; - } - - /* Round off mantissa */ - mantissa = (mantissa + 1) >> 1; - - /* Check if mantissa went over 2.0 */ - if (mantissa & 0x800000) - { - exponent += 1; - mantissa &= 0x7FFFFF; - mantissa >>= 1; - } - } - - /* Combine fields */ - out.i = mantissa; - out.i |= (uint32_t)(exponent + 127) << 23; - out.i |= (uint32_t)sign << 31; - - *dest = out.f; - return true; -} -#endif diff --git a/Sming/Components/nanopb/src/pb_encode.c b/Sming/Components/nanopb/src/pb_encode.c deleted file mode 100644 index 54cd5bae2d..0000000000 --- a/Sming/Components/nanopb/src/pb_encode.c +++ /dev/null @@ -1,978 +0,0 @@ -/* pb_encode.c -- encode a protobuf using minimal resources - * - * 2011 Petteri Aimonen - */ - -#include "pb.h" -#include "pb_encode.h" -#include "pb_common.h" - -/* Use the GCC warn_unused_result attribute to check that all return values - * are propagated correctly. On other compilers and gcc before 3.4.0 just - * ignore the annotation. - */ -#if !defined(__GNUC__) || ( __GNUC__ < 3) || (__GNUC__ == 3 && __GNUC_MINOR__ < 4) - #define checkreturn -#else - #define checkreturn __attribute__((warn_unused_result)) -#endif - -/************************************** - * Declarations internal to this file * - **************************************/ -static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count); -static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field); -static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field); -static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field); -static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension); -static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high); -static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field); -static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field); - -#ifdef PB_WITHOUT_64BIT -#define pb_int64_t int32_t -#define pb_uint64_t uint32_t -#else -#define pb_int64_t int64_t -#define pb_uint64_t uint64_t -#endif - -/******************************* - * pb_ostream_t implementation * - *******************************/ - -static bool checkreturn buf_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -{ - size_t i; - pb_byte_t *dest = (pb_byte_t*)stream->state; - stream->state = dest + count; - - for (i = 0; i < count; i++) - dest[i] = buf[i]; - - return true; -} - -pb_ostream_t pb_ostream_from_buffer(pb_byte_t *buf, size_t bufsize) -{ - pb_ostream_t stream; -#ifdef PB_BUFFER_ONLY - stream.callback = (void*)1; /* Just a marker value */ -#else - stream.callback = &buf_write; -#endif - stream.state = buf; - stream.max_size = bufsize; - stream.bytes_written = 0; -#ifndef PB_NO_ERRMSG - stream.errmsg = NULL; -#endif - return stream; -} - -bool checkreturn pb_write(pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -{ - if (count > 0 && stream->callback != NULL) - { - if (stream->bytes_written + count < stream->bytes_written || - stream->bytes_written + count > stream->max_size) - { - PB_RETURN_ERROR(stream, "stream full"); - } - -#ifdef PB_BUFFER_ONLY - if (!buf_write(stream, buf, count)) - PB_RETURN_ERROR(stream, "io error"); -#else - if (!stream->callback(stream, buf, count)) - PB_RETURN_ERROR(stream, "io error"); -#endif - } - - stream->bytes_written += count; - return true; -} - -/************************* - * Encode a single field * - *************************/ - -/* Read a bool value without causing undefined behavior even if the value - * is invalid. See issue #434 and - * https://stackoverflow.com/questions/27661768/weird-results-for-conditional - */ -static bool safe_read_bool(const void *pSize) -{ - const char *p = (const char *)pSize; - size_t i; - for (i = 0; i < sizeof(bool); i++) - { - if (p[i] != 0) - return true; - } - return false; -} - -/* Encode a static array. Handles the size calculations and possible packing. */ -static bool checkreturn encode_array(pb_ostream_t *stream, pb_field_iter_t *field) -{ - pb_size_t i; - pb_size_t count; -#ifndef PB_ENCODE_ARRAYS_UNPACKED - size_t size; -#endif - - count = *(pb_size_t*)field->pSize; - - if (count == 0) - return true; - - if (PB_ATYPE(field->type) != PB_ATYPE_POINTER && count > field->array_size) - PB_RETURN_ERROR(stream, "array max size exceeded"); - -#ifndef PB_ENCODE_ARRAYS_UNPACKED - /* We always pack arrays if the datatype allows it. */ - if (PB_LTYPE(field->type) <= PB_LTYPE_LAST_PACKABLE) - { - if (!pb_encode_tag(stream, PB_WT_STRING, field->tag)) - return false; - - /* Determine the total size of packed array. */ - if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32) - { - size = 4 * (size_t)count; - } - else if (PB_LTYPE(field->type) == PB_LTYPE_FIXED64) - { - size = 8 * (size_t)count; - } - else - { - pb_ostream_t sizestream = PB_OSTREAM_SIZING; - void *pData_orig = field->pData; - for (i = 0; i < count; i++) - { - if (!pb_enc_varint(&sizestream, field)) - PB_RETURN_ERROR(stream, PB_GET_ERROR(&sizestream)); - field->pData = (char*)field->pData + field->data_size; - } - field->pData = pData_orig; - size = sizestream.bytes_written; - } - - if (!pb_encode_varint(stream, (pb_uint64_t)size)) - return false; - - if (stream->callback == NULL) - return pb_write(stream, NULL, size); /* Just sizing.. */ - - /* Write the data */ - for (i = 0; i < count; i++) - { - if (PB_LTYPE(field->type) == PB_LTYPE_FIXED32 || PB_LTYPE(field->type) == PB_LTYPE_FIXED64) - { - if (!pb_enc_fixed(stream, field)) - return false; - } - else - { - if (!pb_enc_varint(stream, field)) - return false; - } - - field->pData = (char*)field->pData + field->data_size; - } - } - else /* Unpacked fields */ -#endif - { - for (i = 0; i < count; i++) - { - /* Normally the data is stored directly in the array entries, but - * for pointer-type string and bytes fields, the array entries are - * actually pointers themselves also. So we have to dereference once - * more to get to the actual data. */ - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER && - (PB_LTYPE(field->type) == PB_LTYPE_STRING || - PB_LTYPE(field->type) == PB_LTYPE_BYTES)) - { - bool status; - void *pData_orig = field->pData; - field->pData = *(void* const*)field->pData; - - if (!field->pData) - { - /* Null pointer in array is treated as empty string / bytes */ - status = pb_encode_tag_for_field(stream, field) && - pb_encode_varint(stream, 0); - } - else - { - status = encode_basic_field(stream, field); - } - - field->pData = pData_orig; - - if (!status) - return false; - } - else - { - if (!encode_basic_field(stream, field)) - return false; - } - field->pData = (char*)field->pData + field->data_size; - } - } - - return true; -} - -/* In proto3, all fields are optional and are only encoded if their value is "non-zero". - * This function implements the check for the zero value. */ -static bool checkreturn pb_check_proto3_default_value(const pb_field_iter_t *field) -{ - pb_type_t type = field->type; - - if (PB_ATYPE(type) == PB_ATYPE_STATIC) - { - if (PB_HTYPE(type) == PB_HTYPE_REQUIRED) - { - /* Required proto2 fields inside proto3 submessage, pretty rare case */ - return false; - } - else if (PB_HTYPE(type) == PB_HTYPE_REPEATED) - { - /* Repeated fields inside proto3 submessage: present if count != 0 */ - return *(const pb_size_t*)field->pSize == 0; - } - else if (PB_HTYPE(type) == PB_HTYPE_ONEOF) - { - /* Oneof fields */ - return *(const pb_size_t*)field->pSize == 0; - } - else if (PB_HTYPE(type) == PB_HTYPE_OPTIONAL && field->pSize != NULL) - { - /* Proto2 optional fields inside proto3 message, or proto3 - * submessage fields. */ - return safe_read_bool(field->pSize) == false; - } - - /* Rest is proto3 singular fields */ - if (PB_LTYPE(type) <= PB_LTYPE_LAST_PACKABLE) - { - /* Simple integer / float fields */ - pb_size_t i; - const char *p = (const char*)field->pData; - for (i = 0; i < field->data_size; i++) - { - if (p[i] != 0) - { - return false; - } - } - - return true; - } - else if (PB_LTYPE(type) == PB_LTYPE_BYTES) - { - const pb_bytes_array_t *bytes = (const pb_bytes_array_t*)field->pData; - return bytes->size == 0; - } - else if (PB_LTYPE(type) == PB_LTYPE_STRING) - { - return *(const char*)field->pData == '\0'; - } - else if (PB_LTYPE(type) == PB_LTYPE_FIXED_LENGTH_BYTES) - { - /* Fixed length bytes is only empty if its length is fixed - * as 0. Which would be pretty strange, but we can check - * it anyway. */ - return field->data_size == 0; - } - else if (PB_LTYPE_IS_SUBMSG(type)) - { - /* Check all fields in the submessage to find if any of them - * are non-zero. The comparison cannot be done byte-per-byte - * because the C struct may contain padding bytes that must - * be skipped. Note that usually proto3 submessages have - * a separate has_field that is checked earlier in this if. - */ - pb_field_iter_t iter; - if (pb_field_iter_begin(&iter, field->submsg_desc, field->pData)) - { - do - { - if (!pb_check_proto3_default_value(&iter)) - { - return false; - } - } while (pb_field_iter_next(&iter)); - } - return true; - } - } - else if (PB_ATYPE(type) == PB_ATYPE_POINTER) - { - return field->pData == NULL; - } - else if (PB_ATYPE(type) == PB_ATYPE_CALLBACK) - { - if (PB_LTYPE(type) == PB_LTYPE_EXTENSION) - { - const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; - return extension == NULL; - } - else if (field->descriptor->field_callback == pb_default_field_callback) - { - pb_callback_t *pCallback = (pb_callback_t*)field->pData; - return pCallback->funcs.encode == NULL; - } - else - { - return field->descriptor->field_callback == NULL; - } - } - - return false; /* Not typically reached, safe default for weird special cases. */ -} - -/* Encode a field with static or pointer allocation, i.e. one whose data - * is available to the encoder directly. */ -static bool checkreturn encode_basic_field(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - if (!field->pData) - { - /* Missing pointer field */ - return true; - } - - if (!pb_encode_tag_for_field(stream, field)) - return false; - - switch (PB_LTYPE(field->type)) - { - case PB_LTYPE_BOOL: - return pb_enc_bool(stream, field); - - case PB_LTYPE_VARINT: - case PB_LTYPE_UVARINT: - case PB_LTYPE_SVARINT: - return pb_enc_varint(stream, field); - - case PB_LTYPE_FIXED32: - case PB_LTYPE_FIXED64: - return pb_enc_fixed(stream, field); - - case PB_LTYPE_BYTES: - return pb_enc_bytes(stream, field); - - case PB_LTYPE_STRING: - return pb_enc_string(stream, field); - - case PB_LTYPE_SUBMESSAGE: - case PB_LTYPE_SUBMSG_W_CB: - return pb_enc_submessage(stream, field); - - case PB_LTYPE_FIXED_LENGTH_BYTES: - return pb_enc_fixed_length_bytes(stream, field); - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } -} - -/* Encode a field with callback semantics. This means that a user function is - * called to provide and encode the actual data. */ -static bool checkreturn encode_callback_field(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - if (field->descriptor->field_callback != NULL) - { - if (!field->descriptor->field_callback(NULL, stream, field)) - PB_RETURN_ERROR(stream, "callback error"); - } - return true; -} - -/* Encode a single field of any callback, pointer or static type. */ -static bool checkreturn encode_field(pb_ostream_t *stream, pb_field_iter_t *field) -{ - /* Check field presence */ - if (PB_HTYPE(field->type) == PB_HTYPE_ONEOF) - { - if (*(const pb_size_t*)field->pSize != field->tag) - { - /* Different type oneof field */ - return true; - } - } - else if (PB_HTYPE(field->type) == PB_HTYPE_OPTIONAL) - { - if (field->pSize) - { - if (safe_read_bool(field->pSize) == false) - { - /* Missing optional field */ - return true; - } - } - else if (PB_ATYPE(field->type) == PB_ATYPE_STATIC) - { - /* Proto3 singular field */ - if (pb_check_proto3_default_value(field)) - return true; - } - } - - if (!field->pData) - { - if (PB_HTYPE(field->type) == PB_HTYPE_REQUIRED) - PB_RETURN_ERROR(stream, "missing required field"); - - /* Pointer field set to NULL */ - return true; - } - - /* Then encode field contents */ - if (PB_ATYPE(field->type) == PB_ATYPE_CALLBACK) - { - return encode_callback_field(stream, field); - } - else if (PB_HTYPE(field->type) == PB_HTYPE_REPEATED) - { - return encode_array(stream, field); - } - else - { - return encode_basic_field(stream, field); - } -} - -/* Default handler for extension fields. Expects to have a pb_msgdesc_t - * pointer in the extension->type->arg field, pointing to a message with - * only one field in it. */ -static bool checkreturn default_extension_encoder(pb_ostream_t *stream, const pb_extension_t *extension) -{ - pb_field_iter_t iter; - - if (!pb_field_iter_begin_extension_const(&iter, extension)) - PB_RETURN_ERROR(stream, "invalid extension"); - - return encode_field(stream, &iter); -} - - -/* Walk through all the registered extensions and give them a chance - * to encode themselves. */ -static bool checkreturn encode_extension_field(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - const pb_extension_t *extension = *(const pb_extension_t* const *)field->pData; - - while (extension) - { - bool status; - if (extension->type->encode) - status = extension->type->encode(stream, extension); - else - status = default_extension_encoder(stream, extension); - - if (!status) - return false; - - extension = extension->next; - } - - return true; -} - -/********************* - * Encode all fields * - *********************/ - -bool checkreturn pb_encode(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) -{ - pb_field_iter_t iter; - if (!pb_field_iter_begin_const(&iter, fields, src_struct)) - return true; /* Empty message type */ - - do { - if (PB_LTYPE(iter.type) == PB_LTYPE_EXTENSION) - { - /* Special case for the extension field placeholder */ - if (!encode_extension_field(stream, &iter)) - return false; - } - else - { - /* Regular field */ - if (!encode_field(stream, &iter)) - return false; - } - } while (pb_field_iter_next(&iter)); - - return true; -} - -bool checkreturn pb_encode_ex(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct, unsigned int flags) -{ - if ((flags & PB_ENCODE_DELIMITED) != 0) - { - return pb_encode_submessage(stream, fields, src_struct); - } - else if ((flags & PB_ENCODE_NULLTERMINATED) != 0) - { - const pb_byte_t zero = 0; - - if (!pb_encode(stream, fields, src_struct)) - return false; - - return pb_write(stream, &zero, 1); - } - else - { - return pb_encode(stream, fields, src_struct); - } -} - -bool pb_get_encoded_size(size_t *size, const pb_msgdesc_t *fields, const void *src_struct) -{ - pb_ostream_t stream = PB_OSTREAM_SIZING; - - if (!pb_encode(&stream, fields, src_struct)) - return false; - - *size = stream.bytes_written; - return true; -} - -/******************** - * Helper functions * - ********************/ - -/* This function avoids 64-bit shifts as they are quite slow on many platforms. */ -static bool checkreturn pb_encode_varint_32(pb_ostream_t *stream, uint32_t low, uint32_t high) -{ - size_t i = 0; - pb_byte_t buffer[10]; - pb_byte_t byte = (pb_byte_t)(low & 0x7F); - low >>= 7; - - while (i < 4 && (low != 0 || high != 0)) - { - byte |= 0x80; - buffer[i++] = byte; - byte = (pb_byte_t)(low & 0x7F); - low >>= 7; - } - - if (high) - { - byte = (pb_byte_t)(byte | ((high & 0x07) << 4)); - high >>= 3; - - while (high) - { - byte |= 0x80; - buffer[i++] = byte; - byte = (pb_byte_t)(high & 0x7F); - high >>= 7; - } - } - - buffer[i++] = byte; - - return pb_write(stream, buffer, i); -} - -bool checkreturn pb_encode_varint(pb_ostream_t *stream, pb_uint64_t value) -{ - if (value <= 0x7F) - { - /* Fast path: single byte */ - pb_byte_t byte = (pb_byte_t)value; - return pb_write(stream, &byte, 1); - } - else - { -#ifdef PB_WITHOUT_64BIT - return pb_encode_varint_32(stream, value, 0); -#else - return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)(value >> 32)); -#endif - } -} - -bool checkreturn pb_encode_svarint(pb_ostream_t *stream, pb_int64_t value) -{ - pb_uint64_t zigzagged; - if (value < 0) - zigzagged = ~((pb_uint64_t)value << 1); - else - zigzagged = (pb_uint64_t)value << 1; - - return pb_encode_varint(stream, zigzagged); -} - -bool checkreturn pb_encode_fixed32(pb_ostream_t *stream, const void *value) -{ - uint32_t val = *(const uint32_t*)value; - pb_byte_t bytes[4]; - bytes[0] = (pb_byte_t)(val & 0xFF); - bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); - bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); - bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); - return pb_write(stream, bytes, 4); -} - -#ifndef PB_WITHOUT_64BIT -bool checkreturn pb_encode_fixed64(pb_ostream_t *stream, const void *value) -{ - uint64_t val = *(const uint64_t*)value; - pb_byte_t bytes[8]; - bytes[0] = (pb_byte_t)(val & 0xFF); - bytes[1] = (pb_byte_t)((val >> 8) & 0xFF); - bytes[2] = (pb_byte_t)((val >> 16) & 0xFF); - bytes[3] = (pb_byte_t)((val >> 24) & 0xFF); - bytes[4] = (pb_byte_t)((val >> 32) & 0xFF); - bytes[5] = (pb_byte_t)((val >> 40) & 0xFF); - bytes[6] = (pb_byte_t)((val >> 48) & 0xFF); - bytes[7] = (pb_byte_t)((val >> 56) & 0xFF); - return pb_write(stream, bytes, 8); -} -#endif - -bool checkreturn pb_encode_tag(pb_ostream_t *stream, pb_wire_type_t wiretype, uint32_t field_number) -{ - pb_uint64_t tag = ((pb_uint64_t)field_number << 3) | wiretype; - return pb_encode_varint(stream, tag); -} - -bool pb_encode_tag_for_field ( pb_ostream_t* stream, const pb_field_iter_t* field ) -{ - pb_wire_type_t wiretype; - switch (PB_LTYPE(field->type)) - { - case PB_LTYPE_BOOL: - case PB_LTYPE_VARINT: - case PB_LTYPE_UVARINT: - case PB_LTYPE_SVARINT: - wiretype = PB_WT_VARINT; - break; - - case PB_LTYPE_FIXED32: - wiretype = PB_WT_32BIT; - break; - - case PB_LTYPE_FIXED64: - wiretype = PB_WT_64BIT; - break; - - case PB_LTYPE_BYTES: - case PB_LTYPE_STRING: - case PB_LTYPE_SUBMESSAGE: - case PB_LTYPE_SUBMSG_W_CB: - case PB_LTYPE_FIXED_LENGTH_BYTES: - wiretype = PB_WT_STRING; - break; - - default: - PB_RETURN_ERROR(stream, "invalid field type"); - } - - return pb_encode_tag(stream, wiretype, field->tag); -} - -bool checkreturn pb_encode_string(pb_ostream_t *stream, const pb_byte_t *buffer, size_t size) -{ - if (!pb_encode_varint(stream, (pb_uint64_t)size)) - return false; - - return pb_write(stream, buffer, size); -} - -bool checkreturn pb_encode_submessage(pb_ostream_t *stream, const pb_msgdesc_t *fields, const void *src_struct) -{ - /* First calculate the message size using a non-writing substream. */ - pb_ostream_t substream = PB_OSTREAM_SIZING; - size_t size; - bool status; - - if (!pb_encode(&substream, fields, src_struct)) - { -#ifndef PB_NO_ERRMSG - stream->errmsg = substream.errmsg; -#endif - return false; - } - - size = substream.bytes_written; - - if (!pb_encode_varint(stream, (pb_uint64_t)size)) - return false; - - if (stream->callback == NULL) - return pb_write(stream, NULL, size); /* Just sizing */ - - if (stream->bytes_written + size > stream->max_size) - PB_RETURN_ERROR(stream, "stream full"); - - /* Use a substream to verify that a callback doesn't write more than - * what it did the first time. */ - substream.callback = stream->callback; - substream.state = stream->state; - substream.max_size = size; - substream.bytes_written = 0; -#ifndef PB_NO_ERRMSG - substream.errmsg = NULL; -#endif - - status = pb_encode(&substream, fields, src_struct); - - stream->bytes_written += substream.bytes_written; - stream->state = substream.state; -#ifndef PB_NO_ERRMSG - stream->errmsg = substream.errmsg; -#endif - - if (substream.bytes_written != size) - PB_RETURN_ERROR(stream, "submsg size changed"); - - return status; -} - -/* Field encoders */ - -static bool checkreturn pb_enc_bool(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - uint32_t value = safe_read_bool(field->pData) ? 1 : 0; - PB_UNUSED(field); - return pb_encode_varint(stream, value); -} - -static bool checkreturn pb_enc_varint(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - if (PB_LTYPE(field->type) == PB_LTYPE_UVARINT) - { - /* Perform unsigned integer extension */ - pb_uint64_t value = 0; - - if (field->data_size == sizeof(uint_least8_t)) - value = *(const uint_least8_t*)field->pData; - else if (field->data_size == sizeof(uint_least16_t)) - value = *(const uint_least16_t*)field->pData; - else if (field->data_size == sizeof(uint32_t)) - value = *(const uint32_t*)field->pData; - else if (field->data_size == sizeof(pb_uint64_t)) - value = *(const pb_uint64_t*)field->pData; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - return pb_encode_varint(stream, value); - } - else - { - /* Perform signed integer extension */ - pb_int64_t value = 0; - - if (field->data_size == sizeof(int_least8_t)) - value = *(const int_least8_t*)field->pData; - else if (field->data_size == sizeof(int_least16_t)) - value = *(const int_least16_t*)field->pData; - else if (field->data_size == sizeof(int32_t)) - value = *(const int32_t*)field->pData; - else if (field->data_size == sizeof(pb_int64_t)) - value = *(const pb_int64_t*)field->pData; - else - PB_RETURN_ERROR(stream, "invalid data_size"); - - if (PB_LTYPE(field->type) == PB_LTYPE_SVARINT) - return pb_encode_svarint(stream, value); -#ifdef PB_WITHOUT_64BIT - else if (value < 0) - return pb_encode_varint_32(stream, (uint32_t)value, (uint32_t)-1); -#endif - else - return pb_encode_varint(stream, (pb_uint64_t)value); - - } -} - -static bool checkreturn pb_enc_fixed(pb_ostream_t *stream, const pb_field_iter_t *field) -{ -#ifdef PB_CONVERT_DOUBLE_FLOAT - if (field->data_size == sizeof(float) && PB_LTYPE(field->type) == PB_LTYPE_FIXED64) - { - return pb_encode_float_as_double(stream, *(float*)field->pData); - } -#endif - - if (field->data_size == sizeof(uint32_t)) - { - return pb_encode_fixed32(stream, field->pData); - } -#ifndef PB_WITHOUT_64BIT - else if (field->data_size == sizeof(uint64_t)) - { - return pb_encode_fixed64(stream, field->pData); - } -#endif - else - { - PB_RETURN_ERROR(stream, "invalid data_size"); - } -} - -static bool checkreturn pb_enc_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - const pb_bytes_array_t *bytes = NULL; - - bytes = (const pb_bytes_array_t*)field->pData; - - if (bytes == NULL) - { - /* Treat null pointer as an empty bytes field */ - return pb_encode_string(stream, NULL, 0); - } - - if (PB_ATYPE(field->type) == PB_ATYPE_STATIC && - bytes->size > field->data_size - offsetof(pb_bytes_array_t, bytes)) - { - PB_RETURN_ERROR(stream, "bytes size exceeded"); - } - - return pb_encode_string(stream, bytes->bytes, (size_t)bytes->size); -} - -static bool checkreturn pb_enc_string(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - size_t size = 0; - size_t max_size = (size_t)field->data_size; - const char *str = (const char*)field->pData; - - if (PB_ATYPE(field->type) == PB_ATYPE_POINTER) - { - max_size = (size_t)-1; - } - else - { - /* pb_dec_string() assumes string fields end with a null - * terminator when the type isn't PB_ATYPE_POINTER, so we - * shouldn't allow more than max-1 bytes to be written to - * allow space for the null terminator. - */ - if (max_size == 0) - PB_RETURN_ERROR(stream, "zero-length string"); - - max_size -= 1; - } - - - if (str == NULL) - { - size = 0; /* Treat null pointer as an empty string */ - } - else - { - const char *p = str; - - /* strnlen() is not always available, so just use a loop */ - while (size < max_size && *p != '\0') - { - size++; - p++; - } - - if (*p != '\0') - { - PB_RETURN_ERROR(stream, "unterminated string"); - } - } - -#ifdef PB_VALIDATE_UTF8 - if (!pb_validate_utf8(str)) - PB_RETURN_ERROR(stream, "invalid utf8"); -#endif - - return pb_encode_string(stream, (const pb_byte_t*)str, size); -} - -static bool checkreturn pb_enc_submessage(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - if (field->submsg_desc == NULL) - PB_RETURN_ERROR(stream, "invalid field descriptor"); - - if (PB_LTYPE(field->type) == PB_LTYPE_SUBMSG_W_CB && field->pSize != NULL) - { - /* Message callback is stored right before pSize. */ - pb_callback_t *callback = (pb_callback_t*)field->pSize - 1; - if (callback->funcs.encode) - { - if (!callback->funcs.encode(stream, field, &callback->arg)) - return false; - } - } - - return pb_encode_submessage(stream, field->submsg_desc, field->pData); -} - -static bool checkreturn pb_enc_fixed_length_bytes(pb_ostream_t *stream, const pb_field_iter_t *field) -{ - return pb_encode_string(stream, (const pb_byte_t*)field->pData, (size_t)field->data_size); -} - -#ifdef PB_CONVERT_DOUBLE_FLOAT -bool pb_encode_float_as_double(pb_ostream_t *stream, float value) -{ - union { float f; uint32_t i; } in; - uint_least8_t sign; - int exponent; - uint64_t mantissa; - - in.f = value; - - /* Decompose input value */ - sign = (uint_least8_t)((in.i >> 31) & 1); - exponent = (int)((in.i >> 23) & 0xFF) - 127; - mantissa = in.i & 0x7FFFFF; - - if (exponent == 128) - { - /* Special value (NaN etc.) */ - exponent = 1024; - } - else if (exponent == -127) - { - if (!mantissa) - { - /* Zero */ - exponent = -1023; - } - else - { - /* Denormalized */ - mantissa <<= 1; - while (!(mantissa & 0x800000)) - { - mantissa <<= 1; - exponent--; - } - mantissa &= 0x7FFFFF; - } - } - - /* Combine fields */ - mantissa <<= 29; - mantissa |= (uint64_t)(exponent + 1023) << 52; - mantissa |= (uint64_t)sign << 63; - - return pb_encode_fixed64(stream, &mantissa); -} -#endif From 3fe8f7772083a1ae19575185627875956553ede6 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 9 Jul 2020 22:28:53 +0200 Subject: [PATCH 05/28] Ready with the initial version of Hosted. --- .../Arch/Host/Components/hostlib/startup.cpp | 30 ++++++---- .../Host/Components/sming-arch/component.mk | 2 +- .../Hosted-Lib/src/DigitalHosted.cpp | 18 +++++- .../Components/Hosted-Lib/src/InitClient.cpp | 8 --- .../Components/Hosted-Transport/component.mk | 8 +++ .../src/tcp/HostedTcpStream.h} | 29 ++++++++-- .../Hosted-Transport/src/tcp/InitClient.cpp | 41 ++++++++++++++ .../Components/Hosted/app/app/application.cpp | 1 - Sming/Components/Hosted/component.mk | 2 +- Sming/Components/Hosted/proto/hosted.pb.h | 9 +-- Sming/Components/Hosted/proto/hosted.proto | 1 - Sming/Components/Hosted/src/HostedClient.h | 55 +++++++++++++++---- Sming/Components/Hosted/src/HostedServer.h | 5 +- Sming/Core/Network/TcpClient.h | 6 ++ 14 files changed, 165 insertions(+), 50 deletions(-) delete mode 100644 Sming/Components/Hosted-Lib/src/InitClient.cpp create mode 100644 Sming/Components/Hosted-Transport/component.mk rename Sming/Components/{Hosted/src/HostedClientStream.h => Hosted-Transport/src/tcp/HostedTcpStream.h} (55%) create mode 100644 Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index f33bba2f5b..399781be62 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -39,11 +39,18 @@ static int exitCode = 0; static bool done = false; +static bool lwip_initialised = false; +static OneShotElapseTimer lwipServiceTimer; extern void init(); extern void host_wifi_lwip_init_complete(); extern void host_init_bootloader(); +void __attribute__((weak)) host_init() +{ + init(); +} + static void cleanup() { hw_timer_cleanup(); @@ -104,6 +111,16 @@ static void pause(int secs) } } +void host_main_loop() { + host_service_tasks(); + host_service_timers(); + if (lwip_initialised && lwipServiceTimer.expired()) { + host_lwip_service(); + lwipServiceTimer.start(); + } + system_soft_wdt_feed(); +} + int main(int argc, char* argv[]) { trap_exceptions(); @@ -230,7 +247,7 @@ int main(int argc, char* argv[]) sockets_initialise(); CUartServer::startup(config.uart); - bool lwip_initialised = false; + if(config.enable_network) { lwip_initialised = host_lwip_init(&config.lwip); if(lwip_initialised) { @@ -247,18 +264,11 @@ int main(int argc, char* argv[]) System.initialize(); - init(); + host_init(); - OneShotElapseTimer lwipServiceTimer; lwipServiceTimer.reset(); while(!done) { - host_service_tasks(); - host_service_timers(); - if(lwip_initialised && lwipServiceTimer.expired()) { - host_lwip_service(); - lwipServiceTimer.start(); - } - system_soft_wdt_feed(); + host_main_loop(); } host_debug_i(">> Normal Exit <<\n"); diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index 1132de90e4..ae36a8ffe7 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -40,7 +40,7 @@ COMPONENT_DEPENDS := \ ifneq ($(ENABLE_HOSTED),) - COMPONENT_DEPENDS += Hosted-Lib + COMPONENT_DEPENDS += Hosted-Lib Hosted-Transport endif # => Platform WiFi diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp index 1ce8a206f1..bb5a445d7c 100644 --- a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp @@ -1,7 +1,7 @@ #include #include -extern HostedClient hostedClient; +extern HostedClient* hostedClient; void pinMode(uint16_t pin, uint8_t mode) { @@ -10,7 +10,7 @@ void pinMode(uint16_t pin, uint8_t mode) command->mode = (PinMode)mode; }); - hostedClient.send(&message); + hostedClient->send(&message); } void digitalWrite(uint16_t pin, uint8_t val) @@ -20,5 +20,17 @@ void digitalWrite(uint16_t pin, uint8_t val) command->value = val; }); - hostedClient.send(&message); + hostedClient->send(&message); +} + +uint8_t digitalRead(uint16_t pin) +{ + NEW_HD_COMMAND(message, DigitalRead, { + command->pin = pin; + }); + + hostedClient->send(&message); + HostedCommand response = hostedClient->wait(); + + return (uint8_t)response.payload.responseDigitalRead.value; } diff --git a/Sming/Components/Hosted-Lib/src/InitClient.cpp b/Sming/Components/Hosted-Lib/src/InitClient.cpp deleted file mode 100644 index e64508374a..0000000000 --- a/Sming/Components/Hosted-Lib/src/InitClient.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include -#include - - -// TODO: Initialize the code transport and the client - -HostedClient hostedClient(new HostedTcpStream("192.168.13.3", 4031)); diff --git a/Sming/Components/Hosted-Transport/component.mk b/Sming/Components/Hosted-Transport/component.mk new file mode 100644 index 0000000000..13812e450b --- /dev/null +++ b/Sming/Components/Hosted-Transport/component.mk @@ -0,0 +1,8 @@ +COMPONENT_DEPENDS := Hosted +ifneq ($(ENABLE_HOSTED),) + COMPONENT_SRCDIRS += src/$(ENABLE_HOSTED) +endif + +COMPONENT_VARS := ENABLE_HOSTED HOSTED_SERVER_IP +COMPONENT_CFLAGS := -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) +COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) \ No newline at end of file diff --git a/Sming/Components/Hosted/src/HostedClientStream.h b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h similarity index 55% rename from Sming/Components/Hosted/src/HostedClientStream.h rename to Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h index b0a075363e..93202f3f4e 100644 --- a/Sming/Components/Hosted/src/HostedClientStream.h +++ b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h @@ -1,4 +1,5 @@ #include +#include class HostedTcpStream: public ReadWriteStream { @@ -14,8 +15,9 @@ class HostedTcpStream: public ReadWriteStream }; - auto onReceive = [](TcpClient& client, char* data, int size)->bool { - return true; + auto onReceive = [this](TcpClient& client, char* data, int size)->bool { + size_t written = this->buffer.write((const uint8_t*)data, size); + return (written == (size_t)size); }; client = new TcpClient(onCompleted, onReadyToSend, onReceive); @@ -23,16 +25,25 @@ class HostedTcpStream: public ReadWriteStream size_t write(const uint8_t* buffer, size_t size) override { - if(client->getConnectionState() == eTCS_Ready) { + if(!client->isProcessing()) { client->connect(host, (int)port); } - client->send((const char *)buffer, size); + + if(!client->send((const char *)buffer, size)) { + return 0; + } + return size; } uint16_t readMemoryBlock(char* data, int bufSize) override { - return 0; + return buffer.readMemoryBlock(data, bufSize); + } + + bool seek(int len) override + { + return buffer.seek(len); } @@ -41,9 +52,15 @@ class HostedTcpStream: public ReadWriteStream return false; } + void flush() override + { + client->commit(); + } + private: - TcpClient* client =nullptr; + TcpClient* client = nullptr; + CircularBuffer buffer = CircularBuffer(1024); String host; uint16_t port = 0; }; diff --git a/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp b/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp new file mode 100644 index 0000000000..05f58c0ad0 --- /dev/null +++ b/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp @@ -0,0 +1,41 @@ +#include +#include +#include "../../src/tcp/HostedTcpStream.h" + +HostedClient* hostedClient = nullptr; + +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif + + + +#ifndef HOSTED_SERVER_IP +#define REMOTE_IP IpAddress("192.168.13.1") +#else +#define STRINGIFY_HELPER(X) #X +#define STRINGIFY(X) STRINGIFY_HELPER(X) +#define REMOTE_IP STRINGIFY(HOSTED_SERVER_IP) +#endif + +extern void init(); + +static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + IpAddress remoteIp(REMOTE_IP); + if(gateway == IpAddress("192.168.4.1")) { + remoteIp = gateway; + } + + hostedClient = new HostedClient(new HostedTcpStream(remoteIp.toString(), 4031)); + init(); +} + +void host_init() +{ + WifiEvents.onStationGotIP(ready); + WifiStation.enable(true); + WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); + WifiStation.connect(); +} diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp index 538f8df703..d762315760 100644 --- a/Sming/Components/Hosted/app/app/application.cpp +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -52,7 +52,6 @@ void init() hostedServer.registerCommand(HostedCommand_requestDigitalRead_tag, [](HostedCommand *request, HostedCommand *response)-> int { uint8_t result = digitalRead((uint16_t)request->payload.requestDigitalRead.pin); - response->id = request->id; response->which_payload = HostedCommand_responseDigitalRead_tag; response->payload.responseDigitalRead.value = result; diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 0ad1f68c73..b125bed6c6 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -7,7 +7,7 @@ HOSTED_ARCH ?= Esp8266 HOSTED_APP_DIR := $(COMPONENT_PATH)/app -RELINK_VARS = ENABLE_HOSTED +RELINK_VARS := ENABLE_HOSTED ##@Building diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h index eef81ebf86..81837b79bf 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.h +++ b/Sming/Components/Hosted/proto/hosted.pb.h @@ -45,7 +45,6 @@ typedef struct _ResponseDigitalRead { typedef struct _HostedCommand { HostedCommand_Version version; - uint32_t id; pb_size_t which_payload; union { RequestDigitalWrite requestDigitalWrite; @@ -71,12 +70,12 @@ typedef struct _HostedCommand { #define RequestPinMode_init_default {0, _PinMode_MIN} #define RequestDigitalRead_init_default {0} #define ResponseDigitalRead_init_default {0} -#define HostedCommand_init_default {_HostedCommand_Version_MIN, 0, 0, {RequestDigitalWrite_init_default}} +#define HostedCommand_init_default {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_default}} #define RequestDigitalWrite_init_zero {0, 0} #define RequestPinMode_init_zero {0, _PinMode_MIN} #define RequestDigitalRead_init_zero {0} #define ResponseDigitalRead_init_zero {0} -#define HostedCommand_init_zero {_HostedCommand_Version_MIN, 0, 0, {RequestDigitalWrite_init_zero}} +#define HostedCommand_init_zero {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_zero}} /* Field tags (for use in manual encoding/decoding) */ #define RequestDigitalRead_pin_tag 1 @@ -86,7 +85,6 @@ typedef struct _HostedCommand { #define RequestPinMode_mode_tag 2 #define ResponseDigitalRead_value_tag 1 #define HostedCommand_version_tag 1 -#define HostedCommand_id_tag 2 #define HostedCommand_requestDigitalWrite_tag 10 #define HostedCommand_requestPinMode_tag 11 #define HostedCommand_requestDigitalRead_tag 12 @@ -117,7 +115,6 @@ X(a, STATIC, SINGULAR, UINT32, value, 1) #define HostedCommand_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, version, 1) \ -X(a, STATIC, SINGULAR, UINT32, id, 2) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalWrite,payload.requestDigitalWrite), 10) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestPinMode,payload.requestPinMode), 11) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalRead,payload.requestDigitalRead), 12) \ @@ -147,7 +144,7 @@ extern const pb_msgdesc_t HostedCommand_msg; #define RequestPinMode_size 8 #define RequestDigitalRead_size 6 #define ResponseDigitalRead_size 6 -#define HostedCommand_size 22 +#define HostedCommand_size 16 #ifdef __cplusplus } /* extern "C" */ diff --git a/Sming/Components/Hosted/proto/hosted.proto b/Sming/Components/Hosted/proto/hosted.proto index 1b1c5fc71a..f811861745 100644 --- a/Sming/Components/Hosted/proto/hosted.proto +++ b/Sming/Components/Hosted/proto/hosted.proto @@ -31,7 +31,6 @@ message HostedCommand { HOSTED_V_1_0 = 0; } Version version = 1; - uint32 id = 2; // unique message id, > 0 oneof payload { RequestDigitalWrite requestDigitalWrite = 10; RequestPinMode requestPinMode = 11; diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src/HostedClient.h index 6e2e00bb04..e3653f541e 100644 --- a/Sming/Components/Hosted/src/HostedClient.h +++ b/Sming/Components/Hosted/src/HostedClient.h @@ -4,6 +4,8 @@ #include #include "HostedCommon.h" +extern void host_main_loop(); + class HostedClient { public: @@ -21,13 +23,8 @@ class HostedClient this->stream = stream; } - bool send(HostedCommand* message, HostedCommandDelegate callback = nullptr) + bool send(HostedCommand* message) { - if(++messageId == 0) { - messageId = 1; // messages with id 0 will be discarded... - } - message->id = messageId; - pb_ostream_t ouput = newOutputStream(); bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); if (!success) { @@ -35,13 +32,35 @@ class HostedClient return false; } - if(callback != nullptr) { - responseCallbacks[message->id] = callback; - } + stream->flush(); return true; } + /** + * @brief This method will block the execution until a message is detected + * @retval HostedCommand + */ + HostedCommand wait() + { + HostedCommand command = HostedCommand_init_zero; + + pb_istream_t input = newInputStream(); + size_t leftBytes = input.bytes_left; + size_t totalBytes = input.bytes_left; + bool success = false; + do { + stream->flush(); + success = pb_decode_ex(&input, HostedCommand_fields, &command, PB_DECODE_DELIMITED); + host_main_loop(); + } + while(!success); + + stream->seek(totalBytes - input.bytes_left); + + return command; + } + /** * @brief This method handles incoming data */ @@ -50,6 +69,23 @@ class HostedClient return true; } private: + pb_istream_t newInputStream() + { + pb_istream_t stream; + stream.callback = [](pb_istream_t *stream, pb_byte_t *buf, size_t count) -> bool { + ReadWriteStream* source = (ReadWriteStream* )stream->state; + size_t read = source->readMemoryBlock((char *)buf, count); + source->seek(read); + + return true; + }; + stream.state = (void*)this->stream; + stream.bytes_left = this->stream->available(); + stream.errmsg = nullptr; + + return stream; + } + pb_ostream_t newOutputStream() { pb_ostream_t outputStream; @@ -68,6 +104,5 @@ class HostedClient } private: HashMap responseCallbacks; - uint32_t messageId = 0; ReadWriteStream* stream = nullptr; }; diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h index 62beecbfc4..99edee9eaa 100644 --- a/Sming/Components/Hosted/src/HostedServer.h +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -52,13 +52,12 @@ class HostedServer size_t leftBytes = input.bytes_left; do { success = pb_decode_ex(&input, HostedCommand_fields, &request, PB_DECODE_DELIMITED); - if (!(success && request.id)) { + if (!(success && request.which_payload)) { Serial.printf("Decoding failed: %s\n", PB_GET_ERROR(&input)); inputBuffer->seek(inputBuffer->available()- leftBytes); break; } - // and send it back... leftBytes = input.bytes_left; // dispatch the command @@ -73,7 +72,7 @@ class HostedServer } // process the response - if(response.id && !send(&response)) { + if(response.which_payload && !send(&response)) { result = HOSTED_FAIL; break; } diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Core/Network/TcpClient.h index 71789f7b54..e9e7640f7f 100644 --- a/Sming/Core/Network/TcpClient.h +++ b/Sming/Core/Network/TcpClient.h @@ -126,6 +126,12 @@ class TcpClient : public TcpConnection closeAfterSent = ignoreIncomingData ? eTCCASS_AfterSent_Ignore_Received : eTCCASS_AfterSent; } + void commit() + { + onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + TcpConnection::flush(); + } + protected: err_t onConnected(err_t err) override; err_t onReceive(pbuf* buf) override; From 5aa9226af6a43e0a028674d24fba121913075688 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Mon, 13 Jul 2020 13:11:18 +0200 Subject: [PATCH 06/28] Added initial documentation. --- Sming/Components/Hosted/README.rst | 55 +++++++++++++++++++++--------- 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index 83036449ef..cdaf83f3fe 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -3,32 +3,53 @@ HostEd .. highlight:: bash -The hosted component allows Sming's host emulator to run parts of the code on an actual microcontroller. -TBD... +The hosted component allows Sming's host emulator to run parts of the commands on an actual microcontroller. +The communication is done via protobuf messages and the microcontroller has to be flashed with a special application. -Configuration variables ------------------------ +Overview +-------- +Sming's host emulator allows easier debugging and development of embedded applications. This component named "HostEd" extends the host emulator +and facilitates testing functionality that only a real microcontroller can provide as for example digital I/O operations or SPI operations. -Serial Communications -~~~~~~~~~~~~~~~~~~~~~ +For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller +we can compile the application using the following directives:: -.. envvar:: COM_SPEED +    make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1 - Default baud rate for serial port. +`SMING_ARCH=Host` instructs the build system to build the application for the Host architecture. +`ENABLE_HOSTED=tcp` instructs the host emulator to communication with the real microcontroller using TCP +`HOSTED_SERVER_IP=192.168.4.1` instructs the host emulator to connect to IP `192.168.4.1`. - This will recompile your application to use the revised baud rate. - Note that this will change the default speed used for both flashing and serial comms. - See also :component-esp8266:`esptool` and :component:`terminal` for further details. +We need to compile and flash also a special application on the desired microcontroller. That application will execute the commands send from the host emulator. +The compilation and flashing for ESP32, for example, can be done using the following commands:: +    make hosted-app HOSTED_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword +    make hosted-flash + +If you replace `HOSTED_ARCH=Esp32` with `HOSTED_ARCH=Esp8266` then the hosted application will be compiled and flashed on ESP8266 microcontroller. +Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP). Development ----------- -If you are interested in ... -TDB +The communication between the host emulator and the hosted application is done via protocol buffer messages. The messages are describe in the `proto/hosted.proto` +file. + +Adding a new command +~~~~~~~~~~~~~~~~~~~~ +Adding a new command can be done in the following way. + +1. You should add the command in the `proto/hosted.proto` file. Make sure to compile it using `nanopb`. This can be done with the commands below:: + +    cd $SMING_HOME/Components/Hosted/proto +    python $SMING_HOME/Components/nanopb/nanopb/generator/nanopb_generator.py hosted.proto + +2. See `DigitalHosted.cpp` for inspiration how to add a new command to `Hosted-Lib`. +These functions will try to communicate with the hosted application on the microcontroller. -Protobuf with nanopb:: +3. The actual work is done by the hosted application. See `app/application.cpp`. See the `init` method for inspiration how to register a new command that +should be executed on the controller. - cd $SMING_HOME/Components/Hosted/proto - python $SMING_HOME/Components/nanopb/nanopb/generator/nanopb_generator.py hosted.proto +.. envvar:: ENABLE_HOSTED -TBD... \ No newline at end of file +   Enables the hosted component. Valid values for the moment are: +   - tcp - for communication over TCP network. \ No newline at end of file From 27633cba9d12bf61def3d7b422bdc0f0f4b99784 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 7 Aug 2020 18:00:30 +0200 Subject: [PATCH 07/28] Initial work on SPI support. --- .gitmodules | 1 + Sming/Components/Hosted-Lib/src/SpiHosted.cpp | 71 +++++++ .../Components/Hosted/app/app/application.cpp | 29 +-- Sming/Components/Hosted/app/component.mk | 3 + .../Hosted/app/src/DigitalHosted.cpp | 27 +++ .../Components/Hosted/app/src/DigitalHosted.h | 12 ++ Sming/Components/Hosted/app/src/SpiHosted.cpp | 46 ++++ Sming/Components/Hosted/app/src/SpiHosted.h | 12 ++ Sming/Components/Hosted/proto/hosted.pb.c | 29 +++ Sming/Components/Hosted/proto/hosted.pb.h | 198 +++++++++++++++++- Sming/Components/Hosted/proto/hosted.proto | 62 ++++++ Sming/Components/Hosted/src/HostedCommon.h | 5 +- Sming/Components/Hosted/src/HostedServer.h | 5 + Sming/Components/Hosted/src/HostedUtils.cpp | 42 ++++ Sming/Components/Hosted/src/HostedUtils.h | 14 ++ 15 files changed, 535 insertions(+), 21 deletions(-) create mode 100644 Sming/Components/Hosted-Lib/src/SpiHosted.cpp create mode 100644 Sming/Components/Hosted/app/src/DigitalHosted.cpp create mode 100644 Sming/Components/Hosted/app/src/DigitalHosted.h create mode 100644 Sming/Components/Hosted/app/src/SpiHosted.cpp create mode 100644 Sming/Components/Hosted/app/src/SpiHosted.h create mode 100644 Sming/Components/Hosted/src/HostedUtils.cpp create mode 100644 Sming/Components/Hosted/src/HostedUtils.h diff --git a/.gitmodules b/.gitmodules index 3066e1bef4..28d6cf829b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,6 +61,7 @@ path = Sming/Components/nanopb/nanopb url = https://github.com/nanopb/nanopb.git ignore = dirty + ignore = dirty [submodule "rboot"] path = Sming/Components/rboot/rboot url = https://github.com/mikee47/rboot diff --git a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp new file mode 100644 index 0000000000..8d95e0f50b --- /dev/null +++ b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp @@ -0,0 +1,71 @@ +#include +#include +#include + +extern HostedClient* hostedClient; + +// define the static singleton +SPIClass SPI; + +void SPIClass::begin() +{ + NEW_HD_COMMAND(message, SpiBeginTransaction, { + command->has_settings = 0; + }); + + hostedClient->send(&message); +} + +void SPIClass::beginTransaction(SPISettings mySettings) +{ + NEW_HD_COMMAND(message, SpiBeginTransaction, { + command->has_settings = 1; + command->settings.speed = mySettings.speed; + command->settings.byteOrder = (SpiSettings_ByteOrder)mySettings.byteOrder; + command->settings.dataMode = (SpiSettings_DataMode)mySettings.dataMode; + }); + + hostedClient->send(&message); +} + +/** @brief transfer(uint8_t *buffer, size_t numberBytes) + * @param buffer in/out + * @param numberBytes length of buffer + * + * SPI transfer is based on a simultaneous send and receive: + * The buffered transfers does split up the conversation internaly into 64 byte blocks. + * The received data is stored in the buffer passed by reference. + * (the data past in is replaced with the data received). + * + * SPI.transfer(buffer, size) : memory buffer of length size + */ +void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) +{ + PbData data; + data.value = buffer; + data.length = numberBytes; + + NEW_HD_COMMAND(message, SpiTransfer, { + command->data.arg = (void*)&data; + command->data.funcs.encode = &pbEncodeData; + }); + + hostedClient->send(&message); + HostedCommand response = hostedClient->wait(); + + MemoryDataStream* resultData = (MemoryDataStream*)response.payload.responseSpiTransfer.data.arg; + if(resultData == nullptr) { + memset(buffer, 0, numberBytes); + return; + } + + resultData->readBytes((char *)buffer, numberBytes); + delete resultData; +} + + +uint32_t SPIClass::transfer32(uint32_t val, uint8_t bits) +{ + // TODO: + return 0; +} diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp index d762315760..a52ac532ce 100644 --- a/Sming/Components/Hosted/app/app/application.cpp +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -1,6 +1,5 @@ #include #include -#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -11,6 +10,15 @@ HostedServer hostedServer; TcpServer* tcpServer; +namespace Hosted { + namespace Digital { + void registerCommands(HostedServer& server); + } + namespace Spi { + void registerCommands(HostedServer& server); + } +} + // Will be called when WiFi station was connected to AP void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) { @@ -39,24 +47,9 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) void init() { - // Register Command Handlers - hostedServer.registerCommand(HostedCommand_requestPinMode_tag, [](HostedCommand *request, HostedCommand *response)-> int { - pinMode((uint16_t)request->payload.requestPinMode.pin, (uint8_t)request->payload.requestPinMode.mode); - return 0; - }); + Hosted::Digital::registerCommands(hostedServer); + Hosted::Spi::registerCommands(hostedServer); - hostedServer.registerCommand(HostedCommand_requestDigitalWrite_tag, [](HostedCommand *request, HostedCommand *response)-> int { - digitalWrite((uint16_t)request->payload.requestDigitalWrite.pin, (uint8_t)request->payload.requestDigitalWrite.value); - return 0; - }); - - hostedServer.registerCommand(HostedCommand_requestDigitalRead_tag, [](HostedCommand *request, HostedCommand *response)-> int { - uint8_t result = digitalRead((uint16_t)request->payload.requestDigitalRead.pin); - response->which_payload = HostedCommand_responseDigitalRead_tag; - response->payload.responseDigitalRead.value = result; - - return 0; - }); // Connect to same AP as the client application WifiStation.enable(true); diff --git a/Sming/Components/Hosted/app/component.mk b/Sming/Components/Hosted/app/component.mk index 9f991745a3..b4ead4dbba 100644 --- a/Sming/Components/Hosted/app/component.mk +++ b/Sming/Components/Hosted/app/component.mk @@ -5,3 +5,6 @@ ENABLE_HOSTED := ## SPIFFS options DISABLE_SPIFFS := 1 # SPIFF_FILES = files + +COMPONENT_APPCODE := src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.cpp b/Sming/Components/Hosted/app/src/DigitalHosted.cpp new file mode 100644 index 0000000000..4a62fcb650 --- /dev/null +++ b/Sming/Components/Hosted/app/src/DigitalHosted.cpp @@ -0,0 +1,27 @@ +#include "DigitalHosted.h" + +namespace Hosted { +namespace Digital { + + void registerCommands(HostedServer& server) + { + // Register Command Handlers + server.registerCommand(HostedCommand_requestPinMode_tag, [](HostedCommand *request, HostedCommand *response)-> int { + pinMode((uint16_t)request->payload.requestPinMode.pin, (uint8_t)request->payload.requestPinMode.mode); + return 0; + }); + + server.registerCommand(HostedCommand_requestDigitalWrite_tag, [](HostedCommand *request, HostedCommand *response)-> int { + digitalWrite((uint16_t)request->payload.requestDigitalWrite.pin, (uint8_t)request->payload.requestDigitalWrite.value); + return 0; + }); + + server.registerCommand(HostedCommand_requestDigitalRead_tag, [](HostedCommand *request, HostedCommand *response)-> int { + uint8_t result = digitalRead((uint16_t)request->payload.requestDigitalRead.pin); + response->which_payload = HostedCommand_responseDigitalRead_tag; + response->payload.responseDigitalRead.value = result; + + return 0; + }); + } +}} diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.h b/Sming/Components/Hosted/app/src/DigitalHosted.h new file mode 100644 index 0000000000..9913134f98 --- /dev/null +++ b/Sming/Components/Hosted/app/src/DigitalHosted.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +namespace Hosted { + namespace Digital { + + void registerCommands(HostedServer& server); + } +} diff --git a/Sming/Components/Hosted/app/src/SpiHosted.cpp b/Sming/Components/Hosted/app/src/SpiHosted.cpp new file mode 100644 index 0000000000..3a80658d45 --- /dev/null +++ b/Sming/Components/Hosted/app/src/SpiHosted.cpp @@ -0,0 +1,46 @@ +#include "SpiHosted.h" +#include + +namespace Hosted { +namespace Spi { + + void registerCommands(HostedServer& server) + { + // Register Command Handlers + server.registerCommand(HostedCommand_requestSpiBeginTransaction_tag, [](HostedCommand *request, HostedCommand *response)-> int { + if(request->payload.requestSpiBeginTransaction.has_settings) { + SPI.begin(); + } + else { + SPISettings settings(request->payload.requestSpiBeginTransaction.settings.speed, + request->payload.requestSpiBeginTransaction.settings.byteOrder, + request->payload.requestSpiBeginTransaction.settings.dataMode); + SPI.beginTransaction(settings); + } + + return 0; + }); + + server.registerCommand(HostedCommand_requestSpiTransfer_tag, [](HostedCommand *request, HostedCommand *response)-> int { + PbData* responseData = new PbData; + responseData->length = 0; + + MemoryDataStream* data = (MemoryDataStream*)request->payload.requestSpiTransfer.data.arg; + if(data != nullptr) { + size_t available = data->available(); + uint8_t* buffer = new uint8_t[available]; + size_t length = data->readBytes((char *)&buffer, available); + SPI.transfer(buffer, length); + responseData->value = buffer; + responseData->length = length; + + delete data; + } + + response->payload.responseSpiTransfer.data.funcs.encode = &pbEncodeData; + response->payload.responseSpiTransfer.data.arg = (void* )responseData; + + return 0; + }); + } +}} diff --git a/Sming/Components/Hosted/app/src/SpiHosted.h b/Sming/Components/Hosted/app/src/SpiHosted.h new file mode 100644 index 0000000000..94f9111949 --- /dev/null +++ b/Sming/Components/Hosted/app/src/SpiHosted.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include +#include + +namespace Hosted { + namespace Spi { + + void registerCommands(HostedServer& server); + } +} diff --git a/Sming/Components/Hosted/proto/hosted.pb.c b/Sming/Components/Hosted/proto/hosted.pb.c index 7bb50c07db..f85e9d98d5 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.c +++ b/Sming/Components/Hosted/proto/hosted.pb.c @@ -18,9 +18,38 @@ PB_BIND(RequestDigitalRead, RequestDigitalRead, AUTO) PB_BIND(ResponseDigitalRead, ResponseDigitalRead, AUTO) +PB_BIND(SpiSettings, SpiSettings, AUTO) + + +PB_BIND(RequestSpiBeginTransaction, RequestSpiBeginTransaction, AUTO) + + +PB_BIND(RequestSpiEndTransaction, RequestSpiEndTransaction, AUTO) + + +PB_BIND(RequestSpiTransfer, RequestSpiTransfer, AUTO) + + +PB_BIND(ResponseSpiTransfer, ResponseSpiTransfer, AUTO) + + +PB_BIND(RequestSpiWrite, RequestSpiWrite, AUTO) + + +PB_BIND(ResponseSpiWrite, ResponseSpiWrite, AUTO) + + +PB_BIND(RequestSpiRead, RequestSpiRead, AUTO) + + +PB_BIND(ResponseSpiRead, ResponseSpiRead, AUTO) + + PB_BIND(HostedCommand, HostedCommand, AUTO) + + diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h index 81837b79bf..47da2da93e 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.h +++ b/Sming/Components/Hosted/proto/hosted.pb.h @@ -20,11 +20,43 @@ typedef enum _PinMode { PinMode_INPUT_PULLUP = 2 } PinMode; +typedef enum _SpiSettings_ByteOrder { + SpiSettings_ByteOrder_MSBFIRST = 0, + SpiSettings_ByteOrder_LSBFIRST = 1 +} SpiSettings_ByteOrder; + +typedef enum _SpiSettings_DataMode { + SpiSettings_DataMode_SPI_MODE0 = 0, + SpiSettings_DataMode_SPI_MODE1 = 1, + SpiSettings_DataMode_SPI_MODE2 = 2, + SpiSettings_DataMode_SPI_MODE3 = 3 +} SpiSettings_DataMode; + typedef enum _HostedCommand_Version { HostedCommand_Version_HOSTED_V_1_0 = 0 } HostedCommand_Version; /* Struct definitions */ +typedef struct _RequestSpiEndTransaction { + char dummy_field; +} RequestSpiEndTransaction; + +typedef struct _RequestSpiTransfer { + pb_callback_t data; +} RequestSpiTransfer; + +typedef struct _RequestSpiWrite { + pb_callback_t data; +} RequestSpiWrite; + +typedef struct _ResponseSpiRead { + pb_callback_t data; +} ResponseSpiRead; + +typedef struct _ResponseSpiTransfer { + pb_callback_t data; +} ResponseSpiTransfer; + typedef struct _RequestDigitalRead { uint32_t pin; } RequestDigitalRead; @@ -39,10 +71,29 @@ typedef struct _RequestPinMode { PinMode mode; } RequestPinMode; +typedef struct _RequestSpiRead { + uint32_t length; +} RequestSpiRead; + typedef struct _ResponseDigitalRead { uint32_t value; } ResponseDigitalRead; +typedef struct _ResponseSpiWrite { + uint32_t length; +} ResponseSpiWrite; + +typedef struct _SpiSettings { + uint32_t speed; + SpiSettings_ByteOrder byteOrder; + SpiSettings_DataMode dataMode; +} SpiSettings; + +typedef struct _RequestSpiBeginTransaction { + bool has_settings; + SpiSettings settings; +} RequestSpiBeginTransaction; + typedef struct _HostedCommand { HostedCommand_Version version; pb_size_t which_payload; @@ -51,6 +102,14 @@ typedef struct _HostedCommand { RequestPinMode requestPinMode; RequestDigitalRead requestDigitalRead; ResponseDigitalRead responseDigitalRead; + RequestSpiBeginTransaction requestSpiBeginTransaction; + RequestSpiEndTransaction requestSpiEndTransaction; + RequestSpiTransfer requestSpiTransfer; + ResponseSpiTransfer responseSpiTransfer; + RequestSpiWrite requestSpiWrite; + ResponseSpiWrite responseSpiWrite; + RequestSpiRead requestSpiRead; + ResponseSpiRead responseSpiRead; } payload; } HostedCommand; @@ -60,6 +119,14 @@ typedef struct _HostedCommand { #define _PinMode_MAX PinMode_INPUT_PULLUP #define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP+1)) +#define _SpiSettings_ByteOrder_MIN SpiSettings_ByteOrder_MSBFIRST +#define _SpiSettings_ByteOrder_MAX SpiSettings_ByteOrder_LSBFIRST +#define _SpiSettings_ByteOrder_ARRAYSIZE ((SpiSettings_ByteOrder)(SpiSettings_ByteOrder_LSBFIRST+1)) + +#define _SpiSettings_DataMode_MIN SpiSettings_DataMode_SPI_MODE0 +#define _SpiSettings_DataMode_MAX SpiSettings_DataMode_SPI_MODE3 +#define _SpiSettings_DataMode_ARRAYSIZE ((SpiSettings_DataMode)(SpiSettings_DataMode_SPI_MODE3+1)) + #define _HostedCommand_Version_MIN HostedCommand_Version_HOSTED_V_1_0 #define _HostedCommand_Version_MAX HostedCommand_Version_HOSTED_V_1_0 #define _HostedCommand_Version_ARRAYSIZE ((HostedCommand_Version)(HostedCommand_Version_HOSTED_V_1_0+1)) @@ -70,25 +137,61 @@ typedef struct _HostedCommand { #define RequestPinMode_init_default {0, _PinMode_MIN} #define RequestDigitalRead_init_default {0} #define ResponseDigitalRead_init_default {0} +#define SpiSettings_init_default {0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN} +#define RequestSpiBeginTransaction_init_default {false, SpiSettings_init_default} +#define RequestSpiEndTransaction_init_default {0} +#define RequestSpiTransfer_init_default {{{NULL}, NULL}} +#define ResponseSpiTransfer_init_default {{{NULL}, NULL}} +#define RequestSpiWrite_init_default {{{NULL}, NULL}} +#define ResponseSpiWrite_init_default {0} +#define RequestSpiRead_init_default {0} +#define ResponseSpiRead_init_default {{{NULL}, NULL}} #define HostedCommand_init_default {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_default}} #define RequestDigitalWrite_init_zero {0, 0} #define RequestPinMode_init_zero {0, _PinMode_MIN} #define RequestDigitalRead_init_zero {0} #define ResponseDigitalRead_init_zero {0} +#define SpiSettings_init_zero {0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN} +#define RequestSpiBeginTransaction_init_zero {false, SpiSettings_init_zero} +#define RequestSpiEndTransaction_init_zero {0} +#define RequestSpiTransfer_init_zero {{{NULL}, NULL}} +#define ResponseSpiTransfer_init_zero {{{NULL}, NULL}} +#define RequestSpiWrite_init_zero {{{NULL}, NULL}} +#define ResponseSpiWrite_init_zero {0} +#define RequestSpiRead_init_zero {0} +#define ResponseSpiRead_init_zero {{{NULL}, NULL}} #define HostedCommand_init_zero {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_zero}} /* Field tags (for use in manual encoding/decoding) */ +#define RequestSpiTransfer_data_tag 1 +#define RequestSpiWrite_data_tag 1 +#define ResponseSpiRead_data_tag 1 +#define ResponseSpiTransfer_data_tag 1 #define RequestDigitalRead_pin_tag 1 #define RequestDigitalWrite_pin_tag 1 #define RequestDigitalWrite_value_tag 2 #define RequestPinMode_pin_tag 1 #define RequestPinMode_mode_tag 2 +#define RequestSpiRead_length_tag 1 #define ResponseDigitalRead_value_tag 1 +#define ResponseSpiWrite_length_tag 1 +#define SpiSettings_speed_tag 1 +#define SpiSettings_byteOrder_tag 2 +#define SpiSettings_dataMode_tag 3 +#define RequestSpiBeginTransaction_settings_tag 1 #define HostedCommand_version_tag 1 #define HostedCommand_requestDigitalWrite_tag 10 #define HostedCommand_requestPinMode_tag 11 #define HostedCommand_requestDigitalRead_tag 12 #define HostedCommand_responseDigitalRead_tag 13 +#define HostedCommand_requestSpiBeginTransaction_tag 14 +#define HostedCommand_requestSpiEndTransaction_tag 15 +#define HostedCommand_requestSpiTransfer_tag 16 +#define HostedCommand_responseSpiTransfer_tag 17 +#define HostedCommand_requestSpiWrite_tag 18 +#define HostedCommand_responseSpiWrite_tag 19 +#define HostedCommand_requestSpiRead_tag 20 +#define HostedCommand_responseSpiRead_tag 21 /* Struct field encoding specification for nanopb */ #define RequestDigitalWrite_FIELDLIST(X, a) \ @@ -113,23 +216,96 @@ X(a, STATIC, SINGULAR, UINT32, value, 1) #define ResponseDigitalRead_CALLBACK NULL #define ResponseDigitalRead_DEFAULT NULL +#define SpiSettings_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, speed, 1) \ +X(a, STATIC, SINGULAR, UENUM, byteOrder, 2) \ +X(a, STATIC, SINGULAR, UENUM, dataMode, 3) +#define SpiSettings_CALLBACK NULL +#define SpiSettings_DEFAULT NULL + +#define RequestSpiBeginTransaction_FIELDLIST(X, a) \ +X(a, STATIC, OPTIONAL, MESSAGE, settings, 1) +#define RequestSpiBeginTransaction_CALLBACK NULL +#define RequestSpiBeginTransaction_DEFAULT NULL +#define RequestSpiBeginTransaction_settings_MSGTYPE SpiSettings + +#define RequestSpiEndTransaction_FIELDLIST(X, a) \ + +#define RequestSpiEndTransaction_CALLBACK NULL +#define RequestSpiEndTransaction_DEFAULT NULL + +#define RequestSpiTransfer_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define RequestSpiTransfer_CALLBACK pb_default_field_callback +#define RequestSpiTransfer_DEFAULT NULL + +#define ResponseSpiTransfer_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define ResponseSpiTransfer_CALLBACK pb_default_field_callback +#define ResponseSpiTransfer_DEFAULT NULL + +#define RequestSpiWrite_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define RequestSpiWrite_CALLBACK pb_default_field_callback +#define RequestSpiWrite_DEFAULT NULL + +#define ResponseSpiWrite_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, length, 1) +#define ResponseSpiWrite_CALLBACK NULL +#define ResponseSpiWrite_DEFAULT NULL + +#define RequestSpiRead_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, length, 1) +#define RequestSpiRead_CALLBACK NULL +#define RequestSpiRead_DEFAULT NULL + +#define ResponseSpiRead_FIELDLIST(X, a) \ +X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define ResponseSpiRead_CALLBACK pb_default_field_callback +#define ResponseSpiRead_DEFAULT NULL + #define HostedCommand_FIELDLIST(X, a) \ X(a, STATIC, SINGULAR, UENUM, version, 1) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalWrite,payload.requestDigitalWrite), 10) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestPinMode,payload.requestPinMode), 11) \ X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalRead,payload.requestDigitalRead), 12) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,responseDigitalRead,payload.responseDigitalRead), 13) +X(a, STATIC, ONEOF, MESSAGE, (payload,responseDigitalRead,payload.responseDigitalRead), 13) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiBeginTransaction,payload.requestSpiBeginTransaction), 14) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiEndTransaction,payload.requestSpiEndTransaction), 15) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiTransfer,payload.requestSpiTransfer), 16) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiTransfer,payload.responseSpiTransfer), 17) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiWrite,payload.requestSpiWrite), 18) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiWrite,payload.responseSpiWrite), 19) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiRead,payload.requestSpiRead), 20) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiRead,payload.responseSpiRead), 21) #define HostedCommand_CALLBACK NULL #define HostedCommand_DEFAULT NULL #define HostedCommand_payload_requestDigitalWrite_MSGTYPE RequestDigitalWrite #define HostedCommand_payload_requestPinMode_MSGTYPE RequestPinMode #define HostedCommand_payload_requestDigitalRead_MSGTYPE RequestDigitalRead #define HostedCommand_payload_responseDigitalRead_MSGTYPE ResponseDigitalRead +#define HostedCommand_payload_requestSpiBeginTransaction_MSGTYPE RequestSpiBeginTransaction +#define HostedCommand_payload_requestSpiEndTransaction_MSGTYPE RequestSpiEndTransaction +#define HostedCommand_payload_requestSpiTransfer_MSGTYPE RequestSpiTransfer +#define HostedCommand_payload_responseSpiTransfer_MSGTYPE ResponseSpiTransfer +#define HostedCommand_payload_requestSpiWrite_MSGTYPE RequestSpiWrite +#define HostedCommand_payload_responseSpiWrite_MSGTYPE ResponseSpiWrite +#define HostedCommand_payload_requestSpiRead_MSGTYPE RequestSpiRead +#define HostedCommand_payload_responseSpiRead_MSGTYPE ResponseSpiRead extern const pb_msgdesc_t RequestDigitalWrite_msg; extern const pb_msgdesc_t RequestPinMode_msg; extern const pb_msgdesc_t RequestDigitalRead_msg; extern const pb_msgdesc_t ResponseDigitalRead_msg; +extern const pb_msgdesc_t SpiSettings_msg; +extern const pb_msgdesc_t RequestSpiBeginTransaction_msg; +extern const pb_msgdesc_t RequestSpiEndTransaction_msg; +extern const pb_msgdesc_t RequestSpiTransfer_msg; +extern const pb_msgdesc_t ResponseSpiTransfer_msg; +extern const pb_msgdesc_t RequestSpiWrite_msg; +extern const pb_msgdesc_t ResponseSpiWrite_msg; +extern const pb_msgdesc_t RequestSpiRead_msg; +extern const pb_msgdesc_t ResponseSpiRead_msg; extern const pb_msgdesc_t HostedCommand_msg; /* Defines for backwards compatibility with code written before nanopb-0.4.0 */ @@ -137,6 +313,15 @@ extern const pb_msgdesc_t HostedCommand_msg; #define RequestPinMode_fields &RequestPinMode_msg #define RequestDigitalRead_fields &RequestDigitalRead_msg #define ResponseDigitalRead_fields &ResponseDigitalRead_msg +#define SpiSettings_fields &SpiSettings_msg +#define RequestSpiBeginTransaction_fields &RequestSpiBeginTransaction_msg +#define RequestSpiEndTransaction_fields &RequestSpiEndTransaction_msg +#define RequestSpiTransfer_fields &RequestSpiTransfer_msg +#define ResponseSpiTransfer_fields &ResponseSpiTransfer_msg +#define RequestSpiWrite_fields &RequestSpiWrite_msg +#define ResponseSpiWrite_fields &ResponseSpiWrite_msg +#define RequestSpiRead_fields &RequestSpiRead_msg +#define ResponseSpiRead_fields &ResponseSpiRead_msg #define HostedCommand_fields &HostedCommand_msg /* Maximum encoded size of messages (where known) */ @@ -144,7 +329,16 @@ extern const pb_msgdesc_t HostedCommand_msg; #define RequestPinMode_size 8 #define RequestDigitalRead_size 6 #define ResponseDigitalRead_size 6 -#define HostedCommand_size 16 +#define SpiSettings_size 10 +#define RequestSpiBeginTransaction_size 12 +#define RequestSpiEndTransaction_size 0 +/* RequestSpiTransfer_size depends on runtime parameters */ +/* ResponseSpiTransfer_size depends on runtime parameters */ +/* RequestSpiWrite_size depends on runtime parameters */ +#define ResponseSpiWrite_size 6 +#define RequestSpiRead_size 6 +/* ResponseSpiRead_size depends on runtime parameters */ +/* HostedCommand_size depends on runtime parameters */ #ifdef __cplusplus } /* extern "C" */ diff --git a/Sming/Components/Hosted/proto/hosted.proto b/Sming/Components/Hosted/proto/hosted.proto index f811861745..9897bb43ba 100644 --- a/Sming/Components/Hosted/proto/hosted.proto +++ b/Sming/Components/Hosted/proto/hosted.proto @@ -25,6 +25,60 @@ message ResponseDigitalRead { uint32 value = 1; } +message SpiSettings { + uint32 speed = 1; + enum ByteOrder { + MSBFIRST = 0; + LSBFIRST = 1; + } + ByteOrder byteOrder = 2; + enum DataMode { + SPI_MODE0 = 0; + SPI_MODE1 = 1; + SPI_MODE2 = 2; + SPI_MODE3 = 3; + } + DataMode dataMode = 3; +} + +message RequestSpiBeginTransaction +{ + SpiSettings settings = 1; +} + +message RequestSpiEndTransaction +{ + +} + +message RequestSpiTransfer { + bytes data = 1; +} + +message ResponseSpiTransfer { + bytes data = 1; +} + +message RequestSpiWrite +{ + bytes data = 1; +} + +message ResponseSpiWrite +{ + uint32 length = 1; +} + +message RequestSpiRead +{ + uint32 length = 1; +} + +message ResponseSpiRead +{ + bytes data = 1; +} + message HostedCommand { // Version of the protocol for future compatibility enum Version { @@ -36,5 +90,13 @@ message HostedCommand { RequestPinMode requestPinMode = 11; RequestDigitalRead requestDigitalRead = 12; ResponseDigitalRead responseDigitalRead = 13; + RequestSpiBeginTransaction requestSpiBeginTransaction = 14; + RequestSpiEndTransaction requestSpiEndTransaction = 15; + RequestSpiTransfer requestSpiTransfer = 16; + ResponseSpiTransfer responseSpiTransfer = 17; + RequestSpiWrite requestSpiWrite = 18; + ResponseSpiWrite responseSpiWrite = 19; + RequestSpiRead requestSpiRead = 20; + ResponseSpiRead responseSpiRead = 21; } } diff --git a/Sming/Components/Hosted/src/HostedCommon.h b/Sming/Components/Hosted/src/HostedCommon.h index 363fb72bec..b4ebd42d71 100644 --- a/Sming/Components/Hosted/src/HostedCommon.h +++ b/Sming/Components/Hosted/src/HostedCommon.h @@ -4,6 +4,7 @@ #include #include #include "hosted.pb.h" +#include "HostedUtils.h" constexpr int HOSTED_OK = 0; constexpr int HOSTED_FAIL = -1; @@ -24,6 +25,8 @@ class HostedCommon { public: virtual bool onData(const char* at, size_t length) = 0; - + virtual ~HostedCommon() + { + } private: }; diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h index 99edee9eaa..743ed8cac3 100644 --- a/Sming/Components/Hosted/src/HostedServer.h +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -35,6 +35,8 @@ class HostedServer HostedCommand request = HostedCommand_init_zero; HostedCommand response = HostedCommand_init_zero; + request.payload.requestSpiTransfer.data.funcs.decode = &pbDecodeData; + if (at == nullptr || length < 0) { debug_e("empty buffer"); return HOSTED_FAIL; @@ -76,6 +78,9 @@ class HostedServer result = HOSTED_FAIL; break; } + + // TODO: cleanup + } while(input.bytes_left && success); return result; diff --git a/Sming/Components/Hosted/src/HostedUtils.cpp b/Sming/Components/Hosted/src/HostedUtils.cpp new file mode 100644 index 0000000000..2279e2c204 --- /dev/null +++ b/Sming/Components/Hosted/src/HostedUtils.cpp @@ -0,0 +1,42 @@ +#include "HostedUtils.h" +#include + +// See: https://iam777.tistory.com/538 + +bool pbEncodeData(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +{ + PbData *data = (PbData*) *arg; + if(data == nullptr) { + return false; + } + + if (!pb_encode_tag_for_field(stream, field)) { + return false; + } + + return pb_encode_string(stream, (uint8_t*)data->value, data->length); +} + +bool pbDecodeData(pb_istream_t *stream, const pb_field_t *field, void **arg) +{ + uint8_t buffer[1024] = {0}; + + /* We could read block-by-block to avoid the large buffer... */ + if (stream->bytes_left > sizeof(buffer) - 1) { + return false; + } + + size_t available = stream->bytes_left; + if (!pb_read(stream, buffer, stream->bytes_left)) { + return false; + } + + + MemoryDataStream* data = (MemoryDataStream*) *arg; + if(data == nullptr) { + data = new MemoryDataStream(); + *arg = (void*)data; + } + data->write(buffer, available); + return true; +} diff --git a/Sming/Components/Hosted/src/HostedUtils.h b/Sming/Components/Hosted/src/HostedUtils.h new file mode 100644 index 0000000000..e4436e35e2 --- /dev/null +++ b/Sming/Components/Hosted/src/HostedUtils.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include +#include +#include "hosted.pb.h" + +typedef struct { + uint8_t* value; + size_t length; +} PbData; + +bool pbEncodeData(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); +bool pbDecodeData(pb_istream_t *stream, const pb_field_t *field, void **arg); From 711ddc31f3babb42a8b7112dbccf79c93ff3003d Mon Sep 17 00:00:00 2001 From: Mike Date: Sun, 14 Feb 2021 10:18:15 +0000 Subject: [PATCH 08/28] Feature/component hosted review (#68) --- .gitmodules | 5 +- Sming/Components/Hosted-Lib/.cs | 0 .../Hosted-Lib/src/DigitalHosted.cpp | 6 +- Sming/Components/Hosted-Lib/src/SpiHosted.cpp | 25 +- Sming/Components/Hosted-Transport/.cs | 0 .../src/tcp/HostedTcpStream.h | 35 +- .../Hosted-Transport/src/tcp/InitClient.cpp | 6 +- Sming/Components/Hosted/.cs | 0 .../Components/Hosted/app/app/application.cpp | 25 +- .../Hosted/app/src/DigitalHosted.cpp | 52 ++- .../Components/Hosted/app/src/DigitalHosted.h | 11 +- Sming/Components/Hosted/app/src/SpiHosted.cpp | 89 ++-- Sming/Components/Hosted/app/src/SpiHosted.h | 11 +- Sming/Components/Hosted/proto/hosted.pb.c | 20 - Sming/Components/Hosted/proto/hosted.pb.h | 411 +++++++++++------- Sming/Components/Hosted/src/HostedClient.h | 64 ++- Sming/Components/Hosted/src/HostedCommon.h | 24 +- Sming/Components/Hosted/src/HostedServer.h | 83 ++-- Sming/Components/Hosted/src/HostedUtils.cpp | 59 ++- Sming/Components/Hosted/src/HostedUtils.h | 8 +- 20 files changed, 500 insertions(+), 434 deletions(-) create mode 100644 Sming/Components/Hosted-Lib/.cs create mode 100644 Sming/Components/Hosted-Transport/.cs create mode 100644 Sming/Components/Hosted/.cs diff --git a/.gitmodules b/.gitmodules index 28d6cf829b..ceb81cd04e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -57,11 +57,14 @@ path = Sming/Components/libyuarel url = https://github.com/jacketizer/libyuarel.git ignore = dirty +[submodule "mqtt-codec"] + path = Sming/Components/mqtt-codec + url = https://github.com/slaff/mqtt-codec.git + ignore = dirty [submodule "nanopb"] path = Sming/Components/nanopb/nanopb url = https://github.com/nanopb/nanopb.git ignore = dirty - ignore = dirty [submodule "rboot"] path = Sming/Components/rboot/rboot url = https://github.com/mikee47/rboot diff --git a/Sming/Components/Hosted-Lib/.cs b/Sming/Components/Hosted-Lib/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp index bb5a445d7c..dcd251f2f7 100644 --- a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp @@ -25,12 +25,10 @@ void digitalWrite(uint16_t pin, uint8_t val) uint8_t digitalRead(uint16_t pin) { - NEW_HD_COMMAND(message, DigitalRead, { - command->pin = pin; - }); + NEW_HD_COMMAND(message, DigitalRead, { command->pin = pin; }); hostedClient->send(&message); HostedCommand response = hostedClient->wait(); - return (uint8_t)response.payload.responseDigitalRead.value; + return uint8_t(response.payload.responseDigitalRead.value); } diff --git a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp index 8d95e0f50b..1c8f9838fc 100644 --- a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp @@ -9,9 +9,7 @@ SPIClass SPI; void SPIClass::begin() { - NEW_HD_COMMAND(message, SpiBeginTransaction, { - command->has_settings = 0; - }); + NEW_HD_COMMAND(message, SpiBeginTransaction, { command->has_settings = 0; }); hostedClient->send(&message); } @@ -19,10 +17,10 @@ void SPIClass::begin() void SPIClass::beginTransaction(SPISettings mySettings) { NEW_HD_COMMAND(message, SpiBeginTransaction, { - command->has_settings = 1; - command->settings.speed = mySettings.speed; - command->settings.byteOrder = (SpiSettings_ByteOrder)mySettings.byteOrder; - command->settings.dataMode = (SpiSettings_DataMode)mySettings.dataMode; + command->has_settings = 1; + command->settings.speed = mySettings.speed; + command->settings.byteOrder = SpiSettings_ByteOrder(mySettings.byteOrder); + command->settings.dataMode = SpiSettings_DataMode(mySettings.dataMode); }); hostedClient->send(&message); @@ -41,29 +39,26 @@ void SPIClass::beginTransaction(SPISettings mySettings) */ void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) { - PbData data; - data.value = buffer; - data.length = numberBytes; + PbData data{buffer, numberBytes}; NEW_HD_COMMAND(message, SpiTransfer, { - command->data.arg = (void*)&data; - command->data.funcs.encode = &pbEncodeData; + command->data.arg = &data; + command->data.funcs.encode = &pbEncodeData; }); hostedClient->send(&message); HostedCommand response = hostedClient->wait(); - MemoryDataStream* resultData = (MemoryDataStream*)response.payload.responseSpiTransfer.data.arg; + auto resultData = static_cast(response.payload.responseSpiTransfer.data.arg); if(resultData == nullptr) { memset(buffer, 0, numberBytes); return; } - resultData->readBytes((char *)buffer, numberBytes); + resultData->readBytes(reinterpret_cast(buffer), numberBytes); delete resultData; } - uint32_t SPIClass::transfer32(uint32_t val, uint8_t bits) { // TODO: diff --git a/Sming/Components/Hosted-Transport/.cs b/Sming/Components/Hosted-Transport/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h index 93202f3f4e..7098bc52e9 100644 --- a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h +++ b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h @@ -1,35 +1,34 @@ #include #include -class HostedTcpStream: public ReadWriteStream +class HostedTcpStream : public ReadWriteStream { public: - HostedTcpStream(const String& host, uint16_t port): host(host), port(port) + HostedTcpStream(const String& host, uint16_t port) : host(host), port(port), buffer(1024) { - auto onCompleted = [](TcpClient& client, bool successful) { + auto onCompleted = [](TcpClient& client, bool successful) { // onCompleted; + }; - }; + auto onReadyToSend = [](TcpClient& client, TcpConnectionEvent sourceEvent) { - auto onReadyToSend = [](TcpClient& client, TcpConnectionEvent sourceEvent) { + }; - }; + auto onReceive = [this](TcpClient& client, char* data, int size) -> bool { + size_t written = buffer.write(reinterpret_cast(data), size); + return written == size_t(size); + }; - auto onReceive = [this](TcpClient& client, char* data, int size)->bool { - size_t written = this->buffer.write((const uint8_t*)data, size); - return (written == (size_t)size); - }; - - client = new TcpClient(onCompleted, onReadyToSend, onReceive); + client = new TcpClient(onCompleted, onReadyToSend, onReceive); } size_t write(const uint8_t* buffer, size_t size) override { if(!client->isProcessing()) { - client->connect(host, (int)port); + client->connect(host, int(port)); } - if(!client->send((const char *)buffer, size)) { + if(!client->send(reinterpret_cast(buffer), size)) { return 0; } @@ -46,7 +45,6 @@ class HostedTcpStream: public ReadWriteStream return buffer.seek(len); } - bool isFinished() override { return false; @@ -57,10 +55,9 @@ class HostedTcpStream: public ReadWriteStream client->commit(); } - private: - TcpClient* client = nullptr; - CircularBuffer buffer = CircularBuffer(1024); + TcpClient* client{nullptr}; + CircularBuffer buffer; String host; - uint16_t port = 0; + uint16_t port{0}; }; diff --git a/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp b/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp index 05f58c0ad0..387fef4a58 100644 --- a/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp +++ b/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp @@ -1,16 +1,14 @@ #include #include -#include "../../src/tcp/HostedTcpStream.h" +#include "HostedTcpStream.h" -HostedClient* hostedClient = nullptr; +HostedClient* hostedClient{nullptr}; #ifndef WIFI_SSID #define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here #define WIFI_PWD "PleaseEnterPass" #endif - - #ifndef HOSTED_SERVER_IP #define REMOTE_IP IpAddress("192.168.13.1") #else diff --git a/Sming/Components/Hosted/.cs b/Sming/Components/Hosted/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp index a52ac532ce..666b97ab06 100644 --- a/Sming/Components/Hosted/app/app/application.cpp +++ b/Sming/Components/Hosted/app/app/application.cpp @@ -10,14 +10,17 @@ HostedServer hostedServer; TcpServer* tcpServer; -namespace Hosted { - namespace Digital { - void registerCommands(HostedServer& server); - } - namespace Spi { - void registerCommands(HostedServer& server); - } +namespace Hosted +{ +namespace Digital +{ +void registerCommands(HostedServer& server); } +namespace Spi +{ +void registerCommands(HostedServer& server); +} +} // namespace Hosted // Will be called when WiFi station was connected to AP void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) @@ -29,13 +32,13 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) tcpServer = new TcpServer([](TcpClient& client, char* data, int size) -> bool { // clientReceiveDataHandler - int result = hostedServer.process((const uint8_t*)data, size); + int result = hostedServer.process(reinterpret_cast(data), size); if(result != HOSTED_OK) { - return result == HOSTED_NO_MEM ? false: true; + return result == HOSTED_NO_MEM ? false : true; } hostedServer.transfer([&client](const uint8_t* data, size_t size) -> bool { - return client.send((const char*)data, size); + return client.send(reinterpret_cast(data), size); }); return true; @@ -50,7 +53,6 @@ void init() Hosted::Digital::registerCommands(hostedServer); Hosted::Spi::registerCommands(hostedServer); - // Connect to same AP as the client application WifiStation.enable(true); WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); @@ -58,4 +60,3 @@ void init() // Set callback that should be triggered when we have assigned IP WifiEvents.onStationGotIP(connectOk); } - diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.cpp b/Sming/Components/Hosted/app/src/DigitalHosted.cpp index 4a62fcb650..7afc4954f0 100644 --- a/Sming/Components/Hosted/app/src/DigitalHosted.cpp +++ b/Sming/Components/Hosted/app/src/DigitalHosted.cpp @@ -1,27 +1,35 @@ #include "DigitalHosted.h" -namespace Hosted { -namespace Digital { +namespace Hosted +{ +namespace Digital +{ +void registerCommands(HostedServer& server) +{ + // Register Command Handlers + server.registerCommand(HostedCommand_requestPinMode_tag, + [](HostedCommand* request, HostedCommand* response) -> int { + auto& r = request->payload.requestPinMode; + pinMode(r.pin, r.mode); + return 0; + }); - void registerCommands(HostedServer& server) - { - // Register Command Handlers - server.registerCommand(HostedCommand_requestPinMode_tag, [](HostedCommand *request, HostedCommand *response)-> int { - pinMode((uint16_t)request->payload.requestPinMode.pin, (uint8_t)request->payload.requestPinMode.mode); - return 0; - }); + server.registerCommand(HostedCommand_requestDigitalWrite_tag, + [](HostedCommand* request, HostedCommand* response) -> int { + auto& r = request->payload.requestDigitalWrite; + digitalWrite(r.pin, r.value); + return 0; + }); - server.registerCommand(HostedCommand_requestDigitalWrite_tag, [](HostedCommand *request, HostedCommand *response)-> int { - digitalWrite((uint16_t)request->payload.requestDigitalWrite.pin, (uint8_t)request->payload.requestDigitalWrite.value); - return 0; - }); + server.registerCommand(HostedCommand_requestDigitalRead_tag, + [](HostedCommand* request, HostedCommand* response) -> int { + auto& r = request->payload.requestDigitalRead; + uint8_t result = digitalRead(r.pin); + response->which_payload = HostedCommand_responseDigitalRead_tag; + response->payload.responseDigitalRead.value = result; - server.registerCommand(HostedCommand_requestDigitalRead_tag, [](HostedCommand *request, HostedCommand *response)-> int { - uint8_t result = digitalRead((uint16_t)request->payload.requestDigitalRead.pin); - response->which_payload = HostedCommand_responseDigitalRead_tag; - response->payload.responseDigitalRead.value = result; - - return 0; - }); - } -}} + return 0; + }); +} +} // namespace Digital +} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.h b/Sming/Components/Hosted/app/src/DigitalHosted.h index 9913134f98..a618ad339b 100644 --- a/Sming/Components/Hosted/app/src/DigitalHosted.h +++ b/Sming/Components/Hosted/app/src/DigitalHosted.h @@ -4,9 +4,10 @@ #include #include -namespace Hosted { - namespace Digital { - - void registerCommands(HostedServer& server); - } +namespace Hosted +{ +namespace Digital +{ +void registerCommands(HostedServer& server); } +} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/SpiHosted.cpp b/Sming/Components/Hosted/app/src/SpiHosted.cpp index 3a80658d45..71e829999b 100644 --- a/Sming/Components/Hosted/app/src/SpiHosted.cpp +++ b/Sming/Components/Hosted/app/src/SpiHosted.cpp @@ -1,46 +1,49 @@ #include "SpiHosted.h" #include -namespace Hosted { -namespace Spi { - - void registerCommands(HostedServer& server) - { - // Register Command Handlers - server.registerCommand(HostedCommand_requestSpiBeginTransaction_tag, [](HostedCommand *request, HostedCommand *response)-> int { - if(request->payload.requestSpiBeginTransaction.has_settings) { - SPI.begin(); - } - else { - SPISettings settings(request->payload.requestSpiBeginTransaction.settings.speed, - request->payload.requestSpiBeginTransaction.settings.byteOrder, - request->payload.requestSpiBeginTransaction.settings.dataMode); - SPI.beginTransaction(settings); - } - - return 0; - }); - - server.registerCommand(HostedCommand_requestSpiTransfer_tag, [](HostedCommand *request, HostedCommand *response)-> int { - PbData* responseData = new PbData; - responseData->length = 0; - - MemoryDataStream* data = (MemoryDataStream*)request->payload.requestSpiTransfer.data.arg; - if(data != nullptr) { - size_t available = data->available(); - uint8_t* buffer = new uint8_t[available]; - size_t length = data->readBytes((char *)&buffer, available); - SPI.transfer(buffer, length); - responseData->value = buffer; - responseData->length = length; - - delete data; - } - - response->payload.responseSpiTransfer.data.funcs.encode = &pbEncodeData; - response->payload.responseSpiTransfer.data.arg = (void* )responseData; - - return 0; - }); - } -}} +namespace Hosted +{ +namespace Spi +{ +void registerCommands(HostedServer& server) +{ + // Register Command Handlers + server.registerCommand(HostedCommand_requestSpiBeginTransaction_tag, + [](HostedCommand* request, HostedCommand* response) -> int { + auto& r = request->payload.requestSpiBeginTransaction; + if(r.has_settings) { + SPI.begin(); + } else { + auto& rs = r.settings; + SPISettings settings(rs.speed, rs.byteOrder, rs.dataMode); + SPI.beginTransaction(settings); + } + + return 0; + }); + + server.registerCommand(HostedCommand_requestSpiTransfer_tag, + [](HostedCommand* request, HostedCommand* response) -> int { + auto responseData = new PbData{}; + + auto data = static_cast(request->payload.requestSpiTransfer.data.arg); + if(data != nullptr) { + size_t available = data->available(); + auto buffer = new uint8_t[available]; + size_t length = data->readBytes(reinterpret_cast(&buffer), available); + SPI.transfer(buffer, length); + responseData->value = buffer; + responseData->length = length; + + delete data; + } + + auto& data = response->payload.responseSpiTransfer.data; + data.funcs.encode = &pbEncodeData; + data.arg = responseData; + + return 0; + }); +} +} // namespace Spi +} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/SpiHosted.h b/Sming/Components/Hosted/app/src/SpiHosted.h index 94f9111949..1d7f1f84be 100644 --- a/Sming/Components/Hosted/app/src/SpiHosted.h +++ b/Sming/Components/Hosted/app/src/SpiHosted.h @@ -4,9 +4,10 @@ #include #include -namespace Hosted { - namespace Spi { - - void registerCommands(HostedServer& server); - } +namespace Hosted +{ +namespace Spi +{ +void registerCommands(HostedServer& server); } +} // namespace Hosted diff --git a/Sming/Components/Hosted/proto/hosted.pb.c b/Sming/Components/Hosted/proto/hosted.pb.c index f85e9d98d5..c288740dbe 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.c +++ b/Sming/Components/Hosted/proto/hosted.pb.c @@ -8,48 +8,28 @@ PB_BIND(RequestDigitalWrite, RequestDigitalWrite, AUTO) - PB_BIND(RequestPinMode, RequestPinMode, AUTO) - PB_BIND(RequestDigitalRead, RequestDigitalRead, AUTO) - PB_BIND(ResponseDigitalRead, ResponseDigitalRead, AUTO) - PB_BIND(SpiSettings, SpiSettings, AUTO) - PB_BIND(RequestSpiBeginTransaction, RequestSpiBeginTransaction, AUTO) - PB_BIND(RequestSpiEndTransaction, RequestSpiEndTransaction, AUTO) - PB_BIND(RequestSpiTransfer, RequestSpiTransfer, AUTO) - PB_BIND(ResponseSpiTransfer, ResponseSpiTransfer, AUTO) - PB_BIND(RequestSpiWrite, RequestSpiWrite, AUTO) - PB_BIND(ResponseSpiWrite, ResponseSpiWrite, AUTO) - PB_BIND(RequestSpiRead, RequestSpiRead, AUTO) - PB_BIND(ResponseSpiRead, ResponseSpiRead, AUTO) - PB_BIND(HostedCommand, HostedCommand, AUTO) - - - - - - - diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h index 47da2da93e..7b08f66f49 100644 --- a/Sming/Components/Hosted/proto/hosted.pb.h +++ b/Sming/Components/Hosted/proto/hosted.pb.h @@ -14,270 +14,359 @@ extern "C" { #endif /* Enum definitions */ -typedef enum _PinMode { - PinMode_INPUT = 0, - PinMode_OUTPUT = 1, - PinMode_INPUT_PULLUP = 2 -} PinMode; +typedef enum _PinMode { PinMode_INPUT = 0, PinMode_OUTPUT = 1, PinMode_INPUT_PULLUP = 2 } PinMode; typedef enum _SpiSettings_ByteOrder { - SpiSettings_ByteOrder_MSBFIRST = 0, - SpiSettings_ByteOrder_LSBFIRST = 1 + SpiSettings_ByteOrder_MSBFIRST = 0, + SpiSettings_ByteOrder_LSBFIRST = 1 } SpiSettings_ByteOrder; typedef enum _SpiSettings_DataMode { - SpiSettings_DataMode_SPI_MODE0 = 0, - SpiSettings_DataMode_SPI_MODE1 = 1, - SpiSettings_DataMode_SPI_MODE2 = 2, - SpiSettings_DataMode_SPI_MODE3 = 3 + SpiSettings_DataMode_SPI_MODE0 = 0, + SpiSettings_DataMode_SPI_MODE1 = 1, + SpiSettings_DataMode_SPI_MODE2 = 2, + SpiSettings_DataMode_SPI_MODE3 = 3 } SpiSettings_DataMode; -typedef enum _HostedCommand_Version { - HostedCommand_Version_HOSTED_V_1_0 = 0 -} HostedCommand_Version; +typedef enum _HostedCommand_Version { HostedCommand_Version_HOSTED_V_1_0 = 0 } HostedCommand_Version; /* Struct definitions */ typedef struct _RequestSpiEndTransaction { - char dummy_field; + char dummy_field; } RequestSpiEndTransaction; typedef struct _RequestSpiTransfer { - pb_callback_t data; + pb_callback_t data; } RequestSpiTransfer; typedef struct _RequestSpiWrite { - pb_callback_t data; + pb_callback_t data; } RequestSpiWrite; typedef struct _ResponseSpiRead { - pb_callback_t data; + pb_callback_t data; } ResponseSpiRead; typedef struct _ResponseSpiTransfer { - pb_callback_t data; + pb_callback_t data; } ResponseSpiTransfer; typedef struct _RequestDigitalRead { - uint32_t pin; + uint32_t pin; } RequestDigitalRead; typedef struct _RequestDigitalWrite { - uint32_t pin; - uint32_t value; + uint32_t pin; + uint32_t value; } RequestDigitalWrite; typedef struct _RequestPinMode { - uint32_t pin; - PinMode mode; + uint32_t pin; + PinMode mode; } RequestPinMode; typedef struct _RequestSpiRead { - uint32_t length; + uint32_t length; } RequestSpiRead; typedef struct _ResponseDigitalRead { - uint32_t value; + uint32_t value; } ResponseDigitalRead; typedef struct _ResponseSpiWrite { - uint32_t length; + uint32_t length; } ResponseSpiWrite; typedef struct _SpiSettings { - uint32_t speed; - SpiSettings_ByteOrder byteOrder; - SpiSettings_DataMode dataMode; + uint32_t speed; + SpiSettings_ByteOrder byteOrder; + SpiSettings_DataMode dataMode; } SpiSettings; typedef struct _RequestSpiBeginTransaction { - bool has_settings; - SpiSettings settings; + bool has_settings; + SpiSettings settings; } RequestSpiBeginTransaction; typedef struct _HostedCommand { - HostedCommand_Version version; - pb_size_t which_payload; - union { - RequestDigitalWrite requestDigitalWrite; - RequestPinMode requestPinMode; - RequestDigitalRead requestDigitalRead; - ResponseDigitalRead responseDigitalRead; - RequestSpiBeginTransaction requestSpiBeginTransaction; - RequestSpiEndTransaction requestSpiEndTransaction; - RequestSpiTransfer requestSpiTransfer; - ResponseSpiTransfer responseSpiTransfer; - RequestSpiWrite requestSpiWrite; - ResponseSpiWrite responseSpiWrite; - RequestSpiRead requestSpiRead; - ResponseSpiRead responseSpiRead; - } payload; + HostedCommand_Version version; + pb_size_t which_payload; + union { + RequestDigitalWrite requestDigitalWrite; + RequestPinMode requestPinMode; + RequestDigitalRead requestDigitalRead; + ResponseDigitalRead responseDigitalRead; + RequestSpiBeginTransaction requestSpiBeginTransaction; + RequestSpiEndTransaction requestSpiEndTransaction; + RequestSpiTransfer requestSpiTransfer; + ResponseSpiTransfer responseSpiTransfer; + RequestSpiWrite requestSpiWrite; + ResponseSpiWrite responseSpiWrite; + RequestSpiRead requestSpiRead; + ResponseSpiRead responseSpiRead; + } payload; } HostedCommand; - /* Helper constants for enums */ #define _PinMode_MIN PinMode_INPUT #define _PinMode_MAX PinMode_INPUT_PULLUP -#define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP+1)) +#define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP + 1)) #define _SpiSettings_ByteOrder_MIN SpiSettings_ByteOrder_MSBFIRST #define _SpiSettings_ByteOrder_MAX SpiSettings_ByteOrder_LSBFIRST -#define _SpiSettings_ByteOrder_ARRAYSIZE ((SpiSettings_ByteOrder)(SpiSettings_ByteOrder_LSBFIRST+1)) +#define _SpiSettings_ByteOrder_ARRAYSIZE ((SpiSettings_ByteOrder)(SpiSettings_ByteOrder_LSBFIRST + 1)) #define _SpiSettings_DataMode_MIN SpiSettings_DataMode_SPI_MODE0 #define _SpiSettings_DataMode_MAX SpiSettings_DataMode_SPI_MODE3 -#define _SpiSettings_DataMode_ARRAYSIZE ((SpiSettings_DataMode)(SpiSettings_DataMode_SPI_MODE3+1)) +#define _SpiSettings_DataMode_ARRAYSIZE ((SpiSettings_DataMode)(SpiSettings_DataMode_SPI_MODE3 + 1)) #define _HostedCommand_Version_MIN HostedCommand_Version_HOSTED_V_1_0 #define _HostedCommand_Version_MAX HostedCommand_Version_HOSTED_V_1_0 -#define _HostedCommand_Version_ARRAYSIZE ((HostedCommand_Version)(HostedCommand_Version_HOSTED_V_1_0+1)) - +#define _HostedCommand_Version_ARRAYSIZE ((HostedCommand_Version)(HostedCommand_Version_HOSTED_V_1_0 + 1)) /* Initializer values for message structs */ -#define RequestDigitalWrite_init_default {0, 0} -#define RequestPinMode_init_default {0, _PinMode_MIN} -#define RequestDigitalRead_init_default {0} -#define ResponseDigitalRead_init_default {0} -#define SpiSettings_init_default {0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN} -#define RequestSpiBeginTransaction_init_default {false, SpiSettings_init_default} -#define RequestSpiEndTransaction_init_default {0} -#define RequestSpiTransfer_init_default {{{NULL}, NULL}} -#define ResponseSpiTransfer_init_default {{{NULL}, NULL}} -#define RequestSpiWrite_init_default {{{NULL}, NULL}} -#define ResponseSpiWrite_init_default {0} -#define RequestSpiRead_init_default {0} -#define ResponseSpiRead_init_default {{{NULL}, NULL}} -#define HostedCommand_init_default {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_default}} -#define RequestDigitalWrite_init_zero {0, 0} -#define RequestPinMode_init_zero {0, _PinMode_MIN} -#define RequestDigitalRead_init_zero {0} -#define ResponseDigitalRead_init_zero {0} -#define SpiSettings_init_zero {0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN} -#define RequestSpiBeginTransaction_init_zero {false, SpiSettings_init_zero} -#define RequestSpiEndTransaction_init_zero {0} -#define RequestSpiTransfer_init_zero {{{NULL}, NULL}} -#define ResponseSpiTransfer_init_zero {{{NULL}, NULL}} -#define RequestSpiWrite_init_zero {{{NULL}, NULL}} -#define ResponseSpiWrite_init_zero {0} -#define RequestSpiRead_init_zero {0} -#define ResponseSpiRead_init_zero {{{NULL}, NULL}} -#define HostedCommand_init_zero {_HostedCommand_Version_MIN, 0, {RequestDigitalWrite_init_zero}} +#define RequestDigitalWrite_init_default \ + { \ + 0, 0 \ + } +#define RequestPinMode_init_default \ + { \ + 0, _PinMode_MIN \ + } +#define RequestDigitalRead_init_default \ + { \ + 0 \ + } +#define ResponseDigitalRead_init_default \ + { \ + 0 \ + } +#define SpiSettings_init_default \ + { \ + 0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN \ + } +#define RequestSpiBeginTransaction_init_default \ + { \ + false, SpiSettings_init_default \ + } +#define RequestSpiEndTransaction_init_default \ + { \ + 0 \ + } +#define RequestSpiTransfer_init_default \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define ResponseSpiTransfer_init_default \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define RequestSpiWrite_init_default \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define ResponseSpiWrite_init_default \ + { \ + 0 \ + } +#define RequestSpiRead_init_default \ + { \ + 0 \ + } +#define ResponseSpiRead_init_default \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define HostedCommand_init_default \ + { \ + _HostedCommand_Version_MIN, 0, \ + { \ + RequestDigitalWrite_init_default \ + } \ + } +#define RequestDigitalWrite_init_zero \ + { \ + 0, 0 \ + } +#define RequestPinMode_init_zero \ + { \ + 0, _PinMode_MIN \ + } +#define RequestDigitalRead_init_zero \ + { \ + 0 \ + } +#define ResponseDigitalRead_init_zero \ + { \ + 0 \ + } +#define SpiSettings_init_zero \ + { \ + 0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN \ + } +#define RequestSpiBeginTransaction_init_zero \ + { \ + false, SpiSettings_init_zero \ + } +#define RequestSpiEndTransaction_init_zero \ + { \ + 0 \ + } +#define RequestSpiTransfer_init_zero \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define ResponseSpiTransfer_init_zero \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define RequestSpiWrite_init_zero \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define ResponseSpiWrite_init_zero \ + { \ + 0 \ + } +#define RequestSpiRead_init_zero \ + { \ + 0 \ + } +#define ResponseSpiRead_init_zero \ + { \ + { \ + {NULL}, NULL \ + } \ + } +#define HostedCommand_init_zero \ + { \ + _HostedCommand_Version_MIN, 0, \ + { \ + RequestDigitalWrite_init_zero \ + } \ + } /* Field tags (for use in manual encoding/decoding) */ -#define RequestSpiTransfer_data_tag 1 -#define RequestSpiWrite_data_tag 1 -#define ResponseSpiRead_data_tag 1 -#define ResponseSpiTransfer_data_tag 1 -#define RequestDigitalRead_pin_tag 1 -#define RequestDigitalWrite_pin_tag 1 -#define RequestDigitalWrite_value_tag 2 -#define RequestPinMode_pin_tag 1 -#define RequestPinMode_mode_tag 2 -#define RequestSpiRead_length_tag 1 -#define ResponseDigitalRead_value_tag 1 -#define ResponseSpiWrite_length_tag 1 -#define SpiSettings_speed_tag 1 -#define SpiSettings_byteOrder_tag 2 -#define SpiSettings_dataMode_tag 3 -#define RequestSpiBeginTransaction_settings_tag 1 -#define HostedCommand_version_tag 1 -#define HostedCommand_requestDigitalWrite_tag 10 -#define HostedCommand_requestPinMode_tag 11 -#define HostedCommand_requestDigitalRead_tag 12 -#define HostedCommand_responseDigitalRead_tag 13 +#define RequestSpiTransfer_data_tag 1 +#define RequestSpiWrite_data_tag 1 +#define ResponseSpiRead_data_tag 1 +#define ResponseSpiTransfer_data_tag 1 +#define RequestDigitalRead_pin_tag 1 +#define RequestDigitalWrite_pin_tag 1 +#define RequestDigitalWrite_value_tag 2 +#define RequestPinMode_pin_tag 1 +#define RequestPinMode_mode_tag 2 +#define RequestSpiRead_length_tag 1 +#define ResponseDigitalRead_value_tag 1 +#define ResponseSpiWrite_length_tag 1 +#define SpiSettings_speed_tag 1 +#define SpiSettings_byteOrder_tag 2 +#define SpiSettings_dataMode_tag 3 +#define RequestSpiBeginTransaction_settings_tag 1 +#define HostedCommand_version_tag 1 +#define HostedCommand_requestDigitalWrite_tag 10 +#define HostedCommand_requestPinMode_tag 11 +#define HostedCommand_requestDigitalRead_tag 12 +#define HostedCommand_responseDigitalRead_tag 13 #define HostedCommand_requestSpiBeginTransaction_tag 14 #define HostedCommand_requestSpiEndTransaction_tag 15 -#define HostedCommand_requestSpiTransfer_tag 16 -#define HostedCommand_responseSpiTransfer_tag 17 -#define HostedCommand_requestSpiWrite_tag 18 -#define HostedCommand_responseSpiWrite_tag 19 -#define HostedCommand_requestSpiRead_tag 20 -#define HostedCommand_responseSpiRead_tag 21 +#define HostedCommand_requestSpiTransfer_tag 16 +#define HostedCommand_responseSpiTransfer_tag 17 +#define HostedCommand_requestSpiWrite_tag 18 +#define HostedCommand_responseSpiWrite_tag 19 +#define HostedCommand_requestSpiRead_tag 20 +#define HostedCommand_responseSpiRead_tag 21 /* Struct field encoding specification for nanopb */ -#define RequestDigitalWrite_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pin, 1) \ -X(a, STATIC, SINGULAR, UINT32, value, 2) +#define RequestDigitalWrite_FIELDLIST(X, a) \ + X(a, STATIC, SINGULAR, UINT32, pin, 1) \ + X(a, STATIC, SINGULAR, UINT32, value, 2) #define RequestDigitalWrite_CALLBACK NULL #define RequestDigitalWrite_DEFAULT NULL -#define RequestPinMode_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pin, 1) \ -X(a, STATIC, SINGULAR, UENUM, mode, 2) +#define RequestPinMode_FIELDLIST(X, a) \ + X(a, STATIC, SINGULAR, UINT32, pin, 1) \ + X(a, STATIC, SINGULAR, UENUM, mode, 2) #define RequestPinMode_CALLBACK NULL #define RequestPinMode_DEFAULT NULL -#define RequestDigitalRead_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, pin, 1) +#define RequestDigitalRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, pin, 1) #define RequestDigitalRead_CALLBACK NULL #define RequestDigitalRead_DEFAULT NULL -#define ResponseDigitalRead_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, value, 1) +#define ResponseDigitalRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, value, 1) #define ResponseDigitalRead_CALLBACK NULL #define ResponseDigitalRead_DEFAULT NULL -#define SpiSettings_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, speed, 1) \ -X(a, STATIC, SINGULAR, UENUM, byteOrder, 2) \ -X(a, STATIC, SINGULAR, UENUM, dataMode, 3) +#define SpiSettings_FIELDLIST(X, a) \ + X(a, STATIC, SINGULAR, UINT32, speed, 1) \ + X(a, STATIC, SINGULAR, UENUM, byteOrder, 2) \ + X(a, STATIC, SINGULAR, UENUM, dataMode, 3) #define SpiSettings_CALLBACK NULL #define SpiSettings_DEFAULT NULL -#define RequestSpiBeginTransaction_FIELDLIST(X, a) \ -X(a, STATIC, OPTIONAL, MESSAGE, settings, 1) +#define RequestSpiBeginTransaction_FIELDLIST(X, a) X(a, STATIC, OPTIONAL, MESSAGE, settings, 1) #define RequestSpiBeginTransaction_CALLBACK NULL #define RequestSpiBeginTransaction_DEFAULT NULL #define RequestSpiBeginTransaction_settings_MSGTYPE SpiSettings -#define RequestSpiEndTransaction_FIELDLIST(X, a) \ +#define RequestSpiEndTransaction_FIELDLIST(X, a) #define RequestSpiEndTransaction_CALLBACK NULL #define RequestSpiEndTransaction_DEFAULT NULL -#define RequestSpiTransfer_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define RequestSpiTransfer_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) #define RequestSpiTransfer_CALLBACK pb_default_field_callback #define RequestSpiTransfer_DEFAULT NULL -#define ResponseSpiTransfer_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define ResponseSpiTransfer_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) #define ResponseSpiTransfer_CALLBACK pb_default_field_callback #define ResponseSpiTransfer_DEFAULT NULL -#define RequestSpiWrite_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define RequestSpiWrite_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) #define RequestSpiWrite_CALLBACK pb_default_field_callback #define RequestSpiWrite_DEFAULT NULL -#define ResponseSpiWrite_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, length, 1) +#define ResponseSpiWrite_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, length, 1) #define ResponseSpiWrite_CALLBACK NULL #define ResponseSpiWrite_DEFAULT NULL -#define RequestSpiRead_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UINT32, length, 1) +#define RequestSpiRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, length, 1) #define RequestSpiRead_CALLBACK NULL #define RequestSpiRead_DEFAULT NULL -#define ResponseSpiRead_FIELDLIST(X, a) \ -X(a, CALLBACK, SINGULAR, BYTES, data, 1) +#define ResponseSpiRead_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) #define ResponseSpiRead_CALLBACK pb_default_field_callback #define ResponseSpiRead_DEFAULT NULL -#define HostedCommand_FIELDLIST(X, a) \ -X(a, STATIC, SINGULAR, UENUM, version, 1) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalWrite,payload.requestDigitalWrite), 10) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestPinMode,payload.requestPinMode), 11) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestDigitalRead,payload.requestDigitalRead), 12) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,responseDigitalRead,payload.responseDigitalRead), 13) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiBeginTransaction,payload.requestSpiBeginTransaction), 14) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiEndTransaction,payload.requestSpiEndTransaction), 15) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiTransfer,payload.requestSpiTransfer), 16) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiTransfer,payload.responseSpiTransfer), 17) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiWrite,payload.requestSpiWrite), 18) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiWrite,payload.responseSpiWrite), 19) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,requestSpiRead,payload.requestSpiRead), 20) \ -X(a, STATIC, ONEOF, MESSAGE, (payload,responseSpiRead,payload.responseSpiRead), 21) +#define HostedCommand_FIELDLIST(X, a) \ + X(a, STATIC, SINGULAR, UENUM, version, 1) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestDigitalWrite, payload.requestDigitalWrite), 10) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestPinMode, payload.requestPinMode), 11) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestDigitalRead, payload.requestDigitalRead), 12) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, responseDigitalRead, payload.responseDigitalRead), 13) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiBeginTransaction, payload.requestSpiBeginTransaction), 14) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiEndTransaction, payload.requestSpiEndTransaction), 15) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiTransfer, payload.requestSpiTransfer), 16) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiTransfer, payload.responseSpiTransfer), 17) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiWrite, payload.requestSpiWrite), 18) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiWrite, payload.responseSpiWrite), 19) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiRead, payload.requestSpiRead), 20) \ + X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiRead, payload.responseSpiRead), 21) #define HostedCommand_CALLBACK NULL #define HostedCommand_DEFAULT NULL #define HostedCommand_payload_requestDigitalWrite_MSGTYPE RequestDigitalWrite @@ -325,18 +414,18 @@ extern const pb_msgdesc_t HostedCommand_msg; #define HostedCommand_fields &HostedCommand_msg /* Maximum encoded size of messages (where known) */ -#define RequestDigitalWrite_size 12 -#define RequestPinMode_size 8 -#define RequestDigitalRead_size 6 -#define ResponseDigitalRead_size 6 -#define SpiSettings_size 10 -#define RequestSpiBeginTransaction_size 12 -#define RequestSpiEndTransaction_size 0 +#define RequestDigitalWrite_size 12 +#define RequestPinMode_size 8 +#define RequestDigitalRead_size 6 +#define ResponseDigitalRead_size 6 +#define SpiSettings_size 10 +#define RequestSpiBeginTransaction_size 12 +#define RequestSpiEndTransaction_size 0 /* RequestSpiTransfer_size depends on runtime parameters */ /* ResponseSpiTransfer_size depends on runtime parameters */ /* RequestSpiWrite_size depends on runtime parameters */ -#define ResponseSpiWrite_size 6 -#define RequestSpiRead_size 6 +#define ResponseSpiWrite_size 6 +#define RequestSpiRead_size 6 /* ResponseSpiRead_size depends on runtime parameters */ /* HostedCommand_size depends on runtime parameters */ diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src/HostedClient.h index e3653f541e..194ca8f0de 100644 --- a/Sming/Components/Hosted/src/HostedClient.h +++ b/Sming/Components/Hosted/src/HostedClient.h @@ -9,9 +9,8 @@ extern void host_main_loop(); class HostedClient { public: - - HostedClient(ReadWriteStream* stream): stream(stream) - { + HostedClient(ReadWriteStream* stream) : stream(stream) + { } void setStream(ReadWriteStream* stream) @@ -27,8 +26,8 @@ class HostedClient { pb_ostream_t ouput = newOutputStream(); bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); - if (!success) { - debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); + if(!success) { + debug_e("Encoding failed: %s", PB_GET_ERROR(&ouput)); return false; } @@ -53,8 +52,7 @@ class HostedClient stream->flush(); success = pb_decode_ex(&input, HostedCommand_fields, &command, PB_DECODE_DELIMITED); host_main_loop(); - } - while(!success); + } while(!success); stream->seek(totalBytes - input.bytes_left); @@ -68,41 +66,41 @@ class HostedClient { return true; } + private: pb_istream_t newInputStream() { - pb_istream_t stream; - stream.callback = [](pb_istream_t *stream, pb_byte_t *buf, size_t count) -> bool { - ReadWriteStream* source = (ReadWriteStream* )stream->state; - size_t read = source->readMemoryBlock((char *)buf, count); - source->seek(read); - - return true; + return pb_istream_t{ + .callback = [](pb_istream_t* stream, pb_byte_t* buf, size_t count) -> bool { + auto source = static_cast(stream->state); + size_t read = source->readMemoryBlock(reinterpret_cast(buf), count); + source->seek(read); + + return true; + }, + .state = this->stream, + .bytes_left = this->stream->available(), + .errmsg = nullptr, }; - stream.state = (void*)this->stream; - stream.bytes_left = this->stream->available(); - stream.errmsg = nullptr; - - return stream; } pb_ostream_t newOutputStream() { - pb_ostream_t outputStream; - outputStream.callback = [](pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -> bool { - ReadWriteStream* destination = (ReadWriteStream* )stream->state; - size_t written = destination->write((const uint8_t *)buf, count); - - return (written == count); + return pb_ostream_t{ + .callback = [](pb_ostream_t* stream, const pb_byte_t* buf, size_t count) -> bool { + auto destination = static_cast(stream->state); + size_t written = destination->write(reinterpret_cast(buf), count); + + return written == count; + }, + .state = stream, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = nullptr, }; - outputStream.state = (void*)this->stream; - outputStream.max_size = SIZE_MAX; - outputStream.bytes_written = 0; - outputStream.errmsg = nullptr; - - return outputStream; } + private: - HashMap responseCallbacks; - ReadWriteStream* stream = nullptr; + HashMap responseCallbacks; + ReadWriteStream* stream{nullptr}; }; diff --git a/Sming/Components/Hosted/src/HostedCommon.h b/Sming/Components/Hosted/src/HostedCommon.h index b4ebd42d71..411b3b1268 100644 --- a/Sming/Components/Hosted/src/HostedCommon.h +++ b/Sming/Components/Hosted/src/HostedCommon.h @@ -6,27 +6,27 @@ #include "hosted.pb.h" #include "HostedUtils.h" -constexpr int HOSTED_OK = 0; -constexpr int HOSTED_FAIL = -1; -constexpr int HOSTED_NO_MEM = -2; +constexpr int HOSTED_OK{0}; +constexpr int HOSTED_FAIL{-1}; +constexpr int HOSTED_NO_MEM{-2}; -typedef Delegate HostedCommandDelegate; +typedef Delegate HostedCommandDelegate; // Creates new hosted command -#define NEW_HD_COMMAND(NAME, XX, YY) \ - HostedCommand NAME = HostedCommand_init_zero; \ - NAME.which_payload = HostedCommand_request ## XX ##_tag; \ - { \ - auto command = &NAME.payload.request ## XX; \ - YY \ +#define NEW_HD_COMMAND(NAME, XX, YY) \ + HostedCommand NAME = HostedCommand_init_zero; \ + NAME.which_payload = HostedCommand_request##XX##_tag; \ + { \ + auto command = &NAME.payload.request##XX; \ + YY \ } class HostedCommon { public: - virtual bool onData(const char* at, size_t length) = 0; virtual ~HostedCommon() { } -private: + + virtual bool onData(const char* at, size_t length) = 0; }; diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src/HostedServer.h index 743ed8cac3..7dadf8aa1f 100644 --- a/Sming/Components/Hosted/src/HostedServer.h +++ b/Sming/Components/Hosted/src/HostedServer.h @@ -11,15 +11,14 @@ #include #include -typedef Delegate HostedTransferDelegate; +typedef Delegate HostedTransferDelegate; class HostedServer { public: - HostedServer(size_t storageSize = 1024): inputBuffer(new CircularBuffer(storageSize)) - { - - } + HostedServer(size_t storageSize = 1024) : inputBuffer(new CircularBuffer(storageSize)), outputBuffer(1024) + { + } void registerCommand(uint32_t type, HostedCommandDelegate callback) { @@ -30,14 +29,14 @@ class HostedServer * @brief Process incoming commands * @return number of processed bytes */ - int process(const uint8_t *at, size_t length) + int process(const uint8_t* at, size_t length) { HostedCommand request = HostedCommand_init_zero; HostedCommand response = HostedCommand_init_zero; request.payload.requestSpiTransfer.data.funcs.decode = &pbDecodeData; - if (at == nullptr || length < 0) { + if(at == nullptr || length < 0) { debug_e("empty buffer"); return HOSTED_FAIL; } @@ -54,9 +53,9 @@ class HostedServer size_t leftBytes = input.bytes_left; do { success = pb_decode_ex(&input, HostedCommand_fields, &request, PB_DECODE_DELIMITED); - if (!(success && request.which_payload)) { - Serial.printf("Decoding failed: %s\n", PB_GET_ERROR(&input)); - inputBuffer->seek(inputBuffer->available()- leftBytes); + if(!success || request.which_payload == 0) { + debug_e("Decoding failed: %s", PB_GET_ERROR(&input)); + inputBuffer->seek(inputBuffer->available() - leftBytes); break; } @@ -86,29 +85,28 @@ class HostedServer return result; } - bool send(HostedCommand *message) + bool send(HostedCommand* message) { pb_ostream_t ouput = newOutputStream(); bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); - if (!success) { + if(!success) { debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); - return false; } - return true; + return success; } bool transfer(HostedTransferDelegate callback) { uint8_t buf[512]; while(outputBuffer.available() > 0) { - int read = outputBuffer.readMemoryBlock((char *)buf, 512); + int read = outputBuffer.readMemoryBlock((char*)buf, sizeof(buf)); outputBuffer.seek(read); if(!callback(buf, read)) { return false; } - if(read < 1024) { + if(read != sizeof(buf)) { break; } } @@ -118,41 +116,38 @@ class HostedServer private: pb_istream_t newInputStream() { - pb_istream_t stream; - stream.callback = [](pb_istream_t *stream, pb_byte_t *buf, size_t count) -> bool { - CircularBuffer* source = (CircularBuffer* )stream->state; - size_t read = source->readMemoryBlock((char *)buf, count); - source->seek(read); - - return true; - }; - stream.state = (void*)inputBuffer; - stream.bytes_left = inputBuffer->available(); - stream.errmsg = nullptr; - - return stream; + return pb_istream_t{ + .callback = [](pb_istream_t* stream, pb_byte_t* buf, size_t count) -> bool { + CircularBuffer* source = (CircularBuffer*)stream->state; + size_t read = source->readMemoryBlock((char*)buf, count); + source->seek(read); + + return true; + }, + .state = inputBuffer, + .bytes_left = inputBuffer->available(), + .errmsg = nullptr, + }; } pb_ostream_t newOutputStream() { - pb_ostream_t outputStream; - outputStream.callback = [](pb_ostream_t *stream, const pb_byte_t *buf, size_t count) -> bool { - CircularBuffer* destination = (CircularBuffer* )stream->state; - size_t written = destination->write((const uint8_t *)buf, count); - - return (written == count); + return pb_ostream_t{ + .callback = [](pb_ostream_t* stream, const pb_byte_t* buf, size_t count) -> bool { + CircularBuffer* destination = (CircularBuffer*)stream->state; + size_t written = destination->write((const uint8_t*)buf, count); + + return (written == count); + }, + .state = &outputBuffer, + .max_size = SIZE_MAX, + .bytes_written = 0, + .errmsg = nullptr, }; - outputStream.state = (void*)&this->outputBuffer; - outputStream.max_size = SIZE_MAX; - outputStream.bytes_written = 0; - outputStream.errmsg = nullptr; - - return outputStream; } - private: - CircularBuffer* inputBuffer = nullptr; - CircularBuffer outputBuffer = CircularBuffer(1024); + CircularBuffer* inputBuffer{nullptr}; + CircularBuffer outputBuffer; HashMap commands; }; diff --git a/Sming/Components/Hosted/src/HostedUtils.cpp b/Sming/Components/Hosted/src/HostedUtils.cpp index 2279e2c204..a7b5942c5c 100644 --- a/Sming/Components/Hosted/src/HostedUtils.cpp +++ b/Sming/Components/Hosted/src/HostedUtils.cpp @@ -3,40 +3,39 @@ // See: https://iam777.tistory.com/538 -bool pbEncodeData(pb_ostream_t *stream, const pb_field_t *field, void * const *arg) +bool pbEncodeData(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) { - PbData *data = (PbData*) *arg; - if(data == nullptr) { - return false; - } + auto data = static_cast(*arg); + if(data == nullptr) { + return false; + } - if (!pb_encode_tag_for_field(stream, field)) { - return false; - } + if(!pb_encode_tag_for_field(stream, field)) { + return false; + } - return pb_encode_string(stream, (uint8_t*)data->value, data->length); + return pb_encode_string(stream, (uint8_t*)data->value, data->length); } -bool pbDecodeData(pb_istream_t *stream, const pb_field_t *field, void **arg) +bool pbDecodeData(pb_istream_t* stream, const pb_field_t* field, void** arg) { - uint8_t buffer[1024] = {0}; - - /* We could read block-by-block to avoid the large buffer... */ - if (stream->bytes_left > sizeof(buffer) - 1) { - return false; - } - - size_t available = stream->bytes_left; - if (!pb_read(stream, buffer, stream->bytes_left)) { - return false; - } - - - MemoryDataStream* data = (MemoryDataStream*) *arg; - if(data == nullptr) { - data = new MemoryDataStream(); - *arg = (void*)data; - } - data->write(buffer, available); - return true; + uint8_t buffer[1024]{}; + + /* We could read block-by-block to avoid the large buffer... */ + if(stream->bytes_left >= sizeof(buffer)) { + return false; + } + + size_t available = stream->bytes_left; + if(!pb_read(stream, buffer, stream->bytes_left)) { + return false; + } + + auto data = static_cast(*arg); + if(data == nullptr) { + data = new MemoryDataStream(); + *arg = data; + } + data->write(buffer, available); + return true; } diff --git a/Sming/Components/Hosted/src/HostedUtils.h b/Sming/Components/Hosted/src/HostedUtils.h index e4436e35e2..4bf31513be 100644 --- a/Sming/Components/Hosted/src/HostedUtils.h +++ b/Sming/Components/Hosted/src/HostedUtils.h @@ -5,10 +5,10 @@ #include #include "hosted.pb.h" -typedef struct { +struct PbData { uint8_t* value; size_t length; -} PbData; +}; -bool pbEncodeData(pb_ostream_t *stream, const pb_field_t *field, void * const *arg); -bool pbDecodeData(pb_istream_t *stream, const pb_field_t *field, void **arg); +bool pbEncodeData(pb_ostream_t* stream, const pb_field_t* field, void* const* arg); +bool pbDecodeData(pb_istream_t* stream, const pb_field_t* field, void** arg); From 48abae7332a810761b42f046d48bed552d6b719a Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 18 Feb 2021 17:45:09 +0100 Subject: [PATCH 09/28] Removed Components/nanopb. Will be using Libraries/nanopb. --- .gitmodules | 4 ---- Sming/Components/nanopb/component.mk | 4 ---- Sming/Components/nanopb/nanopb | 1 - 3 files changed, 9 deletions(-) delete mode 100644 Sming/Components/nanopb/component.mk delete mode 160000 Sming/Components/nanopb/nanopb diff --git a/.gitmodules b/.gitmodules index ceb81cd04e..85003018ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -61,10 +61,6 @@ path = Sming/Components/mqtt-codec url = https://github.com/slaff/mqtt-codec.git ignore = dirty -[submodule "nanopb"] - path = Sming/Components/nanopb/nanopb - url = https://github.com/nanopb/nanopb.git - ignore = dirty [submodule "rboot"] path = Sming/Components/rboot/rboot url = https://github.com/mikee47/rboot diff --git a/Sming/Components/nanopb/component.mk b/Sming/Components/nanopb/component.mk deleted file mode 100644 index 9f5bf56b87..0000000000 --- a/Sming/Components/nanopb/component.mk +++ /dev/null @@ -1,4 +0,0 @@ -COMPONENT_SRCDIRS := nanopb -COMPONENT_INCDIRS := nanopb - -COMPONENT_SUBMODULES += nanopb \ No newline at end of file diff --git a/Sming/Components/nanopb/nanopb b/Sming/Components/nanopb/nanopb deleted file mode 160000 index b4bc585963..0000000000 --- a/Sming/Components/nanopb/nanopb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b4bc585963801338fcb8db8eb650c075f492da24 From 4093b3fac9ea31fa5c552d245fa8cf1dcaeaa430 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 18 Feb 2021 18:17:12 +0100 Subject: [PATCH 10/28] Trying to compile the code with the latest develop version. --- Sming/Arch/Host/Core/SPI.cpp | 14 ++++++++++++++ Sming/Arch/Host/Core/SPI.h | 14 ++++---------- Sming/Components/Hosted-Lib/src/SpiHosted.cpp | 14 ++++---------- .../Hosted-Transport/src/tcp/HostedTcpStream.h | 2 +- Sming/Components/Hosted/src/HostedClient.h | 2 +- 5 files changed, 24 insertions(+), 22 deletions(-) create mode 100644 Sming/Arch/Host/Core/SPI.cpp diff --git a/Sming/Arch/Host/Core/SPI.cpp b/Sming/Arch/Host/Core/SPI.cpp new file mode 100644 index 0000000000..22edc86773 --- /dev/null +++ b/Sming/Arch/Host/Core/SPI.cpp @@ -0,0 +1,14 @@ +#include "SPI.h" + +bool SPIClass::begin() +{ + return false; +} + +void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) +{ +} + +void SPIClass::prepare(SPISettings& settings) +{ +} diff --git a/Sming/Arch/Host/Core/SPI.h b/Sming/Arch/Host/Core/SPI.h index edcc04bd16..f1c6287d9c 100644 --- a/Sming/Arch/Host/Core/SPI.h +++ b/Sming/Arch/Host/Core/SPI.h @@ -30,24 +30,18 @@ class SPIClass : public SPIBase { public: - bool begin() override - { - return false; - } + bool begin() override; void end() override { } + using SPIBase::beginTransaction; using SPIBase::transfer; - void transfer(uint8_t* buffer, size_t numberBytes) override - { - } + void transfer(uint8_t* buffer, size_t numberBytes) override; protected: - void prepare(SPISettings& settings) override - { - } + void prepare(SPISettings& settings) override; }; /** @brief Global instance of SPI class */ diff --git a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp index 1c8f9838fc..5c37a40f2b 100644 --- a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp @@ -7,18 +7,18 @@ extern HostedClient* hostedClient; // define the static singleton SPIClass SPI; -void SPIClass::begin() +bool SPIClass::begin() { NEW_HD_COMMAND(message, SpiBeginTransaction, { command->has_settings = 0; }); - hostedClient->send(&message); + return hostedClient->send(&message); } -void SPIClass::beginTransaction(SPISettings mySettings) +void SPIClass::prepare(SPISettings& mySettings) { NEW_HD_COMMAND(message, SpiBeginTransaction, { command->has_settings = 1; - command->settings.speed = mySettings.speed; + // command->settings.speed = mySettings.speed; command->settings.byteOrder = SpiSettings_ByteOrder(mySettings.byteOrder); command->settings.dataMode = SpiSettings_DataMode(mySettings.dataMode); }); @@ -58,9 +58,3 @@ void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) resultData->readBytes(reinterpret_cast(buffer), numberBytes); delete resultData; } - -uint32_t SPIClass::transfer32(uint32_t val, uint8_t bits) -{ - // TODO: - return 0; -} diff --git a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h index 7098bc52e9..fd7f567d11 100644 --- a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h +++ b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h @@ -4,7 +4,7 @@ class HostedTcpStream : public ReadWriteStream { public: - HostedTcpStream(const String& host, uint16_t port) : host(host), port(port), buffer(1024) + HostedTcpStream(const String& host, uint16_t port) : buffer(1024), host(host), port(port) { auto onCompleted = [](TcpClient& client, bool successful) { // onCompleted; diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src/HostedClient.h index 194ca8f0de..ae48ad9adf 100644 --- a/Sming/Components/Hosted/src/HostedClient.h +++ b/Sming/Components/Hosted/src/HostedClient.h @@ -79,7 +79,7 @@ class HostedClient return true; }, .state = this->stream, - .bytes_left = this->stream->available(), + .bytes_left = (size_t)this->stream->available(), .errmsg = nullptr, }; } From 9658b9ea234b6c2938e28b0197e84e020e6897cc Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 8 Apr 2021 13:15:04 +0200 Subject: [PATCH 11/28] Initial code. Nothing to see yet. --- .gitmodules | 6 ++- Sming/Libraries/rpc/component.mk | 6 +++ Sming/Libraries/rpc/simpleRPC | 1 + Sming/Libraries/rpc/simpleRPC.patch | 65 +++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 Sming/Libraries/rpc/component.mk create mode 160000 Sming/Libraries/rpc/simpleRPC create mode 100644 Sming/Libraries/rpc/simpleRPC.patch diff --git a/.gitmodules b/.gitmodules index 85003018ee..0816d5ebef 100644 --- a/.gitmodules +++ b/.gitmodules @@ -194,7 +194,7 @@ [submodule "Libraries.flatbuffers"] path = Sming/Libraries/flatbuffers/src url = https://github.com/google/flatbuffers.git - ignore = dirty + ignore = dirty [submodule "Libraries.GoogleCast"] path = Sming/Libraries/GoogleCast url = https://github.com/slaff/Sming-GoogleCast.git @@ -259,6 +259,10 @@ path = Sming/Libraries/SignalGenerator url = https://github.com/mikee47/SignalGenerator ignore = dirty +[submodule "Libraries.simpleRPC"] + path = Sming/Libraries/rpc/simpleRPC + url = https://github.com/jfjlaros/simpleRPC.git + ignore = dirty [submodule "Libraries.SmingTest"] path = Sming/Libraries/SmingTest url = https://github.com/mikee47/SmingTest diff --git a/Sming/Libraries/rpc/component.mk b/Sming/Libraries/rpc/component.mk new file mode 100644 index 0000000000..15bcc78b81 --- /dev/null +++ b/Sming/Libraries/rpc/component.mk @@ -0,0 +1,6 @@ +COMPONENT_SUBMODULES += simpleRPC + +SIMPLE_RPC_ROOT := $(COMPONENT_PATH)/simpleRPC + +COMPONENT_SRCDIRS := $(SIMPLE_RPC_ROOT)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) diff --git a/Sming/Libraries/rpc/simpleRPC b/Sming/Libraries/rpc/simpleRPC new file mode 160000 index 0000000000..5696f34c42 --- /dev/null +++ b/Sming/Libraries/rpc/simpleRPC @@ -0,0 +1 @@ +Subproject commit 5696f34c429c4e87721ebdb191b21c2ea0f23791 diff --git a/Sming/Libraries/rpc/simpleRPC.patch b/Sming/Libraries/rpc/simpleRPC.patch new file mode 100644 index 0000000000..ba6edc8500 --- /dev/null +++ b/Sming/Libraries/rpc/simpleRPC.patch @@ -0,0 +1,65 @@ +diff --git a/src/read.tcc b/src/read.tcc +index 2e5810f..513d691 100644 +--- a/src/read.tcc ++++ b/src/read.tcc +@@ -3,7 +3,6 @@ + + #include "defs.h" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup read + +diff --git a/src/signature.tcc b/src/signature.tcc +index 00f5eb1..f3a675d 100644 +--- a/src/signature.tcc ++++ b/src/signature.tcc +@@ -25,7 +25,7 @@ void _parameterTypes(Stream& io, void (*f_)(H, Tail...)) { + * `rpcTypeOf()` to encode its type. The first parameter type `H` is removed + * from function pointer `*f_` in the recursive call. + */ +- H data; ++ H data{}; + + rpcPrint(io, ' '); + rpcTypeOf(io, data); +@@ -49,13 +49,13 @@ void _parameterTypes(Stream& io, void (*f_)(H&, Tail...)) { + */ + template + void signature(Stream& io, R (*f)(FArgs...)) { +- /* ++ /* + * A dummy function pointer is prepared, referred to as `f_` in the template + * functions above, which will be used to isolate parameter types. The return + * type of this function pointer is removed to avoid unneeded template + * expansion. + */ +- R data; ++ R data{}; + + rpcTypeOf(io, data); + rpcPrint(io, ':'); +diff --git a/src/types.tcc b/src/types.tcc +index 651fb38..8a7cfa5 100644 +--- a/src/types.tcc ++++ b/src/types.tcc +@@ -3,7 +3,6 @@ + + #include "print.tcc" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup types + +diff --git a/src/write.tcc b/src/write.tcc +index d51cdd1..abf1e7b 100644 +--- a/src/write.tcc ++++ b/src/write.tcc +@@ -3,7 +3,6 @@ + + #include "print.tcc" + #include "tuple.tcc" +-#include "vector.tcc" + + //! \defgroup write + From c17a553129b7a2195de104eeb557c874729ead11 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 8 Apr 2021 16:06:41 +0200 Subject: [PATCH 12/28] Untested changes. --- .../Components/Hosted/app/app/application.cpp | 62 --- Sming/Components/Hosted/app/component.mk | 10 - .../Hosted/app/src/DigitalHosted.cpp | 35 -- .../Components/Hosted/app/src/DigitalHosted.h | 13 - Sming/Components/Hosted/app/src/SpiHosted.cpp | 49 -- Sming/Components/Hosted/app/src/SpiHosted.h | 13 - Sming/Components/Hosted/app/test/data.pb | 1 - Sming/Components/Hosted/component.mk | 6 +- .../include/Hosted/Transport/SerialStream.h | 25 + .../Hosted/include/Hosted/Transport/TStream.h | 29 ++ .../Hosted/Transport/TcpClientStream.h | 38 ++ .../Hosted/Transport/TcpServerStream.h | 46 ++ .../include/Hosted/Transport/TcpStream.h | 78 ++++ Sming/Components/Hosted/proto/hosted.pb.c | 35 -- Sming/Components/Hosted/proto/hosted.pb.h | 436 ------------------ Sming/Components/Hosted/proto/hosted.proto | 102 ---- .../Hosted/{app => samples/serial}/Makefile | 0 .../Hosted/{app => samples/serial}/README.rst | 0 .../Hosted/samples/serial/app/application.cpp | 25 + .../Hosted/samples/serial/component.mk | 9 + Sming/Components/Hosted/samples/tcp/Makefile | 9 + .../Components/Hosted/samples/tcp/README.rst | 21 + .../Hosted/samples/tcp/app/application.cpp | 53 +++ .../Hosted/samples/tcp/component.mk | 3 + .../{src => src.untested}/HostedClient.h | 0 .../{src => src.untested}/HostedCommon.h | 0 .../{src => src.untested}/HostedServer.h | 0 .../{src => src.untested}/HostedUtils.cpp | 0 .../{src => src.untested}/HostedUtils.h | 0 Sming/Core/Network/TcpServer.h | 5 + 30 files changed, 344 insertions(+), 759 deletions(-) delete mode 100644 Sming/Components/Hosted/app/app/application.cpp delete mode 100644 Sming/Components/Hosted/app/component.mk delete mode 100644 Sming/Components/Hosted/app/src/DigitalHosted.cpp delete mode 100644 Sming/Components/Hosted/app/src/DigitalHosted.h delete mode 100644 Sming/Components/Hosted/app/src/SpiHosted.cpp delete mode 100644 Sming/Components/Hosted/app/src/SpiHosted.h delete mode 100644 Sming/Components/Hosted/app/test/data.pb create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h delete mode 100644 Sming/Components/Hosted/proto/hosted.pb.c delete mode 100644 Sming/Components/Hosted/proto/hosted.pb.h delete mode 100644 Sming/Components/Hosted/proto/hosted.proto rename Sming/Components/Hosted/{app => samples/serial}/Makefile (100%) rename Sming/Components/Hosted/{app => samples/serial}/README.rst (100%) create mode 100644 Sming/Components/Hosted/samples/serial/app/application.cpp create mode 100644 Sming/Components/Hosted/samples/serial/component.mk create mode 100644 Sming/Components/Hosted/samples/tcp/Makefile create mode 100644 Sming/Components/Hosted/samples/tcp/README.rst create mode 100644 Sming/Components/Hosted/samples/tcp/app/application.cpp create mode 100644 Sming/Components/Hosted/samples/tcp/component.mk rename Sming/Components/Hosted/{src => src.untested}/HostedClient.h (100%) rename Sming/Components/Hosted/{src => src.untested}/HostedCommon.h (100%) rename Sming/Components/Hosted/{src => src.untested}/HostedServer.h (100%) rename Sming/Components/Hosted/{src => src.untested}/HostedUtils.cpp (100%) rename Sming/Components/Hosted/{src => src.untested}/HostedUtils.h (100%) diff --git a/Sming/Components/Hosted/app/app/application.cpp b/Sming/Components/Hosted/app/app/application.cpp deleted file mode 100644 index 666b97ab06..0000000000 --- a/Sming/Components/Hosted/app/app/application.cpp +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include - -// If you want, you can define WiFi settings globally in Eclipse Environment Variables -#ifndef WIFI_SSID -#define WIFI_SSID "PleaseEnterSSID" // Put you SSID and Password here -#define WIFI_PWD "PleaseEnterPass" -#endif - -HostedServer hostedServer; -TcpServer* tcpServer; - -namespace Hosted -{ -namespace Digital -{ -void registerCommands(HostedServer& server); -} -namespace Spi -{ -void registerCommands(HostedServer& server); -} -} // namespace Hosted - -// Will be called when WiFi station was connected to AP -void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) -{ - // Start the TcpServer - if(tcpServer != nullptr) { - return; - } - - tcpServer = new TcpServer([](TcpClient& client, char* data, int size) -> bool { - // clientReceiveDataHandler - int result = hostedServer.process(reinterpret_cast(data), size); - if(result != HOSTED_OK) { - return result == HOSTED_NO_MEM ? false : true; - } - - hostedServer.transfer([&client](const uint8_t* data, size_t size) -> bool { - return client.send(reinterpret_cast(data), size); - }); - - return true; - }); - - tcpServer->listen(4031); - tcpServer->setTimeOut(USHRT_MAX); // disable connection timeout -} - -void init() -{ - Hosted::Digital::registerCommands(hostedServer); - Hosted::Spi::registerCommands(hostedServer); - - // Connect to same AP as the client application - WifiStation.enable(true); - WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); - - // Set callback that should be triggered when we have assigned IP - WifiEvents.onStationGotIP(connectOk); -} diff --git a/Sming/Components/Hosted/app/component.mk b/Sming/Components/Hosted/app/component.mk deleted file mode 100644 index b4ead4dbba..0000000000 --- a/Sming/Components/Hosted/app/component.mk +++ /dev/null @@ -1,10 +0,0 @@ -## List the names of any additional Components required for this project -COMPONENT_DEPENDS := Hosted -ENABLE_HOSTED := - -## SPIFFS options -DISABLE_SPIFFS := 1 -# SPIFF_FILES = files - -COMPONENT_APPCODE := src -COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.cpp b/Sming/Components/Hosted/app/src/DigitalHosted.cpp deleted file mode 100644 index 7afc4954f0..0000000000 --- a/Sming/Components/Hosted/app/src/DigitalHosted.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "DigitalHosted.h" - -namespace Hosted -{ -namespace Digital -{ -void registerCommands(HostedServer& server) -{ - // Register Command Handlers - server.registerCommand(HostedCommand_requestPinMode_tag, - [](HostedCommand* request, HostedCommand* response) -> int { - auto& r = request->payload.requestPinMode; - pinMode(r.pin, r.mode); - return 0; - }); - - server.registerCommand(HostedCommand_requestDigitalWrite_tag, - [](HostedCommand* request, HostedCommand* response) -> int { - auto& r = request->payload.requestDigitalWrite; - digitalWrite(r.pin, r.value); - return 0; - }); - - server.registerCommand(HostedCommand_requestDigitalRead_tag, - [](HostedCommand* request, HostedCommand* response) -> int { - auto& r = request->payload.requestDigitalRead; - uint8_t result = digitalRead(r.pin); - response->which_payload = HostedCommand_responseDigitalRead_tag; - response->payload.responseDigitalRead.value = result; - - return 0; - }); -} -} // namespace Digital -} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/DigitalHosted.h b/Sming/Components/Hosted/app/src/DigitalHosted.h deleted file mode 100644 index a618ad339b..0000000000 --- a/Sming/Components/Hosted/app/src/DigitalHosted.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Hosted -{ -namespace Digital -{ -void registerCommands(HostedServer& server); -} -} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/SpiHosted.cpp b/Sming/Components/Hosted/app/src/SpiHosted.cpp deleted file mode 100644 index 71e829999b..0000000000 --- a/Sming/Components/Hosted/app/src/SpiHosted.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "SpiHosted.h" -#include - -namespace Hosted -{ -namespace Spi -{ -void registerCommands(HostedServer& server) -{ - // Register Command Handlers - server.registerCommand(HostedCommand_requestSpiBeginTransaction_tag, - [](HostedCommand* request, HostedCommand* response) -> int { - auto& r = request->payload.requestSpiBeginTransaction; - if(r.has_settings) { - SPI.begin(); - } else { - auto& rs = r.settings; - SPISettings settings(rs.speed, rs.byteOrder, rs.dataMode); - SPI.beginTransaction(settings); - } - - return 0; - }); - - server.registerCommand(HostedCommand_requestSpiTransfer_tag, - [](HostedCommand* request, HostedCommand* response) -> int { - auto responseData = new PbData{}; - - auto data = static_cast(request->payload.requestSpiTransfer.data.arg); - if(data != nullptr) { - size_t available = data->available(); - auto buffer = new uint8_t[available]; - size_t length = data->readBytes(reinterpret_cast(&buffer), available); - SPI.transfer(buffer, length); - responseData->value = buffer; - responseData->length = length; - - delete data; - } - - auto& data = response->payload.responseSpiTransfer.data; - data.funcs.encode = &pbEncodeData; - data.arg = responseData; - - return 0; - }); -} -} // namespace Spi -} // namespace Hosted diff --git a/Sming/Components/Hosted/app/src/SpiHosted.h b/Sming/Components/Hosted/app/src/SpiHosted.h deleted file mode 100644 index 1d7f1f84be..0000000000 --- a/Sming/Components/Hosted/app/src/SpiHosted.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Hosted -{ -namespace Spi -{ -void registerCommands(HostedServer& server); -} -} // namespace Hosted diff --git a/Sming/Components/Hosted/app/test/data.pb b/Sming/Components/Hosted/app/test/data.pb deleted file mode 100644 index 092a9bac0b..0000000000 --- a/Sming/Components/Hosted/app/test/data.pb +++ /dev/null @@ -1 +0,0 @@ - RÒR \ No newline at end of file diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index b125bed6c6..d7962c49cf 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -1,6 +1,6 @@ -COMPONENT_SRCDIRS := src proto -COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) -COMPONENT_DEPENDS := nanopb +COMPONENT_SRCDIRS := $(COMPONENT_PATH)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include +COMPONENT_DEPENDS := rpc # Architecture of the device where the hosted service will be flashed HOSTED_ARCH ?= Esp8266 diff --git a/Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h b/Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h new file mode 100644 index 0000000000..d72d4c2319 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h @@ -0,0 +1,25 @@ +#include +#include "TStream.h" + +namespace Hosted +{ +namespace Transport +{ +class SerialStream : public TStream +{ +public: + SerialStream(HardwareSerial& stream) + { + stream.onDataReceived(StreamDataReceivedDelegate(&SerialStream::process, this)); + } + +private: + void process(Stream& source, char arrivedChar, uint16_t availableCharsCount) + { + handler(source); + } +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TStream.h new file mode 100644 index 0000000000..fb80a00872 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TStream.h @@ -0,0 +1,29 @@ +#pragma once +#include +#include + +namespace Hosted +{ +namespace Transport +{ +class TStream +{ +public: + using DataHandler = Delegate; + + void onData(DataHandler handler) + { + this->handler = handler; + } + + virtual ~TStream() + { + } + +protected: + DataHandler handler; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h new file mode 100644 index 0000000000..a80ec9b095 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -0,0 +1,38 @@ +#include +#include "TcpStream.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpClientStream : public TcpStream +{ +public: + TcpClientStream(TcpClient& client) + { + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::process, this)); + stream = new CompoundTcpClientStream(client); + } + + ~TcpClientStream() + { + delete stream; + } + +protected: + bool process(TcpClient& client, char* data, int size) override + { + if(!stream.push(data, size)) { + return false; + } + + return handler(*stream); + } + +private: + CompoundTcpClientStream* stream = nullptr; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h new file mode 100644 index 0000000000..d3a471382f --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h @@ -0,0 +1,46 @@ +#include +#include +#include "TcpStream.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpServerStream : public TcpStream +{ +public: + using ClientMap = ObjectMap; + + TcpServerStream(TcpServer& server) + { + server.setClientReceiveHandler(TcpClientDataDelegate(&TcpServerStream::process, this)); + } + +protected: + bool process(TcpClient& client, char* data, int size) override + { + uint32_t key = uint32_t(&client); + + CompoundTcpClientStream* stream = nullptr; + + int i = map.indexOf(key); + if(i >= 0) { + stream = map.valueAt(i); + } else { + map[key] = new CompoundTcpClientStream(client); + } + + if(!stream->push(reinterpret_cast(data), size)) { + return false; + } + + return handler(*stream); + } + +private: + ClientMap map; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h new file mode 100644 index 0000000000..5f4a04aaa7 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h @@ -0,0 +1,78 @@ +#include +#include +#include "TStream.h" + +namespace Hosted +{ +namespace Transport +{ +class CompoundTcpClientStream : public Stream +{ +public: + CompoundTcpClientStream(TcpClient& client) : client(client), cBuffer(1024) + { + } + + void setClient(TcpClient& client) + { + this->client = client; + } + + bool push(const uint8_t* buffer, size_t size) + { + int written = cBuffer.write(buffer, size); + return (written == size); + } + + size_t readBytes(char* buffer, size_t length) override + { + return cBuffer.readBytes(buffer, length); + } + + size_t write(const uint8_t* buffer, size_t size) override + { + if(client.send(reinterpret_cast(buffer), size)) { + return size; + } + + return 0; + } + + size_t write(uint8_t c) override + { + return cBuffer.write(c); + } + + int available() override + { + return cBuffer.available(); + } + + int peek() override + { + return cBuffer.peek(); + } + + int read() override + { + return cBuffer.read(); + } + + void flush() override + { + } + +private: + CircularBuffer cBuffer; + TcpClient& client; +}; + +class TcpStream : public TStream +{ +protected: + virtual bool process(TcpClient& client, char* data, int size) = 0; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/proto/hosted.pb.c b/Sming/Components/Hosted/proto/hosted.pb.c deleted file mode 100644 index c288740dbe..0000000000 --- a/Sming/Components/Hosted/proto/hosted.pb.c +++ /dev/null @@ -1,35 +0,0 @@ -/* Automatically generated nanopb constant definitions */ -/* Generated by nanopb-0.4.3-dev */ - -#include "hosted.pb.h" -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -PB_BIND(RequestDigitalWrite, RequestDigitalWrite, AUTO) - -PB_BIND(RequestPinMode, RequestPinMode, AUTO) - -PB_BIND(RequestDigitalRead, RequestDigitalRead, AUTO) - -PB_BIND(ResponseDigitalRead, ResponseDigitalRead, AUTO) - -PB_BIND(SpiSettings, SpiSettings, AUTO) - -PB_BIND(RequestSpiBeginTransaction, RequestSpiBeginTransaction, AUTO) - -PB_BIND(RequestSpiEndTransaction, RequestSpiEndTransaction, AUTO) - -PB_BIND(RequestSpiTransfer, RequestSpiTransfer, AUTO) - -PB_BIND(ResponseSpiTransfer, ResponseSpiTransfer, AUTO) - -PB_BIND(RequestSpiWrite, RequestSpiWrite, AUTO) - -PB_BIND(ResponseSpiWrite, ResponseSpiWrite, AUTO) - -PB_BIND(RequestSpiRead, RequestSpiRead, AUTO) - -PB_BIND(ResponseSpiRead, ResponseSpiRead, AUTO) - -PB_BIND(HostedCommand, HostedCommand, AUTO) diff --git a/Sming/Components/Hosted/proto/hosted.pb.h b/Sming/Components/Hosted/proto/hosted.pb.h deleted file mode 100644 index 7b08f66f49..0000000000 --- a/Sming/Components/Hosted/proto/hosted.pb.h +++ /dev/null @@ -1,436 +0,0 @@ -/* Automatically generated nanopb header */ -/* Generated by nanopb-0.4.3-dev */ - -#ifndef PB_HOSTED_PB_H_INCLUDED -#define PB_HOSTED_PB_H_INCLUDED -#include - -#if PB_PROTO_HEADER_VERSION != 40 -#error Regenerate this file with the current version of nanopb generator. -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -/* Enum definitions */ -typedef enum _PinMode { PinMode_INPUT = 0, PinMode_OUTPUT = 1, PinMode_INPUT_PULLUP = 2 } PinMode; - -typedef enum _SpiSettings_ByteOrder { - SpiSettings_ByteOrder_MSBFIRST = 0, - SpiSettings_ByteOrder_LSBFIRST = 1 -} SpiSettings_ByteOrder; - -typedef enum _SpiSettings_DataMode { - SpiSettings_DataMode_SPI_MODE0 = 0, - SpiSettings_DataMode_SPI_MODE1 = 1, - SpiSettings_DataMode_SPI_MODE2 = 2, - SpiSettings_DataMode_SPI_MODE3 = 3 -} SpiSettings_DataMode; - -typedef enum _HostedCommand_Version { HostedCommand_Version_HOSTED_V_1_0 = 0 } HostedCommand_Version; - -/* Struct definitions */ -typedef struct _RequestSpiEndTransaction { - char dummy_field; -} RequestSpiEndTransaction; - -typedef struct _RequestSpiTransfer { - pb_callback_t data; -} RequestSpiTransfer; - -typedef struct _RequestSpiWrite { - pb_callback_t data; -} RequestSpiWrite; - -typedef struct _ResponseSpiRead { - pb_callback_t data; -} ResponseSpiRead; - -typedef struct _ResponseSpiTransfer { - pb_callback_t data; -} ResponseSpiTransfer; - -typedef struct _RequestDigitalRead { - uint32_t pin; -} RequestDigitalRead; - -typedef struct _RequestDigitalWrite { - uint32_t pin; - uint32_t value; -} RequestDigitalWrite; - -typedef struct _RequestPinMode { - uint32_t pin; - PinMode mode; -} RequestPinMode; - -typedef struct _RequestSpiRead { - uint32_t length; -} RequestSpiRead; - -typedef struct _ResponseDigitalRead { - uint32_t value; -} ResponseDigitalRead; - -typedef struct _ResponseSpiWrite { - uint32_t length; -} ResponseSpiWrite; - -typedef struct _SpiSettings { - uint32_t speed; - SpiSettings_ByteOrder byteOrder; - SpiSettings_DataMode dataMode; -} SpiSettings; - -typedef struct _RequestSpiBeginTransaction { - bool has_settings; - SpiSettings settings; -} RequestSpiBeginTransaction; - -typedef struct _HostedCommand { - HostedCommand_Version version; - pb_size_t which_payload; - union { - RequestDigitalWrite requestDigitalWrite; - RequestPinMode requestPinMode; - RequestDigitalRead requestDigitalRead; - ResponseDigitalRead responseDigitalRead; - RequestSpiBeginTransaction requestSpiBeginTransaction; - RequestSpiEndTransaction requestSpiEndTransaction; - RequestSpiTransfer requestSpiTransfer; - ResponseSpiTransfer responseSpiTransfer; - RequestSpiWrite requestSpiWrite; - ResponseSpiWrite responseSpiWrite; - RequestSpiRead requestSpiRead; - ResponseSpiRead responseSpiRead; - } payload; -} HostedCommand; - -/* Helper constants for enums */ -#define _PinMode_MIN PinMode_INPUT -#define _PinMode_MAX PinMode_INPUT_PULLUP -#define _PinMode_ARRAYSIZE ((PinMode)(PinMode_INPUT_PULLUP + 1)) - -#define _SpiSettings_ByteOrder_MIN SpiSettings_ByteOrder_MSBFIRST -#define _SpiSettings_ByteOrder_MAX SpiSettings_ByteOrder_LSBFIRST -#define _SpiSettings_ByteOrder_ARRAYSIZE ((SpiSettings_ByteOrder)(SpiSettings_ByteOrder_LSBFIRST + 1)) - -#define _SpiSettings_DataMode_MIN SpiSettings_DataMode_SPI_MODE0 -#define _SpiSettings_DataMode_MAX SpiSettings_DataMode_SPI_MODE3 -#define _SpiSettings_DataMode_ARRAYSIZE ((SpiSettings_DataMode)(SpiSettings_DataMode_SPI_MODE3 + 1)) - -#define _HostedCommand_Version_MIN HostedCommand_Version_HOSTED_V_1_0 -#define _HostedCommand_Version_MAX HostedCommand_Version_HOSTED_V_1_0 -#define _HostedCommand_Version_ARRAYSIZE ((HostedCommand_Version)(HostedCommand_Version_HOSTED_V_1_0 + 1)) - -/* Initializer values for message structs */ -#define RequestDigitalWrite_init_default \ - { \ - 0, 0 \ - } -#define RequestPinMode_init_default \ - { \ - 0, _PinMode_MIN \ - } -#define RequestDigitalRead_init_default \ - { \ - 0 \ - } -#define ResponseDigitalRead_init_default \ - { \ - 0 \ - } -#define SpiSettings_init_default \ - { \ - 0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN \ - } -#define RequestSpiBeginTransaction_init_default \ - { \ - false, SpiSettings_init_default \ - } -#define RequestSpiEndTransaction_init_default \ - { \ - 0 \ - } -#define RequestSpiTransfer_init_default \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define ResponseSpiTransfer_init_default \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define RequestSpiWrite_init_default \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define ResponseSpiWrite_init_default \ - { \ - 0 \ - } -#define RequestSpiRead_init_default \ - { \ - 0 \ - } -#define ResponseSpiRead_init_default \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define HostedCommand_init_default \ - { \ - _HostedCommand_Version_MIN, 0, \ - { \ - RequestDigitalWrite_init_default \ - } \ - } -#define RequestDigitalWrite_init_zero \ - { \ - 0, 0 \ - } -#define RequestPinMode_init_zero \ - { \ - 0, _PinMode_MIN \ - } -#define RequestDigitalRead_init_zero \ - { \ - 0 \ - } -#define ResponseDigitalRead_init_zero \ - { \ - 0 \ - } -#define SpiSettings_init_zero \ - { \ - 0, _SpiSettings_ByteOrder_MIN, _SpiSettings_DataMode_MIN \ - } -#define RequestSpiBeginTransaction_init_zero \ - { \ - false, SpiSettings_init_zero \ - } -#define RequestSpiEndTransaction_init_zero \ - { \ - 0 \ - } -#define RequestSpiTransfer_init_zero \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define ResponseSpiTransfer_init_zero \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define RequestSpiWrite_init_zero \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define ResponseSpiWrite_init_zero \ - { \ - 0 \ - } -#define RequestSpiRead_init_zero \ - { \ - 0 \ - } -#define ResponseSpiRead_init_zero \ - { \ - { \ - {NULL}, NULL \ - } \ - } -#define HostedCommand_init_zero \ - { \ - _HostedCommand_Version_MIN, 0, \ - { \ - RequestDigitalWrite_init_zero \ - } \ - } - -/* Field tags (for use in manual encoding/decoding) */ -#define RequestSpiTransfer_data_tag 1 -#define RequestSpiWrite_data_tag 1 -#define ResponseSpiRead_data_tag 1 -#define ResponseSpiTransfer_data_tag 1 -#define RequestDigitalRead_pin_tag 1 -#define RequestDigitalWrite_pin_tag 1 -#define RequestDigitalWrite_value_tag 2 -#define RequestPinMode_pin_tag 1 -#define RequestPinMode_mode_tag 2 -#define RequestSpiRead_length_tag 1 -#define ResponseDigitalRead_value_tag 1 -#define ResponseSpiWrite_length_tag 1 -#define SpiSettings_speed_tag 1 -#define SpiSettings_byteOrder_tag 2 -#define SpiSettings_dataMode_tag 3 -#define RequestSpiBeginTransaction_settings_tag 1 -#define HostedCommand_version_tag 1 -#define HostedCommand_requestDigitalWrite_tag 10 -#define HostedCommand_requestPinMode_tag 11 -#define HostedCommand_requestDigitalRead_tag 12 -#define HostedCommand_responseDigitalRead_tag 13 -#define HostedCommand_requestSpiBeginTransaction_tag 14 -#define HostedCommand_requestSpiEndTransaction_tag 15 -#define HostedCommand_requestSpiTransfer_tag 16 -#define HostedCommand_responseSpiTransfer_tag 17 -#define HostedCommand_requestSpiWrite_tag 18 -#define HostedCommand_responseSpiWrite_tag 19 -#define HostedCommand_requestSpiRead_tag 20 -#define HostedCommand_responseSpiRead_tag 21 - -/* Struct field encoding specification for nanopb */ -#define RequestDigitalWrite_FIELDLIST(X, a) \ - X(a, STATIC, SINGULAR, UINT32, pin, 1) \ - X(a, STATIC, SINGULAR, UINT32, value, 2) -#define RequestDigitalWrite_CALLBACK NULL -#define RequestDigitalWrite_DEFAULT NULL - -#define RequestPinMode_FIELDLIST(X, a) \ - X(a, STATIC, SINGULAR, UINT32, pin, 1) \ - X(a, STATIC, SINGULAR, UENUM, mode, 2) -#define RequestPinMode_CALLBACK NULL -#define RequestPinMode_DEFAULT NULL - -#define RequestDigitalRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, pin, 1) -#define RequestDigitalRead_CALLBACK NULL -#define RequestDigitalRead_DEFAULT NULL - -#define ResponseDigitalRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, value, 1) -#define ResponseDigitalRead_CALLBACK NULL -#define ResponseDigitalRead_DEFAULT NULL - -#define SpiSettings_FIELDLIST(X, a) \ - X(a, STATIC, SINGULAR, UINT32, speed, 1) \ - X(a, STATIC, SINGULAR, UENUM, byteOrder, 2) \ - X(a, STATIC, SINGULAR, UENUM, dataMode, 3) -#define SpiSettings_CALLBACK NULL -#define SpiSettings_DEFAULT NULL - -#define RequestSpiBeginTransaction_FIELDLIST(X, a) X(a, STATIC, OPTIONAL, MESSAGE, settings, 1) -#define RequestSpiBeginTransaction_CALLBACK NULL -#define RequestSpiBeginTransaction_DEFAULT NULL -#define RequestSpiBeginTransaction_settings_MSGTYPE SpiSettings - -#define RequestSpiEndTransaction_FIELDLIST(X, a) - -#define RequestSpiEndTransaction_CALLBACK NULL -#define RequestSpiEndTransaction_DEFAULT NULL - -#define RequestSpiTransfer_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) -#define RequestSpiTransfer_CALLBACK pb_default_field_callback -#define RequestSpiTransfer_DEFAULT NULL - -#define ResponseSpiTransfer_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) -#define ResponseSpiTransfer_CALLBACK pb_default_field_callback -#define ResponseSpiTransfer_DEFAULT NULL - -#define RequestSpiWrite_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) -#define RequestSpiWrite_CALLBACK pb_default_field_callback -#define RequestSpiWrite_DEFAULT NULL - -#define ResponseSpiWrite_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, length, 1) -#define ResponseSpiWrite_CALLBACK NULL -#define ResponseSpiWrite_DEFAULT NULL - -#define RequestSpiRead_FIELDLIST(X, a) X(a, STATIC, SINGULAR, UINT32, length, 1) -#define RequestSpiRead_CALLBACK NULL -#define RequestSpiRead_DEFAULT NULL - -#define ResponseSpiRead_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1) -#define ResponseSpiRead_CALLBACK pb_default_field_callback -#define ResponseSpiRead_DEFAULT NULL - -#define HostedCommand_FIELDLIST(X, a) \ - X(a, STATIC, SINGULAR, UENUM, version, 1) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestDigitalWrite, payload.requestDigitalWrite), 10) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestPinMode, payload.requestPinMode), 11) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestDigitalRead, payload.requestDigitalRead), 12) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, responseDigitalRead, payload.responseDigitalRead), 13) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiBeginTransaction, payload.requestSpiBeginTransaction), 14) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiEndTransaction, payload.requestSpiEndTransaction), 15) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiTransfer, payload.requestSpiTransfer), 16) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiTransfer, payload.responseSpiTransfer), 17) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiWrite, payload.requestSpiWrite), 18) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiWrite, payload.responseSpiWrite), 19) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, requestSpiRead, payload.requestSpiRead), 20) \ - X(a, STATIC, ONEOF, MESSAGE, (payload, responseSpiRead, payload.responseSpiRead), 21) -#define HostedCommand_CALLBACK NULL -#define HostedCommand_DEFAULT NULL -#define HostedCommand_payload_requestDigitalWrite_MSGTYPE RequestDigitalWrite -#define HostedCommand_payload_requestPinMode_MSGTYPE RequestPinMode -#define HostedCommand_payload_requestDigitalRead_MSGTYPE RequestDigitalRead -#define HostedCommand_payload_responseDigitalRead_MSGTYPE ResponseDigitalRead -#define HostedCommand_payload_requestSpiBeginTransaction_MSGTYPE RequestSpiBeginTransaction -#define HostedCommand_payload_requestSpiEndTransaction_MSGTYPE RequestSpiEndTransaction -#define HostedCommand_payload_requestSpiTransfer_MSGTYPE RequestSpiTransfer -#define HostedCommand_payload_responseSpiTransfer_MSGTYPE ResponseSpiTransfer -#define HostedCommand_payload_requestSpiWrite_MSGTYPE RequestSpiWrite -#define HostedCommand_payload_responseSpiWrite_MSGTYPE ResponseSpiWrite -#define HostedCommand_payload_requestSpiRead_MSGTYPE RequestSpiRead -#define HostedCommand_payload_responseSpiRead_MSGTYPE ResponseSpiRead - -extern const pb_msgdesc_t RequestDigitalWrite_msg; -extern const pb_msgdesc_t RequestPinMode_msg; -extern const pb_msgdesc_t RequestDigitalRead_msg; -extern const pb_msgdesc_t ResponseDigitalRead_msg; -extern const pb_msgdesc_t SpiSettings_msg; -extern const pb_msgdesc_t RequestSpiBeginTransaction_msg; -extern const pb_msgdesc_t RequestSpiEndTransaction_msg; -extern const pb_msgdesc_t RequestSpiTransfer_msg; -extern const pb_msgdesc_t ResponseSpiTransfer_msg; -extern const pb_msgdesc_t RequestSpiWrite_msg; -extern const pb_msgdesc_t ResponseSpiWrite_msg; -extern const pb_msgdesc_t RequestSpiRead_msg; -extern const pb_msgdesc_t ResponseSpiRead_msg; -extern const pb_msgdesc_t HostedCommand_msg; - -/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ -#define RequestDigitalWrite_fields &RequestDigitalWrite_msg -#define RequestPinMode_fields &RequestPinMode_msg -#define RequestDigitalRead_fields &RequestDigitalRead_msg -#define ResponseDigitalRead_fields &ResponseDigitalRead_msg -#define SpiSettings_fields &SpiSettings_msg -#define RequestSpiBeginTransaction_fields &RequestSpiBeginTransaction_msg -#define RequestSpiEndTransaction_fields &RequestSpiEndTransaction_msg -#define RequestSpiTransfer_fields &RequestSpiTransfer_msg -#define ResponseSpiTransfer_fields &ResponseSpiTransfer_msg -#define RequestSpiWrite_fields &RequestSpiWrite_msg -#define ResponseSpiWrite_fields &ResponseSpiWrite_msg -#define RequestSpiRead_fields &RequestSpiRead_msg -#define ResponseSpiRead_fields &ResponseSpiRead_msg -#define HostedCommand_fields &HostedCommand_msg - -/* Maximum encoded size of messages (where known) */ -#define RequestDigitalWrite_size 12 -#define RequestPinMode_size 8 -#define RequestDigitalRead_size 6 -#define ResponseDigitalRead_size 6 -#define SpiSettings_size 10 -#define RequestSpiBeginTransaction_size 12 -#define RequestSpiEndTransaction_size 0 -/* RequestSpiTransfer_size depends on runtime parameters */ -/* ResponseSpiTransfer_size depends on runtime parameters */ -/* RequestSpiWrite_size depends on runtime parameters */ -#define ResponseSpiWrite_size 6 -#define RequestSpiRead_size 6 -/* ResponseSpiRead_size depends on runtime parameters */ -/* HostedCommand_size depends on runtime parameters */ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif diff --git a/Sming/Components/Hosted/proto/hosted.proto b/Sming/Components/Hosted/proto/hosted.proto deleted file mode 100644 index 9897bb43ba..0000000000 --- a/Sming/Components/Hosted/proto/hosted.proto +++ /dev/null @@ -1,102 +0,0 @@ -syntax = "proto3"; - -message RequestDigitalWrite { - uint32 pin = 1; - uint32 value = 2; -} - -// See: https://www.arduino.cc/reference/en/language/functions/digital-io/pinmode/ -enum PinMode { - INPUT = 0; - OUTPUT = 1; - INPUT_PULLUP = 2; -} - -message RequestPinMode { - uint32 pin = 1; - PinMode mode = 2; -} - -message RequestDigitalRead { - uint32 pin = 1; -} - -message ResponseDigitalRead { - uint32 value = 1; -} - -message SpiSettings { - uint32 speed = 1; - enum ByteOrder { - MSBFIRST = 0; - LSBFIRST = 1; - } - ByteOrder byteOrder = 2; - enum DataMode { - SPI_MODE0 = 0; - SPI_MODE1 = 1; - SPI_MODE2 = 2; - SPI_MODE3 = 3; - } - DataMode dataMode = 3; -} - -message RequestSpiBeginTransaction -{ - SpiSettings settings = 1; -} - -message RequestSpiEndTransaction -{ - -} - -message RequestSpiTransfer { - bytes data = 1; -} - -message ResponseSpiTransfer { - bytes data = 1; -} - -message RequestSpiWrite -{ - bytes data = 1; -} - -message ResponseSpiWrite -{ - uint32 length = 1; -} - -message RequestSpiRead -{ - uint32 length = 1; -} - -message ResponseSpiRead -{ - bytes data = 1; -} - -message HostedCommand { - // Version of the protocol for future compatibility - enum Version { - HOSTED_V_1_0 = 0; - } - Version version = 1; - oneof payload { - RequestDigitalWrite requestDigitalWrite = 10; - RequestPinMode requestPinMode = 11; - RequestDigitalRead requestDigitalRead = 12; - ResponseDigitalRead responseDigitalRead = 13; - RequestSpiBeginTransaction requestSpiBeginTransaction = 14; - RequestSpiEndTransaction requestSpiEndTransaction = 15; - RequestSpiTransfer requestSpiTransfer = 16; - ResponseSpiTransfer responseSpiTransfer = 17; - RequestSpiWrite requestSpiWrite = 18; - ResponseSpiWrite responseSpiWrite = 19; - RequestSpiRead requestSpiRead = 20; - ResponseSpiRead responseSpiRead = 21; - } -} diff --git a/Sming/Components/Hosted/app/Makefile b/Sming/Components/Hosted/samples/serial/Makefile similarity index 100% rename from Sming/Components/Hosted/app/Makefile rename to Sming/Components/Hosted/samples/serial/Makefile diff --git a/Sming/Components/Hosted/app/README.rst b/Sming/Components/Hosted/samples/serial/README.rst similarity index 100% rename from Sming/Components/Hosted/app/README.rst rename to Sming/Components/Hosted/samples/serial/README.rst diff --git a/Sming/Components/Hosted/samples/serial/app/application.cpp b/Sming/Components/Hosted/samples/serial/app/application.cpp new file mode 100644 index 0000000000..88e0b8e2a7 --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/app/application.cpp @@ -0,0 +1,25 @@ +/* + * This sample application demostrates communication via the serial interface + */ + +#include +#include +#include + +using namespace Hosted::Transport; + +SerialStream* serialStream = nullptr; + +void init() +{ + Serial.begin(SERIAL_BAUD_RATE); + + serialStream = new SerialStream(Serial); + serialStream->onData([](Stream& stream) { + interface(stream, pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", + digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", digitalWrite, + "digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."); + + return true; + }); +} diff --git a/Sming/Components/Hosted/samples/serial/component.mk b/Sming/Components/Hosted/samples/serial/component.mk new file mode 100644 index 0000000000..a2140118b1 --- /dev/null +++ b/Sming/Components/Hosted/samples/serial/component.mk @@ -0,0 +1,9 @@ +## This sample depends on the HostEd component +COMPONENT_DEPENDS := Hosted +ENABLE_HOSTED := + +## And does not require WIFI +DISABLE_WIFI := 1 + +ENABLE_HOST_UARTID := 0 +HOST_NETWORK_OPTIONS := --pause \ No newline at end of file diff --git a/Sming/Components/Hosted/samples/tcp/Makefile b/Sming/Components/Hosted/samples/tcp/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/Sming/Components/Hosted/samples/tcp/README.rst b/Sming/Components/Hosted/samples/tcp/README.rst new file mode 100644 index 0000000000..8d91a5e57d --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/README.rst @@ -0,0 +1,21 @@ +Hosted Server Application +========================= + +TBD... + + +Testing +------- + +You can compile the Hosted App to run also under the Host system. This can be done with the following command:: + + cd $SMING_HOME/Components/Hosted/app + make run SMING_ARCH=Host ENABLE_GDB=1 + +Once the HostedServer is up and running you can send protobuffer encoded commands to it. A sample test client can be +run with the following command:: + + nc 192.168.13.10 4031 < test/data.pb + + + diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp new file mode 100644 index 0000000000..88a0a447da --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -0,0 +1,53 @@ +/* + * This sample application demostrates communication via the TCP + */ + +#include +#include +#include + +// If you want, you can define WiFi settings globally in Eclipse Environment Variables +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +namespace +{ +using namespace Hosted::Transport; + +TcpServer* server = nullptr; +TcpServerStream* transportStream = nullptr; + +// Will be called when WiFi station was connected to AP +void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) +{ + if(server != nullptr) { + return; + } + + server = new TcpServer(); + server->listen(4031); + server->setTimeOut(USHRT_MAX); // disable connection timeout + + transportStream = new TcpServerStream(*server); + transportStream->onData([](Stream& stream) { + interface(stream, pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", + digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", digitalWrite, + "digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."); + + return true; + }); +} + +} // namespace + +void init() +{ + // Connect to same AP as the client application + WifiStation.enable(true); + WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); + + // Set callback that should be triggered when we have assigned IP + WifiEvents.onStationGotIP(connectOk); +} diff --git a/Sming/Components/Hosted/samples/tcp/component.mk b/Sming/Components/Hosted/samples/tcp/component.mk new file mode 100644 index 0000000000..2ab38b06f7 --- /dev/null +++ b/Sming/Components/Hosted/samples/tcp/component.mk @@ -0,0 +1,3 @@ +## List the names of any additional Components required for this project +COMPONENT_DEPENDS := Hosted +ENABLE_HOSTED := \ No newline at end of file diff --git a/Sming/Components/Hosted/src/HostedClient.h b/Sming/Components/Hosted/src.untested/HostedClient.h similarity index 100% rename from Sming/Components/Hosted/src/HostedClient.h rename to Sming/Components/Hosted/src.untested/HostedClient.h diff --git a/Sming/Components/Hosted/src/HostedCommon.h b/Sming/Components/Hosted/src.untested/HostedCommon.h similarity index 100% rename from Sming/Components/Hosted/src/HostedCommon.h rename to Sming/Components/Hosted/src.untested/HostedCommon.h diff --git a/Sming/Components/Hosted/src/HostedServer.h b/Sming/Components/Hosted/src.untested/HostedServer.h similarity index 100% rename from Sming/Components/Hosted/src/HostedServer.h rename to Sming/Components/Hosted/src.untested/HostedServer.h diff --git a/Sming/Components/Hosted/src/HostedUtils.cpp b/Sming/Components/Hosted/src.untested/HostedUtils.cpp similarity index 100% rename from Sming/Components/Hosted/src/HostedUtils.cpp rename to Sming/Components/Hosted/src.untested/HostedUtils.cpp diff --git a/Sming/Components/Hosted/src/HostedUtils.h b/Sming/Components/Hosted/src.untested/HostedUtils.h similarity index 100% rename from Sming/Components/Hosted/src/HostedUtils.h rename to Sming/Components/Hosted/src.untested/HostedUtils.h diff --git a/Sming/Core/Network/TcpServer.h b/Sming/Core/Network/TcpServer.h index 2d28d25e21..15b362d269 100644 --- a/Sming/Core/Network/TcpServer.h +++ b/Sming/Core/Network/TcpServer.h @@ -56,6 +56,11 @@ class TcpServer : public TcpConnection TcpConnection::timeOut = TCP_SERVER_TIMEOUT; } + void setClientReceiveHandler(TcpClientDataDelegate clientReceiveDataHandler) + { + clientReceiveDelegate = clientReceiveDataHandler; + } + ~TcpServer() { debug_i("TcpServer destroyed"); From 6ce358f40fbb267692c1881f56302417f01c77a3 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 9 Apr 2021 17:00:53 +0200 Subject: [PATCH 13/28] Some progress .... --- .../hostlib/include/hostlib/hostlib.h | 3 + .../Arch/Host/Components/hostlib/startup.cpp | 4 +- Sming/Components/Hosted-Lib/.cs | 0 Sming/Components/Hosted-Lib/component.mk | 2 - .../Hosted-Lib/src/DigitalHosted.cpp | 34 ---- Sming/Components/Hosted-Lib/src/SpiHosted.cpp | 60 ------- Sming/Components/Hosted-Transport/.cs | 0 .../Components/Hosted-Transport/component.mk | 8 - .../src/tcp/HostedTcpStream.h | 63 -------- Sming/Components/Hosted/component.mk | 18 +-- .../Components/Hosted/include/Hosted/Client.h | 58 +++++++ .../Transport/{TStream.h => BaseTransport.h} | 4 +- .../{SerialStream.h => SerialTransport.h} | 8 +- .../Hosted/Transport/TcpClientStream.h | 61 +++++-- .../Hosted/Transport/TcpClientTransport.h | 39 +++++ ...TcpServerStream.h => TcpServerTransport.h} | 15 +- .../include/Hosted/Transport/TcpStream.h | 78 --------- .../include/Hosted/Transport/TcpTransport.h | 16 ++ .../Hosted/init/serial/InitClient.cpp | 18 +++ .../src => Hosted/init}/tcp/InitClient.cpp | 23 ++- Sming/Components/Hosted/lib/DigitalHosted.cpp | 22 +++ .../Hosted/samples/serial/app/application.cpp | 29 ++-- .../Hosted/samples/tcp/app/application.cpp | 35 ++-- .../Hosted/samples/tcp/component.mk | 2 +- .../Hosted/src.untested/HostedClient.h | 106 ------------ .../Hosted/src.untested/HostedCommon.h | 32 ---- .../Hosted/src.untested/HostedServer.h | 153 ------------------ .../Hosted/src.untested/HostedUtils.cpp | 41 ----- .../Hosted/src.untested/HostedUtils.h | 14 -- 29 files changed, 293 insertions(+), 653 deletions(-) delete mode 100644 Sming/Components/Hosted-Lib/.cs delete mode 100644 Sming/Components/Hosted-Lib/component.mk delete mode 100644 Sming/Components/Hosted-Lib/src/DigitalHosted.cpp delete mode 100644 Sming/Components/Hosted-Lib/src/SpiHosted.cpp delete mode 100644 Sming/Components/Hosted-Transport/.cs delete mode 100644 Sming/Components/Hosted-Transport/component.mk delete mode 100644 Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Client.h rename Sming/Components/Hosted/include/Hosted/Transport/{TStream.h => BaseTransport.h} (87%) rename Sming/Components/Hosted/include/Hosted/Transport/{SerialStream.h => SerialTransport.h} (55%) create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h rename Sming/Components/Hosted/include/Hosted/Transport/{TcpServerStream.h => TcpServerTransport.h} (65%) delete mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h create mode 100644 Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h create mode 100644 Sming/Components/Hosted/init/serial/InitClient.cpp rename Sming/Components/{Hosted-Transport/src => Hosted/init}/tcp/InitClient.cpp (60%) create mode 100644 Sming/Components/Hosted/lib/DigitalHosted.cpp delete mode 100644 Sming/Components/Hosted/src.untested/HostedClient.h delete mode 100644 Sming/Components/Hosted/src.untested/HostedCommon.h delete mode 100644 Sming/Components/Hosted/src.untested/HostedServer.h delete mode 100644 Sming/Components/Hosted/src.untested/HostedUtils.cpp delete mode 100644 Sming/Components/Hosted/src.untested/HostedUtils.h diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h index b30b0cec12..1dfbff5151 100644 --- a/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h @@ -59,6 +59,9 @@ int msleep(unsigned ms); */ size_t getHostAppDir(char* path, size_t bufSize); + +void host_main_loop(); + #ifdef __cplusplus } #endif diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index 399781be62..529bee3257 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "include/hostlib/hostlib.h" #include "include/hostlib/CommandLine.h" #include @@ -111,7 +112,8 @@ static void pause(int secs) } } -void host_main_loop() { +void host_main_loop() +{ host_service_tasks(); host_service_timers(); if (lwip_initialised && lwipServiceTimer.expired()) { diff --git a/Sming/Components/Hosted-Lib/.cs b/Sming/Components/Hosted-Lib/.cs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Sming/Components/Hosted-Lib/component.mk b/Sming/Components/Hosted-Lib/component.mk deleted file mode 100644 index 9d2f99535c..0000000000 --- a/Sming/Components/Hosted-Lib/component.mk +++ /dev/null @@ -1,2 +0,0 @@ -COMPONENT_SRCDIRS := src -COMPONENT_DEPENDS := Hosted \ No newline at end of file diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp deleted file mode 100644 index dcd251f2f7..0000000000 --- a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include - -extern HostedClient* hostedClient; - -void pinMode(uint16_t pin, uint8_t mode) -{ - NEW_HD_COMMAND(message, PinMode, { - command->pin = pin; - command->mode = (PinMode)mode; - }); - - hostedClient->send(&message); -} - -void digitalWrite(uint16_t pin, uint8_t val) -{ - NEW_HD_COMMAND(message, DigitalWrite, { - command->pin = pin; - command->value = val; - }); - - hostedClient->send(&message); -} - -uint8_t digitalRead(uint16_t pin) -{ - NEW_HD_COMMAND(message, DigitalRead, { command->pin = pin; }); - - hostedClient->send(&message); - HostedCommand response = hostedClient->wait(); - - return uint8_t(response.payload.responseDigitalRead.value); -} diff --git a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp b/Sming/Components/Hosted-Lib/src/SpiHosted.cpp deleted file mode 100644 index 5c37a40f2b..0000000000 --- a/Sming/Components/Hosted-Lib/src/SpiHosted.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include - -extern HostedClient* hostedClient; - -// define the static singleton -SPIClass SPI; - -bool SPIClass::begin() -{ - NEW_HD_COMMAND(message, SpiBeginTransaction, { command->has_settings = 0; }); - - return hostedClient->send(&message); -} - -void SPIClass::prepare(SPISettings& mySettings) -{ - NEW_HD_COMMAND(message, SpiBeginTransaction, { - command->has_settings = 1; - // command->settings.speed = mySettings.speed; - command->settings.byteOrder = SpiSettings_ByteOrder(mySettings.byteOrder); - command->settings.dataMode = SpiSettings_DataMode(mySettings.dataMode); - }); - - hostedClient->send(&message); -} - -/** @brief transfer(uint8_t *buffer, size_t numberBytes) - * @param buffer in/out - * @param numberBytes length of buffer - * - * SPI transfer is based on a simultaneous send and receive: - * The buffered transfers does split up the conversation internaly into 64 byte blocks. - * The received data is stored in the buffer passed by reference. - * (the data past in is replaced with the data received). - * - * SPI.transfer(buffer, size) : memory buffer of length size - */ -void SPIClass::transfer(uint8_t* buffer, size_t numberBytes) -{ - PbData data{buffer, numberBytes}; - - NEW_HD_COMMAND(message, SpiTransfer, { - command->data.arg = &data; - command->data.funcs.encode = &pbEncodeData; - }); - - hostedClient->send(&message); - HostedCommand response = hostedClient->wait(); - - auto resultData = static_cast(response.payload.responseSpiTransfer.data.arg); - if(resultData == nullptr) { - memset(buffer, 0, numberBytes); - return; - } - - resultData->readBytes(reinterpret_cast(buffer), numberBytes); - delete resultData; -} diff --git a/Sming/Components/Hosted-Transport/.cs b/Sming/Components/Hosted-Transport/.cs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Sming/Components/Hosted-Transport/component.mk b/Sming/Components/Hosted-Transport/component.mk deleted file mode 100644 index 13812e450b..0000000000 --- a/Sming/Components/Hosted-Transport/component.mk +++ /dev/null @@ -1,8 +0,0 @@ -COMPONENT_DEPENDS := Hosted -ifneq ($(ENABLE_HOSTED),) - COMPONENT_SRCDIRS += src/$(ENABLE_HOSTED) -endif - -COMPONENT_VARS := ENABLE_HOSTED HOSTED_SERVER_IP -COMPONENT_CFLAGS := -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) -COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) \ No newline at end of file diff --git a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h b/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h deleted file mode 100644 index fd7f567d11..0000000000 --- a/Sming/Components/Hosted-Transport/src/tcp/HostedTcpStream.h +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include - -class HostedTcpStream : public ReadWriteStream -{ -public: - HostedTcpStream(const String& host, uint16_t port) : buffer(1024), host(host), port(port) - { - auto onCompleted = [](TcpClient& client, bool successful) { - // onCompleted; - }; - - auto onReadyToSend = [](TcpClient& client, TcpConnectionEvent sourceEvent) { - - }; - - auto onReceive = [this](TcpClient& client, char* data, int size) -> bool { - size_t written = buffer.write(reinterpret_cast(data), size); - return written == size_t(size); - }; - - client = new TcpClient(onCompleted, onReadyToSend, onReceive); - } - - size_t write(const uint8_t* buffer, size_t size) override - { - if(!client->isProcessing()) { - client->connect(host, int(port)); - } - - if(!client->send(reinterpret_cast(buffer), size)) { - return 0; - } - - return size; - } - - uint16_t readMemoryBlock(char* data, int bufSize) override - { - return buffer.readMemoryBlock(data, bufSize); - } - - bool seek(int len) override - { - return buffer.seek(len); - } - - bool isFinished() override - { - return false; - } - - void flush() override - { - client->commit(); - } - -private: - TcpClient* client{nullptr}; - CircularBuffer buffer; - String host; - uint16_t port{0}; -}; diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index d7962c49cf..8eca11e6d5 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -9,16 +9,14 @@ HOSTED_APP_DIR := $(COMPONENT_PATH)/app RELINK_VARS := ENABLE_HOSTED -##@Building +COMPONENT_VARS := ENABLE_HOSTED +ENABLE_HOSTED ?= -hosted-app: ##Builds the hosted service firmware - $(MAKE) -C $(HOSTED_APP_DIR) SMING_ARCH=$(HOSTED_ARCH) +ifneq ($(ENABLE_HOSTED),) + COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) +endif -##@Flashing - -hosted-flash: ##Flashes the hosted service firmware to an actual device - $(MAKE) -C $(HOSTED_APP_DIR) flash SMING_ARCH=$(HOSTED_ARCH) - -hosted-flashapp: ##Flashes only the hosted service app - $(MAKE) -C $(HOSTED_APP_DIR) flashapp SMING_ARCH=$(HOSTED_ARCH) +COMPONENT_VARS += HOSTED_SERVER_IP +COMPONENT_CFLAGS := -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) +COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h new file mode 100644 index 0000000000..690ff88a73 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +namespace Hosted +{ +class Client +{ +public: + Client(Stream& stream) : stream(stream) + { + } + + template bool send(const char* functionName, Args... args) + { + uint8_t functionId = getFunctionId(functionName); + + rpcPrint(stream, functionId); + rpcPrint(stream, args...); + + return true; + } + + /** + * @brief This method will block the execution until a message is detected + * @retval HostedCommand + */ + template R wait() + { + size_t neededBytes = sizeof(R); + + while(stream.available() < neededBytes) { + stream.flush(); + host_main_loop(); + } + + std::shared_ptr data(new uint8_t[neededBytes]); + // std::unique_ptr data = std::make_unique(neededBytes); + // uint8_t* data = new uint8_t[neededBytes]; + stream.readBytes(reinterpret_cast(&data), neededBytes); + + return *(reinterpret_cast(&data)); + } + + uint8_t getFunctionId(const char* name) + { + // TODO: get the id corresponding to this function + return 0; + } + +private: + Stream& stream; +}; + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TStream.h b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h similarity index 87% rename from Sming/Components/Hosted/include/Hosted/Transport/TStream.h rename to Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h index fb80a00872..0985f52478 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h @@ -6,7 +6,7 @@ namespace Hosted { namespace Transport { -class TStream +class BaseTransport { public: using DataHandler = Delegate; @@ -16,7 +16,7 @@ class TStream this->handler = handler; } - virtual ~TStream() + virtual ~BaseTransport() { } diff --git a/Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h similarity index 55% rename from Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h rename to Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h index d72d4c2319..946ae108b4 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/SerialStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h @@ -1,16 +1,16 @@ #include -#include "TStream.h" +#include "BaseTransport.h" namespace Hosted { namespace Transport { -class SerialStream : public TStream +class SerialTransport : public BaseTransport { public: - SerialStream(HardwareSerial& stream) + SerialTransport(HardwareSerial& stream) { - stream.onDataReceived(StreamDataReceivedDelegate(&SerialStream::process, this)); + stream.onDataReceived(StreamDataReceivedDelegate(&SerialTransport::process, this)); } private: diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index a80ec9b095..d23e3db5c1 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -1,36 +1,69 @@ -#include -#include "TcpStream.h" +#include +#include namespace Hosted { namespace Transport { -class TcpClientStream : public TcpStream +class TcpClientStream : public Stream { public: - TcpClientStream(TcpClient& client) + TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : client(client), cBuffer(cbufferSize) { - client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::process, this)); - stream = new CompoundTcpClientStream(client); } - ~TcpClientStream() + void setClient(TcpClient& client) { - delete stream; + this->client = client; } -protected: - bool process(TcpClient& client, char* data, int size) override + bool push(const uint8_t* buffer, size_t size) { - if(!stream.push(data, size)) { - return false; + int written = cBuffer.write(buffer, size); + return (written == size); + } + + size_t readBytes(char* buffer, size_t length) override + { + return cBuffer.readBytes(buffer, length); + } + + size_t write(const uint8_t* buffer, size_t size) override + { + if(client.send(reinterpret_cast(buffer), size)) { + return size; } - return handler(*stream); + return 0; + } + + size_t write(uint8_t c) override + { + return cBuffer.write(c); + } + + int available() override + { + return cBuffer.available(); + } + + int peek() override + { + return cBuffer.peek(); + } + + int read() override + { + return cBuffer.read(); + } + + void flush() override + { } private: - CompoundTcpClientStream* stream = nullptr; + CircularBuffer cBuffer; + TcpClient& client; }; } // namespace Transport diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h new file mode 100644 index 0000000000..e4e32316fc --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h @@ -0,0 +1,39 @@ +#include +#include "TcpTransport.h" +#include "TcpClientStream.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpClientTransport : public TcpTransport +{ +public: + TcpClientTransport(TcpClient& client) + { + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientTransport::process, this)); + stream = new TcpClientStream(client); + } + + ~TcpClientTransport() + { + delete stream; + } + +protected: + bool process(TcpClient& client, char* data, int size) override + { + if(!stream.push(data, size)) { + return false; + } + + return handler(*stream); + } + +private: + TcpClientStream* stream = nullptr; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h similarity index 65% rename from Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h rename to Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h index d3a471382f..ff53200809 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -1,19 +1,20 @@ #include #include -#include "TcpStream.h" +#include "TcpTransport.h" +#include "TcpClientStream.h" namespace Hosted { namespace Transport { -class TcpServerStream : public TcpStream +class TcpServerTransport : public TcpTransport { public: - using ClientMap = ObjectMap; + using ClientMap = ObjectMap; - TcpServerStream(TcpServer& server) + TcpServerTransport(TcpServer& server) { - server.setClientReceiveHandler(TcpClientDataDelegate(&TcpServerStream::process, this)); + server.setClientReceiveHandler(TcpClientDataDelegate(&TcpServerTransport::process, this)); } protected: @@ -21,13 +22,13 @@ class TcpServerStream : public TcpStream { uint32_t key = uint32_t(&client); - CompoundTcpClientStream* stream = nullptr; + TcpClientStream* stream = nullptr; int i = map.indexOf(key); if(i >= 0) { stream = map.valueAt(i); } else { - map[key] = new CompoundTcpClientStream(client); + map[key] = new TcpClientStream(client); } if(!stream->push(reinterpret_cast(data), size)) { diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h deleted file mode 100644 index 5f4a04aaa7..0000000000 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpStream.h +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include "TStream.h" - -namespace Hosted -{ -namespace Transport -{ -class CompoundTcpClientStream : public Stream -{ -public: - CompoundTcpClientStream(TcpClient& client) : client(client), cBuffer(1024) - { - } - - void setClient(TcpClient& client) - { - this->client = client; - } - - bool push(const uint8_t* buffer, size_t size) - { - int written = cBuffer.write(buffer, size); - return (written == size); - } - - size_t readBytes(char* buffer, size_t length) override - { - return cBuffer.readBytes(buffer, length); - } - - size_t write(const uint8_t* buffer, size_t size) override - { - if(client.send(reinterpret_cast(buffer), size)) { - return size; - } - - return 0; - } - - size_t write(uint8_t c) override - { - return cBuffer.write(c); - } - - int available() override - { - return cBuffer.available(); - } - - int peek() override - { - return cBuffer.peek(); - } - - int read() override - { - return cBuffer.read(); - } - - void flush() override - { - } - -private: - CircularBuffer cBuffer; - TcpClient& client; -}; - -class TcpStream : public TStream -{ -protected: - virtual bool process(TcpClient& client, char* data, int size) = 0; -}; - -} // namespace Transport - -} // namespace Hosted diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h new file mode 100644 index 0000000000..8fa0506ae8 --- /dev/null +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h @@ -0,0 +1,16 @@ +#include +#include "BaseTransport.h" + +namespace Hosted +{ +namespace Transport +{ +class TcpTransport : public BaseTransport +{ +protected: + virtual bool process(TcpClient& client, char* data, int size) = 0; +}; + +} // namespace Transport + +} // namespace Hosted diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp new file mode 100644 index 0000000000..8c55f2933b --- /dev/null +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -0,0 +1,18 @@ +#include +#include + +Hosted::Client* hostedClient{nullptr}; + +#ifndef WIFI_SSID +#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here +#define WIFI_PWD "PleaseEnterPass" +#endif + +extern void init(); + +void host_init() +{ + Serial.begin(SERIAL_BAUD_RATE); + hostedClient = new Hosted::Client(Serial); + init(); +} diff --git a/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp similarity index 60% rename from Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp rename to Sming/Components/Hosted/init/tcp/InitClient.cpp index 387fef4a58..3a54a31513 100644 --- a/Sming/Components/Hosted-Transport/src/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -1,8 +1,8 @@ #include -#include -#include "HostedTcpStream.h" +#include +#include -HostedClient* hostedClient{nullptr}; +Hosted::Client* hostedClient{nullptr}; #ifndef WIFI_SSID #define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here @@ -19,17 +19,32 @@ HostedClient* hostedClient{nullptr}; extern void init(); +namespace +{ +TcpClient* tcpClient = nullptr; +Hosted::Transport::TcpClientStream* stream = nullptr; + static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) { + if(hostedClient != nullptr) { + return; + } + IpAddress remoteIp(REMOTE_IP); if(gateway == IpAddress("192.168.4.1")) { remoteIp = gateway; } - hostedClient = new HostedClient(new HostedTcpStream(remoteIp.toString(), 4031)); + tcpClient = new TcpClient(false); + tcpClient->connect(remoteIp.toString(), 4031); + stream = new Hosted::Transport::TcpClientStream(*tcpClient); + + hostedClient = new Hosted::Client(stream); init(); } +} // namespace + void host_init() { WifiEvents.onStationGotIP(ready); diff --git a/Sming/Components/Hosted/lib/DigitalHosted.cpp b/Sming/Components/Hosted/lib/DigitalHosted.cpp new file mode 100644 index 0000000000..129457acfa --- /dev/null +++ b/Sming/Components/Hosted/lib/DigitalHosted.cpp @@ -0,0 +1,22 @@ +#include +#include + +extern Hosted::Client* hostedClient; + +void pinMode(uint16_t pin, uint8_t mode) +{ + hostedClient->send(__func__, pin, mode); +} + +void digitalWrite(uint16_t pin, uint8_t val) +{ + hostedClient->send(__func__, pin, val); +} + +uint8_t digitalRead(uint16_t pin) +{ + hostedClient->send(__func__, pin); + uint8_t response = hostedClient->wait(); + + return response; +} diff --git a/Sming/Components/Hosted/samples/serial/app/application.cpp b/Sming/Components/Hosted/samples/serial/app/application.cpp index 88e0b8e2a7..ba70d7576f 100644 --- a/Sming/Components/Hosted/samples/serial/app/application.cpp +++ b/Sming/Components/Hosted/samples/serial/app/application.cpp @@ -1,24 +1,35 @@ /* - * This sample application demostrates communication via the serial interface + * This sample application demostrates RPC communication via the serial interface */ - #include -#include #include +#include using namespace Hosted::Transport; -SerialStream* serialStream = nullptr; +SerialTransport* transport = nullptr; void init() { Serial.begin(SERIAL_BAUD_RATE); - serialStream = new SerialStream(Serial); - serialStream->onData([](Stream& stream) { - interface(stream, pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", - digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", digitalWrite, - "digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."); + transport = new SerialTransport(Serial); + transport->onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), + digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + ); + // clang-format on return true; }); diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index 88a0a447da..1a77318998 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -1,10 +1,11 @@ /* - * This sample application demostrates communication via the TCP + * This sample application demostrates RPC communication via TCP. + * It will try to connect to a WIFI access point and start a TCP server. + * The TCP server will listen on port 4031 for remote commands. */ - #include -#include #include +#include // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID @@ -14,10 +15,12 @@ namespace { +constexpr size_t port = 4031; + using namespace Hosted::Transport; TcpServer* server = nullptr; -TcpServerStream* transportStream = nullptr; +TcpServerTransport* transport = nullptr; // Will be called when WiFi station was connected to AP void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) @@ -27,14 +30,26 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) } server = new TcpServer(); - server->listen(4031); + server->listen(port); server->setTimeOut(USHRT_MAX); // disable connection timeout - transportStream = new TcpServerStream(*server); - transportStream->onData([](Stream& stream) { - interface(stream, pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", - digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", digitalWrite, - "digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value."); + transport = new TcpServerTransport(*server); + transport->onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, F("pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type."), + digitalRead, F("digitalRead: Read digital pin. @pin: Pin number. @return: Pin value."), + digitalWrite, F("digitalWrite: Write to a digital pin. @pin: Pin number. @value: Pin value.") + ); + // clang-format on return true; }); diff --git a/Sming/Components/Hosted/samples/tcp/component.mk b/Sming/Components/Hosted/samples/tcp/component.mk index 2ab38b06f7..bbaa5b7e96 100644 --- a/Sming/Components/Hosted/samples/tcp/component.mk +++ b/Sming/Components/Hosted/samples/tcp/component.mk @@ -1,3 +1,3 @@ -## List the names of any additional Components required for this project +## This sample depends on the HostEd component COMPONENT_DEPENDS := Hosted ENABLE_HOSTED := \ No newline at end of file diff --git a/Sming/Components/Hosted/src.untested/HostedClient.h b/Sming/Components/Hosted/src.untested/HostedClient.h deleted file mode 100644 index ae48ad9adf..0000000000 --- a/Sming/Components/Hosted/src.untested/HostedClient.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include -#include -#include "HostedCommon.h" - -extern void host_main_loop(); - -class HostedClient -{ -public: - HostedClient(ReadWriteStream* stream) : stream(stream) - { - } - - void setStream(ReadWriteStream* stream) - { - if(this->stream != nullptr) { - debug_w("Discarding current stream!"); - } - - this->stream = stream; - } - - bool send(HostedCommand* message) - { - pb_ostream_t ouput = newOutputStream(); - bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); - if(!success) { - debug_e("Encoding failed: %s", PB_GET_ERROR(&ouput)); - return false; - } - - stream->flush(); - - return true; - } - - /** - * @brief This method will block the execution until a message is detected - * @retval HostedCommand - */ - HostedCommand wait() - { - HostedCommand command = HostedCommand_init_zero; - - pb_istream_t input = newInputStream(); - size_t leftBytes = input.bytes_left; - size_t totalBytes = input.bytes_left; - bool success = false; - do { - stream->flush(); - success = pb_decode_ex(&input, HostedCommand_fields, &command, PB_DECODE_DELIMITED); - host_main_loop(); - } while(!success); - - stream->seek(totalBytes - input.bytes_left); - - return command; - } - - /** - * @brief This method handles incoming data - */ - bool onData(const char* at, size_t length) - { - return true; - } - -private: - pb_istream_t newInputStream() - { - return pb_istream_t{ - .callback = [](pb_istream_t* stream, pb_byte_t* buf, size_t count) -> bool { - auto source = static_cast(stream->state); - size_t read = source->readMemoryBlock(reinterpret_cast(buf), count); - source->seek(read); - - return true; - }, - .state = this->stream, - .bytes_left = (size_t)this->stream->available(), - .errmsg = nullptr, - }; - } - - pb_ostream_t newOutputStream() - { - return pb_ostream_t{ - .callback = [](pb_ostream_t* stream, const pb_byte_t* buf, size_t count) -> bool { - auto destination = static_cast(stream->state); - size_t written = destination->write(reinterpret_cast(buf), count); - - return written == count; - }, - .state = stream, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = nullptr, - }; - } - -private: - HashMap responseCallbacks; - ReadWriteStream* stream{nullptr}; -}; diff --git a/Sming/Components/Hosted/src.untested/HostedCommon.h b/Sming/Components/Hosted/src.untested/HostedCommon.h deleted file mode 100644 index 411b3b1268..0000000000 --- a/Sming/Components/Hosted/src.untested/HostedCommon.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include "hosted.pb.h" -#include "HostedUtils.h" - -constexpr int HOSTED_OK{0}; -constexpr int HOSTED_FAIL{-1}; -constexpr int HOSTED_NO_MEM{-2}; - -typedef Delegate HostedCommandDelegate; - -// Creates new hosted command -#define NEW_HD_COMMAND(NAME, XX, YY) \ - HostedCommand NAME = HostedCommand_init_zero; \ - NAME.which_payload = HostedCommand_request##XX##_tag; \ - { \ - auto command = &NAME.payload.request##XX; \ - YY \ - } - -class HostedCommon -{ -public: - virtual ~HostedCommon() - { - } - - virtual bool onData(const char* at, size_t length) = 0; -}; diff --git a/Sming/Components/Hosted/src.untested/HostedServer.h b/Sming/Components/Hosted/src.untested/HostedServer.h deleted file mode 100644 index 7dadf8aa1f..0000000000 --- a/Sming/Components/Hosted/src.untested/HostedServer.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * HostedServer.h - * - * Created on: Jun 29, 2020 - * Author: slavey - */ - -#pragma once - -#include -#include -#include - -typedef Delegate HostedTransferDelegate; - -class HostedServer -{ -public: - HostedServer(size_t storageSize = 1024) : inputBuffer(new CircularBuffer(storageSize)), outputBuffer(1024) - { - } - - void registerCommand(uint32_t type, HostedCommandDelegate callback) - { - commands[type] = callback; - } - - /** - * @brief Process incoming commands - * @return number of processed bytes - */ - int process(const uint8_t* at, size_t length) - { - HostedCommand request = HostedCommand_init_zero; - HostedCommand response = HostedCommand_init_zero; - - request.payload.requestSpiTransfer.data.funcs.decode = &pbDecodeData; - - if(at == nullptr || length < 0) { - debug_e("empty buffer"); - return HOSTED_FAIL; - } - - size_t written = inputBuffer->write(at, length); - if(written != length) { - // Not enough space to store the message... - return HOSTED_NO_MEM; - } - - int result = HOSTED_OK; - bool success; - pb_istream_t input = newInputStream(); - size_t leftBytes = input.bytes_left; - do { - success = pb_decode_ex(&input, HostedCommand_fields, &request, PB_DECODE_DELIMITED); - if(!success || request.which_payload == 0) { - debug_e("Decoding failed: %s", PB_GET_ERROR(&input)); - inputBuffer->seek(inputBuffer->available() - leftBytes); - break; - } - - leftBytes = input.bytes_left; - - // dispatch the command - if(!(commands.contains(request.which_payload) && commands[request.which_payload] != nullptr)) { - debug_w("No command registered for type: %d", request.which_payload); - continue; - } - - result = commands[request.which_payload](&request, &response); - if(result != HOSTED_OK) { - break; - } - - // process the response - if(response.which_payload && !send(&response)) { - result = HOSTED_FAIL; - break; - } - - // TODO: cleanup - - } while(input.bytes_left && success); - - return result; - } - - bool send(HostedCommand* message) - { - pb_ostream_t ouput = newOutputStream(); - bool success = pb_encode_ex(&ouput, HostedCommand_fields, message, PB_ENCODE_DELIMITED); - if(!success) { - debug_e("Encoding failed: %s\n", PB_GET_ERROR(&ouput)); - } - - return success; - } - - bool transfer(HostedTransferDelegate callback) - { - uint8_t buf[512]; - while(outputBuffer.available() > 0) { - int read = outputBuffer.readMemoryBlock((char*)buf, sizeof(buf)); - outputBuffer.seek(read); - if(!callback(buf, read)) { - return false; - } - - if(read != sizeof(buf)) { - break; - } - } - return true; - } - -private: - pb_istream_t newInputStream() - { - return pb_istream_t{ - .callback = [](pb_istream_t* stream, pb_byte_t* buf, size_t count) -> bool { - CircularBuffer* source = (CircularBuffer*)stream->state; - size_t read = source->readMemoryBlock((char*)buf, count); - source->seek(read); - - return true; - }, - .state = inputBuffer, - .bytes_left = inputBuffer->available(), - .errmsg = nullptr, - }; - } - - pb_ostream_t newOutputStream() - { - return pb_ostream_t{ - .callback = [](pb_ostream_t* stream, const pb_byte_t* buf, size_t count) -> bool { - CircularBuffer* destination = (CircularBuffer*)stream->state; - size_t written = destination->write((const uint8_t*)buf, count); - - return (written == count); - }, - .state = &outputBuffer, - .max_size = SIZE_MAX, - .bytes_written = 0, - .errmsg = nullptr, - }; - } - -private: - CircularBuffer* inputBuffer{nullptr}; - CircularBuffer outputBuffer; - HashMap commands; -}; diff --git a/Sming/Components/Hosted/src.untested/HostedUtils.cpp b/Sming/Components/Hosted/src.untested/HostedUtils.cpp deleted file mode 100644 index a7b5942c5c..0000000000 --- a/Sming/Components/Hosted/src.untested/HostedUtils.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "HostedUtils.h" -#include - -// See: https://iam777.tistory.com/538 - -bool pbEncodeData(pb_ostream_t* stream, const pb_field_t* field, void* const* arg) -{ - auto data = static_cast(*arg); - if(data == nullptr) { - return false; - } - - if(!pb_encode_tag_for_field(stream, field)) { - return false; - } - - return pb_encode_string(stream, (uint8_t*)data->value, data->length); -} - -bool pbDecodeData(pb_istream_t* stream, const pb_field_t* field, void** arg) -{ - uint8_t buffer[1024]{}; - - /* We could read block-by-block to avoid the large buffer... */ - if(stream->bytes_left >= sizeof(buffer)) { - return false; - } - - size_t available = stream->bytes_left; - if(!pb_read(stream, buffer, stream->bytes_left)) { - return false; - } - - auto data = static_cast(*arg); - if(data == nullptr) { - data = new MemoryDataStream(); - *arg = data; - } - data->write(buffer, available); - return true; -} diff --git a/Sming/Components/Hosted/src.untested/HostedUtils.h b/Sming/Components/Hosted/src.untested/HostedUtils.h deleted file mode 100644 index 4bf31513be..0000000000 --- a/Sming/Components/Hosted/src.untested/HostedUtils.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include -#include -#include "hosted.pb.h" - -struct PbData { - uint8_t* value; - size_t length; -}; - -bool pbEncodeData(pb_ostream_t* stream, const pb_field_t* field, void* const* arg); -bool pbDecodeData(pb_istream_t* stream, const pb_field_t* field, void** arg); From c4755f33f436a5053e5c5d93a9941aeffcc4e501 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Sat, 10 Apr 2021 15:55:42 +0200 Subject: [PATCH 14/28] PoC is ready. TODO: - [ ] get id for a remote RPC function(class method) - [x] fix the documentation - [x] add to the tcp sample also an example of creating own access point and connecting to it. - [ ] fix copyright headers --- .gitmodules | 6 +-- .../Host/Components/sming-arch/component.mk | 4 +- Sming/Arch/Host/app.mk | 2 +- Sming/Components/Hosted-Lib/.cs | 0 Sming/Components/Hosted-Lib/component.mk | 2 + .../lib => Hosted-Lib/src}/DigitalHosted.cpp | 0 Sming/Components/Hosted/README.rst | 53 +++++++++---------- Sming/Components/Hosted/component.mk | 7 +-- .../Components/Hosted/include/Hosted/Client.h | 6 ++- .../Hosted/Transport/TcpClientStream.h | 4 +- .../Hosted/init/serial/InitClient.cpp | 7 +-- .../Components/Hosted/init/tcp/InitClient.cpp | 2 +- .../Hosted/samples/serial/README.rst | 26 +++------ .../Components/Hosted/samples/tcp/README.rst | 33 ++++++++---- .../Hosted/samples/tcp/app/application.cpp | 15 +++++- .../Hosted/samples/tcp/component.mk | 9 +++- Sming/Core/Network/TcpClient.h | 6 --- 17 files changed, 94 insertions(+), 88 deletions(-) create mode 100644 Sming/Components/Hosted-Lib/.cs create mode 100644 Sming/Components/Hosted-Lib/component.mk rename Sming/Components/{Hosted/lib => Hosted-Lib/src}/DigitalHosted.cpp (100%) diff --git a/.gitmodules b/.gitmodules index 0816d5ebef..09aac2d758 100644 --- a/.gitmodules +++ b/.gitmodules @@ -260,9 +260,9 @@ url = https://github.com/mikee47/SignalGenerator ignore = dirty [submodule "Libraries.simpleRPC"] - path = Sming/Libraries/rpc/simpleRPC - url = https://github.com/jfjlaros/simpleRPC.git - ignore = dirty + path = Sming/Libraries/rpc/simpleRPC + url = https://github.com/jfjlaros/simpleRPC.git + ignore = dirty [submodule "Libraries.SmingTest"] path = Sming/Libraries/SmingTest url = https://github.com/mikee47/SmingTest diff --git a/Sming/Arch/Host/Components/sming-arch/component.mk b/Sming/Arch/Host/Components/sming-arch/component.mk index ae36a8ffe7..db03e70106 100644 --- a/Sming/Arch/Host/Components/sming-arch/component.mk +++ b/Sming/Arch/Host/Components/sming-arch/component.mk @@ -36,11 +36,11 @@ COMPONENT_DEPENDS := \ lwip \ spi_flash \ vflash \ - rboot \ + rboot ifneq ($(ENABLE_HOSTED),) - COMPONENT_DEPENDS += Hosted-Lib Hosted-Transport + COMPONENT_DEPENDS += Hosted-Lib endif # => Platform WiFi diff --git a/Sming/Arch/Host/app.mk b/Sming/Arch/Host/app.mk index 0a26dbdf46..3322bd77e1 100644 --- a/Sming/Arch/Host/app.mk +++ b/Sming/Arch/Host/app.mk @@ -14,7 +14,7 @@ TARGET_OUT_0 := $(FW_BASE)/$(APP_NAME)$(TOOL_EXT) # Hosted Settings ifneq ($(ENABLE_HOSTED),) - COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted.a $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib.a $(COMPONENTS_AR) + COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-$(CMP_Hosted_LIBHASH).a $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib-$(CMP_Hosted-Lib_LIBHASH).a $(COMPONENTS_AR) endif # Target definitions diff --git a/Sming/Components/Hosted-Lib/.cs b/Sming/Components/Hosted-Lib/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Components/Hosted-Lib/component.mk b/Sming/Components/Hosted-Lib/component.mk new file mode 100644 index 0000000000..f2466e7923 --- /dev/null +++ b/Sming/Components/Hosted-Lib/component.mk @@ -0,0 +1,2 @@ +COMPONENT_SRCDIRS := $(COMPONENT_PATH)/src +COMPONENT_DEPENDS := Hosted \ No newline at end of file diff --git a/Sming/Components/Hosted/lib/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp similarity index 100% rename from Sming/Components/Hosted/lib/DigitalHosted.cpp rename to Sming/Components/Hosted-Lib/src/DigitalHosted.cpp diff --git a/Sming/Components/Hosted/README.rst b/Sming/Components/Hosted/README.rst index cdaf83f3fe..824f811d39 100644 --- a/Sming/Components/Hosted/README.rst +++ b/Sming/Components/Hosted/README.rst @@ -1,10 +1,10 @@ HostEd -============ +====== .. highlight:: bash The hosted component allows Sming's host emulator to run parts of the commands on an actual microcontroller. -The communication is done via protobuf messages and the microcontroller has to be flashed with a special application. +The communication is done via `simplePRC `_ and the microcontroller has to be flashed with a special application. Overview -------- @@ -14,42 +14,39 @@ and facilitates testing functionality that only a real microcontroller can provi For example in order to run the Basic_Blink application under the host emulator and run the actual blinking of a LED on a microcontroller we can compile the application using the following directives:: -    make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1 + make SMING_ARCH=Host ENABLE_HOSTED=tcp HOSTED_SERVER_IP=192.168.4.1 `SMING_ARCH=Host` instructs the build system to build the application for the Host architecture. `ENABLE_HOSTED=tcp` instructs the host emulator to communication with the real microcontroller using TCP `HOSTED_SERVER_IP=192.168.4.1` instructs the host emulator to connect to IP `192.168.4.1`. -We need to compile and flash also a special application on the desired microcontroller. That application will execute the commands send from the host emulator. -The compilation and flashing for ESP32, for example, can be done using the following commands:: - -    make hosted-app HOSTED_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword -    make hosted-flash - -If you replace `HOSTED_ARCH=Esp32` with `HOSTED_ARCH=Esp8266` then the hosted application will be compiled and flashed on ESP8266 microcontroller. -Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP). +We need to compile and flash also a special application on the desired microcontroller. +This application will act as an RPC Server and will execute the commands from the host emulator on the microcontroller. -Development ------------ -The communication between the host emulator and the hosted application is done via protocol buffer messages. The messages are describe in the `proto/hosted.proto` -file. +In the sub-directory ``samples`` inside this directory you will find the sample applications that will turn your microcontroller into +RCP server. -Adding a new command -~~~~~~~~~~~~~~~~~~~~ -Adding a new command can be done in the following way. - -1. You should add the command in the `proto/hosted.proto` file. Make sure to compile it using `nanopb`. This can be done with the commands below:: +The compilation and flashing for ESP32, for example, can be done using the following commands:: -    cd $SMING_HOME/Components/Hosted/proto -    python $SMING_HOME/Components/nanopb/nanopb/generator/nanopb_generator.py hosted.proto + cd samples/tcp + make SMING_ARCH=Esp32 WIFI_SSID=YourSSID WIFI_PWD=YourPassword + make flash -2. See `DigitalHosted.cpp` for inspiration how to add a new command to `Hosted-Lib`. -These functions will try to communicate with the hosted application on the microcontroller. +If you replace ``SMING_ARCH=Esp32`` with ``SMING_ARCH=Esp8266`` then the hosted application will be compiled and flashed on a ESP8266 microcontroller. +Make sure to replace the values of  WIFI_SSID and WIFI_PWD with the actual name and password for the Access Point (AP). -3. The actual work is done by the hosted application. See `app/application.cpp`. See the `init` method for inspiration how to register a new command that -should be executed on the controller. +Communication +------------- +At the moment the communication between an application running on the Host and the RCP server running on a microcontroller +can be done using TCP or serial interface. +The ``transport`` classes are located under ``include/Hosted/Transport``. +Configuration +------------- .. envvar:: ENABLE_HOSTED -   Enables the hosted component. Valid values for the moment are: -   - tcp - for communication over TCP network. \ No newline at end of file + Default: empty (disabled) + + Enables the hosted component. Valid values for the moment are: + - tcp - for communication over TCP network. + - serial - for communication over serial interface \ No newline at end of file diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index 8eca11e6d5..b100290e87 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -5,11 +5,8 @@ COMPONENT_DEPENDS := rpc # Architecture of the device where the hosted service will be flashed HOSTED_ARCH ?= Esp8266 -HOSTED_APP_DIR := $(COMPONENT_PATH)/app - -RELINK_VARS := ENABLE_HOSTED - COMPONENT_VARS := ENABLE_HOSTED + ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) @@ -18,5 +15,5 @@ endif COMPONENT_VARS += HOSTED_SERVER_IP -COMPONENT_CFLAGS := -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) +COMPONENT_CFLAGS = -DHOSTED_SERVER_IP=$(HOSTED_SERVER_IP) COMPONENT_CXXFLAGS := $(COMPONENT_CFLAGS) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index 690ff88a73..01345b4055 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -1,5 +1,9 @@ #pragma once +#ifndef ARCH_HOST +#error "Hosted::Client can be used only on the Host architecture!" +#endif + #include #include #include @@ -32,7 +36,7 @@ class Client { size_t neededBytes = sizeof(R); - while(stream.available() < neededBytes) { + while(stream.available() < int(neededBytes)) { stream.flush(); host_main_loop(); } diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index d23e3db5c1..4429a268a5 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -8,7 +8,7 @@ namespace Transport class TcpClientStream : public Stream { public: - TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : client(client), cBuffer(cbufferSize) + TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : cBuffer(cbufferSize), client(client) { } @@ -19,7 +19,7 @@ class TcpClientStream : public Stream bool push(const uint8_t* buffer, size_t size) { - int written = cBuffer.write(buffer, size); + size_t written = cBuffer.write(buffer, size); return (written == size); } diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index 8c55f2933b..6f83d52d46 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -3,16 +3,11 @@ Hosted::Client* hostedClient{nullptr}; -#ifndef WIFI_SSID -#define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here -#define WIFI_PWD "PleaseEnterPass" -#endif - extern void init(); void host_init() { - Serial.begin(SERIAL_BAUD_RATE); + Serial.begin(115200); hostedClient = new Hosted::Client(Serial); init(); } diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp index 3a54a31513..0cfb77d937 100644 --- a/Sming/Components/Hosted/init/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -39,7 +39,7 @@ static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) tcpClient->connect(remoteIp.toString(), 4031); stream = new Hosted::Transport::TcpClientStream(*tcpClient); - hostedClient = new Hosted::Client(stream); + hostedClient = new Hosted::Client(*stream); init(); } diff --git a/Sming/Components/Hosted/samples/serial/README.rst b/Sming/Components/Hosted/samples/serial/README.rst index 8d91a5e57d..d1635d59fb 100644 --- a/Sming/Components/Hosted/samples/serial/README.rst +++ b/Sming/Components/Hosted/samples/serial/README.rst @@ -1,21 +1,9 @@ -Hosted Server Application -========================= - -TBD... - - -Testing -------- - -You can compile the Hosted App to run also under the Host system. This can be done with the following command:: - - cd $SMING_HOME/Components/Hosted/app - make run SMING_ARCH=Host ENABLE_GDB=1 - -Once the HostedServer is up and running you can send protobuffer encoded commands to it. A sample test client can be -run with the following command:: - - nc 192.168.13.10 4031 < test/data.pb - +Hosted RCP Server over Serial +============================= +Overview +-------- +This application creates a RPC server that will communicate over serial interface. To compile it for an Esp8266 microcontroller you can +use the following command:: + make SMING_ARCH=Esp8266 diff --git a/Sming/Components/Hosted/samples/tcp/README.rst b/Sming/Components/Hosted/samples/tcp/README.rst index 8d91a5e57d..308e5757fb 100644 --- a/Sming/Components/Hosted/samples/tcp/README.rst +++ b/Sming/Components/Hosted/samples/tcp/README.rst @@ -1,21 +1,32 @@ -Hosted Server Application -========================= +Hosted RCP Server over TCP +========================== -TBD... +Overview +-------- +This application creates a RPC server that will communicate over TCP. You can either start an Access Point from the controller +or connect the application to an existing WIFI Access point. The latter can be compiled using the following command:: + make SMING_ARCH=Esp8266 CONNECT_TO_WIFI=1 WIFI_SSID="MySSID" WIFI_PWD="Secr3tP4Ssw0rd" -Testing -------- +Configuration +------------- -You can compile the Hosted App to run also under the Host system. This can be done with the following command:: +.. envvar:: CONNECT_TO_WIFI - cd $SMING_HOME/Components/Hosted/app - make run SMING_ARCH=Host ENABLE_GDB=1 + Default: 0 (disabled) -Once the HostedServer is up and running you can send protobuffer encoded commands to it. A sample test client can be -run with the following command:: + If set to 1 the application will try to connect to a WIFI access point. Make sure to provide also the WIFI_SSID and WIFI_PWD values. - nc 192.168.13.10 4031 < test/data.pb + If set to 0 the application will start an access point to which the Host application can connect. +.. envvar:: WIFI_SSID + Default: PleaseEnterSSID + WIFI Access Point name. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values. + +.. envvar:: WIFI_PWD + + Default: PleaseEnterPass + + WIFI Access Point password. If you have enabled CONNECT_TO_WIFI then make sure to set also WIFI_SSID and WIFI_PWD values. diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index 1a77318998..f299ae09f5 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -1,17 +1,19 @@ /* * This sample application demostrates RPC communication via TCP. - * It will try to connect to a WIFI access point and start a TCP server. + * It will try to connect to create an access point or create to a WIFI access point and start a TCP server. * The TCP server will listen on port 4031 for remote commands. */ #include #include #include +#if CONNECT_TO_WIFI // If you want, you can define WiFi settings globally in Eclipse Environment Variables #ifndef WIFI_SSID #define WIFI_SSID "PleaseEnterSSID" // Put your SSID and password here #define WIFI_PWD "PleaseEnterPass" #endif +#endif /* CONNECT_TO_WIFI */ namespace { @@ -53,16 +55,25 @@ void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) return true; }); + + Serial.printf("Running RCP server on: %s:%u", ip.toString().c_str(), port); } } // namespace void init() { + Serial.begin(SERIAL_BAUD_RATE); + +#if CONNECT_TO_WIFI // Connect to same AP as the client application WifiStation.enable(true); WifiStation.config(_F(WIFI_SSID), _F(WIFI_PWD)); - // Set callback that should be triggered when we have assigned IP WifiEvents.onStationGotIP(connectOk); +#else + WifiAccessPoint.enable(true); + WifiAccessPoint.config(_F("RCP Server"), nullptr, AUTH_OPEN); + connectOk(WifiAccessPoint.getIP(), WifiAccessPoint.getNetworkMask(), WifiAccessPoint.getNetworkGateway()); +#endif } diff --git a/Sming/Components/Hosted/samples/tcp/component.mk b/Sming/Components/Hosted/samples/tcp/component.mk index bbaa5b7e96..a3b186f112 100644 --- a/Sming/Components/Hosted/samples/tcp/component.mk +++ b/Sming/Components/Hosted/samples/tcp/component.mk @@ -1,3 +1,10 @@ ## This sample depends on the HostEd component COMPONENT_DEPENDS := Hosted -ENABLE_HOSTED := \ No newline at end of file +ENABLE_HOSTED := + +# If set the application should connect to a WIFI access point +# otherwise it will set its own access point +COMPONENT_VARS := CONNECT_TO_WIFI +CONNECT_TO_WIFI ?= 0 + +APP_CFLAGS = -DCONNECT_TO_WIFI=$(CONNECT_TO_WIFI) diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Core/Network/TcpClient.h index e9e7640f7f..71789f7b54 100644 --- a/Sming/Core/Network/TcpClient.h +++ b/Sming/Core/Network/TcpClient.h @@ -126,12 +126,6 @@ class TcpClient : public TcpConnection closeAfterSent = ignoreIncomingData ? eTCCASS_AfterSent_Ignore_Received : eTCCASS_AfterSent; } - void commit() - { - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); - TcpConnection::flush(); - } - protected: err_t onConnected(err_t err) override; err_t onReceive(pbuf* buf) override; From 49013b849df2595e15cfd8e7bbe3eb007d7ecb4d Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Sun, 11 Apr 2021 14:50:52 +0200 Subject: [PATCH 15/28] Initial version of the SimpleRPC stream parser for the headers. TODO: - [x] move the parser to be part of the rcp component. - [x] add parser test case. --- .gitmodules | 2 +- Sming/Components/Hosted/component.mk | 2 +- .../Components/Hosted/include/Hosted/Client.h | 98 +++++++++- .../Hosted/init/serial/InitClient.cpp | 1 + .../Components/Hosted/init/tcp/InitClient.cpp | 1 + Sming/Libraries/rpc/component.mk | 6 - Sming/Libraries/simpleRPC/component.mk | 6 + .../Libraries/simpleRPC/include/simpleRPC/.cs | 0 .../simpleRPC/include/simpleRPC/parser.h | 50 +++++ Sming/Libraries/{rpc => simpleRPC}/simpleRPC | 0 .../{rpc => simpleRPC}/simpleRPC.patch | 0 Sming/Libraries/simpleRPC/src/.cs | 0 Sming/Libraries/simpleRPC/src/parser.cpp | 182 ++++++++++++++++++ tests/HostTests/component.mk | 5 +- tests/HostTests/include/modules.h | 3 +- tests/HostTests/modules/Hosted.cpp | 87 +++++++++ 16 files changed, 429 insertions(+), 14 deletions(-) delete mode 100644 Sming/Libraries/rpc/component.mk create mode 100644 Sming/Libraries/simpleRPC/component.mk create mode 100644 Sming/Libraries/simpleRPC/include/simpleRPC/.cs create mode 100644 Sming/Libraries/simpleRPC/include/simpleRPC/parser.h rename Sming/Libraries/{rpc => simpleRPC}/simpleRPC (100%) rename Sming/Libraries/{rpc => simpleRPC}/simpleRPC.patch (100%) create mode 100644 Sming/Libraries/simpleRPC/src/.cs create mode 100644 Sming/Libraries/simpleRPC/src/parser.cpp create mode 100644 tests/HostTests/modules/Hosted.cpp diff --git a/.gitmodules b/.gitmodules index 09aac2d758..4308edcb62 100644 --- a/.gitmodules +++ b/.gitmodules @@ -260,7 +260,7 @@ url = https://github.com/mikee47/SignalGenerator ignore = dirty [submodule "Libraries.simpleRPC"] - path = Sming/Libraries/rpc/simpleRPC + path = Sming/Libraries/simpleRPC/simpleRPC url = https://github.com/jfjlaros/simpleRPC.git ignore = dirty [submodule "Libraries.SmingTest"] diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index b100290e87..dc65e53f03 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -1,6 +1,6 @@ COMPONENT_SRCDIRS := $(COMPONENT_PATH)/src COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include -COMPONENT_DEPENDS := rpc +COMPONENT_DEPENDS := simpleRPC # Architecture of the device where the hosted service will be flashed HOSTED_ARCH ?= Esp8266 diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index 01345b4055..fa3ce4095f 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -5,15 +5,25 @@ #endif #include +#include +#include #include +#include #include #include +#include + +using namespace simpleRPC; namespace Hosted { +constexpr int COMMAND_NOT_FOUND = -1; + class Client { public: + using RemoteCommands = HashMap; + Client(Stream& stream) : stream(stream) { } @@ -49,14 +59,96 @@ class Client return *(reinterpret_cast(&data)); } - uint8_t getFunctionId(const char* name) + int getFunctionId(const char* name) { - // TODO: get the id corresponding to this function - return 0; + if(fetchCommands) { + if(getRemoteCommands()) { + fetchCommands = false; + } + } + + int id = commands.indexOf(name); + if(id < 0) { + return COMMAND_NOT_FOUND; + } + + return commands[name]; + } + + /** + * @brief Gets list of remote command names and their ids + * @retval true on success, false otherwise + */ + bool getRemoteCommands() + { + host_debug_i("Getting remote RPC commands \033[5m...\033[0m"); + + stream.write("\0xff", 1); + char buffer[512]; + ParserSettings settings; + settings.startMethods = ParserSettings::SimpleMethod(&Client::startMethods, this); + settings.startMethod = ParserSettings::SimpleMethod(&Client::startMethod, this); + settings.methodName = ParserSettings::CharMethod(&Client::methodName, this); + settings.endMethod = ParserSettings::SimpleMethod(&Client::endMethod, this); + settings.endMethods = ParserSettings::SimpleMethod(&Client::endMethods, this); + settings.state = ParserState::ready; + + do { + stream.flush(); + host_main_loop(); + + size_t length = stream.readBytes(buffer, 512); + if(!length) { + continue; + } + + ParserResult result = parse(settings, buffer, length); + if(result == ParserResult::finished) { + break; + } + + if(result == ParserResult::error) { + debug_e("Invalid header"); + return false; + } + } while(true); + + host_debug_i("Connected. Starting application"); + + return true; } private: Stream& stream; + bool fetchCommands{true}; + RemoteCommands commands; + uint8_t methodPosition = 0; + String parsedCommand; + + void startMethods() + { + methodPosition = 0; + commands.clear(); + } + + void startMethod() + { + parsedCommand = ""; + } + + void methodName(char ch) + { + parsedCommand += ch; + } + + void endMethod() + { + commands[parsedCommand] = methodPosition++; + } + + void endMethods() + { + } }; } // namespace Hosted diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index 6f83d52d46..5fd0f9065f 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -9,5 +9,6 @@ void host_init() { Serial.begin(115200); hostedClient = new Hosted::Client(Serial); + hostedClient->getRemoteCommands(); init(); } diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp index 0cfb77d937..91f8621874 100644 --- a/Sming/Components/Hosted/init/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -40,6 +40,7 @@ static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) stream = new Hosted::Transport::TcpClientStream(*tcpClient); hostedClient = new Hosted::Client(*stream); + hostedClient->getRemoteCommands(); init(); } diff --git a/Sming/Libraries/rpc/component.mk b/Sming/Libraries/rpc/component.mk deleted file mode 100644 index 15bcc78b81..0000000000 --- a/Sming/Libraries/rpc/component.mk +++ /dev/null @@ -1,6 +0,0 @@ -COMPONENT_SUBMODULES += simpleRPC - -SIMPLE_RPC_ROOT := $(COMPONENT_PATH)/simpleRPC - -COMPONENT_SRCDIRS := $(SIMPLE_RPC_ROOT)/src -COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) diff --git a/Sming/Libraries/simpleRPC/component.mk b/Sming/Libraries/simpleRPC/component.mk new file mode 100644 index 0000000000..4fd217ca80 --- /dev/null +++ b/Sming/Libraries/simpleRPC/component.mk @@ -0,0 +1,6 @@ +COMPONENT_SUBMODULES += simpleRPC + +SIMPLE_RPC_ROOT := $(COMPONENT_PATH)/simpleRPC + +COMPONENT_SRCDIRS := $(SIMPLE_RPC_ROOT)/src $(COMPONENT_PATH)/src +COMPONENT_INCDIRS := $(COMPONENT_SRCDIRS) $(COMPONENT_PATH)/include diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/.cs b/Sming/Libraries/simpleRPC/include/simpleRPC/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h new file mode 100644 index 0000000000..5f6d422ed2 --- /dev/null +++ b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace simpleRPC +{ +enum class ParserResult { finished = 0, more, error }; + +enum class ParserState { + ready = 0, + header_s, + header_si, + header_sim, + header_simp, + header_simpl, + header_simple, + header_simpleR, + header_simpleRP, + header_simpleRPC, + header_end, + version_major, + version_minor, + version_patch, + ness, + type, + type_end, + start_methods, + extract_method_start, + extract_method_name, + extract_method, + extract_method_end, + end_methods, + finished +}; + +struct ParserSettings { + using SimpleMethod = Delegate; + using CharMethod = Delegate; + + SimpleMethod startMethods; + SimpleMethod startMethod; + CharMethod methodName; + SimpleMethod endMethod; + SimpleMethod endMethods; + ParserState state = ParserState::ready; +}; + +ParserResult parse(ParserSettings& settings, const char* buffer, size_t length); + +} // namespace simpleRPC diff --git a/Sming/Libraries/rpc/simpleRPC b/Sming/Libraries/simpleRPC/simpleRPC similarity index 100% rename from Sming/Libraries/rpc/simpleRPC rename to Sming/Libraries/simpleRPC/simpleRPC diff --git a/Sming/Libraries/rpc/simpleRPC.patch b/Sming/Libraries/simpleRPC/simpleRPC.patch similarity index 100% rename from Sming/Libraries/rpc/simpleRPC.patch rename to Sming/Libraries/simpleRPC/simpleRPC.patch diff --git a/Sming/Libraries/simpleRPC/src/.cs b/Sming/Libraries/simpleRPC/src/.cs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Libraries/simpleRPC/src/parser.cpp b/Sming/Libraries/simpleRPC/src/parser.cpp new file mode 100644 index 0000000000..6e715bdf40 --- /dev/null +++ b/Sming/Libraries/simpleRPC/src/parser.cpp @@ -0,0 +1,182 @@ +#include "../include/simpleRPC/parser.h" + +namespace simpleRPC +{ +#define EXPECT(X, NEW_STATE) \ + { \ + if(ch != X) { \ + hasError = true; \ + goto ERROR; \ + } \ + state = NEW_STATE; \ + break; \ + } + +#define SKIP_UNTIL(X, NEW_STATE) \ + { \ + if(ch != X) { \ + break; \ + } \ + state = NEW_STATE; \ + } + +ParserResult parse(ParserSettings& settings, const char* buffer, size_t length) +{ + auto& state = settings.state; + /* + * See: https://simplerpc.readthedocs.io/en/latest/protocol.html# +00000000 ff . + 00000000 73 s + 00000001 69 i + 00000002 6d m + 00000003 70 p + 00000004 6c l + 00000005 65 e + 00000006 52 R + 00000007 50 P + 00000008 43 C + 00000009 00 . + 0000000A 03 00 00 ... + 3c < + 49 I + 00 . + 3a 20 : + 3a 20 48 20 42 3b 70 69 6e 4d ... +#include + +#include + +using namespace simpleRPC; + +class HostedTest : public TestGroup +{ +public: + using RemoteCommands = HashMap; + + HostedTest() : TestGroup(_F("Hosted")) + { + } + + void execute() override + { + char packet[] = { + 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x52, 0x50, 0x43, 0x00, 0x03, 0x00, 0x00, 0x3c, 0x49, 0x00, 0x3a, 0x20, + 0x48, 0x20, 0x42, 0x3b, 0x70, 0x69, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x53, 0x65, 0x74, 0x73, 0x20, + 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, + 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2c, 0x20, 0x40, 0x6d, 0x6f, 0x64, 0x65, 0x3a, 0x20, 0x4d, 0x6f, 0x64, 0x65, 0x20, 0x74, 0x79, 0x70, + 0x65, 0x2e, 0x00, 0x42, 0x3a, 0x20, 0x48, 0x3b, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x52, 0x65, 0x61, + 0x64, 0x3a, 0x20, 0x52, 0x65, 0x61, 0x64, 0x20, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, + 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x2e, 0x20, 0x40, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x2e, 0x00, 0x3a, 0x20, 0x48, 0x20, 0x42, 0x3b, 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, + 0x57, 0x72, 0x69, 0x74, 0x65, 0x3a, 0x20, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x61, 0x20, + 0x64, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x70, 0x69, 0x6e, 0x2e, 0x20, 0x40, 0x70, 0x69, 0x6e, 0x3a, + 0x20, 0x50, 0x69, 0x6e, 0x20, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x2e, 0x20, 0x40, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x20, 0x50, 0x69, 0x6e, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2e, 0x00, 0x00}; + + ParserSettings settings; + settings.startMethods = ParserSettings::SimpleMethod(&HostedTest::startMethods, this); + settings.startMethod = ParserSettings::SimpleMethod(&HostedTest::startMethod, this); + settings.methodName = ParserSettings::CharMethod(&HostedTest::methodName, this); + settings.endMethod = ParserSettings::SimpleMethod(&HostedTest::endMethod, this); + settings.endMethods = ParserSettings::SimpleMethod(&HostedTest::endMethods, this); + settings.state = ParserState::ready; + + TEST_CASE("simpleRPC::parse()") + { + REQUIRE(parse(settings, packet, sizeof(packet)) == ParserResult::finished); + REQUIRE(commands.count() == 3); + REQUIRE(commands["digitalWrite"] == 2); + REQUIRE(commands["pinMode"] != 2); + REQUIRE(commands["pinMode"] == 0); + } + } + +private: + RemoteCommands commands; + uint8_t methodPosition = 0; + String parsedCommand; + + void startMethods() + { + methodPosition = 0; + commands.clear(); + } + + void startMethod() + { + parsedCommand = ""; + } + + void methodName(char ch) + { + parsedCommand += ch; + } + + void endMethod() + { + commands[parsedCommand] = methodPosition++; + } + + void endMethods() + { + } +}; + +void REGISTER_TEST(Hosted) +{ + registerGroup(); +} From 9d56171adad6a88fa7c078946fa0b0088fb8fd9f Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Wed, 14 Apr 2021 18:39:39 +0200 Subject: [PATCH 16/28] Fixes. --- Sming/Components/Hosted/include/Hosted/Client.h | 11 +++++------ .../include/Hosted/Transport/TcpServerTransport.h | 1 + Sming/Components/Hosted/init/tcp/InitClient.cpp | 2 +- .../Components/Hosted/samples/tcp/app/application.cpp | 2 +- tests/HostTests/component.mk | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index fa3ce4095f..37043386a3 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -32,8 +32,7 @@ class Client { uint8_t functionId = getFunctionId(functionName); - rpcPrint(stream, functionId); - rpcPrint(stream, args...); + rpcPrint(stream, functionId, args...); return true; } @@ -62,9 +61,7 @@ class Client int getFunctionId(const char* name) { if(fetchCommands) { - if(getRemoteCommands()) { - fetchCommands = false; - } + getRemoteCommands(); } int id = commands.indexOf(name); @@ -83,7 +80,8 @@ class Client { host_debug_i("Getting remote RPC commands \033[5m...\033[0m"); - stream.write("\0xff", 1); + uint8_t head = 0xff; + stream.write(&head, 1); char buffer[512]; ParserSettings settings; settings.startMethods = ParserSettings::SimpleMethod(&Client::startMethods, this); @@ -148,6 +146,7 @@ class Client void endMethods() { + fetchCommands = false; } }; diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h index ff53200809..9adaa7e306 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -29,6 +29,7 @@ class TcpServerTransport : public TcpTransport stream = map.valueAt(i); } else { map[key] = new TcpClientStream(client); + stream = map[key]; } if(!stream->push(reinterpret_cast(data), size)) { diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp index 91f8621874..cf44e414f9 100644 --- a/Sming/Components/Hosted/init/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -36,7 +36,7 @@ static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) } tcpClient = new TcpClient(false); - tcpClient->connect(remoteIp.toString(), 4031); + tcpClient->connect(remoteIp, 4031); stream = new Hosted::Transport::TcpClientStream(*tcpClient); hostedClient = new Hosted::Client(*stream); diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index f299ae09f5..fa4cfb5db3 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -1,6 +1,6 @@ /* * This sample application demostrates RPC communication via TCP. - * It will try to connect to create an access point or create to a WIFI access point and start a TCP server. + * It will try to connect to create an existing Access Point (AP) or create to a new AP and start a TCP server. * The TCP server will listen on port 4031 for remote commands. */ #include diff --git a/tests/HostTests/component.mk b/tests/HostTests/component.mk index 5f75260180..35af69539a 100644 --- a/tests/HostTests/component.mk +++ b/tests/HostTests/component.mk @@ -8,7 +8,7 @@ ARDUINO_LIBRARIES := \ SmingTest \ ArduinoJson5 \ ArduinoJson6 \ - simpleRPC + Hosted COMPONENT_DEPENDS := \ malloc_count \ From b9115e564aad1c096cc869d8a5c446bfc7c5796a Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Thu, 15 Apr 2021 19:35:33 +0200 Subject: [PATCH 17/28] Added Hosted tests. --- .../Hosted/Transport/TcpClientStream.h | 6 ++ .../Hosted/Transport/TcpServerTransport.h | 1 + tests/HostTests/modules/Hosted.cpp | 64 ++++++++++++++++++- 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index 4429a268a5..557dd0f3bc 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -10,6 +10,7 @@ class TcpClientStream : public Stream public: TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : cBuffer(cbufferSize), client(client) { + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::consume, this)); } void setClient(TcpClient& client) @@ -64,6 +65,11 @@ class TcpClientStream : public Stream private: CircularBuffer cBuffer; TcpClient& client; + + bool consume(TcpClient& client, char* data, int size) + { + return push(reinterpret_cast(data), size); + } }; } // namespace Transport diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h index 9adaa7e306..ae44e0d39f 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -29,6 +29,7 @@ class TcpServerTransport : public TcpTransport stream = map.valueAt(i); } else { map[key] = new TcpClientStream(client); + client.setReceiveDelegate(TcpClientDataDelegate(&TcpServerTransport::process, this)); stream = map[key]; } diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/modules/Hosted.cpp index 57df0987ea..a5f4fe0cb7 100644 --- a/tests/HostTests/modules/Hosted.cpp +++ b/tests/HostTests/modules/Hosted.cpp @@ -1,10 +1,21 @@ #include #include -#include +#include +#include +#include +#include +#include using namespace simpleRPC; +static uint8_t sum = 0; +static uint8_t plusCommand(uint8_t a, uint8_t b) +{ + sum = a + b; + return sum; +}; + class HostedTest : public TestGroup { public: @@ -48,6 +59,53 @@ class HostedTest : public TestGroup REQUIRE(commands["pinMode"] != 2); REQUIRE(commands["pinMode"] == 0); } + + // RPC Server + server = new TcpServer(); + server->listen(4031); + server->setTimeOut(USHRT_MAX); // disable connection timeout + server->setKeepAlive(USHRT_MAX); // disable connection timeout + + Hosted::Transport::TcpServerTransport transport(*server); + transport.onData([](Stream& stream) { + // clang-format off + interface(stream, + /* + * Below we are exporting the following remote commands: + * - pinMode + * - digitalRead + * - digitalWrite + * You can add more commands here. For every command you should specify command and text description in the format below. + * For more information read the SimpleRPC interface API: https://simplerpc.readthedocs.io/en/latest/api/interface.html + */ + pinMode, "pinMode: Sets mode of digital pin. @pin: Pin number, @mode: Mode type.", + digitalRead, "digitalRead: Read digital pin. @pin: Pin number. @return: Pin value.", + plusCommand, "plusCommand: Sum two numbers. @a: number one. @b: number two." + ); + // clang-format on + + return true; + }); + + // RCP Client + + IpAddress remoteIp = WifiStation.getIP(); + client.connect(remoteIp, 4031); + Hosted::Transport::TcpClientStream stream(client, 1024); + + Hosted::Client hostedClient(stream); + + TEST_CASE("Client::getRemoteCommands()") + { + REQUIRE(hostedClient.getRemoteCommands() == true); + REQUIRE(hostedClient.getFunctionId("plusCommand") == 2); + } + + TEST_CASE("Client::send and wait()") + { + REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint8_t(2)) == true); + REQUIRE(hostedClient.wait() == 5); + } } private: @@ -55,6 +113,10 @@ class HostedTest : public TestGroup uint8_t methodPosition = 0; String parsedCommand; + TcpServer* server{nullptr}; + TcpClient client{false}; + Hosted::Transport::TcpClientStream* stream{nullptr}; + void startMethods() { methodPosition = 0; From a465ba13f4bce87e44e35629d78728f62ca80c69 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 08:33:18 +0200 Subject: [PATCH 18/28] Headers and coding style. --- .../src/{DigitalHosted.cpp => Digital.cpp} | 13 ++++++++++ .../Components/Hosted/include/Hosted/Client.h | 25 +++++++++++++++++++ .../include/Hosted/Transport/BaseTransport.h | 14 +++++++++++ .../Hosted/Transport/SerialTransport.h | 15 +++++++++++ .../Hosted/Transport/TcpClientStream.h | 15 +++++++++++ .../Hosted/Transport/TcpClientTransport.h | 15 +++++++++++ .../Hosted/Transport/TcpServerTransport.h | 15 +++++++++++ .../include/Hosted/Transport/TcpTransport.h | 15 +++++++++++ .../Hosted/init/serial/InitClient.cpp | 13 ++++++++++ .../Components/Hosted/init/tcp/InitClient.cpp | 13 ++++++++++ tests/HostTests/component.mk | 2 +- tests/HostTests/modules/Hosted.cpp | 17 ++++++------- 12 files changed, 161 insertions(+), 11 deletions(-) rename Sming/Components/Hosted-Lib/src/{DigitalHosted.cpp => Digital.cpp} (55%) diff --git a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp b/Sming/Components/Hosted-Lib/src/Digital.cpp similarity index 55% rename from Sming/Components/Hosted-Lib/src/DigitalHosted.cpp rename to Sming/Components/Hosted-Lib/src/Digital.cpp index 129457acfa..4160c8c726 100644 --- a/Sming/Components/Hosted-Lib/src/DigitalHosted.cpp +++ b/Sming/Components/Hosted-Lib/src/Digital.cpp @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Digital.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #include #include diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index 37043386a3..ef9245d0f1 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * Client.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #pragma once #ifndef ARCH_HOST @@ -28,6 +41,13 @@ class Client { } + /** + * @brief Method to send commands to the remote server + * @param functionName + * @param variable arguments + * + * @retval true on success + */ template bool send(const char* functionName, Args... args) { uint8_t functionId = getFunctionId(functionName); @@ -58,6 +78,11 @@ class Client return *(reinterpret_cast(&data)); } + /** + * @brief Fetches a list of commands supported on the RPC server and gives back the id of the desired command + * @param name command name to query + * @retval -1 if not found. Otherwise the id of the function + */ int getFunctionId(const char* name) { if(fetchCommands) { diff --git a/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h index 0985f52478..4f9f6c0132 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h @@ -1,4 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * BaseTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #pragma once + #include #include diff --git a/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h index 946ae108b4..6dca2b1510 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/SerialTransport.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * SerialTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + #include #include "BaseTransport.h" diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index 557dd0f3bc..7a0ae06404 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpClientStream.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + #include #include diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h index e4e32316fc..d6cb1a5dca 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientTransport.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpClientTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + #include #include "TcpTransport.h" #include "TcpClientStream.h" diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h index ae44e0d39f..31fe79e2f1 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpServerTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + #include #include #include "TcpTransport.h" diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h index 8fa0506ae8..9553430592 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpTransport.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * TcpTransport.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + +#pragma once + #include #include "BaseTransport.h" diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index 5fd0f9065f..ed1cf02e52 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InitClient.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #include #include diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp index cf44e414f9..12f67dc30c 100644 --- a/Sming/Components/Hosted/init/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * InitClient.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #include #include #include diff --git a/tests/HostTests/component.mk b/tests/HostTests/component.mk index 35af69539a..be5f787a73 100644 --- a/tests/HostTests/component.mk +++ b/tests/HostTests/component.mk @@ -13,7 +13,7 @@ ARDUINO_LIBRARIES := \ COMPONENT_DEPENDS := \ malloc_count \ axtls-8266 \ - bearssl-esp8266 + bearssl-esp8266 ifeq ($(UNAME),Windows) # Network tests run on Linux only diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/modules/Hosted.cpp index a5f4fe0cb7..3996f15449 100644 --- a/tests/HostTests/modules/Hosted.cpp +++ b/tests/HostTests/modules/Hosted.cpp @@ -1,19 +1,17 @@ #include -#include -#include +#include #include +#include #include #include #include using namespace simpleRPC; -static uint8_t sum = 0; -static uint8_t plusCommand(uint8_t a, uint8_t b) +static uint32_t plusCommand(uint8_t a, uint16_t b) { - sum = a + b; - return sum; + return a + b; }; class HostedTest : public TestGroup @@ -89,8 +87,7 @@ class HostedTest : public TestGroup // RCP Client - IpAddress remoteIp = WifiStation.getIP(); - client.connect(remoteIp, 4031); + client.connect(WifiStation.getIP(), 4031); Hosted::Transport::TcpClientStream stream(client, 1024); Hosted::Client hostedClient(stream); @@ -103,8 +100,8 @@ class HostedTest : public TestGroup TEST_CASE("Client::send and wait()") { - REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint8_t(2)) == true); - REQUIRE(hostedClient.wait() == 5); + REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint16_t(2)) == true); + REQUIRE(hostedClient.wait() == 5); } } From e0a2128292f6665ced733e8bd2796250c3ce5e3f Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 08:49:28 +0200 Subject: [PATCH 19/28] Travis is up and clang-format has to be updated. --- .travis.yml | 2 +- Sming/Arch/Host/Tools/travis/install.sh | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 97b2443e0f..f34faf079b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ jobs: addons: apt: packages: - - clang-format-6.0 + - clang-format-8 - gcc-multilib - g++-multilib - doxygen diff --git a/Sming/Arch/Host/Tools/travis/install.sh b/Sming/Arch/Host/Tools/travis/install.sh index fd27cb5ba9..eb7af6f861 100755 --- a/Sming/Arch/Host/Tools/travis/install.sh +++ b/Sming/Arch/Host/Tools/travis/install.sh @@ -1,7 +1,18 @@ #!/bin/bash set -ex # exit with nonzero exit code if anything fails -sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-6.0 100 +sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 200 + + +sudo update-alternatives --query clang-format + +sudo rm /usr/local/clang-7.0.0/bin/clang-format + +clang-format --version +whereis clang-format +export CLANG_FORMAT=/usr/bin/clang-format +$CLANG_FORMAT --version + python3 -m pip install -q --upgrade pip if [ "$BUILD_DOCS" == "1" ]; then From b51536af37845477f47d2529443b4048e937fe9a Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 09:46:24 +0200 Subject: [PATCH 20/28] Run Hosted Tcp tests only if network is present. --- tests/HostTests/modules/Hosted.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/modules/Hosted.cpp index 3996f15449..7887a840fe 100644 --- a/tests/HostTests/modules/Hosted.cpp +++ b/tests/HostTests/modules/Hosted.cpp @@ -58,6 +58,11 @@ class HostedTest : public TestGroup REQUIRE(commands["pinMode"] == 0); } + if(!WifiStation.isConnected()) { + Serial.println("No network, skipping tests"); + return; + } + // RPC Server server = new TcpServer(); server->listen(4031); From d67b771f2b74b662832c77606d971ffd059f2d43 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 14:30:23 +0200 Subject: [PATCH 21/28] Update Travis to use GCC 9. --- .travis.yml | 2 +- Sming/Arch/Host/Tools/travis/install.sh | 13 +++---------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index f34faf079b..89fb391e62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ jobs: packages: - clang-format-8 - gcc-multilib - - g++-multilib + - g++-9-multilib - doxygen - python3-sphinx - python3-pip diff --git a/Sming/Arch/Host/Tools/travis/install.sh b/Sming/Arch/Host/Tools/travis/install.sh index eb7af6f861..1fcf81b168 100755 --- a/Sming/Arch/Host/Tools/travis/install.sh +++ b/Sming/Arch/Host/Tools/travis/install.sh @@ -1,17 +1,10 @@ #!/bin/bash set -ex # exit with nonzero exit code if anything fails -sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 200 - - -sudo update-alternatives --query clang-format +sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-9 60 --slave /usr/bin/g++ g++ /usr/bin/g++-9 -sudo rm /usr/local/clang-7.0.0/bin/clang-format - -clang-format --version -whereis clang-format -export CLANG_FORMAT=/usr/bin/clang-format -$CLANG_FORMAT --version +sudo update-alternatives --install /usr/bin/clang-format clang-format /usr/bin/clang-format-8 200 +sudo rm -f /usr/local/clang-7.0.0/bin/clang-format python3 -m pip install -q --upgrade pip From be59a417589cb0fc91a2b28aec68ee10cc511059 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 14:46:44 +0200 Subject: [PATCH 22/28] Final touches for the initial and not reviewed yet version. --- Sming/Components/Hosted-Lib/src/Digital.cpp | 4 +--- .../include/Hosted/Transport/TcpClientStream.h | 4 ++-- .../Libraries/simpleRPC/include/simpleRPC/parser.h | 13 +++++++++++++ Sming/Libraries/simpleRPC/src/parser.cpp | 13 +++++++++++++ 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/Sming/Components/Hosted-Lib/src/Digital.cpp b/Sming/Components/Hosted-Lib/src/Digital.cpp index 4160c8c726..d99e748ded 100644 --- a/Sming/Components/Hosted-Lib/src/Digital.cpp +++ b/Sming/Components/Hosted-Lib/src/Digital.cpp @@ -29,7 +29,5 @@ void digitalWrite(uint16_t pin, uint8_t val) uint8_t digitalRead(uint16_t pin) { hostedClient->send(__func__, pin); - uint8_t response = hostedClient->wait(); - - return response; + return hostedClient->wait(); } diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index 7a0ae06404..2fa502418b 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -25,7 +25,7 @@ class TcpClientStream : public Stream public: TcpClientStream(TcpClient& client, size_t cbufferSize = 1024) : cBuffer(cbufferSize), client(client) { - client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::consume, this)); + client.setReceiveDelegate(TcpClientDataDelegate(&TcpClientStream::store, this)); } void setClient(TcpClient& client) @@ -81,7 +81,7 @@ class TcpClientStream : public Stream CircularBuffer cBuffer; TcpClient& client; - bool consume(TcpClient& client, char* data, int size) + bool store(TcpClient& client, char* data, int size) { return push(reinterpret_cast(data), size); } diff --git a/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h index 5f6d422ed2..a1ce2e336e 100644 --- a/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h +++ b/Sming/Libraries/simpleRPC/include/simpleRPC/parser.h @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * parser.h + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #pragma once #include diff --git a/Sming/Libraries/simpleRPC/src/parser.cpp b/Sming/Libraries/simpleRPC/src/parser.cpp index 6e715bdf40..7be4f8183c 100644 --- a/Sming/Libraries/simpleRPC/src/parser.cpp +++ b/Sming/Libraries/simpleRPC/src/parser.cpp @@ -1,3 +1,16 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * parser.cpp + * + * @author 2021 Slavey Karadzhov + * + * + ****/ + #include "../include/simpleRPC/parser.h" namespace simpleRPC From cbb1f81ad9072c9c44cb0d808a0f3b5045110078 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 16 Apr 2021 15:08:30 +0200 Subject: [PATCH 23/28] Applied recommendations from mikee. --- Sming/Components/Hosted/include/Hosted/Client.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index ef9245d0f1..dbc37a161c 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -70,12 +70,9 @@ class Client host_main_loop(); } - std::shared_ptr data(new uint8_t[neededBytes]); - // std::unique_ptr data = std::make_unique(neededBytes); - // uint8_t* data = new uint8_t[neededBytes]; - stream.readBytes(reinterpret_cast(&data), neededBytes); - - return *(reinterpret_cast(&data)); + R result{}; + stream.readBytes(reinterpret_cast(&result), sizeof(result)); + return result; } /** From 583b640a50159578bbf04477362c3ec195be0eae Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Sun, 18 Apr 2021 13:10:36 +0200 Subject: [PATCH 24/28] Applying review recommendations. --- .../include/Hosted/Transport/BaseTransport.h | 6 +++--- .../include/Hosted/Transport/TcpServerTransport.h | 14 +++++--------- .../Hosted/samples/tcp/app/application.cpp | 4 ++-- tests/HostTests/modules/Hosted.cpp | 5 +++++ 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h index 4f9f6c0132..484a568c02 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/BaseTransport.h @@ -25,13 +25,13 @@ class BaseTransport public: using DataHandler = Delegate; - void onData(DataHandler handler) + virtual ~BaseTransport() { - this->handler = handler; } - virtual ~BaseTransport() + void onData(DataHandler handler) { + this->handler = handler; } protected: diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h index 31fe79e2f1..6e85cafc43 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpServerTransport.h @@ -25,7 +25,7 @@ namespace Transport class TcpServerTransport : public TcpTransport { public: - using ClientMap = ObjectMap; + using ClientMap = ObjectMap; TcpServerTransport(TcpServer& server) { @@ -35,15 +35,11 @@ class TcpServerTransport : public TcpTransport protected: bool process(TcpClient& client, char* data, int size) override { - uint32_t key = uint32_t(&client); + auto key = &client; - TcpClientStream* stream = nullptr; - - int i = map.indexOf(key); - if(i >= 0) { - stream = map.valueAt(i); - } else { - map[key] = new TcpClientStream(client); + TcpClientStream* stream = map.find(key); + if(stream == nullptr) { + map[key] = stream = new TcpClientStream(client); client.setReceiveDelegate(TcpClientDataDelegate(&TcpServerTransport::process, this)); stream = map[key]; } diff --git a/Sming/Components/Hosted/samples/tcp/app/application.cpp b/Sming/Components/Hosted/samples/tcp/app/application.cpp index fa4cfb5db3..2e3a467e83 100644 --- a/Sming/Components/Hosted/samples/tcp/app/application.cpp +++ b/Sming/Components/Hosted/samples/tcp/app/application.cpp @@ -21,8 +21,8 @@ constexpr size_t port = 4031; using namespace Hosted::Transport; -TcpServer* server = nullptr; -TcpServerTransport* transport = nullptr; +TcpServer* server; +TcpServerTransport* transport; // Will be called when WiFi station was connected to AP void connectOk(IpAddress ip, IpAddress mask, IpAddress gateway) diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/modules/Hosted.cpp index 7887a840fe..df78b581f8 100644 --- a/tests/HostTests/modules/Hosted.cpp +++ b/tests/HostTests/modules/Hosted.cpp @@ -105,8 +105,13 @@ class HostedTest : public TestGroup TEST_CASE("Client::send and wait()") { + + ElapseTimer timer; + REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint16_t(2)) == true); REQUIRE(hostedClient.wait() == 5); + + debug_i("PlusCommand Roundtrip Time: %s", timer.elapsedTime().toString().c_str()); } } From 563f25a4b80ab2fa73a983c892b8ed71eef4587b Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Sun, 18 Apr 2021 13:12:13 +0200 Subject: [PATCH 25/28] Decrease lag when using TCP. --- .../Hosted/include/Hosted/Transport/TcpClientStream.h | 1 + Sming/Core/Network/MqttClient.cpp | 4 ++-- Sming/Core/Network/TcpClient.h | 10 ++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h index 2fa502418b..9dcc79ece6 100644 --- a/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h +++ b/Sming/Components/Hosted/include/Hosted/Transport/TcpClientStream.h @@ -75,6 +75,7 @@ class TcpClientStream : public Stream void flush() override { + client.commit(); } private: diff --git a/Sming/Core/Network/MqttClient.cpp b/Sming/Core/Network/MqttClient.cpp index 1647272fce..349095d718 100644 --- a/Sming/Core/Network/MqttClient.cpp +++ b/Sming/Core/Network/MqttClient.cpp @@ -264,7 +264,7 @@ bool MqttClient::publish(const String& topic, const String& content, uint8_t fla if(success) { // Try to force-send message to decrease latency. // Should work for small size messages but there is no guarantee. - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + commit(); } return success; @@ -300,7 +300,7 @@ bool MqttClient::publish(const String& topic, IDataSourceStream* stream, uint8_t if(success) { // Try to force-send message to decrease latency. // Should work for small size messages but there is no guarantee. - onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + commit(); } return success; diff --git a/Sming/Core/Network/TcpClient.h b/Sming/Core/Network/TcpClient.h index 71789f7b54..9be0e46cde 100644 --- a/Sming/Core/Network/TcpClient.h +++ b/Sming/Core/Network/TcpClient.h @@ -126,6 +126,16 @@ class TcpClient : public TcpConnection closeAfterSent = ignoreIncomingData ? eTCCASS_AfterSent_Ignore_Received : eTCCASS_AfterSent; } + /** + * @brief Tries to send the pending data immediately. + * @note Call this method to decrease latency. Use it carefully. + */ + void commit() + { + onReadyToSendData(TcpConnectionEvent::eTCE_Poll); + TcpConnection::flush(); + } + protected: err_t onConnected(err_t err) override; err_t onReceive(pbuf* buf) override; From 54882dc352bfb0bc7ff32a778b772d8347654b0e Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Mon, 19 Apr 2021 08:18:38 +0200 Subject: [PATCH 26/28] Moving the main_loop function to a separate header. Should fix issue with Windows compilation. --- .../Components/hostlib/include/hostlib/emu.h | 24 +++++++++++++++++++ .../hostlib/include/hostlib/hostlib.h | 3 --- .../Arch/Host/Components/hostlib/startup.cpp | 1 + .../Components/Hosted/include/Hosted/Client.h | 3 +-- tests/HostTests/modules/Hosted.cpp | 1 - 5 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h new file mode 100644 index 0000000000..5ebda3bce8 --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/emu.h @@ -0,0 +1,24 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * emu.h + * + ****/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Executing this function will run once the main emulator loop. + */ +void host_main_loop(); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h index 1dfbff5151..b30b0cec12 100644 --- a/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/hostlib.h @@ -59,9 +59,6 @@ int msleep(unsigned ms); */ size_t getHostAppDir(char* path, size_t bufSize); - -void host_main_loop(); - #ifdef __cplusplus } #endif diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index 529bee3257..39899959ab 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "include/hostlib/emu.h" #include "include/hostlib/hostlib.h" #include "include/hostlib/CommandLine.h" #include diff --git a/Sming/Components/Hosted/include/Hosted/Client.h b/Sming/Components/Hosted/include/Hosted/Client.h index dbc37a161c..d736e82c28 100644 --- a/Sming/Components/Hosted/include/Hosted/Client.h +++ b/Sming/Components/Hosted/include/Hosted/Client.h @@ -22,8 +22,7 @@ #include #include #include -#include -#include +#include #include using namespace simpleRPC; diff --git a/tests/HostTests/modules/Hosted.cpp b/tests/HostTests/modules/Hosted.cpp index df78b581f8..24d48af805 100644 --- a/tests/HostTests/modules/Hosted.cpp +++ b/tests/HostTests/modules/Hosted.cpp @@ -105,7 +105,6 @@ class HostedTest : public TestGroup TEST_CASE("Client::send and wait()") { - ElapseTimer timer; REQUIRE(hostedClient.send("plusCommand", uint8_t(3), uint16_t(2)) == true); From 24ec69ea26bd470b70fb83826fc68ead4208b8fa Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Mon, 19 Apr 2021 08:45:45 +0200 Subject: [PATCH 27/28] Replace weak function with function wrapper. --- .../Components/hostlib/include/hostlib/init.h | 30 +++++++++++++++++++ Sming/Arch/Host/Components/hostlib/init.cpp | 27 +++++++++++++++++ .../Arch/Host/Components/hostlib/startup.cpp | 7 +---- Sming/Arch/Host/app.mk | 2 +- Sming/Components/Hosted/component.mk | 2 ++ .../Hosted/init/serial/InitClient.cpp | 7 ++++- .../Components/Hosted/init/tcp/InitClient.cpp | 7 ++++- 7 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 Sming/Arch/Host/Components/hostlib/include/hostlib/init.h create mode 100644 Sming/Arch/Host/Components/hostlib/init.cpp diff --git a/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h b/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h new file mode 100644 index 0000000000..76c1f9defe --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/include/hostlib/init.h @@ -0,0 +1,30 @@ +/** + * hostlib.h + * + * Copyright 2019 mikee47 + * + * This file is part of the Sming Framework Project + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SHEM. + * If not, see . + * + ****/ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void host_init(); + +#ifdef __cplusplus +} +#endif diff --git a/Sming/Arch/Host/Components/hostlib/init.cpp b/Sming/Arch/Host/Components/hostlib/init.cpp new file mode 100644 index 0000000000..4246269810 --- /dev/null +++ b/Sming/Arch/Host/Components/hostlib/init.cpp @@ -0,0 +1,27 @@ +/** + * init.cpp - Sming Host Emulator startup code + * + * Copyright 2019 mikee47 + * + * This file is part of the Sming Framework Project + * + * This library is free software: you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation, version 3 or later. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with SHEM. + * If not, see . + * + ****/ + +#include "include/hostlib/init.h" + +extern void init(); + +void host_init() +{ + init(); +} diff --git a/Sming/Arch/Host/Components/hostlib/startup.cpp b/Sming/Arch/Host/Components/hostlib/startup.cpp index 39899959ab..b59632a496 100644 --- a/Sming/Arch/Host/Components/hostlib/startup.cpp +++ b/Sming/Arch/Host/Components/hostlib/startup.cpp @@ -31,6 +31,7 @@ #include #include #include +#include "include/hostlib/init.h" #include "include/hostlib/emu.h" #include "include/hostlib/hostlib.h" #include "include/hostlib/CommandLine.h" @@ -44,15 +45,9 @@ static bool done = false; static bool lwip_initialised = false; static OneShotElapseTimer lwipServiceTimer; -extern void init(); extern void host_wifi_lwip_init_complete(); extern void host_init_bootloader(); -void __attribute__((weak)) host_init() -{ - init(); -} - static void cleanup() { hw_timer_cleanup(); diff --git a/Sming/Arch/Host/app.mk b/Sming/Arch/Host/app.mk index 3322bd77e1..0e5c61c07e 100644 --- a/Sming/Arch/Host/app.mk +++ b/Sming/Arch/Host/app.mk @@ -14,7 +14,7 @@ TARGET_OUT_0 := $(FW_BASE)/$(APP_NAME)$(TOOL_EXT) # Hosted Settings ifneq ($(ENABLE_HOSTED),) - COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-$(CMP_Hosted_LIBHASH).a $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib-$(CMP_Hosted-Lib_LIBHASH).a $(COMPONENTS_AR) + COMPONENTS_AR := $(USER_LIBDIR)/$(CLIB_PREFIX)Hosted-Lib-$(CMP_Hosted-Lib_LIBHASH).a $(COMPONENTS_AR) endif # Target definitions diff --git a/Sming/Components/Hosted/component.mk b/Sming/Components/Hosted/component.mk index dc65e53f03..11f6778e28 100644 --- a/Sming/Components/Hosted/component.mk +++ b/Sming/Components/Hosted/component.mk @@ -5,12 +5,14 @@ COMPONENT_DEPENDS := simpleRPC # Architecture of the device where the hosted service will be flashed HOSTED_ARCH ?= Esp8266 +COMPONENT_RELINK_VARS += ENABLE_HOSTED COMPONENT_VARS := ENABLE_HOSTED ENABLE_HOSTED ?= ifneq ($(ENABLE_HOSTED),) COMPONENT_SRCDIRS += $(COMPONENT_PATH)/init/$(ENABLE_HOSTED) + EXTRA_LDFLAGS := -Wl,-wrap,host_init endif COMPONENT_VARS += HOSTED_SERVER_IP diff --git a/Sming/Components/Hosted/init/serial/InitClient.cpp b/Sming/Components/Hosted/init/serial/InitClient.cpp index ed1cf02e52..cb9d2b2745 100644 --- a/Sming/Components/Hosted/init/serial/InitClient.cpp +++ b/Sming/Components/Hosted/init/serial/InitClient.cpp @@ -18,7 +18,12 @@ Hosted::Client* hostedClient{nullptr}; extern void init(); -void host_init() +extern "C" { +void __real_host_init(); +void __wrap_host_init(); +} + +void __wrap_host_init() { Serial.begin(115200); hostedClient = new Hosted::Client(Serial); diff --git a/Sming/Components/Hosted/init/tcp/InitClient.cpp b/Sming/Components/Hosted/init/tcp/InitClient.cpp index 12f67dc30c..04a71dc3c1 100644 --- a/Sming/Components/Hosted/init/tcp/InitClient.cpp +++ b/Sming/Components/Hosted/init/tcp/InitClient.cpp @@ -30,6 +30,11 @@ Hosted::Client* hostedClient{nullptr}; #define REMOTE_IP STRINGIFY(HOSTED_SERVER_IP) #endif +extern "C" { +void __real_host_init(); +void __wrap_host_init(); +} + extern void init(); namespace @@ -59,7 +64,7 @@ static void ready(IpAddress ip, IpAddress mask, IpAddress gateway) } // namespace -void host_init() +void __wrap_host_init() { WifiEvents.onStationGotIP(ready); WifiStation.enable(true); From 369c483e255e5d488bb3289d2eb5a09be8269e48 Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Mon, 19 Apr 2021 09:24:22 +0200 Subject: [PATCH 28/28] Use only Esp8266 EQT for Travis builds. --- .travis.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 89fb391e62..7c56d90a9e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,14 +28,6 @@ jobs: - jq env: - SMING_ARCH=Host - - - stage: build - name: C++11 - env: - - SMING_ARCH=Esp8266 - - SDK_VERSION=3.0.1 - - ESP_HOME=$TRAVIS_BUILD_DIR/opt/esp-alt-sdk - - stage: build name: C++17 env: