diff --git a/.hash_db b/.hash_db index a7c98268..4b933022 100644 --- a/.hash_db +++ b/.hash_db @@ -1,84 +1,105 @@ -./setup.py:8775ca7225d845ad1118efe5ac753337 -./src/TinyProtocolFd.cpp:acf146d0a76f3cda95429dce729092fc +./setup.py:6a65fb06d718c8ed6ffd5e1e2ce7e337 +./src/TinyProtocolFd.cpp:dc52761597f73e0581db6510d7923cf6 ./src/TinyLightProtocol.h:a684b685ec5ba4641ee10d24caf0f8cb -./src/TinyProtocolFd.h:1e1d12b2171d29c4a54c809575da28d4 +./src/TinyProtocolFd.h:6db1007de88e2fc2dfc2bf18c55ddf4c ./src/TinyLightProtocol.cpp:edacf9b017dece5234deb2c95c5c3e7d -./src/TinyPacket.h:b56b28b96ea3fcbe92caabc451dc2a46 -./src/TinyProtocolHdlc.cpp:faae0c5dafec91d17e36cdb1e79cc8fd +./src/TinyPacket.h:916d0bd1221d3ec2ddfc5ac70a26b344 +./src/TinyProtocolHdlc.cpp:49d00baaa1c26434215aa1cf6c6d75d2 ./src/TinyProtocolHdlc.h:3603d51944e3fbc4e054a303db241a6a -./src/TinyProtocol.h:809be13c3866e0079f241bdf89515905 +./src/TinyProtocol.h:56877f0a2681b3393daab59460a46a97 ./src/proto/crc/tiny_crc.h:ec366c5c7751a822b2c4931e40150a84 ./src/proto/crc/tiny_crc.c:73d4dca2d3805a8127b1a710599bce9e -./src/proto/hdlc/low_level/hdlc.c:e9cef2ceddf62003be442e545606fef7 -./src/proto/hdlc/low_level/hdlc.h:46b3f839d54ccd1e74f1ee6a03e416ec -./src/proto/hdlc/low_level/hdlc_int.h:b0de584cb91f54e076205819482211f7 -./src/proto/hdlc/high_level/hdlc.c:e05cb9f1d665e05c1d6049d6d21098fb -./src/proto/hdlc/high_level/hdlc.h:45f2819c6fc7ca87a9973d85755da7af -./src/proto/fd/tiny_fd_frames.c:4beb7ca7ede2a9d2b95f640eb55b61f8 -./src/proto/fd/tiny_fd_int.h:60c9ca86201de0f701c8f08ac6e64607 -./src/proto/fd/tiny_fd_frames_int.h:d5fd2ddec29cd8fcfcacb65a1efe7b00 -./src/proto/fd/tiny_fd.c:869e50962199beddb7f6d5a6ad1328b8 -./src/proto/fd/tiny_fd.h:72ae1056ac69df1cff98a985299856e2 -./src/proto/light/tiny_light.c:415fd59073e4387b1983b7b7b0a14533 -./src/proto/light/tiny_light.h:47c725e8f06bee91be92af93ed0a1407 +./src/proto/hdlc/low_level/hdlc.c:f9d0a64ccf41c0b8a2c2bbeb622652ab +./src/proto/hdlc/low_level/hdlc.h:e06a1a5c44259759b4260c7b29bfe303 +./src/proto/hdlc/low_level/hdlc_int.h:af5576216218e1d9f55395312d8cf3b1 +./src/proto/hdlc/high_level/hdlc.c:65870a83fbb238e91ec1dfeb0d54439e +./src/proto/hdlc/high_level/hdlc.h:4be46b73f8b2dbbdf2fa737f00fa1b77 +./src/proto/fd/tiny_fd_frames.c:ea1ec41497b45cf62371bd8e6062f284 +./src/proto/fd/tiny_fd_int.h:f6c7329485870ac86a9043f5d2d468fc +./src/proto/fd/tiny_fd_frames_int.h:4153dc1d7f31da43095514c8694c18ec +./src/proto/fd/tiny_fd.c:6c50c6ad70b658d2d317f3a19f9d9bcb +./src/proto/fd/tiny_fd.h:7148a2d7e8f1dfe3121c181534599525 +./src/proto/light/tiny_light.c:c7bbb1ce83bd7f09e2b0a1b372485ef7 +./src/proto/light/tiny_light.h:61e2d702ae9490e451cde3cdd36d25ce ./src/hal/tiny_debug.h:ec3baec310bcef743d2e16957253ca6f ./src/hal/tiny_list.c:db940e41cce84752164648cb9e1613df -./src/hal/tiny_types.h:0bc8dd83fcb5fa5b02a96a58288382e5 -./src/hal/tiny_serial.h:9506b27b1b4db5651406f0afe262266b +./src/hal/tiny_types.h:5bb6f783238245be50cd4e444544889b +./src/hal/tiny_serial.h:0399afef7fd429e9461d9bbeed009c95 ./src/hal/tiny_list.h:69b562367054b805e7e27581e427e5bd -./src/hal/tiny_types.c:77dbfa4d58a4359eac564af2feeee3dc -./src/hal/tiny_serial.c:379a35795a230e2502a67c709aab04b5 +./src/hal/tiny_types.c:1e1a12815d82d93d27d38f5650b97353 +./src/hal/tiny_serial.c:3a33d156aff044633cff04a900601cd5 ./src/hal/arduino/arduino_hal.inl:168da39110fe026168904a881538af6c ./src/hal/arduino/arduino_hal.h:06465d36d34bd55179707e956210544c -./src/hal/linux/linux_hal.h:f22f0625726652e842866bb0ad352411 +./src/hal/linux/linux_hal.h:63c1b74aaedcfa7e5e89098068e99443 ./src/hal/linux/linux_serial.inl:0354da90c95ae791f0c6e64c5eac63f7 ./src/hal/linux/linux_serial.h:9f2594db69e4a98891fe780395c9ae62 -./src/hal/linux/linux_hal.inl:bfbae9584d3320f3b822370a439269bb -./src/hal/esp32/esp32_hal.h:6113e4e5411c75eea3c794bc28861702 +./src/hal/linux/linux_hal.inl:cd6a48e6c95b23c9ec0d68181923622f +./src/hal/esp32/esp32_hal.h:bc3b84848393530797c47a31712cbb4b ./src/hal/esp32/esp32_hal.inl:b9bd509161524eee58743875995d62bd ./src/hal/avr/avr_hal.h:f0faa8c545a6f4259c7eb2980d275bba ./src/hal/avr/avr_hal.inl:dd4e0d37312349c1dc35fdcdff8786c0 ./src/hal/no_platform/noplatform_serial.h:9f2594db69e4a98891fe780395c9ae62 ./src/hal/no_platform/no_platform_hal.h:ac36d0f88f70ce88ca3f5cdd5731ab29 ./src/hal/no_platform/no_platform_hal.inl:459112564afc8e2b271975c39ea83ae2 -./src/hal/mingw32/mingw32_hal.h:fb6697f9d2e7d9248aa4913defe35f95 +./src/hal/mingw32/mingw32_hal.h:ed7464e4405cd7c0148339cf8469f3ea ./src/hal/mingw32/mingw32_hal.inl:e19ec5e42f167f89ee0fff4a6ca77218 ./src/hal/win32/win32_hal.inl:4478824cd82891dd9f87aa5118fa8545 ./src/hal/win32/win32_serial.inl:a455c67d103c5eab8c27b6f2cd77a983 ./src/hal/win32/win32_serial.h:3b28603bd650706c7aea0caccc069b4d -./src/hal/win32/win32_hal.h:2a71b3291935017689ae7585fda36f33 +./src/hal/win32/win32_hal.h:2fd3b03be2a6a501f8cdb00bae97a3f3 ./src/hal/single_core/hal_single_core.inl:c652ec6f2c6f4755d48fd0f389fa3603 ./unittest/packet_tests.cpp:f605740365a18b398e61fed987d892f4 -./unittest/hdlc_tests.cpp:f04c965a8dfd9b493a44fee4ec22b6c6 +./unittest/hdlc_tests.cpp:f17e0ab45d0ec2555be553111f891036 ./unittest/hal_tests.cpp:bcafc594a1d14046d0589b03d79cdeba ./unittest/main.cpp:a5d2d3bcfd2c084575144432699615af -./unittest/light_tests.cpp:11956897536d066270a83ba431c1926e -./unittest/fd_tests.cpp:ccd8151cc8fc22791d5cc6743a370612 +./unittest/light_tests.cpp:d19aaeef4014babe927be441bc744a3d +./unittest/fd_tests.cpp:2effb9109059715ef4c254a5db482233 ./unittest/helpers/fake_wire.cpp:278850827a1f550db720ab0daa3a06d4 -./unittest/helpers/tiny_fd_helper.cpp:ff095f96fd1afc77f3c3607ffbc4a3cc -./unittest/helpers/fake_wire.h:78cbaed09db450c9ebf260fc45362334 -./unittest/helpers/fake_connection.cpp:2279cd42f10b50e81739813783224b8d -./unittest/helpers/fake_endpoint.cpp:e8706b5310db12dca55f28e0a50320bb +./unittest/helpers/tiny_fd_helper.cpp:198b6e1ffe917aaef39e4caea1ef0458 +./unittest/helpers/fake_wire.h:d4376c88bb2900aafe14887085a27607 +./unittest/helpers/fake_connection.cpp:df9ccb0a0019a9e93f3f4cbd513ab0e3 +./unittest/helpers/fake_endpoint.cpp:bf2a8d7d026c9c545f459c7a37712338 ./unittest/helpers/tiny_base_helper.cpp:edf076d2e466029c6613bfe02d3d2272 ./unittest/helpers/semaphore_helper.h:aac4cd56d3f6b4ea76328152d7c13f65 -./unittest/helpers/fake_endpoint.h:2229b37293da9ad97af75086f2756b09 -./unittest/helpers/tiny_hdlc_helper.cpp:805d67c197d8035b9a9643a99e24b21e -./unittest/helpers/tiny_hdlc_helper.h:fdb9e14e35d3a0dcd789ea8a23966f46 -./unittest/helpers/fake_connection.h:c913aae2ed0196d70057d7bd1587b3bc +./unittest/helpers/fake_endpoint.h:61184617911efd05a026e5d169fd8ac6 +./unittest/helpers/tiny_hdlc_helper.cpp:29dea22e1b598538475e3675de3587be +./unittest/helpers/tiny_hdlc_helper.h:88f4ea73a648a7e1a24d6acc944f5b48 +./unittest/helpers/fake_connection.h:702a3253c031ab4e5612d08c4b0c4b1d ./unittest/helpers/tiny_base_helper.h:40ef1492748b45514e0798794536f644 ./unittest/helpers/tiny_light_helper.cpp:2e001bd885b7b71db6d45a66c7d63f54 -./unittest/helpers/tiny_fd_helper.h:52ffc9c680dbf6f7a887ff4b5e780a48 +./unittest/helpers/tiny_fd_helper.h:f171aaef68243291c867acafd04648b6 ./unittest/helpers/tiny_light_helper.h:e6324b2e96a6cbe1a09c5634639c70ba ./python/hdlc_ll.h:888d0725f810e5aaf68475d29c4f9533 ./python/fd.h:fdd07995e6ef9db40e2b1f10d3840d8f -./python/main.cpp:34255ea2532f9ef38d208e59c83a6634 -./python/hdlc_ll.cpp:d79004d6397cc677448c6b7a2f71f082 -./python/fd.cpp:38b67d7a49dea267fffbe9d71a88cd42 +./python/main.cpp:76a3d87011ad890b2fb1f0a33ad074a0 +./python/hdlc_ll.cpp:727b6a7f052efd60325a0a9885f3923b +./python/fd.cpp:78ddabbd9edcb38a850b02c98b7281ed ./python/__init__.py:6960c69f1d180109c61003badb83dc6b ./python/options/__init__.py:bd9232ea6932e44b412796fe57a835c3 ./python/wrappers/__init__.py:d4882c0a8b1d2cd9b93c0ebb4d00f8c8 ./tools/hal_template_functions/platform_hal.c:65b3641a1d6a998aa6758892bbe4b18b -./src/hal/tiny_types_cpp.cpp:67981a27bacb015ed3aac52023444706 -./src/hal/cpp/cpp_hal.h:f35701b7333807fc1c3f591b55112c57 -./src/hal/cpp/cpp_hal.inl:a214dca4b7ba191f4708163b36bbe4c0 -./unittest/fd_multidrop_tests.cpp:c92b46501ad2da42ebb0bcec588e6375 +./src/tinyproto.h:0cb81312452718c98861e4d97c657b42 +./src/TinyProtocol.cpp:f54ac0c67f7cb9e26b17e0dc53096810 +./src/interface/TinySerial.h:3913e5134de45d9f5839fd8022c7acb3 +./src/interface/TinySerial.cpp:af0ab33ca149ee544376d8d493c1694a +./src/link/TinyLinkLayer.h:78a7f59e9c0924a846f3689d91c6e6c8 +./src/link/TinyHdlcLinkLayer.h:48d8fa2929714628d1d3a6fed49db4df +./src/link/TinySerialFdLink.h:6f43bf69afda07153295f5ac086e9bb8 +./src/link/TinyLinkLayer.cpp:fac80ae5781b043285f7b5c58ed75841 +./src/link/TinySerialHdlcLink.cpp:a4dd427178817266fb7c4835e15cb3e3 +./src/link/TinyHdlcLinkLayer.cpp:3f83291eab7d00a782c1f9e12b6e6af8 +./src/link/TinyFdLinkLayer.cpp:b17559e42aca68c3eaa349778727fcef +./src/link/TinySerialFdLink.cpp:a3645d25a8a9276e24115aad3efec1f2 +./src/link/TinySerialLinkLayer.cpp:87f5c8102c0cac6163843df8232cdd29 +./src/link/TinyFdLinkLayer.h:8bddd88e8b1ecad4633cb113c8c36281 +./src/link/TinySerialLinkLayer.h:e5d33a746fa0630ea6d1aa816b5f8fd0 +./src/link/TinySerialHdlcLink.h:bf0b5e032b859f72cc3a4a03e96e138c +./src/hal/tiny_serial_cpp.cpp:3fdefe16cf76a59d364a16d595f2bd7e +./src/hal/arduino/arduino_serial.inl:7bdd66771fae6fcba2fcb11c53f642aa +./src/hal/arduino/arduino_serial.h:d1029eb03354a63b4248513a13da5f6b +./src/hal/esp32/esp32_serial.inl:8dee400fd7c298b269e18f9fba973506 +./src/hal/esp32/esp32_serial.h:c619f28e5762a1da3ac797a6205607c7 +./src/hal/no_platform/noplatform_serial.inl:e33eb4ae4554074cd400c8b98852d023 +./python/py_serial.cpp:4da3de2dbc6b2b72582c06c9f7bbdcda +./python/py_serial.h:68e016b996aea104b0bf8a908df96db6 +./python/helpers/__init__.py:9df0084a1c0b1cadec43caaf0b21cc20 diff --git a/.travis/run_tests.sh b/.travis/run_tests.sh index c2e83fad..a03f0fe6 100755 --- a/.travis/run_tests.sh +++ b/.travis/run_tests.sh @@ -20,8 +20,8 @@ create_terminals ./bld/tiny_loopback -p /tmp/sideB -t fd -r -w 7 & sleep 0.5 # Run tests on sideA for 15 seconds -# Use window 4 for now, need to solve overload connection issues -./bld/tiny_loopback -p /tmp/sideA -t fd -g -r -w 4 +# We don't want to limit loopback side, so test can miss some frames +./bld/tiny_loopback -p /tmp/sideA -t fd -g -r -w 7 if [ $? -ne 0 ]; then close_terminals_and_exit 1 fi diff --git a/Makefile.common b/Makefile.common index d3c78fe5..3e3a6be7 100644 --- a/Makefile.common +++ b/Makefile.common @@ -17,6 +17,8 @@ CPPFLAGS += -I./src CFLAGS += -std=gnu99 CPPFLAGS += -Os -Wall -Werror -ffunction-sections -fdata-sections $(EXTRA_CPPFLAGS) +# Uncomment for performance profiling +# CPPFLAGS += -pg CXXFLAGS += -std=c++11 ifneq ($(LOG_LEVEL),) @@ -74,9 +76,18 @@ OBJ_LIB += \ src/hal/tiny_types.o \ src/hal/tiny_types_cpp.o \ src/hal/tiny_serial.o \ + src/hal/tiny_serial_cpp.o \ src/TinyProtocolHdlc.o \ src/TinyProtocolFd.o \ src/TinyLightProtocol.o \ + src/TinyProtocol.o \ + src/link/TinyLinkLayer.o \ + src/link/TinyFdLinkLayer.o \ + src/link/TinyHdlcLinkLayer.o \ + src/link/TinySerialLinkLayer.o \ + src/link/TinySerialFdLink.o \ + src/link/TinySerialHdlcLink.o \ + src/interface/TinySerial.o \ prep: ifdef CONFIG_FOR_WINDOWS_BUILD diff --git a/README.md b/README.md index a2033ce6..57e1e5bb 100644 --- a/README.md +++ b/README.md @@ -67,13 +67,18 @@ Main features: ### What if my platform is not yet supported? - That's not a problem. Just implement abstraction layer for your platform (timing and mutex functions). + That's not a problem. Just implement abstraction layer for your platform (timing and mutex functions). Please +go through the steps below: + + * add TINY_CUSTOM_PLATFORM define to your compilation flags. + * Implement HAL functions and call `tiny_hal_init()` to pass your platform functions to the library + * add CONFIG_TINYHAL_THREAD_SUPPORT define to your compilation flags if your platform supports standard c++ thread library + Refer to `tiny_hal_init()` function. To understand HAL implementation refer to [Linux](https://github.com/lexus2k/tinyproto/blob/master/src/hal/linux/linux_hal.inl) and [ESP32](https://github.com/lexus2k/tinyproto/blob/master/src/hal/esp32/esp32_hal.inl) examples in [HAL abstraction layer](https://github.com/lexus2k/tinyproto/tree/master/src/hal). -Do not forget to add TINY_CUSTOM_PLATFORM define to your compilation flags. You may use template code -[platform_hal.c](tools/hal_template_functions/platform_hal.c) +You may use template code [platform_hal.c](tools/hal_template_functions/platform_hal.c) ## Easy to use @@ -81,7 +86,7 @@ Do not forget to add TINY_CUSTOM_PLATFORM define to your compilation flags. You Usage of light Tiny Protocol in C++ can look like this: ```.cpp -#include "TinyProtocol.h" +#include "tinyproto.h" tinyproto::Light proto; tinyproto::Packet<256> packet; @@ -104,7 +109,7 @@ void loop() { Example of using full duplex Tiny Protocol in C++ is a little bit bigger, but it is still simple: ```.cpp -#include "TinyProtocol.h" +#include "tinyproto.h" tinyproto::Fd proto; diff --git a/component.mk b/component.mk index bbe693dc..35a07c2e 100644 --- a/component.mk +++ b/component.mk @@ -9,6 +9,8 @@ COMPONENT_SRCDIRS := ./src \ ./src/proto/fd \ ./src/proto/light \ ./src/proto/crc \ + ./src/link \ + ./src/interface \ CPPFLAGS += \ -DTINY_LOG_LEVEL_DEFAULT=0 \ diff --git a/examples/arduino_generic/sketch_light/sketch_light.ino b/examples/arduino_generic/classic_api/sketch_light/sketch_light.ino similarity index 100% rename from examples/arduino_generic/sketch_light/sketch_light.ino rename to examples/arduino_generic/classic_api/sketch_light/sketch_light.ino diff --git a/examples/arduino_generic/tinyfd_loopback/tinyfd_loopback.ino b/examples/arduino_generic/classic_api/tinyfd_loopback/tinyfd_loopback.ino similarity index 100% rename from examples/arduino_generic/tinyfd_loopback/tinyfd_loopback.ino rename to examples/arduino_generic/classic_api/tinyfd_loopback/tinyfd_loopback.ino diff --git a/examples/arduino_generic/tinylight_loopback/tinylight_loopback.ino b/examples/arduino_generic/classic_api/tinylight_loopback/tinylight_loopback.ino similarity index 100% rename from examples/arduino_generic/tinylight_loopback/tinylight_loopback.ino rename to examples/arduino_generic/classic_api/tinylight_loopback/tinylight_loopback.ino diff --git a/examples/arduino_generic/new_api/fd_lookback_callback/fd_loopback_callback.ino b/examples/arduino_generic/new_api/fd_lookback_callback/fd_loopback_callback.ino new file mode 100644 index 00000000..1889af27 --- /dev/null +++ b/examples/arduino_generic/new_api/fd_lookback_callback/fd_loopback_callback.ino @@ -0,0 +1,44 @@ +/* + * This example sends back every buffer received over UART. + * + * !README! + * The sketch is developed to perform UART tests between Arduino + * and PC. + * 1. Burn this program to Arduino + * 2. Compile tiny_loopback tool (see tools folder) for your system + * 3. Connect Arduino TX and RX lines to your PC com port + * 4. Run tiny_loopback on the PC (use correct port name on your system) + * 5. tiny_loopback will print the test speed results + * + * Also, this example demonstrates how to pass data between 2 systems + * By default the sketch and tiny_loopback works as 115200 speed. + */ +#include + +// By default SerialFdProto class allows sending and receiving +// messages upto 32 bytes, we will use Serial0 +tinyproto::SerialFdProto proto(Serial); + +void onRead(tinyproto::Proto &proto, tinyproto::IPacket &packet) +{ + // process received packet here + // send it back + proto.send(packet, 0); +} + +void setup() +{ + /* Run at 115200 */ + proto.getLink().setSpeed( 115200 ); + /* Lets use 8-bit checksum, available on all platforms */ + proto.getLink().setCrc( HDLC_CRC_8 ); //enableCheckSum(); + /* Set callback for incoming packets */ + proto.setRxCallback( onRead ); + /* Start */ + proto.begin(); +} + +void loop() +{ + proto.read(0); +} diff --git a/examples/arduino_generic/new_api/fd_loopback/fd_loopback.ino b/examples/arduino_generic/new_api/fd_loopback/fd_loopback.ino new file mode 100644 index 00000000..7c951558 --- /dev/null +++ b/examples/arduino_generic/new_api/fd_loopback/fd_loopback.ino @@ -0,0 +1,46 @@ +/* + * This example sends back every buffer received over UART. + * + * !README! + * The sketch is developed to perform UART tests between Arduino + * and PC. + * 1. Burn this program to Arduino + * 2. Compile tiny_loopback tool (see tools folder) for your system + * 3. Connect Arduino TX and RX lines to your PC com port + * 4. Run tiny_loopback on the PC (use correct port name on your system) + * 5. tiny_loopback will print the test speed results + * + * Also, this example demonstrates how to pass data between 2 systems + * By default the sketch and tiny_loopback works as 115200 speed. + */ +#include + +// By default SerialFdProto class allows sending and receiving +// messages upto 32 bytes, we will use Serial0 + +tinyproto::SerialFdProto proto(Serial); +tinyproto::StaticPacket<32> poolPacket; + +void setup() +{ + /* Run at 115200 */ + proto.getLink().setSpeed( 115200 ); + /* Lets use 8-bit checksum, available on all platforms */ + proto.getLink().setCrc( HDLC_CRC_8 ); //enableCheckSum(); + /* Register global packet buffer in the pool */ + proto.addRxPool( poolPacket ); + /* Start */ + proto.begin(); +} + +void loop() +{ + IPacket * packet = proto.read(0) + if (packet) + { + // process received packet + // Send it back + proto.send(*packet, 100); + proto.release(packet); + } +} diff --git a/examples/arduino_zero_m0/tinyfd_loopback_m0/tinyfd_loopback_m0.ino b/examples/arduino_zero_m0/classic_api/tinyfd_loopback_m0/tinyfd_loopback_m0.ino similarity index 100% rename from examples/arduino_zero_m0/tinyfd_loopback_m0/tinyfd_loopback_m0.ino rename to examples/arduino_zero_m0/classic_api/tinyfd_loopback_m0/tinyfd_loopback_m0.ino diff --git a/examples/arduino_zero_m0/tinylight_loopback_m0/tinylight_loopback_m0.ino b/examples/arduino_zero_m0/classic_api/tinylight_loopback_m0/tinylight_loopback_m0.ino similarity index 100% rename from examples/arduino_zero_m0/tinylight_loopback_m0/tinylight_loopback_m0.ino rename to examples/arduino_zero_m0/classic_api/tinylight_loopback_m0/tinylight_loopback_m0.ino diff --git a/examples/arduino_zero_m0/new_api/fd_loopback/fd_loopback.ino b/examples/arduino_zero_m0/new_api/fd_loopback/fd_loopback.ino new file mode 100644 index 00000000..dff1a7b5 --- /dev/null +++ b/examples/arduino_zero_m0/new_api/fd_loopback/fd_loopback.ino @@ -0,0 +1,39 @@ +/* + * This example sends back every buffer received over UART. + * + * !README! + * The sketch is developed to perform UART tests between Arduino + * and PC. + * 1. Burn this program to Arduino + * 2. Compile tiny_loopback tool (see tools folder) for your system + * 3. Connect Arduino TX and RX lines to your PC com port + * 4. Run tiny_loopback on the PC (use correct port name on your system) + * 5. tiny_loopback will print the test speed results + * + * Also, this example demonstrates how to pass data between 2 systems + * By default the sketch and tiny_loopback works as 115200 speed. + */ +#include + +// By default SerialFdProto class allows sending and receiving +// messages upto 32 bytes, we will use Serial0 +tinyproto::SerialFdProto proto(SerialUSB); + +void setup() +{ + /* Run at 115200 */ + proto.getLink().setSpeed( 115200 ); + /* Lets use 8-bit checksum, available on all platforms */ + proto.getLink().setCrc( HDLC_CRC_8 ); //enableCheckSum(); + /* Start */ + proto.begin(); +} + +void loop() +{ + tinyproto::StaticPacket<32> packet; + if ( proto.read(packet, 0) ) + { + // process received packet + } +} diff --git a/examples/esp32_idf/uart/tinyfd_generator/main/main.cpp b/examples/esp32_idf/uart/tinyfd_generator/main/main.cpp index 43887ec7..eb25d067 100644 --- a/examples/esp32_idf/uart/tinyfd_generator/main/main.cpp +++ b/examples/esp32_idf/uart/tinyfd_generator/main/main.cpp @@ -22,6 +22,7 @@ SOFTWARE. */ +#include "hal/tiny_serial.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/uart.h" @@ -33,7 +34,6 @@ #define TINY_MULTITHREAD #define WINDOW_SIZE (7) #define GENERATED_PACKET_SIZE (64) -#define BUF_SIZE (512) /* Creating protocol object is simple. Lets define 256 bytes as maximum. * * size for the packet and use 7 packets in outgoing queue. */ @@ -43,6 +43,7 @@ uint32_t s_receivedBytes = 0; uint32_t s_receivedOverheadBytes = 0; uint32_t s_sentBytes = 0; uint32_t s_sentOverheadBytes = 0; +tiny_serial_handle_t s_serial = TINY_SERIAL_INVALID; void onReceive(void *udata, uint8_t addr, tinyproto::IPacket &pkt) { @@ -68,7 +69,7 @@ void tx_task(void *arg) for ( ;; ) { proto.run_tx( - [](void *p, const void *b, int s) -> int { return uart_read_bytes(UART_NUM_1, (uint8_t *)b, s, 10); }); + [](void *p, const void *b, int s) -> int { return tiny_serial_send_timeout(s_serial, b, s, 100); }); } vTaskDelete(NULL); } @@ -77,7 +78,7 @@ void rx_task(void *arg) { for ( ;; ) { - proto.run_rx([](void *p, void *b, int s) -> int { return uart_write_bytes(UART_NUM_1, (const char *)b, s); }); + proto.run_rx([](void *p, void *b, int s) -> int { return tiny_serial_read_timeout(s_serial, b, s, 100); }); } vTaskDelete(NULL); } @@ -86,20 +87,7 @@ void rx_task(void *arg) void main_task(void *args) { - uart_config_t uart_config = { - .baud_rate = 115200, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - .rx_flow_ctrl_thresh = 0, - .use_ref_tick = false, - // .source_clk = 0, // APB - }; - ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config)); - ESP_ERROR_CHECK( - uart_set_pin(UART_NUM_1, GPIO_NUM_4 /*TX*/, GPIO_NUM_5 /* RX */, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); - ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0)); + s_serial = tiny_serial_open("uart1,4,5", 115200); /* Lets use 16-bit checksum as ESP32 allows that */ proto.enableCrc16(); @@ -119,7 +107,7 @@ void main_task(void *args) #endif auto startTs = std::chrono::steady_clock::now(); - tinyproto::Packet packet(GENERATED_PACKET_SIZE); + tinyproto::HeapPacket packet(GENERATED_PACKET_SIZE); packet.put("Generated frame. test in progress"); for ( ;; ) @@ -146,9 +134,8 @@ void main_task(void *args) #if defined(TINY_MULTITHREAD) #else - proto.run_rx([](void *p, void *b, int s) -> int { return uart_read_bytes(UART_NUM_1, (uint8_t *)b, s, 0); }); - proto.run_tx( - [](void *p, const void *b, int s) -> int { return uart_tx_chars(UART_NUM_1, (const char *)b, s); }); + proto.run_rx([](void *p, void *b, int s) -> int { return tiny_serial_read_timeout(s_serial, b, s, 0); }); + proto.run_tx([](void *p, const void *b, int s) -> int { return tiny_serial_send_timeout(s_serial, b, s, 0); }); #endif } vTaskDelete(NULL); diff --git a/examples/esp32_idf/uart/tinyfd_loopback/main/main.cpp b/examples/esp32_idf/uart/tinyfd_loopback/main/main.cpp index db8776aa..9003eb44 100644 --- a/examples/esp32_idf/uart/tinyfd_loopback/main/main.cpp +++ b/examples/esp32_idf/uart/tinyfd_loopback/main/main.cpp @@ -37,6 +37,7 @@ * By default the sketch and tiny_loopback works as 115200 speed. */ +#include "hal/tiny_serial.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/uart.h" @@ -51,6 +52,7 @@ /* Creating protocol object is simple. Lets define 128 bytes as maximum. * * size for the packet and use 7 packets in outgoing queue. */ tinyproto::FdD proto(tiny_fd_buffer_size_by_mtu(128, 7)); +tiny_serial_handle_t s_serial = TINY_SERIAL_INVALID; void onReceive(void *udata, uint8_t addr, tinyproto::IPacket &pkt) { @@ -73,7 +75,7 @@ void tx_task(void *arg) for ( ;; ) { proto.run_tx( - [](void *p, const void *b, int s) -> int { return uart_write_bytes(UART_NUM_1, (const char *)b, s); }); + [](void *p, const void *b, int s) -> int { return tiny_serial_send_timeout(s_serial, b, s, 100); }); } vTaskDelete(NULL); } @@ -81,20 +83,7 @@ void tx_task(void *arg) void main_task(void *args) { - uart_config_t uart_config = { - .baud_rate = 115200, - .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, - .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, - .rx_flow_ctrl_thresh = 0, - .use_ref_tick = false, - // .source_clk = 0, // APB - }; - ESP_ERROR_CHECK(uart_param_config(UART_NUM_1, &uart_config)); - ESP_ERROR_CHECK( - uart_set_pin(UART_NUM_1, GPIO_NUM_4 /*TX*/, GPIO_NUM_5 /* RX */, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); - ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0)); + s_serial = tiny_serial_open("uart1,4,5,-1,-1", 115200); /* Lets use 16-bit checksum as ESP32 allows that */ proto.enableCrc16(); @@ -111,11 +100,10 @@ void main_task(void *args) for ( ;; ) { #if defined(TINY_MULTITHREAD) - proto.run_rx([](void *p, void *b, int s) -> int { return uart_read_bytes(UART_NUM_1, (uint8_t *)b, s, 10); }); + proto.run_rx([](void *p, void *b, int s) -> int { return tiny_serial_read_timeout(s_serial, b, s, 100); }); #else - proto.run_rx([](void *p, void *b, int s) -> int { return uart_read_bytes(UART_NUM_1, (uint8_t *)b, s, 0); }); - proto.run_tx( - [](void *p, const void *b, int s) -> int { return uart_tx_chars(UART_NUM_1, (const char *)b, s); }); + proto.run_rx([](void *p, void *b, int s) -> int { return tiny_serial_read_timeout(s_serial, b, s, 0); }); + proto.run_tx([](void *p, const void *b, int s) -> int { return tiny_serial_send_timeout(s_serial, b, s, 0); }); #endif } vTaskDelete(NULL); diff --git a/examples/linux/loopback/tiny_loopback.cpp b/examples/linux/loopback/tiny_loopback.cpp index df60da84..ad838d95 100644 --- a/examples/linux/loopback/tiny_loopback.cpp +++ b/examples/linux/loopback/tiny_loopback.cpp @@ -1,5 +1,5 @@ /* - Copyright 2019-2020 (C) Alexey Dynda + Copyright 2019-2021 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -18,7 +18,7 @@ */ #include "hal/tiny_serial.h" -#include "TinyProtocol.h" +#include "tinyproto.h" #include #include #include @@ -33,14 +33,14 @@ enum class protocol_type_t : uint8_t static hdlc_crc_t s_crc = HDLC_CRC_8; static char *s_port = nullptr; -static bool s_generatorEnabled = false; static bool s_loopbackMode = true; static protocol_type_t s_protocol = protocol_type_t::FD; -static int s_packetSize = 64; +static int s_packetSize = 32; static int s_windowSize = 7; static bool s_terminate = false; static bool s_runTest = false; static bool s_isArduinoBoard = false; +static int s_lostRxFrames = 0; static int s_receivedBytes = 0; static int s_sentBytes = 0; @@ -57,7 +57,7 @@ static void print_help() fprintf(stderr, " light - full duplex\n"); fprintf(stderr, " -c , --crc crc type: 0, 8, 16, 32\n"); fprintf(stderr, " -g, --generator turn on packet generating\n"); - fprintf(stderr, " -s, --size packet size: 64 (by default)\n"); + fprintf(stderr, " -s, --size packet size: 32 (by default)\n"); fprintf(stderr, " -w, --window window size: 7 (by default)\n"); fprintf(stderr, " -r, --run-test run 15 seconds speed test\n"); fprintf(stderr, " -a, --arduino-tty delay test start by 2 seconds for Arduino ttyUSB interfaces\n"); @@ -122,8 +122,7 @@ static int parse_args(int argc, char *argv[]) } else if ( (!strcmp(argv[i], "-g")) || (!strcmp(argv[i], "--generator")) ) { - s_generatorEnabled = true; - s_loopbackMode = !s_generatorEnabled; + s_loopbackMode = false; } else if ( (!strcmp(argv[i], "-r")) || (!strcmp(argv[i], "--run-test")) ) { @@ -155,160 +154,69 @@ static int parse_args(int argc, char *argv[]) //================================== FD ====================================== -tiny_serial_handle_t s_serialFd; -tinyproto::FdD *s_protoFd = nullptr; - -void onReceiveFrameFd(void *userData, uint8_t addr, tinyproto::IPacket &pkt) -{ - if ( !s_runTest ) - fprintf(stderr, "<<< Frame received payload len=%d\n", (int)pkt.size()); - s_receivedBytes += static_cast(pkt.size()); - if ( !s_generatorEnabled ) - { - if ( s_protoFd->write(pkt) < 0 ) - { - fprintf(stderr, "Failed to send packet\n"); - } - } -} - -void onSendFrameFd(void *userData, uint8_t addr, tinyproto::IPacket &pkt) +static int runLoopBackMode( tinyproto::Proto &proto ) { - if ( !s_runTest ) - fprintf(stderr, ">>> Frame sent payload len=%d\n", (int)pkt.size()); - s_sentBytes += static_cast(pkt.size()); -} - -static int run_fd(tiny_serial_handle_t port) -{ - s_serialFd = port; - tinyproto::FdD proto(tiny_fd_buffer_size_by_mtu(s_packetSize, s_windowSize)); - proto.enableCrc(s_crc); - // Set window size to 4 frames. This should be the same value, used by other size - proto.setWindowSize(s_windowSize); - // Set send timeout to 1000ms as we are going to use multithread mode - // With generator mode it is ok to send with timeout from run_fd() function - // But in loopback mode (!generator), we must resend frames from receiveCallback as soon as possible, use no timeout - // then - proto.setSendTimeout(s_generatorEnabled ? 1000 : 0); - proto.setReceiveCallback(onReceiveFrameFd); - proto.setSendCallback(onSendFrameFd); - s_protoFd = &proto; - - proto.begin(); - std::thread rxThread( - [](tinyproto::FdD &proto) -> void { - while ( !s_terminate ) - { - proto.run_rx([](void *u, void *b, int s) -> int { return tiny_serial_read(s_serialFd, b, s); }); - } - }, - std::ref(proto)); - std::thread txThread( - [](tinyproto::FdD &proto) -> void { - while ( !s_terminate ) - { - proto.run_tx([](void *u, const void *b, int s) -> int { return tiny_serial_send(s_serialFd, b, s); }); - } - }, - std::ref(proto)); - - auto startTs = std::chrono::steady_clock::now(); - auto progressTs = startTs; - /* Run main cycle forever */ while ( !s_terminate ) { - if ( s_generatorEnabled ) + tinyproto::IPacket *packet = proto.read( 100 ); + // Use timeout of 100 milliseconds, since we don't want to create busy loop + if ( packet ) { - tinyproto::Packet packet(s_packetSize); - packet.put("Generated frame. test in progress"); - if ( proto.write(packet.data(), static_cast(packet.size())) < 0 ) + if ( !s_runTest ) + fprintf(stderr, "<<< Frame received payload len=%d\n", packet->size()); + s_receivedBytes += static_cast(packet->size()); + // Add 10 milliseconds timeout to give for Light/Hdlc protocols some time + // to wait until message is sent + if ( !proto.send( *packet, 10 ) ) { - fprintf(stderr, "Failed to send packet\n"); + fprintf(stderr, "Failed to loopback packet\n"); } - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - } - else - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - if ( s_runTest && s_generatorEnabled ) - { - auto ts = std::chrono::steady_clock::now(); - if ( ts - startTs >= std::chrono::seconds(15) ) - s_terminate = true; - if ( ts - progressTs >= std::chrono::seconds(1) ) + else { - progressTs = ts; - fprintf(stderr, "."); + if ( !s_runTest ) + fprintf(stderr, ">>> Frame sent payload len=%d\n", packet->size()); + s_sentBytes += packet->size(); } + proto.release( packet ); } } - rxThread.join(); - txThread.join(); - proto.end(); return 0; } -//================================== LIGHT ====================================== - -static int run_light(tiny_serial_handle_t port) +static int runGeneratorMode(tinyproto::Proto &proto) { - s_serialFd = port; - tinyproto::Light proto; - proto.enableCrc(s_crc); - - proto.begin([](void *a, const void *b, int c) -> int { return tiny_serial_send(s_serialFd, b, c); }, - [](void *a, void *b, int c) -> int { return tiny_serial_read(s_serialFd, b, c); }); - std::thread rxThread( - [](tinyproto::Light &proto) -> void { - tinyproto::Packet packet(s_packetSize + 4); - while ( !s_terminate ) - { - if ( proto.read(packet) > 0 ) - { - s_receivedBytes += packet.size(); - if ( !s_runTest ) - fprintf(stderr, "<<< Frame received payload len=%d\n", (int)packet.size()); - if ( !s_generatorEnabled ) - { - if ( proto.write(packet) < 0 ) - { - fprintf(stderr, "Failed to send packet\n"); - } - } - } - } - }, - std::ref(proto)); - auto startTs = std::chrono::steady_clock::now(); auto progressTs = startTs; /* Run main cycle forever */ while ( !s_terminate ) { - if ( s_generatorEnabled ) + // Use timeout 0. If remote side is not ready yet, attempt to send packet + tinyproto::IPacket *packet = proto.read( 0 ); + if ( packet ) { - tinyproto::Packet packet(s_packetSize); - packet.put("Generated frame. test in progress"); - if ( proto.write(packet) < 0 ) - { - fprintf(stderr, "Failed to send packet\n"); - } - else - { - s_sentBytes += static_cast(packet.size()); - if ( !s_runTest ) - fprintf(stderr, ">>> Frame sent payload len=%d\n", (int)packet.size()); - } + if ( !s_runTest ) + fprintf(stderr, "<<< Frame received payload len=%d\n", (int)packet->size()); + s_receivedBytes += static_cast(packet->size()); + proto.release( packet ); + } + tinyproto::HeapPacket outPacket(s_packetSize); + while ( outPacket.size() < s_packetSize ) + outPacket.put("Generated frame. test in progress..."); + // Use timeout of 20 milliseconds, since we don't want busy loop + // Since we're using single thread for sending and receiving it is normal that we miss some incoming frames + if ( !proto.send(outPacket, 20) ) + { + fprintf(stderr, "Failed to send packet\n"); } else { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if ( !s_runTest ) + fprintf(stderr, ">>> Frame sent payload len=%d\n", (int)outPacket.size()); + s_sentBytes += static_cast(outPacket.size()); } - if ( s_runTest && s_generatorEnabled ) + if ( s_runTest ) { auto ts = std::chrono::steady_clock::now(); if ( ts - startTs >= std::chrono::seconds(15) ) @@ -320,41 +228,76 @@ static int run_light(tiny_serial_handle_t port) } } } - rxThread.join(); - proto.end(); return 0; } -int main(int argc, char *argv[]) +static int run(tiny_serial_handle_t port) { - if ( parse_args(argc, argv) < 0 ) + tinyproto::Proto proto( true ); + tinyproto::ILinkLayer *link = nullptr; + if ( s_protocol == protocol_type_t::FD ) { - print_help(); - return 1; + tinyproto::SerialFdLink *serial = new tinyproto::SerialFdLink( s_port ); + proto.setLink( *serial ); + serial->setMtu( s_packetSize ); + serial->setCrc( s_crc ); + serial->setWindow( s_windowSize ); + serial->setTimeout( 100 ); + link = serial; + } + else if ( s_protocol == protocol_type_t::LIGHT ) + { + tinyproto::SerialHdlcLink *serial = new tinyproto::SerialHdlcLink( s_port ); + proto.setLink( *serial ); + serial->setMtu( s_packetSize ); + serial->setCrc( s_crc ); + serial->setTimeout( 100 ); + link = serial; + } + // Wait for additional 1500 ms after opening serial port if communicating with an Arduino + // Some boards activate bootloader if to send something when board reboots + if ( s_isArduinoBoard ) + { + proto.setTxDelay( 1500 ); } - tiny_serial_handle_t hPort = tiny_serial_open(s_port, 115200); - - if ( hPort == TINY_SERIAL_INVALID ) + tinyproto::HeapPacket packet1(s_packetSize); + tinyproto::HeapPacket packet2(s_packetSize); + proto.addRxPool( packet1 ); + proto.addRxPool( packet2 ); + if ( !proto.begin() ) { - fprintf(stderr, "Error opening serial port\n"); - return 1; + return -1; } if ( s_isArduinoBoard ) { - std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + tiny_sleep( 1400 ); } - int result = -1; - switch ( s_protocol ) + if ( s_loopbackMode ) + runLoopBackMode( proto ); + else + runGeneratorMode( proto ); + + proto.end(); + s_lostRxFrames = proto.getLostRxFrames(); + delete link; + return 0; +} + +int main(int argc, char *argv[]) +{ + if ( parse_args(argc, argv) < 0 ) { - case protocol_type_t::FD: result = run_fd(hPort); break; - case protocol_type_t::LIGHT: result = run_light(hPort); break; - default: fprintf(stderr, "Unknown protocol type"); break; + print_help(); + return 1; } - tiny_serial_close(hPort); + + int result = run( -1 ); + if ( s_runTest ) { + printf("\nLost rx frames: %i\n", s_lostRxFrames); printf("\nRegistered TX speed: %u bps\n", (s_sentBytes)*8 / 15); printf("Registered RX speed: %u bps\n", (s_receivedBytes)*8 / 15); } diff --git a/examples/linux/loopback_old/CMakeLists.txt b/examples/linux/loopback_old/CMakeLists.txt new file mode 100644 index 00000000..b8e94705 --- /dev/null +++ b/examples/linux/loopback_old/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required (VERSION 3.5) + +file(GLOB_RECURSE SOURCE_FILES *.cpp *.c) + +if (NOT DEFINED COMPONENT_DIR) + + project (tiny_loopback) + + add_executable(tiny_loopback ${SOURCE_FILES}) + + target_link_libraries(tiny_loopback tinyproto) + + if (WIN32) + find_package(Threads REQUIRED) + target_link_libraries(${PROJECT_NAME} Threads::Threads) + + elseif (UNIX) + find_package(Threads REQUIRED) + target_link_libraries(${PROJECT_NAME} Threads::Threads) +# target_link_libraries(tiny_loopback pthread) + endif() + +else() + + idf_component_register(SRCS ${SOURCE_FILES} + INCLUDE_DIRS ".") + +endif() diff --git a/examples/linux/loopback_old/tiny_loopback.cpp b/examples/linux/loopback_old/tiny_loopback.cpp new file mode 100644 index 00000000..01c461e8 --- /dev/null +++ b/examples/linux/loopback_old/tiny_loopback.cpp @@ -0,0 +1,361 @@ +/* + Copyright 2019-2020 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "hal/tiny_serial.h" +#include "TinyProtocol.h" +#include +#include +#include +#include + +enum class protocol_type_t : uint8_t +{ + HDLC = 0, + FD = 2, + LIGHT = 3, +}; + +static hdlc_crc_t s_crc = HDLC_CRC_8; +static char *s_port = nullptr; +static bool s_generatorEnabled = false; +static bool s_loopbackMode = true; +static protocol_type_t s_protocol = protocol_type_t::FD; +static int s_packetSize = 64; +static int s_windowSize = 7; +static bool s_terminate = false; +static bool s_runTest = false; +static bool s_isArduinoBoard = false; + +static int s_receivedBytes = 0; +static int s_sentBytes = 0; + +static void print_help() +{ + fprintf(stderr, "Usage: tiny_loopback -p [-c ]\n"); + fprintf(stderr, "Note: communication runs at 115200\n"); + fprintf(stderr, " -p , --port com port to use\n"); + fprintf(stderr, " COM1, COM2 ... for Windows\n"); + fprintf(stderr, " /dev/ttyS0, /dev/ttyS1 ... for Linux\n"); + fprintf(stderr, " -t , --protocol type of protocol to use\n"); + fprintf(stderr, " fd - full duplex (default)\n"); + fprintf(stderr, " light - full duplex\n"); + fprintf(stderr, " -c , --crc crc type: 0, 8, 16, 32\n"); + fprintf(stderr, " -g, --generator turn on packet generating\n"); + fprintf(stderr, " -s, --size packet size: 64 (by default)\n"); + fprintf(stderr, " -w, --window window size: 7 (by default)\n"); + fprintf(stderr, " -r, --run-test run 15 seconds speed test\n"); + fprintf(stderr, " -a, --arduino-tty delay test start by 2 seconds for Arduino ttyUSB interfaces\n"); +} + +static int parse_args(int argc, char *argv[]) +{ + if ( argc < 2 ) + { + return -1; + } + int i = 1; + while ( i < argc ) + { + if ( argv[i][0] != '-' ) + { + break; + } + if ( (!strcmp(argv[i], "-p")) || (!strcmp(argv[i], "--port")) ) + { + if ( ++i < argc ) + s_port = argv[i]; + else + return -1; + } + else if ( (!strcmp(argv[i], "-c")) || (!strcmp(argv[i], "--crc")) ) + { + if ( ++i >= argc ) + return -1; + switch ( strtoul(argv[i], nullptr, 10) ) + { + case 0: s_crc = HDLC_CRC_OFF; break; + case 8: s_crc = HDLC_CRC_8; break; + case 16: s_crc = HDLC_CRC_16; break; + case 32: s_crc = HDLC_CRC_32; break; + default: fprintf(stderr, "CRC type not supported\n"); return -1; + } + } + else if ( (!strcmp(argv[i], "-s")) || (!strcmp(argv[i], "--size")) ) + { + if ( ++i >= argc ) + return -1; + s_packetSize = strtoul(argv[i], nullptr, 10); + if ( s_packetSize < 32 ) + { + fprintf(stderr, "Packets size less than 32 bytes are not supported\n"); + return -1; + return -1; + } + } + else if ( (!strcmp(argv[i], "-w")) || (!strcmp(argv[i], "--window")) ) + { + if ( ++i >= argc ) + return -1; + s_windowSize = strtoul(argv[i], nullptr, 10); + if ( s_windowSize < 1 || s_windowSize > 7 ) + { + fprintf(stderr, "Allowable window size is between 1 and 7 inclusively\n"); + return -1; + return -1; + } + } + else if ( (!strcmp(argv[i], "-g")) || (!strcmp(argv[i], "--generator")) ) + { + s_generatorEnabled = true; + s_loopbackMode = !s_generatorEnabled; + } + else if ( (!strcmp(argv[i], "-r")) || (!strcmp(argv[i], "--run-test")) ) + { + s_runTest = true; + } + else if ( (!strcmp(argv[i], "-a")) || (!strcmp(argv[i], "--arduino-tty")) ) + { + s_isArduinoBoard = true; + } + else if ( (!strcmp(argv[i], "-t")) || (!strcmp(argv[i], "--protocol")) ) + { + if ( ++i >= argc ) + return -1; + else if ( !strcmp(argv[i], "fd") ) + s_protocol = protocol_type_t::FD; + else if ( !strcmp(argv[i], "light") ) + s_protocol = protocol_type_t::LIGHT; + else + return -1; + } + i++; + } + if ( s_port == nullptr ) + { + return -1; + } + return i; +} + +//================================== FD ====================================== + +tiny_serial_handle_t s_serialFd; +tinyproto::FdD *s_protoFd = nullptr; + +void onReceiveFrameFd(void *userData, uint8_t addr, tinyproto::IPacket &pkt) +{ + if ( !s_runTest ) + fprintf(stderr, "<<< Frame received payload len=%d\n", (int)pkt.size()); + s_receivedBytes += static_cast(pkt.size()); + if ( !s_generatorEnabled ) + { + if ( s_protoFd->write(pkt) < 0 ) + { + fprintf(stderr, "Failed to send packet\n"); + } + } +} + +void onSendFrameFd(void *userData, uint8_t addr, tinyproto::IPacket &pkt) +{ + if ( !s_runTest ) + fprintf(stderr, ">>> Frame sent payload len=%d\n", (int)pkt.size()); + s_sentBytes += static_cast(pkt.size()); +} + +static int run_fd(tiny_serial_handle_t port) +{ + s_serialFd = port; + tinyproto::FdD proto(tiny_fd_buffer_size_by_mtu(s_packetSize, s_windowSize)); + proto.enableCrc(s_crc); + // Set window size to 4 frames. This should be the same value, used by other size + proto.setWindowSize(s_windowSize); + // Set send timeout to 1000ms as we are going to use multithread mode + // With generator mode it is ok to send with timeout from run_fd() function + // But in loopback mode (!generator), we must resend frames from receiveCallback as soon as possible, use no timeout + // then + proto.setSendTimeout(s_generatorEnabled ? 1000 : 0); + proto.setReceiveCallback(onReceiveFrameFd); + proto.setSendCallback(onSendFrameFd); + s_protoFd = &proto; + + proto.begin(); + std::thread rxThread( + [](tinyproto::FdD &proto) -> void { + while ( !s_terminate ) + { + proto.run_rx([](void *u, void *b, int s) -> int { return tiny_serial_read(s_serialFd, b, s); }); + } + }, + std::ref(proto)); + std::thread txThread( + [](tinyproto::FdD &proto) -> void { + while ( !s_terminate ) + { + proto.run_tx([](void *u, const void *b, int s) -> int { return tiny_serial_send(s_serialFd, b, s); }); + } + }, + std::ref(proto)); + + auto startTs = std::chrono::steady_clock::now(); + auto progressTs = startTs; + + /* Run main cycle forever */ + while ( !s_terminate ) + { + if ( s_generatorEnabled ) + { + tinyproto::HeapPacket packet(s_packetSize); + packet.put("Generated frame. test in progress"); + if ( proto.write(packet.data(), static_cast(packet.size())) < 0 ) + { + fprintf(stderr, "Failed to send packet\n"); + } + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if ( s_runTest && s_generatorEnabled ) + { + auto ts = std::chrono::steady_clock::now(); + if ( ts - startTs >= std::chrono::seconds(15) ) + s_terminate = true; + if ( ts - progressTs >= std::chrono::seconds(1) ) + { + progressTs = ts; + fprintf(stderr, "."); + } + } + } + rxThread.join(); + txThread.join(); + proto.end(); + return 0; +} + +//================================== LIGHT ====================================== + +static int run_light(tiny_serial_handle_t port) +{ + s_serialFd = port; + tinyproto::Light proto; + proto.enableCrc(s_crc); + + proto.begin([](void *a, const void *b, int c) -> int { return tiny_serial_send(s_serialFd, b, c); }, + [](void *a, void *b, int c) -> int { return tiny_serial_read(s_serialFd, b, c); }); + std::thread rxThread( + [](tinyproto::Light &proto) -> void { + tinyproto::HeapPacket packet(s_packetSize + 4); + while ( !s_terminate ) + { + if ( proto.read(packet) > 0 ) + { + s_receivedBytes += packet.size(); + if ( !s_runTest ) + fprintf(stderr, "<<< Frame received payload len=%d\n", (int)packet.size()); + if ( !s_generatorEnabled ) + { + if ( proto.write(packet) < 0 ) + { + fprintf(stderr, "Failed to send packet\n"); + } + } + } + } + }, + std::ref(proto)); + + auto startTs = std::chrono::steady_clock::now(); + auto progressTs = startTs; + + /* Run main cycle forever */ + while ( !s_terminate ) + { + if ( s_generatorEnabled ) + { + tinyproto::HeapPacket packet(s_packetSize); + packet.put("Generated frame. test in progress"); + if ( proto.write(packet) < 0 ) + { + fprintf(stderr, "Failed to send packet\n"); + } + else + { + s_sentBytes += static_cast(packet.size()); + if ( !s_runTest ) + fprintf(stderr, ">>> Frame sent payload len=%d\n", (int)packet.size()); + } + } + else + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if ( s_runTest && s_generatorEnabled ) + { + auto ts = std::chrono::steady_clock::now(); + if ( ts - startTs >= std::chrono::seconds(15) ) + s_terminate = true; + if ( ts - progressTs >= std::chrono::seconds(1) ) + { + progressTs = ts; + fprintf(stderr, "."); + } + } + } + rxThread.join(); + proto.end(); + return 0; +} + +int main(int argc, char *argv[]) +{ + if ( parse_args(argc, argv) < 0 ) + { + print_help(); + return 1; + } + + tiny_serial_handle_t hPort = tiny_serial_open(s_port, 115200); + + if ( hPort == TINY_SERIAL_INVALID ) + { + fprintf(stderr, "Error opening serial port\n"); + return 1; + } + if ( s_isArduinoBoard ) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1500)); + } + + int result = -1; + switch ( s_protocol ) + { + case protocol_type_t::FD: result = run_fd(hPort); break; + case protocol_type_t::LIGHT: result = run_light(hPort); break; + default: fprintf(stderr, "Unknown protocol type"); break; + } + tiny_serial_close(hPort); + if ( s_runTest ) + { + printf("\nRegistered TX speed: %u bps\n", (s_sentBytes)*8 / 15); + printf("Registered RX speed: %u bps\n", (s_receivedBytes)*8 / 15); + } + return result; +} diff --git a/python/fd.cpp b/python/fd.cpp index 5109d8ef..2c02bda6 100644 --- a/python/fd.cpp +++ b/python/fd.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 (C) Alexey Dynda + Copyright 2021-2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -42,7 +42,7 @@ typedef struct int mtu; int window_size; void *buffer; - PyObject *on_frame_sent; + PyObject *on_frame_send; PyObject *on_frame_read; PyObject *on_connect_event; PyObject *read_func; @@ -59,7 +59,7 @@ static PyMemberDef Fd_members[] = { static void Fd_dealloc(Fd *self) { Py_XDECREF(self->on_frame_read); - Py_XDECREF(self->on_frame_sent); + Py_XDECREF(self->on_frame_send); Py_XDECREF(self->on_connect_event); Py_XDECREF(self->read_func); Py_XDECREF(self->write_func); @@ -71,7 +71,7 @@ static int Fd_init(Fd *self, PyObject *args, PyObject *kwds) self->handle = NULL; self->buffer = NULL; self->crc_type = HDLC_CRC_16; - self->on_frame_sent = NULL; + self->on_frame_send = NULL; self->on_frame_read = NULL; self->on_connect_event = NULL; self->read_func = NULL; @@ -96,7 +96,7 @@ static PyObject *Fd_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ////////////////////////////// Internal callbacks -static void on_frame_read(void *user_data, uint8_t *data, int len) +static void on_frame_read(void *user_data, uint8_t addr, uint8_t *data, int len) { Fd *self = (Fd *)user_data; if ( self->on_frame_read ) @@ -113,16 +113,16 @@ static void on_frame_read(void *user_data, uint8_t *data, int len) } } -static void on_frame_sent(void *user_data, uint8_t *data, int len) +static void on_frame_send(void *user_data, uint8_t addr, const uint8_t *data, int len) { Fd *self = (Fd *)user_data; - if ( self->on_frame_sent ) + if ( self->on_frame_send ) { PyGILState_STATE gstate; gstate = PyGILState_Ensure(); PyObject *arg = PyByteArray_FromStringAndSize((const char *)data, (Py_ssize_t)len); - PyObject *temp = PyObject_CallFunctionObjArgs(self->on_frame_sent, arg, NULL); + PyObject *temp = PyObject_CallFunctionObjArgs(self->on_frame_send, arg, NULL); Py_XDECREF(temp); // Dereference result Py_DECREF(arg); // We do not need ByteArray anymore @@ -157,11 +157,11 @@ static PyObject *Fd_begin(Fd *self) { tiny_fd_init_t init{}; init.pdata = self; - init.on_frame_cb = on_frame_read; - init.on_sent_cb = on_frame_sent; + init.on_read_cb = on_frame_read; + init.on_send_cb = on_frame_send; init.on_connect_event_cb = on_connect_event; init.crc_type = self->crc_type; - init.buffer_size = tiny_fd_buffer_size_by_mtu_ex(1, self->mtu, self->window_size, init.crc_type); + init.buffer_size = tiny_fd_buffer_size_by_mtu_ex(1, self->mtu, self->window_size, init.crc_type, 2); self->buffer = PyObject_Malloc(init.buffer_size); init.buffer = self->buffer; init.send_timeout = 1000; @@ -201,7 +201,7 @@ static PyObject *Fd_send(Fd *self, PyObject *args) int result; Py_BEGIN_ALLOW_THREADS; - result = tiny_fd_send_packet(self->handle, buffer.buf, buffer.len); + result = tiny_fd_send_packet(self->handle, buffer.buf, buffer.len, 1000); Py_END_ALLOW_THREADS; PyBuffer_Release(&buffer); @@ -231,14 +231,14 @@ static PyObject *Fd_tx(Fd *self, PyObject *args) if ( buffer.buf == NULL ) { void *data = PyObject_Malloc(self->mtu); - result = tiny_fd_get_tx_data(self->handle, data, self->mtu); + result = tiny_fd_get_tx_data(self->handle, data, self->mtu, 0); PyObject *to_send = PyByteArray_FromStringAndSize((const char *)data, result); PyObject_Free(data); return to_send; } else { - result = tiny_fd_get_tx_data(self->handle, buffer.buf, buffer.len); + result = tiny_fd_get_tx_data(self->handle, buffer.buf, buffer.len, 0); PyBuffer_Release(&buffer); return PyLong_FromLong((long)result); } @@ -398,15 +398,15 @@ static int Fd_set_on_read(Fd *self, PyObject *value, void *closure) static PyObject *Fd_get_on_send(Fd *self, void *closure) { - Py_INCREF(self->on_frame_sent); - return self->on_frame_sent; + Py_INCREF(self->on_frame_send); + return self->on_frame_send; } static int Fd_set_on_send(Fd *self, PyObject *value, void *closure) { - PyObject *tmp = self->on_frame_sent; + PyObject *tmp = self->on_frame_send; Py_INCREF(value); - self->on_frame_sent = value; + self->on_frame_send = value; Py_XDECREF(tmp); return 0; } diff --git a/python/hdlc_ll.cpp b/python/hdlc_ll.cpp index b2e25e69..a300b7e2 100644 --- a/python/hdlc_ll.cpp +++ b/python/hdlc_ll.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 (C) Alexey Dynda + Copyright 2021-2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -40,7 +40,7 @@ typedef struct hdlc_crc_t crc_type; int mtu; void *buffer; - PyObject *on_frame_sent; + PyObject *on_frame_send; PyObject *on_frame_read; } Hdlc; @@ -70,7 +70,7 @@ static int Hdlc_init(Hdlc *self, PyObject *args, PyObject *kwds) self->buffer = NULL; self->crc_type = HDLC_CRC_16; self->user_buffer.buf = NULL; - self->on_frame_sent = NULL; + self->on_frame_send = NULL; self->on_frame_read = NULL; self->mtu = 1500; return 0; @@ -90,29 +90,22 @@ static PyObject *Hdlc_new(PyTypeObject *type, PyObject *args, PyObject *kwds) ////////////////////////////// Internal callbacks -static int on_frame_read(void *user_data, void *data, int len) +static void on_frame_read(void *user_data, uint8_t *data, int len) { - int result = 0; Hdlc *self = (Hdlc *)user_data; if ( self->on_frame_read ) { PyObject *arg = PyByteArray_FromStringAndSize((const char *)data, (Py_ssize_t)len); PyObject *temp = PyObject_CallFunctionObjArgs(self->on_frame_read, arg, NULL); - if ( temp && PyLong_Check(temp) ) - { - result = PyLong_AsLong(temp); - } Py_XDECREF(temp); // Dereference result Py_DECREF(arg); // We do not need ByteArray anymore } - return result; } -static int on_frame_sent(void *user_data, const void *data, int len) +static void on_frame_send(void *user_data, const uint8_t *data, int len) { - int result = 0; Hdlc *self = (Hdlc *)user_data; - if ( self->on_frame_sent ) + if ( self->on_frame_send ) { PyObject *arg = PyByteArray_FromStringAndSize((const char *)data, (Py_ssize_t)len); // We can free Py_buffer user_buffer only after we constructed new PyObject with the data @@ -121,11 +114,7 @@ static int on_frame_sent(void *user_data, const void *data, int len) PyBuffer_Release(&self->user_buffer); self->user_buffer.buf = NULL; } - PyObject *temp = PyObject_CallFunctionObjArgs(self->on_frame_sent, arg, NULL); - if ( temp && PyLong_Check(temp) ) - { - result = PyLong_AsLong(temp); - } + PyObject *temp = PyObject_CallFunctionObjArgs(self->on_frame_send, arg, NULL); Py_XDECREF(temp); // Dereference result Py_DECREF(arg); // We do not need ByteArray anymore } @@ -137,7 +126,6 @@ static int on_frame_sent(void *user_data, const void *data, int len) self->user_buffer.buf = NULL; } } - return result; } ////////////////////////////// METHODS @@ -148,8 +136,8 @@ static PyObject *Hdlc_begin(Hdlc *self) init.user_data = self; init.crc_type = self->crc_type; init.on_frame_read = on_frame_read; - init.on_frame_sent = on_frame_sent; - init.buf_size = hdlc_ll_get_buf_size_ex(self->mtu, self->crc_type); + init.on_frame_send = on_frame_send; + init.buf_size = hdlc_ll_get_buf_size_ex(self->mtu, self->crc_type, 1); self->buffer = PyObject_Malloc(init.buf_size); init.buf = self->buffer; int result = hdlc_ll_init(&self->handle, &init); @@ -247,15 +235,15 @@ static int Hdlc_set_on_read(Hdlc *self, PyObject *value, void *closure) static PyObject *Hdlc_get_on_send(Hdlc *self, void *closure) { - Py_INCREF(self->on_frame_sent); - return self->on_frame_sent; + Py_INCREF(self->on_frame_send); + return self->on_frame_send; } static int Hdlc_set_on_send(Hdlc *self, PyObject *value, void *closure) { - PyObject *tmp = self->on_frame_sent; + PyObject *tmp = self->on_frame_send; Py_INCREF(value); - self->on_frame_sent = value; + self->on_frame_send = value; Py_XDECREF(tmp); return 0; } diff --git a/python/helpers/__init__.py b/python/helpers/__init__.py new file mode 100644 index 00000000..2dd196bd --- /dev/null +++ b/python/helpers/__init__.py @@ -0,0 +1,32 @@ +""" + Copyright 2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + GNU General Public License Usage + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . + + Commercial License Usage + + Licensees holding valid commercial Tiny Protocol licenses may use this file in + accordance with the commercial license agreement provided in accordance with + the terms contained in a written agreement between you and Alexey Dynda. + For further information contact via email on github account. + +""" + + +class Packet: + pass diff --git a/python/main.cpp b/python/main.cpp index aeec0111..86168758 100644 --- a/python/main.cpp +++ b/python/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 (C) Alexey Dynda + Copyright 2021-2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -29,6 +29,7 @@ #include #include "hdlc_ll.h" #include "fd.h" +#include "py_serial.h" static PyObject *pants(PyObject *self, PyObject *args) { @@ -62,10 +63,17 @@ PyMODINIT_FUNC PyInit_tinyproto_(void) return NULL; } + if ( PyType_Ready(&SerialType) < 0 ) + { + return NULL; + } + Py_INCREF(&HdlcType); PyModule_AddObject(m, "Hdlc", (PyObject *)&HdlcType); Py_INCREF(&FdType); PyModule_AddObject(m, "Fd", (PyObject *)&FdType); + Py_INCREF(&SerialType); + PyModule_AddObject(m, "Serial", (PyObject *)&SerialType); return m; } diff --git a/python/py_serial.cpp b/python/py_serial.cpp new file mode 100644 index 00000000..c4f2f880 --- /dev/null +++ b/python/py_serial.cpp @@ -0,0 +1,180 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . + */ + +#define PY_SSIZE_T_CLEAN + +#include +#include "structmember.h" +//#include "proto/hdlc/low_level/hdlc.h" +#include "interface/TinySerial.h" +#include "py_serial.h" + +typedef struct +{ + PyObject_HEAD // no semicolon + tinyproto::Serial *serial; + int error_flag; +} PySerial; + +static PyMemberDef Serial_members[] = { + {NULL} /* Sentinel */ +}; + +/////////////////////////////// ALLOC/DEALLOC + +static void Serial_dealloc(PySerial *self) +{ + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int Serial_init(PySerial *self, PyObject *args, PyObject *kwds) +{ + const char *s = NULL; + if ( !PyArg_ParseTuple(args, "s", &s) ) + { + return 1; + } + self->serial = new tinyproto::Serial(s); + self->error_flag = 0; + return 0; +} + +static PyObject *Serial_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PySerial *self; + + self = (PySerial *)type->tp_alloc(type, 0); + if ( self != NULL ) + { + if ( Serial_init(self, args, kwds) ) + { + Py_RETURN_NONE; + } + } + return (PyObject *)self; +} + +////////////////////////////// METHODS + +static PyObject *Serial_begin(PySerial *self, PyObject *args) +{ + unsigned int i = 115200; + if ( !PyArg_ParseTuple(args, "I", &i) ) + { + return NULL; + } + int result = self->serial->begin(i); // TODO: + return PyLong_FromLong((long)result); +} + +static PyObject *Serial_end(PySerial *self) +{ + self->serial->end(); + Py_RETURN_NONE; +} + +static PyObject *Serial_send(PySerial *self, PyObject *args) +{ + Py_buffer buffer{}; + if ( !PyArg_ParseTuple(args, "s*", &buffer) ) + { + return NULL; + } + int result = self->serial->write( (const uint8_t *)buffer.buf, buffer.len ); + PyBuffer_Release(&buffer); + return PyLong_FromLong((long)result); +} + +static PyObject *Serial_read(PySerial *self, PyObject *args) +{ + int result; + Py_buffer buffer{}; + if ( !PyArg_ParseTuple(args, "|s*", &buffer) ) + { + return NULL; + } + if ( buffer.buf == NULL ) + { + void *data = PyObject_Malloc( 4096 ); // TODO: Magic number + result = self->serial->readBytes( (uint8_t *)data, 4096 ); + PyObject *to_send = PyByteArray_FromStringAndSize((const char *)data, result); + PyObject_Free(data); + return to_send; + } + else + { + result = self->serial->readBytes( (uint8_t *)buffer.buf, buffer.len ); + PyBuffer_Release(&buffer); + return PyLong_FromLong((long)result); + } +} + +static PyGetSetDef Serial_getsetters[] = { + {NULL} /* Sentinel */ +}; + +///////////////////////////////// BINDINGS + +static PyMethodDef Serial_methods[] = { + {"begin", (PyCFunction)Serial_begin, METH_VARARGS, "Starts serial communication"}, + {"end", (PyCFunction)Serial_end, METH_NOARGS, "Stops serial communication"}, + {"write", (PyCFunction)Serial_send, METH_VARARGS, "Sends new bytes to remote side"}, + {"read", (PyCFunction)Serial_read, METH_VARARGS, "Reads new bytes from remote side"}, + {NULL} /* Sentinel */ +}; + +PyTypeObject SerialType = { + PyVarObject_HEAD_INIT(NULL, 0) "tinyproto.Serial", /* tp_name */ + sizeof(PySerial), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)Serial_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_reserved */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Serial object", /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + 0, /* tp_iter */ + 0, /* tp_iternext */ + Serial_methods, /* tp_methods */ + Serial_members, /* tp_members */ + Serial_getsetters, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + (initproc)Serial_init, /* tp_init */ + 0, /* tp_alloc */ + Serial_new, /* tp_new */ +}; diff --git a/python/py_serial.h b/python/py_serial.h new file mode 100644 index 00000000..bf461c02 --- /dev/null +++ b/python/py_serial.h @@ -0,0 +1,24 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . + */ + +#pragma once + +#include + +extern "C" PyTypeObject SerialType; diff --git a/setup.py b/setup.py index 1b3ad097..c4be4e47 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ """ - Copyright 2021-2022 (C) Alexey Dynda + Copyright 2021-2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -39,7 +39,7 @@ libs = [] tinyproto_module = Extension( - 'tinyproto_', + 'tinyproto', sources=source_files, include_dirs=['./src','./python'], define_macros=[("MYDEF", None), ("TINY_FD_DEBUG", 0), ("TINY_LOG_LEVEL_DEFAULT", 0)], @@ -59,8 +59,7 @@ version='1.0.0', description='tinyproto module wrapper', package_dir = { "tinyproto": "./python" }, - packages = ['tinyproto', 'tinyproto.wrappers', 'tinyproto.options'], - # py_modules = ['tinyproto.options'], + packages = ['tinyproto.wrappers', 'tinyproto.helpers'], ext_modules=[tinyproto_module], ) diff --git a/src/TinyPacket.h b/src/TinyPacket.h index 4e50dee1..70e30380 100644 --- a/src/TinyPacket.h +++ b/src/TinyPacket.h @@ -62,12 +62,21 @@ class IPacket * @param size - size of the buffer to hold packet data * @note passed buffer must exist all lifecycle of the Packet object. */ - IPacket(char *buf, size_t size) + IPacket(char *buf, int size) { - m_len = 0; m_size = static_cast(size); m_buf = (uint8_t *)buf; - m_p = 0; + } + + IPacket(const IPacket &packet) + { + m_len = packet.m_len; + m_size = packet.m_size; + m_buf = packet.m_buf; + } + + IPacket() + { } /** @@ -139,8 +148,11 @@ class IPacket */ inline void put(const char *str) { - strncpy((char *)&m_buf[m_len], str, m_size - m_len); - m_len += static_cast(strlen(str)); + int room = m_size - m_len - 1; + if ( !room ) return; + int strSize = static_cast(strlen(str)); + strncpy((char *)&m_buf[m_len], str, room); + m_len += strSize < room ? strSize : room; m_buf[m_len++] = 0; } @@ -215,7 +227,7 @@ class IPacket * Returns size of payload data in the received packet. * @return size of payload data. */ - inline size_t size() const + inline int size() const { return m_len; } @@ -224,7 +236,7 @@ class IPacket * Returns maximum size of packet buffer. * @return max size of packet buffer. */ - inline size_t maxSize() const + inline int maxSize() const { return m_size; } @@ -233,7 +245,7 @@ class IPacket * Returns pointer to payload data in the received packet. * @return pointer to payload data. */ - inline char *data() + inline char *data() const { return (char *)m_buf; } @@ -250,27 +262,50 @@ class IPacket /** * You may refer to Packet payload data directly by using operator [] */ - uint8_t &operator[](size_t idx) + uint8_t &operator[](int idx) { return m_buf[idx]; } + /** + * Assign operator doesn't copy the data from the source packet, but + * it copies only pointers + */ + IPacket &operator=(const IPacket &source) + { + m_size = source.m_size; + m_buf = source.m_buf; + return *this; + } + + /** + * Allocates space inside the packet buffer, the next data will be written after allocated block. + * Allocate operation doesn't changes the data inside the buffer + */ + void allocate(int bytes) + { + m_len += bytes; + } + private: friend class Hdlc; friend class IFd; friend class Light; - - uint8_t *m_buf; - int m_size; - int m_len; - int m_p; + friend class Proto; + + uint8_t *m_buf = nullptr; + IPacket *m_next = nullptr; // For organizing ring buffers + IPacket *m_prev = nullptr; // For organizing ring buffers + int m_size = 0; // maximum space available for payload data + int m_len = 0; // length of payload data + int m_p = 0; // current pointer }; /** * Template class to create packet with static allocation of buffer * Use this class for microcontrollers with few resources. */ -template class StaticPacket: public IPacket +template class StaticPacket: public IPacket { public: /** @@ -289,19 +324,25 @@ template class StaticPacket: public IPacket * Class which allocated buffer for packet dynamically. * Use this class only on powerful microcontrollers. */ -class Packet: public IPacket +class HeapPacket: public IPacket { public: /** * Creates packet with dynamically allocated buffer. * @param size number of bytes to allocate for the packet buffer. */ - explicit Packet(int size) + explicit HeapPacket(int size) : IPacket((char *)(new uint8_t[size]), size) { } - ~Packet() + explicit HeapPacket( const IPacket &src ) + : IPacket((char *)(new uint8_t[src.size()]), src.size()) + { + memcpy(data(), src.data(), src.size()); + } + + ~HeapPacket() { delete[](uint8_t *) data(); } diff --git a/src/TinyProtocol.cpp b/src/TinyProtocol.cpp new file mode 100644 index 00000000..13cfa643 --- /dev/null +++ b/src/TinyProtocol.cpp @@ -0,0 +1,387 @@ +/* + Copyright 2021-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinyProtocol.h" + +namespace tinyproto +{ + +enum +{ + PROTO_RX_MESSAGE = 1, +}; + +Proto::Proto(bool multithread) + : m_link(nullptr) + , m_multithread( multithread ) +{ + tiny_events_create( &m_events ); + tiny_mutex_create( &m_mutex ); +} + +Proto::~Proto() +{ + end(); + tiny_mutex_destroy( &m_mutex ); + tiny_events_destroy( &m_events ); +} + +void Proto::setLink(ILinkLayer &link) +{ + m_link = &link; +} + +ILinkLayer &Proto::getLink() +{ + return *m_link; +} + +bool Proto::begin() +{ + uint32_t timeout = m_link->getTimeout(); + if ( m_multithread && !timeout ) + { + timeout = 1000; + } + else if ( !m_multithread) + { + timeout = 0; + } + m_link->setTimeout( timeout ); + if (!m_link->begin(onReadCb, onSendCb, this)) + { + m_terminate = true; + return false; + } +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 + if ( m_multithread ) + { + m_terminate = false; + m_readThread = new std::thread(&Proto::runRx, this); + m_sendThread = new std::thread(&Proto::runTx, this); + } +#endif + return true; +} + +bool Proto::begin(int poolBuffers) +{ + return Proto::begin(); +} + +bool Proto::send(const IPacket &packet, uint32_t timeout) +{ + bool result = false; + uint32_t startTs = tiny_millis(); + for ( ;; ) + { + // Try to put message to outgoing queue + result = m_link->put( packet.m_buf, packet.m_len, m_multithread ? timeout : 0 ); + if ( result ) + { + break; + } + if ( static_cast(tiny_millis() - startTs) >= timeout ) + { + // Cancel send operation if possible + m_link->flushTx(); + break; + } + if ( !m_multithread ) + { + m_link->runTx(); + m_link->runRx(); + } + } + return result; +} + +/*void Proto::printCount(const char * name, IPacket *queue) +{ + int counter = 0; + IPacket *p = queue; + while ( p ) + { + p = p->m_next; + counter++; + } + printf("[%p] %s counter %i \n", this, name, counter); +}*/ + + +IPacket *Proto::read(uint32_t timeout) +{ + IPacket *p = nullptr; + uint32_t startTs = tiny_millis(); + for ( ;; ) + { + tiny_events_wait(&m_events, PROTO_RX_MESSAGE, EVENT_BITS_CLEAR, m_multithread ? timeout : 0); + tiny_mutex_lock( &m_mutex ); + if ( m_queue != nullptr ) + { + p = m_queue; + m_queue = m_queue->m_next; + if ( m_queue != nullptr ) + { + m_queue->m_prev = nullptr; + tiny_events_set( &m_events, PROTO_RX_MESSAGE ); + } + else + { + m_last = nullptr; + } + //printCount( "read Pool", m_pool ); + //printCount( "read Queue", m_queue ); + tiny_mutex_unlock( &m_mutex ); + break; + } + tiny_mutex_unlock( &m_mutex ); + // Always run Tx/Rx loop before checking timings, otherwise messages will be never received + if ( !m_multithread ) + { + m_link->runTx(); + m_link->runRx(); + } + if ( static_cast(tiny_millis() - startTs) >= timeout ) + { + break; + } + } + return p; +} + +void Proto::end() +{ + if ( m_terminate ) + { + return; + } + m_terminate = true; +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 + if ( m_sendThread ) + { + m_sendThread->join(); + m_sendThread = nullptr; + } + if ( m_readThread ) + { + m_readThread->join(); + m_readThread = nullptr; + } +#endif + m_link->end(); + return; +} + +void Proto::onRead(uint8_t addr, uint8_t *buf, int len) +{ + // We do not need pool for callback mode + if ( m_onRx ) + { + IPacket packet((char *)buf, len); + packet.m_len = len; + m_onRx(*this, packet); + return; + } + tiny_mutex_lock( &m_mutex ); + IPacket * p = m_pool; + if ( p == nullptr ) + { + // TODO: Lost frame + m_lostRxFrames++; + // printf("Lost -------------------- \n"); + } + else + { + // Remove from pool + m_pool = m_pool->m_next; + if ( m_pool ) + { + m_pool->m_prev = nullptr; + } + // Add to rx queue + p->m_next = nullptr; + p->m_prev = m_last; + if ( m_last ) + { + m_last->m_next = p; + } + m_last = p; + if ( m_queue == nullptr ) + { + m_queue = p; + } + // Copy data if needed + if ( p->m_size == 0 ) + { + p->m_buf = buf; + p->m_len = len; + } + else + { + // TODO: Error if oversize + p->m_len = p->m_size < len ? p->m_size: len; + memcpy( p->m_buf, buf, p->m_len ); + } + p->m_p = 0; + tiny_events_set( &m_events, PROTO_RX_MESSAGE ); + } + //printCount( "new Pool", m_pool ); + //printCount( "new Queue", m_queue ); + tiny_mutex_unlock( &m_mutex ); +} + +void Proto::onSend(uint8_t addr, const uint8_t *buf, int len) +{ +} + +void Proto::onReadCb(void *udata, uint8_t addr, uint8_t *buf, int len) +{ + Proto *proto = reinterpret_cast(udata); + proto->onRead(addr, buf, len); +} + +void Proto::onSendCb(void *udata, uint8_t addr, const uint8_t *buf, int len) +{ + Proto *proto = reinterpret_cast(udata); + proto->onSend(addr, buf, len); +} + +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 +void Proto::runTx() +{ + if (m_multithread) + { + if (m_txDelay) + { + tiny_sleep( m_txDelay ); + } + while ( !m_terminate ) + { + getLink().runTx(); + } + } +} + +void Proto::runRx() +{ + if (m_multithread) + { + while ( !m_terminate ) + { + getLink().runRx(); + } + } +} + +void Proto::setTxDelay( uint32_t delay ) +{ + m_txDelay = delay; +} + +int Proto::getLostRxFrames() +{ + int val = m_lostRxFrames; + m_lostRxFrames = 0; + return val; +} +#endif + +void Proto::release(IPacket *message) +{ + addRxPool(*message); +} + +void Proto::addRxPool(IPacket &message) +{ + tiny_mutex_lock( &m_mutex ); + message.m_next = m_pool; + message.m_prev = nullptr; + if ( m_pool != nullptr ) + { + m_pool->m_prev = &message; + } + m_pool = &message; + //printCount( "release Pool", m_pool ); + //printCount( "release Queue", m_queue ); + tiny_mutex_unlock( &m_mutex ); +} + +void Proto::setRxCallback(void (*onRx)(Proto &, IPacket &)) +{ + m_onRx = onRx; +} + +//////////// Platform specific helper classes + +#if defined(ARDUINO) + +SerialFdProto::SerialFdProto(HardwareSerial &port) + : Proto( false ) + , m_layer( &port ) +{ + setLink( m_layer ); +} + +ArduinoSerialFdLink &SerialFdProto::getLink() +{ + return m_layer; +} + +SerialHdlcProto::SerialHdlcProto(HardwareSerial &port) + : Proto( false ) + , m_layer( &port ) +{ + setLink( m_layer ); +} + +ArduinoSerialHdlcLink &SerialHdlcProto::getLink() +{ + return m_layer; +} + +#else + +SerialFdProto::SerialFdProto(char *dev, bool multithread) + : Proto( multithread ) + , m_layer( dev ) +{ + setLink( m_layer ); +} + +SerialFdLink &SerialFdProto::getLink() +{ + return m_layer; +} + +SerialHdlcProto::SerialHdlcProto(char *dev, bool multithread) + : Proto( multithread ) + , m_layer( dev ) +{ + setLink( m_layer ); +} + +SerialHdlcLink &SerialHdlcProto::getLink() +{ + return m_layer; +} + +#endif + +} // namespace tinyproto diff --git a/src/TinyProtocol.h b/src/TinyProtocol.h index 3c661001..3d7d34a9 100644 --- a/src/TinyProtocol.h +++ b/src/TinyProtocol.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2020,2022 (C) Alexey Dynda + Copyright 2016-2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -40,3 +40,141 @@ #include "TinyLightProtocol.h" #include "TinyProtocolHdlc.h" #include "TinyProtocolFd.h" +#include "link/TinyLinkLayer.h" +#include "link/TinySerialFdLink.h" +#include "link/TinySerialHdlcLink.h" + +#include "hal/tiny_types.h" + +#include +#include + +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 +#include +#endif + +namespace tinyproto +{ + +class Proto +{ +public: + Proto(bool multithread = false); + + ~Proto(); + + void setLink(ILinkLayer &link); + + ILinkLayer &getLink(); + + bool begin(); + + bool begin(int poolBuffers); + + bool send(const IPacket &message, uint32_t timeout); + + IPacket *read(uint32_t timeout); + + void end(); + + void release(IPacket *message); + + void addRxPool(IPacket &message); + + void setRxCallback(void (*onRx)(Proto &, IPacket &)); + +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 + void setTxDelay( uint32_t delay ); + + int getLostRxFrames(); +#endif + +private: + ILinkLayer *m_link = nullptr; + void (*m_onRx)(Proto &, IPacket &) = nullptr; + bool m_multithread = false; + bool m_terminate = true; + IPacket *m_pool = nullptr; + IPacket *m_queue = nullptr; + IPacket *m_last = nullptr; +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 + std::thread *m_sendThread = nullptr; + std::thread *m_readThread = nullptr; + uint32_t m_txDelay = 0; + int m_lostRxFrames = 0; +#endif + + tiny_events_t m_events{}; + + tiny_mutex_t m_mutex{}; + + void onRead(uint8_t addr, uint8_t *buf, int len); + + void onSend(uint8_t addr, const uint8_t *buf, int len); + + static void onReadCb(void *udata, uint8_t addr, uint8_t *buf, int len); + + static void onSendCb(void *udata, uint8_t addr, const uint8_t *buf, int len); + +#if CONFIG_TINYHAL_THREAD_SUPPORT == 1 + void runTx(); + + void runRx(); +#endif + +// void printCount(const char *, IPacket *queue); +}; + +/////// Helper classes, platform specific + +#if defined(ARDUINO) + +class SerialFdProto: public Proto +{ +public: + SerialFdProto(HardwareSerial &port); + + ArduinoSerialFdLink &getLink(); + +private: + ArduinoSerialFdLink m_layer; +}; + +class SerialHdlcProto: public Proto +{ +public: + SerialHdlcProto(HardwareSerial &port); + + ArduinoSerialHdlcLink &getLink(); + +private: + ArduinoSerialHdlcLink m_layer; +}; + +#else + +class SerialFdProto: public Proto +{ +public: + SerialFdProto(char *dev, bool multithread = false); + + SerialFdLink &getLink(); + +private: + SerialFdLink m_layer; +}; + +class SerialHdlcProto: public Proto +{ +public: + SerialHdlcProto(char *dev, bool multithread = false); + + SerialHdlcLink &getLink(); + +private: + SerialHdlcLink m_layer; +}; + +#endif + +} // namespace tinyproto diff --git a/src/TinyProtocolFd.cpp b/src/TinyProtocolFd.cpp index 49b2e8ed..2c1d95c9 100644 --- a/src/TinyProtocolFd.cpp +++ b/src/TinyProtocolFd.cpp @@ -84,12 +84,12 @@ void IFd::end() int IFd::write(const char *buf, int size) { - return tiny_fd_send_packet(m_handle, buf, size); + return tiny_fd_send_packet(m_handle, buf, size, m_sendTimeout); } int IFd::write(const IPacket &pkt) { - return tiny_fd_send_packet(m_handle, pkt.m_buf, pkt.m_len); + return tiny_fd_send_packet(m_handle, pkt.m_buf, pkt.m_len, m_sendTimeout); } int IFd::run_rx(const void *data, int len) @@ -110,13 +110,13 @@ int IFd::run_rx(read_block_cb_t read_func) int IFd::run_tx(void *data, int max_size) { - return tiny_fd_get_tx_data(m_handle, data, max_size); + return tiny_fd_get_tx_data(m_handle, data, max_size, 0); } int IFd::run_tx(write_block_cb_t write_func) { uint8_t buf[4]; - int len = tiny_fd_get_tx_data(m_handle, buf, sizeof(buf)); + int len = tiny_fd_get_tx_data(m_handle, buf, sizeof(buf), 0); if ( len <= 0 ) { return len; diff --git a/src/TinyProtocolHdlc.cpp b/src/TinyProtocolHdlc.cpp index 5790b286..4a5534cf 100644 --- a/src/TinyProtocolHdlc.cpp +++ b/src/TinyProtocolHdlc.cpp @@ -57,7 +57,7 @@ void Hdlc::begin(write_block_cb_t writecb, read_block_cb_t readcb) { m_data.send_tx = writecb; m_data.on_frame_read = onReceiveInternal; - m_data.on_frame_sent = onSendInternal; + m_data.on_frame_send = onSendInternal; m_data.rx_buf = m_buffer; m_data.rx_buf_size = m_bufferSize; m_data.crc_type = m_crc; diff --git a/src/hal/arduino/arduino_serial.h b/src/hal/arduino/arduino_serial.h new file mode 100644 index 00000000..31e2f556 --- /dev/null +++ b/src/hal/arduino/arduino_serial.h @@ -0,0 +1,37 @@ +/* + Copyright 2017,2020 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#ifdef __cplusplus + +extern "C" +{ +#endif + + + /// Unique port handle + typedef uintptr_t tiny_serial_handle_t; + +/** Invalid serial handle definition */ +#define TINY_SERIAL_INVALID (0) + +#ifdef __cplusplus +} +#endif diff --git a/src/hal/arduino/arduino_serial.inl b/src/hal/arduino/arduino_serial.inl new file mode 100644 index 00000000..fac381cd --- /dev/null +++ b/src/hal/arduino/arduino_serial.inl @@ -0,0 +1,65 @@ +/* + Copyright 2017-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "hal/tiny_serial.h" + +#include + +void tiny_serial_close(tiny_serial_handle_t port) +{ + reinterpret_cast(port)->end(); +} + +tiny_serial_handle_t tiny_serial_open(const char *name, uint32_t baud) +{ + tiny_serial_handle_t handle = reinterpret_cast(name); + reinterpret_cast(handle)->begin(baud); + return handle; +} + +int tiny_serial_send(tiny_serial_handle_t port, const void *buf, int len) +{ + return tiny_serial_send_timeout( port, buf, len, 100 ); +} + +int tiny_serial_send_timeout(tiny_serial_handle_t port, const void *buf, int len, uint32_t timeout_ms) +{ + reinterpret_cast(port)->setTimeout(timeout_ms); + return reinterpret_cast(port)->write(reinterpret_cast(buf), len); +} + +int tiny_serial_read(tiny_serial_handle_t port, void *buf, int len) +{ + reinterpret_cast(port)->setTimeout(100); + if ( !reinterpret_cast(port)->available() ) + { + return 0; + } + return reinterpret_cast(port)->readBytes(reinterpret_cast(buf), len); +} + +int tiny_serial_read_timeout(tiny_serial_handle_t port, void *buf, int len, uint32_t timeout_ms) +{ + reinterpret_cast(port)->setTimeout(timeout_ms); + if ( !reinterpret_cast(port)->available() ) + { + return 0; + } + return reinterpret_cast(port)->readBytes(reinterpret_cast(buf), len); +} diff --git a/src/hal/esp32/esp32_hal.h b/src/hal/esp32/esp32_hal.h index c9976a87..26653d1a 100644 --- a/src/hal/esp32/esp32_hal.h +++ b/src/hal/esp32/esp32_hal.h @@ -45,6 +45,8 @@ #define CONFIG_ENABLE_FCS32 #endif +#define CONFIG_TINYHAL_THREAD_SUPPORT 1 + /** * Mutex type used by Tiny Protocol implementation. * The type declaration depends on platform. diff --git a/src/hal/esp32/esp32_serial.h b/src/hal/esp32/esp32_serial.h new file mode 100644 index 00000000..a8c67935 --- /dev/null +++ b/src/hal/esp32/esp32_serial.h @@ -0,0 +1,37 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + + /// Unique port handle + typedef int tiny_serial_handle_t; + +/** Invalid serial handle definition */ +#define TINY_SERIAL_INVALID (-1) + +#ifdef __cplusplus +} +#endif diff --git a/src/hal/esp32/esp32_serial.inl b/src/hal/esp32/esp32_serial.inl new file mode 100644 index 00000000..7daee8d4 --- /dev/null +++ b/src/hal/esp32/esp32_serial.inl @@ -0,0 +1,161 @@ +/* + MIT License + + Copyright (c) 2021, Alexey Dynda + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/uart.h" +#include "driver/gpio.h" +#include +#include +#include + +#define BUFSIZE 128 + +#if defined(CONFIG_TARGET_PLATFORM_ESP8266) || defined(CONFIG_IDF_TARGET_ESP8266) || defined(ESP8266) || \ + defined(__ESP8266__) +#define ESP8266_DETECTED +#endif + +#if defined(ESP8266_DETECTED) +#ifndef UART_PIN_NO_CHANGE +#define UART_PIN_NO_CHANGE (-1) +#endif +#endif + +void tiny_serial_close(tiny_serial_handle_t port) +{ + uart_driver_delete(port); +} + +tiny_serial_handle_t tiny_serial_open(const char *name, uint32_t baud) +{ + tiny_serial_handle_t handle = -1; + if ( !strcmp(name, "uart0") ) + { + handle = UART_NUM_0; + } + else if ( !strcmp(name, "uart1") ) + { + handle = UART_NUM_1; + } +#if !defined(ESP8266_DETECTED) + else if ( !strcmp(name, "uart2") ) + { + handle = UART_NUM_2; + } +#endif + if ( handle < 0 ) + { + return TINY_SERIAL_INVALID; + } + + int tx_gpio = UART_PIN_NO_CHANGE; + int rx_gpio = UART_PIN_NO_CHANGE; + int rts_gpio = UART_PIN_NO_CHANGE; + int cts_gpio = UART_PIN_NO_CHANGE; + + uart_config_t uart_config = { + .baud_rate = baud, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, +#if !defined(ESP8266_DETECTED) + .use_ref_tick = false, + // .source_clk = 0, // APB +#endif + }; + + char* params = strdup(name); + char *token = strtok(params, ","); + int index = 0; + while ( token ) + { + int value = strtol(token, NULL, 10); + switch ( index ) + { + case 0: break; // Thats the name of the port + case 1: tx_gpio = value; break; + case 2: rx_gpio = value; break; + case 3: rts_gpio = value; break; + case 4: cts_gpio = value; break; + default: break; + } + index++; + token = strtok(NULL, ","); + } + free(params); + + ESP_ERROR_CHECK(uart_param_config(handle, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(handle, tx_gpio, rx_gpio, rts_gpio, cts_gpio)); + // uart, rx size, tx size, event queue size, queue, flags + ESP_ERROR_CHECK(uart_driver_install(handle, BUFSIZE, 0, 0, NULL, 0)); + + return handle; +} + +int tiny_serial_send(tiny_serial_handle_t port, const void *buf, int len) +{ + return tiny_serial_send_timeout(port, buf, len, 100); +} + +int tiny_serial_send_timeout(tiny_serial_handle_t port, const void *buf, int len, uint32_t timeout_ms) +{ + const uint8_t *ptr = (const uint8_t *)buf; + int sent = 0; + TickType_t start_ticks = xTaskGetTickCount(); + TickType_t ticks_left = (timeout_ms + portTICK_PERIOD_MS / 2) / portTICK_PERIOD_MS; + while ( sent < len ) + { + int n = uart_tx_chars(port, (const char *)ptr, len - sent); + if ( n < 0 ) + { + break; + } + sent += n; + ptr += n; + if ( sent >= len || ticks_left <= 0 ) + { + break; + } + uart_wait_tx_done(port, ticks_left); + TickType_t delta = (TickType_t)(xTaskGetTickCount() - start_ticks); + ticks_left = delta < ticks_left ? (ticks_left - delta) : 0; + } + // return uart_write_bytes(port, (const char *)b, s); } + return sent; +} + +int tiny_serial_read(tiny_serial_handle_t port, void *buf, int len) +{ + return tiny_serial_read_timeout(port, buf, len, 100); +} + +int tiny_serial_read_timeout(tiny_serial_handle_t port, void *buf, int len, uint32_t timeout_ms) +{ + return uart_read_bytes(port, (uint8_t *)buf, len, timeout_ms / portTICK_PERIOD_MS); +} + + diff --git a/src/hal/linux/linux_hal.h b/src/hal/linux/linux_hal.h index 65ffc29d..05bc65ed 100644 --- a/src/hal/linux/linux_hal.h +++ b/src/hal/linux/linux_hal.h @@ -45,6 +45,8 @@ #define CONFIG_ENABLE_FCS32 #endif +#define CONFIG_TINYHAL_THREAD_SUPPORT 1 + #ifndef DOXYGEN_SHOULD_SKIP_THIS /** diff --git a/src/hal/mingw32/mingw32_hal.h b/src/hal/mingw32/mingw32_hal.h index dfcd967d..62c791d8 100644 --- a/src/hal/mingw32/mingw32_hal.h +++ b/src/hal/mingw32/mingw32_hal.h @@ -46,6 +46,8 @@ #define CONFIG_ENABLE_FCS32 #endif +#define CONFIG_TINYHAL_THREAD_SUPPORT 1 + #ifndef DOXYGEN_SHOULD_SKIP_THIS /** diff --git a/src/hal/no_platform/noplatform_serial.inl b/src/hal/no_platform/noplatform_serial.inl new file mode 100644 index 00000000..d3e8c2cf --- /dev/null +++ b/src/hal/no_platform/noplatform_serial.inl @@ -0,0 +1,61 @@ +/* + Copyright 2017-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void tiny_serial_close(tiny_serial_handle_t port) +{ +} + +tiny_serial_handle_t tiny_serial_open(const char *name, uint32_t baud) +{ + return TINY_SERIAL_INVALID; +} + +int tiny_serial_send(tiny_serial_handle_t port, const void *buf, int len) +{ + return -1; +} + +int tiny_serial_send_timeout(tiny_serial_handle_t port, const void *buf, int len, uint32_t timeout_ms) +{ + return -1; +} + +int tiny_serial_read(tiny_serial_handle_t port, void *buf, int len) +{ + return -1; +} + +int tiny_serial_read_timeout(tiny_serial_handle_t port, void *buf, int len, uint32_t timeout_ms) +{ + return -1; +} diff --git a/src/hal/tiny_serial.c b/src/hal/tiny_serial.c index 8aaf40f4..28566c34 100644 --- a/src/hal/tiny_serial.c +++ b/src/hal/tiny_serial.c @@ -28,7 +28,9 @@ #include "tiny_serial.h" -#if defined(__linux__) +#if defined(ARDUINO) + +#elif defined(__linux__) #include "linux/linux_serial.inl" @@ -36,6 +38,12 @@ #include "win32/win32_serial.inl" +#elif defined(__XTENSA__) + +#include "esp32/esp32_serial.inl" + #else +#include "no_platform/noplatform_serial.inl" + #endif diff --git a/src/hal/tiny_serial.h b/src/hal/tiny_serial.h index e1e35ef9..df6bb2ba 100644 --- a/src/hal/tiny_serial.h +++ b/src/hal/tiny_serial.h @@ -25,6 +25,7 @@ the terms contained in a written agreement between you and Alexey Dynda. For further information contact via email on github account. */ + /** This is UART support implementation. @@ -35,6 +36,7 @@ #pragma once #ifdef __cplusplus + extern "C" { #endif @@ -45,10 +47,14 @@ extern "C" */ #include -#if defined(__linux__) +#if defined(ARDUINO) +#include "arduino/arduino_serial.h" +#elif defined(__linux__) #include "linux/linux_serial.h" #elif defined(_WIN32) #include "win32/win32_serial.h" +#elif defined(__XTENSA__) +#include "esp32/esp32_serial.h" #else #include "no_platform/noplatform_serial.h" #endif @@ -59,8 +65,13 @@ extern "C" * Opens serial port by name * * @param name path to the port to open - * For linux this can be /dev/ttyO1, /dev/ttyS1, /dev/ttyUSB0, etc. - * For windows this can be COM1, COM2, etc. + * For linux this can be "/dev/ttyO1", "/dev/ttyS1", "/dev/ttyUSB0", etc. + * For windows this can be "COM1", "COM2", etc. + * For ESP32 IDF the line should in format "uart0,tx,rx,rts,cts", where + * uart0 - uart hardware device number ("uart1", "uart2"); "tx", "rx", + * "rts", "cts" are optional arguments, specifying integer pin numbers (-1 to use + * standard pin). + * For Arduino this is must be pointer to HardwareSerial class * @param baud baud rate in bits * @return valid serial handle or TINY_SERIAL_INVALID in case of error */ diff --git a/src/hal/tiny_serial_cpp.cpp b/src/hal/tiny_serial_cpp.cpp new file mode 100644 index 00000000..ad173283 --- /dev/null +++ b/src/hal/tiny_serial_cpp.cpp @@ -0,0 +1,26 @@ +/* + Copyright 2017,2020-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "tiny_serial.h" + +#if defined(ARDUINO) + +#include "arduino/arduino_serial.inl" + +#endif diff --git a/src/hal/tiny_types.h b/src/hal/tiny_types.h index 5ce42cf4..85b81785 100644 --- a/src/hal/tiny_types.h +++ b/src/hal/tiny_types.h @@ -65,6 +65,13 @@ extern "C" #include +#ifndef CONFIG_TINYHAL_THREAD_SUPPORT +/** + * By default disable thread support + */ +#define CONFIG_TINYHAL_THREAD_SUPPORT 0 +#endif + #if defined(_MSC_VER) /// This macro is used internally for aligning the structures @@ -126,8 +133,10 @@ extern "C" #define TINY_ERR_AGAIN (-7) /// Invalid crc field of incoming frame #define TINY_ERR_WRONG_CRC (-8) +/// Out of memory during operation +#define TINY_ERR_OUT_OF_MEMORY (-9) /// Unknown remote peer -#define TINY_ERR_UNKNOWN_PEER (-9) +#define TINY_ERR_UNKNOWN_PEER (-10) /** @} */ @@ -170,15 +179,22 @@ extern "C" typedef int (*read_block_cb_t)(void *pdata, void *buffer, int size); /** - * on_frame_cb_t is a callback function, which is called every time new frame is received, or sent. - * refer to tiny_set_callbacks - * @param handle - handle of Tiny. - * @param pdata - data received from the channel. - * @param size - size of data received. + * on_frame_cb_t is a callback function, which is called every time new frame is received. + * @param udata user data + * @param pdata pointer to data received from the channel. + * @param size size of data received. + * @return None. + */ + typedef void (*on_frame_cb_t)(void *udata, uint8_t *pdata, int size); + + /** + * on_frame_send_cb_t is a callback function, which is called every time new frame is sent. + * @param udata user data + * @param pdata pointer data sent to the channel. + * @param size size of data sent. * @return None. - * @see tiny_set_callbacks */ - typedef void (*on_frame_cb_t)(void *handle, uint8_t *pdata, int size); + typedef void (*on_tx_frame_cb_t)(void *udata, const uint8_t *pdata, int size); /** * on_frame_read_cb_t is a callback function, which is called every time new frame is received. diff --git a/src/hal/win32/win32_hal.h b/src/hal/win32/win32_hal.h index d248bd27..23504a9c 100644 --- a/src/hal/win32/win32_hal.h +++ b/src/hal/win32/win32_hal.h @@ -46,6 +46,8 @@ #define CONFIG_ENABLE_FCS32 #endif +#define CONFIG_TINYHAL_THREAD_SUPPORT 1 + #ifndef DOXYGEN_SHOULD_SKIP_THIS /** diff --git a/src/interface/TinySerial.cpp b/src/interface/TinySerial.cpp new file mode 100644 index 00000000..2a428fe6 --- /dev/null +++ b/src/interface/TinySerial.cpp @@ -0,0 +1,64 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinySerial.h" + +namespace tinyproto +{ + +#if defined(ARDUINO) +Serial::Serial(HardwareSerial &dev) + : m_dev( reinterpret_cast(&dev) ) +{ +} +#endif + +Serial::Serial(const char *dev) + : m_dev(dev) +{ +} + +void Serial::setTimeout(uint32_t timeoutMs) +{ + m_timeoutMs = timeoutMs; +} + +bool Serial::begin(uint32_t speed) +{ + m_handle = tiny_serial_open(m_dev, speed); + return m_handle != TINY_SERIAL_INVALID; +} + +void Serial::end() +{ + tiny_serial_close(m_handle); + m_handle = TINY_SERIAL_INVALID; +} + +int Serial::readBytes(uint8_t *buf, int len) +{ + return tiny_serial_read_timeout(m_handle, buf, len, m_timeoutMs); +} + +int Serial::write(const uint8_t *buf, int len) +{ + return tiny_serial_send_timeout(m_handle, buf, len, m_timeoutMs); +} + +} // namespace tinyproto diff --git a/src/interface/TinySerial.h b/src/interface/TinySerial.h new file mode 100644 index 00000000..e65e6b99 --- /dev/null +++ b/src/interface/TinySerial.h @@ -0,0 +1,57 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "hal/tiny_serial.h" + +#if defined(ARDUINO) +#include +#endif +#include +#include + +namespace tinyproto +{ + +class Serial +{ +public: +#if defined(ARDUINO) + explicit Serial(HardwareSerial &dev); +#endif + explicit Serial(const char *dev); + + void setTimeout(uint32_t timeoutMs); + + bool begin(uint32_t speed); + + void end(); + + int readBytes(uint8_t *buf, int len); + + int write(const uint8_t *buf, int len); + +private: + const char *m_dev; + tiny_serial_handle_t m_handle = TINY_SERIAL_INVALID; + uint32_t m_timeoutMs = 0; +}; + +} // namespace tinyproto diff --git a/src/link/TinyFdLinkLayer.cpp b/src/link/TinyFdLinkLayer.cpp new file mode 100644 index 00000000..f4d47a45 --- /dev/null +++ b/src/link/TinyFdLinkLayer.cpp @@ -0,0 +1,83 @@ +/* + Copyright 2021-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinyFdLinkLayer.h" + +namespace tinyproto +{ + +IFdLinkLayer::IFdLinkLayer(void *buffer, int size) + : m_buffer(reinterpret_cast(buffer)) + , m_bufferSize(size) +{ +} + +IFdLinkLayer::~IFdLinkLayer() +{ +} + +bool IFdLinkLayer::begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) +{ + tiny_fd_init_t init{}; + init.pdata = udata; + init.on_read_cb = onReadCb; + init.on_send_cb = onSendCb; + // TODO: init.on_connect_event_cb = onConnectEventInternal; + init.buffer = m_buffer; + init.buffer_size = m_bufferSize; + init.window_frames = m_txWindow; + init.send_timeout = getTimeout(); + init.retry_timeout = getTimeout() ? (getTimeout() / 4) : 100; + init.retries = 2; + init.crc_type = getCrc(); + init.mtu = getMtu(); + init.mode = TINY_FD_MODE_ABM; + int result = tiny_fd_init(&m_handle, &init); + return result == TINY_SUCCESS; +} + +void IFdLinkLayer::end() +{ + tiny_fd_close(m_handle); + m_handle = nullptr; +} + +bool IFdLinkLayer::put(void *buf, int size, uint32_t timeout) +{ + return tiny_fd_send_packet(m_handle, buf, size, timeout) >= 0; +} + +void IFdLinkLayer::flushTx() +{ +} + +int IFdLinkLayer::parseData(const uint8_t *data, int size) +{ + int code = tiny_fd_on_rx_data(m_handle, data, size); + return code == TINY_SUCCESS ? size : code; +} + +int IFdLinkLayer::getData(uint8_t *data, int size) +{ + return tiny_fd_get_tx_data(m_handle, data, size, getTimeout()); +} + +///////////////////////////////////////////////////////////////////////////// + +} // namespace tinyproto diff --git a/src/link/TinyFdLinkLayer.h b/src/link/TinyFdLinkLayer.h new file mode 100644 index 00000000..6b3af56e --- /dev/null +++ b/src/link/TinyFdLinkLayer.h @@ -0,0 +1,83 @@ +/* + Copyright 2016-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinyLinkLayer.h" +#include "proto/fd/tiny_fd.h" + +#include +#include + +namespace tinyproto +{ + +class IFdLinkLayer: public ILinkLayer +{ +public: + IFdLinkLayer(void *buffer, int size); + + ~IFdLinkLayer(); + + bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) override; + + void end() override; + + bool put(void *buf, int size, uint32_t timeout) override; + + void flushTx() override; + + int getWindow() + { + return m_txWindow; + } + + hdlc_crc_t getCrc() + { + return m_crc; + } + + void setCrc( hdlc_crc_t crc ) { m_crc = crc; } + + void setWindow(int window) + { + m_txWindow = window; + } + + void setBuffer(void *buffer, int size) + { + m_buffer = reinterpret_cast(buffer); + m_bufferSize = size; + } + +protected: + + int parseData(const uint8_t *data, int size); + + int getData(uint8_t *data, int size); + +private: + tiny_fd_handle_t m_handle = nullptr; + uint8_t *m_buffer = nullptr; + int m_bufferSize = 0; + uint8_t m_txWindow = 2; + hdlc_crc_t m_crc = HDLC_CRC_8; +}; + +} // namespace tinyproto diff --git a/src/link/TinyHdlcLinkLayer.cpp b/src/link/TinyHdlcLinkLayer.cpp new file mode 100644 index 00000000..60cca8c5 --- /dev/null +++ b/src/link/TinyHdlcLinkLayer.cpp @@ -0,0 +1,149 @@ +/* + Copyright 2021-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinyHdlcLinkLayer.h" + +namespace tinyproto +{ + +enum +{ + TX_QUEUE_FREE = 1, + TX_MESSAGE_SENT = 2, + TX_MESSAGE_SENDING = 4, +}; + +IHdlcLinkLayer::IHdlcLinkLayer(void *buffer, int size) + : m_buffer(reinterpret_cast(buffer)) + , m_bufferSize(size) +{ + tiny_mutex_create( &m_sendMutex ); + tiny_events_create( &m_events ); +} + +IHdlcLinkLayer::~IHdlcLinkLayer() +{ + tiny_events_destroy( &m_events ); + tiny_mutex_destroy( &m_sendMutex ); +} + +bool IHdlcLinkLayer::begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) +{ + hdlc_ll_init_t init{}; + m_onReadCb = onReadCb; + m_onSendCb = onSendCb; + m_udata = udata; + init.user_data = this; + init.on_frame_read = onRead; + init.on_frame_send = onSend; + init.buf = m_buffer; + init.buf_size = m_bufferSize; + init.crc_type = getCrc(); + init.mtu = getMtu(); + int result = hdlc_ll_init(&m_handle, &init); + m_flushFlag = false; + tiny_events_set( &m_events, TX_QUEUE_FREE ); + tiny_events_clear( &m_events, TX_MESSAGE_SENDING ); + return result == TINY_SUCCESS; +} + +void IHdlcLinkLayer::end() +{ + tiny_events_clear( &m_events, TX_MESSAGE_SENDING ); + hdlc_ll_close(m_handle); + m_handle = nullptr; +} + +bool IHdlcLinkLayer::put(void *buf, int size, uint32_t timeout) +{ + bool result = false; + uint8_t bits = tiny_events_wait( &m_events, TX_QUEUE_FREE, EVENT_BITS_CLEAR, timeout ); + if ( bits ) + { + tiny_mutex_lock( &m_sendMutex ); + m_flushFlag = false; + if ( m_tempBuffer == buf ) + { + m_tempBuffer = nullptr; + tiny_events_set( &m_events, TX_QUEUE_FREE ); + result = true; + } + else if ( hdlc_ll_put(m_handle, buf, size) == TINY_SUCCESS ) + { + m_tempBuffer = buf; + tiny_events_set( &m_events, TX_MESSAGE_SENDING ); + } + else + { + // TODO: This should never happen + tiny_events_set( &m_events, TX_QUEUE_FREE ); + } + tiny_mutex_unlock( &m_sendMutex ); + } + return result; +} + +void IHdlcLinkLayer::flushTx() +{ + tiny_mutex_lock( &m_sendMutex ); + m_flushFlag = true; + tiny_mutex_unlock( &m_sendMutex ); +} + +int IHdlcLinkLayer::parseData(const uint8_t *data, int size) +{ + return hdlc_ll_run_rx(m_handle, data, size, nullptr); +} + +int IHdlcLinkLayer::getData(uint8_t *data, int size) +{ + if ( tiny_events_wait( &m_events, TX_MESSAGE_SENDING, EVENT_BITS_LEAVE, getTimeout() ) ) + { + if ( m_flushFlag ) + { +// tiny_mutex_lock( &m_sendMutex ); + m_flushFlag = false; + hdlc_ll_reset( m_handle, HDLC_LL_RESET_TX_ONLY ); + tiny_events_clear( &m_events, TX_MESSAGE_SENDING ); + tiny_events_set( &m_events, TX_QUEUE_FREE ); +// tiny_events_clear( &m_events, TX_MESSAGE_SENDING ); +// tiny_mutex_unlock( &m_sendMutex ); + } + return hdlc_ll_run_tx(m_handle, data, size); + } + return 0; +} + +void IHdlcLinkLayer::onSend(void *udata, const uint8_t *data, int len) +{ + IHdlcLinkLayer *layer = reinterpret_cast(udata); + tiny_events_clear( &layer->m_events, TX_MESSAGE_SENDING ); + tiny_events_set( &layer->m_events, TX_QUEUE_FREE ); + layer->m_onSendCb( layer->m_udata, 0, data, len ); +} + +void IHdlcLinkLayer::onRead(void *udata, uint8_t *data, int len) +{ + IHdlcLinkLayer *layer = reinterpret_cast(udata); + layer->m_onReadCb( layer->m_udata, 0, data, len ); +} + +///////////////////////////////////////////////////////////////////////////// + +} // namespace tinyproto diff --git a/src/link/TinyHdlcLinkLayer.h b/src/link/TinyHdlcLinkLayer.h new file mode 100644 index 00000000..9d7e2bf5 --- /dev/null +++ b/src/link/TinyHdlcLinkLayer.h @@ -0,0 +1,87 @@ +/* + Copyright 2016-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinyLinkLayer.h" +#include "proto/hdlc/low_level/hdlc.h" + +#include "hal/tiny_types.h" + +#include + +namespace tinyproto +{ + +class IHdlcLinkLayer: public ILinkLayer +{ +public: + IHdlcLinkLayer(void *buffer, int size); + + ~IHdlcLinkLayer(); + + bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) override; + + void end() override; + + bool put(void *buf, int size, uint32_t timeout) override; + + void flushTx() override; + + hdlc_crc_t getCrc() + { + return m_crc; + } + + void setCrc( hdlc_crc_t crc ) + { + m_crc = crc; + } + + void setBuffer(void *buffer, int size) + { + m_buffer = reinterpret_cast(buffer); + m_bufferSize = size; + } + +protected: + + int parseData(const uint8_t *data, int size); + + int getData(uint8_t *data, int size); + +private: + hdlc_ll_handle_t m_handle = nullptr; + void *m_udata = nullptr; + on_frame_read_cb_t m_onReadCb = nullptr; + on_frame_send_cb_t m_onSendCb = nullptr; + tiny_mutex_t m_sendMutex{}; + tiny_events_t m_events{}; + void *m_tempBuffer = nullptr; + + uint8_t *m_buffer = nullptr; + int m_bufferSize = 0; + hdlc_crc_t m_crc = HDLC_CRC_8; + bool m_flushFlag = false; + + static void onSend(void *udata, const uint8_t *data, int len); + static void onRead(void *udata, uint8_t *data, int len); +}; + +} // namespace tinyproto diff --git a/src/link/TinyLinkLayer.cpp b/src/link/TinyLinkLayer.cpp new file mode 100644 index 00000000..aac0d918 --- /dev/null +++ b/src/link/TinyLinkLayer.cpp @@ -0,0 +1,27 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinyLinkLayer.h" + +namespace tinyproto +{ + +///////////////////////////////////////////////////////////////////////////// + +} // namespace tinyproto diff --git a/src/link/TinyLinkLayer.h b/src/link/TinyLinkLayer.h new file mode 100644 index 00000000..7d8d5cad --- /dev/null +++ b/src/link/TinyLinkLayer.h @@ -0,0 +1,132 @@ +/* + Copyright 2016-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinyPacket.h" +#include "proto/fd/tiny_fd.h" + +#include +#include + +namespace tinyproto +{ + +/** + * This is basic class for C++ Link Layer objects. + */ +class ILinkLayer +{ +public: + ILinkLayer() {} + + /** + * The method initializes the link layer protocol, and connects custom callbacks + * to link layer. Once the new frame is received, onReadCb will be called, once frame is sent onSendCb + * will be called. + * + * @param onReadCb callback to call when a frame is received. Remember that processing must be as quick as possible + * @param onSendCb callback to call when a frame is sent. + * @param udata user defined data, will be passed to callbacks + * @return true if successful, false is initialization error happened. + */ + virtual bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) = 0; + + /** + * Stops link layer protocol + */ + virtual void end() = 0; + + /** + * Runs rx part of the protocol. This method is automatically called by the owner of the protocol (i.e. tinyproto::Proto) + * The actual implementation on runRx() method depends on hardware channel used. + */ + virtual void runRx() = 0; + + /** + * Runs tx part of the protocol. This method is automatically called by the owner of the protocol (i.e. tinyproto::Proto) + * The actual implementation on runTx() method depends on hardware channel used. + */ + virtual void runTx() = 0; + + /** + * Puts new data for sending over the link layer. + * + * @param buf pointer to the buffer with the data + * @param size size of the data in the buffer + * @param timeout timeout in milliseconds to wait for operation to complete + * @return true if the data is successfully put to the queue, false if timeout happened or error + */ + virtual bool put(void *buf, int size, uint32_t timeout) = 0; + + /** + * Flush tx operation if possible + */ + virtual void flushTx() = 0; + + /** + * Sets timeout of Rx/Tx operations in milliseconds for the link layer protocol. + * This is not the same timeout, as timeout used by put() method. + * + * @param timeout timeout in milliseconds for Tx/Rx operations + */ + void setTimeout(uint32_t timeout) + { + m_timeout = timeout; + } + + /** + * Returns current timeout of Rx/Tx operations + */ + uint32_t getTimeout() + { + return m_timeout; + } + + /** + * Returns current mtu for the link layer protocol in bytes. + */ + int getMtu() + { + return m_mtu; + } + + /** + * Set protocol mtu (maximum transmission unit) payload. + * Allowable value depends on the resources of the controller used, and actual low layer protocol. + * mtu can be configured only before protocol initialization (before calling begin() method) + * + * @param mtu size in bytes + */ + void setMtu(int mtu) + { + m_mtu = mtu; + } + + /** + * Default virtual destructor + */ + virtual ~ILinkLayer() = default; + +private: + int m_mtu = 16384; + uint32_t m_timeout = 0; +}; + +} // namespace tinyproto diff --git a/src/link/TinySerialFdLink.cpp b/src/link/TinySerialFdLink.cpp new file mode 100644 index 00000000..01f4a390 --- /dev/null +++ b/src/link/TinySerialFdLink.cpp @@ -0,0 +1,53 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinySerialFdLink.h" +#include + +namespace tinyproto +{ + +SerialFdLink::~SerialFdLink() +{ + if ( m_buffer ) + { + free(m_buffer); + m_buffer = nullptr; + } +} + +bool SerialFdLink::begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) +{ + int size = tiny_fd_buffer_size_by_mtu_ex(1, getMtu(), getWindow(), getCrc(), 3); + m_buffer = reinterpret_cast(malloc(size)); + setBuffer(m_buffer, size); + return ISerialLinkLayer::begin(onReadCb, onSendCb, udata); +} + +void SerialFdLink::end() +{ + ISerialLinkLayer::end(); + if ( m_buffer ) + { + free(m_buffer); + m_buffer = nullptr; + } +} + +} // namespace tinyproto diff --git a/src/link/TinySerialFdLink.h b/src/link/TinySerialFdLink.h new file mode 100644 index 00000000..e33b959b --- /dev/null +++ b/src/link/TinySerialFdLink.h @@ -0,0 +1,81 @@ +/* + Copyright 2016-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinySerialLinkLayer.h" +#include "TinyFdLinkLayer.h" + +#if defined(ARDUINO) +#include "proto/fd/tiny_fd_int.h" +#endif + +namespace tinyproto +{ + +template class StaticSerialFdLink: public ISerialLinkLayer +{ +public: + StaticSerialFdLink(char *dev) + : ISerialLinkLayer(dev, this->m_buffer, BUFFER_SIZE) + { + this->setMtu(MTU); + this->setWindow(TX_WINDOW); + } + +private: + uint8_t m_buffer[BUFFER_SIZE]; +}; + + +#if defined(ARDUINO) + +/** Valid only for Arduino IDE, since it has access to internal headers */ +template using ArduinoStaticSerialFdLinkLayer = StaticSerialFdLink; + +class ArduinoSerialFdLink: public ArduinoStaticSerialFdLinkLayer<32, 2, 2, 4> +{ +public: + ArduinoSerialFdLink(HardwareSerial *dev) + : ArduinoStaticSerialFdLinkLayer<32, 2, 2, 4>(reinterpret_cast(dev)) + { + } +}; + +#endif + +class SerialFdLink: public ISerialLinkLayer +{ +public: + SerialFdLink(char *dev) + : ISerialLinkLayer(dev, nullptr, 0) + { + } + + ~SerialFdLink(); + + bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) override; + + void end() override; + +private: + uint8_t *m_buffer = nullptr; +}; + +} // namespace tinyproto diff --git a/src/link/TinySerialHdlcLink.cpp b/src/link/TinySerialHdlcLink.cpp new file mode 100644 index 00000000..6f84c5ef --- /dev/null +++ b/src/link/TinySerialHdlcLink.cpp @@ -0,0 +1,53 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinySerialHdlcLink.h" +#include + +namespace tinyproto +{ + +SerialHdlcLink::~SerialHdlcLink() +{ + if ( m_buffer ) + { + free(m_buffer); + m_buffer = nullptr; + } +} + +bool SerialHdlcLink::begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) +{ + int size = hdlc_ll_get_buf_size_ex(getMtu(), getCrc(), 3); + m_buffer = reinterpret_cast(malloc(size)); + setBuffer(m_buffer, size); + return ISerialLinkLayer::begin(onReadCb, onSendCb, udata); +} + +void SerialHdlcLink::end() +{ + ISerialLinkLayer::end(); + if ( m_buffer ) + { + free(m_buffer); + m_buffer = nullptr; + } +} + +} // namespace tinyproto diff --git a/src/link/TinySerialHdlcLink.h b/src/link/TinySerialHdlcLink.h new file mode 100644 index 00000000..afa9e8fb --- /dev/null +++ b/src/link/TinySerialHdlcLink.h @@ -0,0 +1,80 @@ +/* + Copyright 2016-2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinySerialLinkLayer.h" +#include "TinyHdlcLinkLayer.h" + +#if defined(ARDUINO) +#include "proto/hdlc/low_level/hdlc_int.h" +#endif + +namespace tinyproto +{ + +template class StaticSerialHdlcLink: public ISerialLinkLayer +{ +public: + StaticSerialHdlcLink(char *dev) + : ISerialLinkLayer(dev, this->m_buffer, BUFFER_SIZE) + { + this->setMtu(MTU); + } + +private: + uint8_t m_buffer[BUFFER_SIZE]; +}; + + +#if defined(ARDUINO) + +/** Valid only for Arduino IDE, since it has access to internal headers */ +template using ArduinoStaticSerialHdlcLinkLayer = StaticSerialHdlcLink; + +class ArduinoSerialHdlcLink: public ArduinoStaticSerialHdlcLinkLayer<32, 2, 4> +{ +public: + ArduinoSerialHdlcLink(HardwareSerial *dev) + : ArduinoStaticSerialHdlcLinkLayer<32, 2, 4>(reinterpret_cast(dev)) + { + } +}; + +#endif + +class SerialHdlcLink: public ISerialLinkLayer +{ +public: + SerialHdlcLink(char *dev) + : ISerialLinkLayer(dev, nullptr, 0) + { + } + + ~SerialHdlcLink(); + + bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) override; + + void end() override; + +private: + uint8_t *m_buffer = nullptr; +}; + +} // namespace tinyproto diff --git a/src/link/TinySerialLinkLayer.cpp b/src/link/TinySerialLinkLayer.cpp new file mode 100644 index 00000000..40551407 --- /dev/null +++ b/src/link/TinySerialLinkLayer.cpp @@ -0,0 +1,27 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#include "TinySerialLinkLayer.h" +#include + +namespace tinyproto +{ + + +} // namespace tinyproto diff --git a/src/link/TinySerialLinkLayer.h b/src/link/TinySerialLinkLayer.h new file mode 100644 index 00000000..cd7ee1cf --- /dev/null +++ b/src/link/TinySerialLinkLayer.h @@ -0,0 +1,101 @@ +/* + Copyright 2016-2022 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "link/TinyLinkLayer.h" +#include "interface/TinySerial.h" + +namespace tinyproto +{ + +/** + * Template class for Serial-based communication for any of TinyProto Links + * + * @param BASE Base class for protocol type: IFdLinkLayer or IHdlcLinkLayer. + * @param BSIZE Maximum block size which can be transmitted via Serial Link as single block + */ +template class ISerialLinkLayer: public BASE +{ +public: + ISerialLinkLayer(char *dev, void *buffer, int size) + : BASE(buffer, size) + , m_serial(dev) + { + } + + bool begin(on_frame_read_cb_t onReadCb, on_frame_send_cb_t onSendCb, void *udata) override + { + bool result = BASE::begin(onReadCb, onSendCb, udata); + m_serial.setTimeout( this->getTimeout() ); + return result && m_serial.begin(m_speed); + } + + void end() override + { + m_serial.end(); + BASE::end(); + } + + void runRx() override + { + uint8_t buf[BSIZE]; + uint8_t *p = buf; + + int len = m_serial.readBytes(p, BSIZE); + while ( len > 0 ) + { + int temp = BASE::parseData( p, len); + if ( temp < 0 ) + { + break; + } + len -= temp; + p += temp; + } + } + + void runTx() override + { + uint8_t buf[BSIZE]; + int len = BASE::getData( buf, BSIZE ); + uint8_t *ptr = buf; + while ( len > 0 ) + { + int sent = m_serial.write(ptr, len); + if ( sent < 0 ) + { + break; + } + ptr += sent; + len -= sent; + } + } + + void setSpeed( uint32_t speed ) + { + m_speed = speed; + } + +private: + uint32_t m_speed = 115200; + tinyproto::Serial m_serial; +}; + +} // namespace tinyproto diff --git a/src/mainpage.dox b/src/mainpage.dox index 9584f74d..8c36a9f5 100644 --- a/src/mainpage.dox +++ b/src/mainpage.dox @@ -70,14 +70,14 @@ HDLC callbacks: @code{.cpp} int write_func_cb(void *user_data, const void *data, int len); int on_frame_read(void *user_data, void *data, int len); -int on_frame_sent(void *user_data, const void *data, int len); +int on_frame_send(void *user_data, void *data, int len); @endcode - write_func_cb() is called by HDLC implementation every time, it needs to send bytes to TX channel - on_frame_read() is called by HDLC implementation every time, new frame arrives and checksum is correct. - - on_frame_sent() is called by HDLC implementation every time, new frame is sent + - on_frame_send() is called by HDLC implementation every time, new frame is sent to TX. HDLC protocol requires only write_func_cb() to be defined. Other callbacks are optional. As for RX processes, your application code is responsible for reading data from RX line, @@ -85,14 +85,14 @@ then all you need to do, is to pass received bytes to HDLC implementation for pr via hdlc_run_rx(). All higher level protocols (\ref LIGHT_API, \ref FULL_DUPLEX_API) -needs 4 callback functions, defined by a user: read_func_cb() is added. +needs 4 callback functions, defined by a user: The list of callbacks: @code{.cpp} -int write_func_cb(void *user_data, const void *data, int len); -int read_func_cb(void *user_data, void *data, int len); -int on_frame_read(void *user_data, void *data, int len); -int on_frame_sent(void *user_data, const void *data, int len); +int write_func_cb(void *user_data, const void *data, int len); // LIGHT API Only +int read_func_cb(void *user_data, void *data, int len); // LIGHT API Only +void on_frame_cb(void *udata, uint8_t *pdata, int size); // FD API Only +void on_frame_send_cb(void *udata, const uint8_t *pdata, int size); // FD API Only @endcode Unlike HDLC implementation, higher level protocols use different approach. They control both diff --git a/src/proto/fd/tiny_fd.c b/src/proto/fd/tiny_fd.c index 3be853cf..4cfc051d 100644 --- a/src/proto/fd/tiny_fd.c +++ b/src/proto/fd/tiny_fd.c @@ -81,8 +81,8 @@ enum static const uint8_t seq_bits_mask = 0x07; -static int on_frame_read(void *user_data, void *data, int len); -static int on_frame_sent(void *user_data, const void *data, int len); +static void on_frame_read(void *user_data, uint8_t *data, int len); +static void on_frame_send(void *user_data, const uint8_t *data, int len); /////////////////////////////////////////////////////////////////////////////// // Helper functions @@ -306,12 +306,6 @@ static void __confirm_sent_frames(tiny_fd_handle_t handle, uint8_t peer, uint8_t tiny_fd_frame_info_t *slot = tiny_fd_queue_get_next( &handle->frames.i_queue, TINY_FD_QUEUE_I_FRAME, address, handle->peers[peer].confirm_ns ); if ( slot != NULL ) { - if ( handle->on_sent_cb ) - { - tiny_mutex_unlock(&handle->frames.mutex); - handle->on_sent_cb(handle->user_data, &slot->payload[0], slot->len); - tiny_mutex_lock(&handle->frames.mutex); - } if ( handle->on_send_cb ) { tiny_mutex_unlock(&handle->frames.mutex); @@ -444,12 +438,6 @@ static int __on_i_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, // Provide data to user only if we expect this frame if ( result == TINY_SUCCESS ) { - if ( handle->on_frame_cb ) - { - tiny_mutex_unlock(&handle->frames.mutex); - handle->on_frame_cb(handle->user_data, (uint8_t *)data + 2, len - 2); - tiny_mutex_lock(&handle->frames.mutex); - } if ( handle->on_read_cb ) { tiny_mutex_unlock(&handle->frames.mutex); @@ -564,19 +552,19 @@ static int __on_u_frame_read(tiny_fd_handle_t handle, uint8_t peer, void *data, /////////////////////////////////////////////////////////////////////////////// -static int on_frame_read(void *user_data, void *data, int len) +static void on_frame_read(void *user_data, uint8_t *data, int len) { tiny_fd_handle_t handle = (tiny_fd_handle_t)user_data; if ( len < 2 ) { - LOG(TINY_LOG_WRN, "FD: received too small frame\n"); - return TINY_ERR_FAILED; + LOG(TINY_LOG_WRN, "%s: received too small frame\n", "FD"); + return; } uint8_t peer = __address_field_to_peer( handle, ((uint8_t *)data)[0] ); if ( peer == 0xFF ) { // it seems that the frame is not for us. Just exit - return len; + return; } tiny_mutex_lock(&handle->frames.mutex); handle->peers[peer].last_ka_ts = tiny_millis(); @@ -621,10 +609,9 @@ static int on_frame_read(void *user_data, void *data, int len) tiny_events_set( &handle->events, FD_EVENT_HAS_MARKER ); } tiny_mutex_unlock(&handle->frames.mutex); - return len; } -static int on_frame_sent(void *user_data, const void *data, int len) +static void on_frame_send(void *user_data, const uint8_t *data, int len) { tiny_fd_handle_t handle = (tiny_fd_handle_t)user_data; uint8_t peer = __address_field_to_peer( handle, ((const uint8_t *)data)[0] ); @@ -632,7 +619,7 @@ static int on_frame_sent(void *user_data, const void *data, int len) if ( peer == 0xFF ) { // Do nothing for now, but this should never happen - return len; + return; } tiny_mutex_lock(&handle->frames.mutex); if ( (control & HDLC_I_FRAME_MASK) == HDLC_I_FRAME_BITS ) @@ -666,8 +653,7 @@ static int on_frame_sent(void *user_data, const void *data, int len) LOG(TINY_LOG_INFO, "[%p] [RELEASED MARKER]\n", handle); } tiny_events_clear( &handle->events, flags ); - tiny_mutex_unlock( &handle->frames.mutex ); - return len; + tiny_mutex_unlock(&handle->frames.mutex); } /////////////////////////////////////////////////////////////////////////////// @@ -676,34 +662,35 @@ int tiny_fd_init(tiny_fd_handle_t *handle, tiny_fd_init_t *init) { const uint8_t peers_count = init->peers_count == 0 ? 1 : init->peers_count; *handle = NULL; - if ( (0 == init->on_frame_cb && 0 == init->on_read_cb) || (0 == init->buffer) || (0 == init->buffer_size) ) + if ( (0 == init->on_read_cb) || (0 == init->buffer) || (0 == init->buffer_size) ) { - return TINY_ERR_FAILED; + LOG(TINY_LOG_CRIT, "Invalid input data: null pointers%s", "\n"); + return TINY_ERR_INVALID_DATA; } if ( init->mtu == 0 ) { - int size = tiny_fd_buffer_size_by_mtu_ex(peers_count, 0, init->window_frames, init->crc_type); + int size = tiny_fd_buffer_size_by_mtu_ex(peers_count, 0, init->window_frames, init->crc_type, 1); init->mtu = (init->buffer_size - size) / (init->window_frames + 1); if ( init->mtu < 1 ) { - LOG(TINY_LOG_CRIT, "Calculated mtu size is zero, no payload transfer is available\n"); - return TINY_ERR_INVALID_DATA; + LOG(TINY_LOG_CRIT, "Calculated mtu size is zero, no payload transfer is available%s", "\n"); + return TINY_ERR_OUT_OF_MEMORY; } } - if ( init->buffer_size < tiny_fd_buffer_size_by_mtu_ex(peers_count, init->mtu, init->window_frames, init->crc_type) ) + if ( init->buffer_size < tiny_fd_buffer_size_by_mtu_ex(peers_count, init->mtu, init->window_frames, init->crc_type, 1) ) { LOG(TINY_LOG_CRIT, "Too small buffer for FD protocol %i < %i\n", init->buffer_size, - tiny_fd_buffer_size_by_mtu_ex(peers_count, init->mtu, init->window_frames, init->crc_type)); - return TINY_ERR_INVALID_DATA; + tiny_fd_buffer_size_by_mtu_ex(peers_count, init->mtu, init->window_frames, init->crc_type, 1)); + return TINY_ERR_OUT_OF_MEMORY; } if ( init->window_frames < 2 ) { - LOG(TINY_LOG_CRIT, "HDLC doesn't support less than 2-frames queue\n"); + LOG(TINY_LOG_CRIT, "HDLC doesn't support less than 2-frames queue%s", "\n"); return TINY_ERR_INVALID_DATA; } if ( !init->retry_timeout && !init->send_timeout ) { - LOG(TINY_LOG_CRIT, "HDLC uses timeouts for ACK, at least retry_timeout, or send_timeout must be specified\n"); + LOG(TINY_LOG_CRIT, "HDLC uses timeouts for ACK, at least retry_timeout, or send_timeout must be specified%s", "\n"); return TINY_ERR_INVALID_DATA; } memset(init->buffer, 0, init->buffer_size); @@ -718,7 +705,8 @@ int tiny_fd_init(tiny_fd_handle_t *handle, tiny_fd_init_t *init) * To do that we need to calculate the size required for all FD buffers * We do not need to align the buffer for the HDLC level, since it done by low level API. */ uint8_t *hdlc_ll_ptr = ptr; - int hdlc_ll_size = (int)((uint8_t *)init->buffer + init->buffer_size - ptr - // Remaining size + // TODO: Hack: remove - 4 + int hdlc_ll_size = (int)((uint8_t *)init->buffer + init->buffer_size - ptr - 4 - // Remaining size init->window_frames * // Number of frames multiply by frame size (headers + payload + pointers) ( sizeof(tiny_fd_frame_info_t *) + init->mtu + sizeof(tiny_fd_frame_info_t) - sizeof(((tiny_fd_frame_info_t *)0)->payload) ) - TINY_FD_U_QUEUE_MAX_SIZE * @@ -757,27 +745,26 @@ int tiny_fd_init(tiny_fd_handle_t *handle, tiny_fd_init_t *init) { LOG(TINY_LOG_CRIT, "Out of provided memory: provided %i bytes, used %i bytes\n", init->buffer_size, (int)(ptr - (uint8_t *)init->buffer)); - return TINY_ERR_INVALID_DATA; + return TINY_ERR_OUT_OF_MEMORY; } - /* Lets allocate memory for HDLC low level protocol */ + /* Lets initialize memory for HDLC low level protocol */ hdlc_ll_init_t _init = { 0 }; _init.on_frame_read = on_frame_read; - _init.on_frame_sent = on_frame_sent; + _init.on_frame_send = on_frame_send; _init.user_data = protocol; _init.crc_type = init->crc_type; _init.buf_size = hdlc_ll_size; _init.buf = hdlc_ll_ptr; + _init.mtu = init->mtu + sizeof(tiny_frame_header_t); int result = hdlc_ll_init(&protocol->_hdlc, &_init); if ( result != TINY_SUCCESS ) { - LOG(TINY_LOG_CRIT, "HDLC low level initialization failed"); + LOG(TINY_LOG_CRIT, "HDLC low level initialization failed%s", "\n"); return result; } protocol->user_data = init->pdata; - protocol->on_frame_cb = init->on_frame_cb; - protocol->on_sent_cb = init->on_sent_cb; protocol->on_read_cb = init->on_read_cb; protocol->on_send_cb = init->on_send_cb; protocol->on_connect_event_cb = init->on_connect_event_cb; @@ -1048,7 +1035,7 @@ static void tiny_fd_disconnected_check_idle_timeout(tiny_fd_handle_t handle, uin /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len) +int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len, uint32_t timeout) { bool repeat = true; int result = 0; @@ -1079,9 +1066,9 @@ int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len) } // Since no send operation is in progress, check if we have something to send // Check if the station has marker to send FIRST (That means, we are allowed to send anything still) - if ( tiny_events_wait(&handle->events, FD_EVENT_HAS_MARKER, EVENT_BITS_LEAVE, 0 ) ) + if ( tiny_events_wait(&handle->events, FD_EVENT_HAS_MARKER, EVENT_BITS_LEAVE, timeout ) ) { - if ( handle->mode == TINY_FD_MODE_NRM || tiny_events_wait(&handle->events, FD_EVENT_TX_DATA_AVAILABLE, EVENT_BITS_CLEAR, 0) ) + if ( tiny_events_wait(&handle->events, FD_EVENT_TX_DATA_AVAILABLE, EVENT_BITS_CLEAR, timeout) || handle->mode == TINY_FD_MODE_NRM ) { int frame_len = 0; uint8_t *frame_data = tiny_fd_get_next_frame_to_send(handle, &frame_len, peer); @@ -1142,7 +1129,7 @@ int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len) int tiny_fd_run_tx(tiny_fd_handle_t handle, write_block_cb_t write_func) { uint8_t buf[4]; - int len = tiny_fd_get_tx_data(handle, buf, sizeof(buf)); + int len = tiny_fd_get_tx_data(handle, buf, sizeof(buf), 1); if ( len <= 0 ) { return len; @@ -1163,7 +1150,7 @@ int tiny_fd_run_tx(tiny_fd_handle_t handle, write_block_cb_t write_func) /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void *data, int len) +int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void *data, int len, uint32_t timeout) { int result; uint8_t peer; @@ -1184,16 +1171,15 @@ int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void uint32_t start_ms = tiny_millis(); if ( len > tiny_fd_queue_get_mtu( &handle->frames.i_queue ) ) { - LOG(TINY_LOG_ERR, "[%p] PUT frame error\n", handle); + LOG(TINY_LOG_ERR, "[%p] PUT frame error: data len %i is greater MTU %i\n", handle, len, handle->frames.i_queue.mtu); result = TINY_ERR_DATA_TOO_LARGE; } // Wait until there is room for new frame - else if ( tiny_events_wait(&handle->peers[peer].events, FD_EVENT_CAN_ACCEPT_I_FRAMES, EVENT_BITS_CLEAR, - handle->send_timeout) ) + else if ( tiny_events_wait(&handle->peers[peer].events, FD_EVENT_CAN_ACCEPT_I_FRAMES, EVENT_BITS_CLEAR, timeout) ) { uint32_t delta_ms = (uint32_t)(tiny_millis() - start_ms); if ( tiny_events_wait(&handle->events, FD_EVENT_QUEUE_HAS_FREE_SLOTS, EVENT_BITS_CLEAR, - handle->send_timeout > delta_ms ? (handle->send_timeout - delta_ms) : 0) ) + timeout > delta_ms ? (timeout - delta_ms) : 0) ) { tiny_mutex_lock(&handle->frames.mutex); // Check if space is actually available @@ -1243,21 +1229,21 @@ int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_send_packet(tiny_fd_handle_t handle, const void *data, int len) +int tiny_fd_send_packet(tiny_fd_handle_t handle, const void *data, int len, uint32_t timeout) { - return tiny_fd_send_packet_to(handle, TINY_FD_PRIMARY_ADDR, data, len); + return tiny_fd_send_packet_to(handle, TINY_FD_PRIMARY_ADDR, data, len, timeout); } /////////////////////////////////////////////////////////////////////////////// int tiny_fd_buffer_size_by_mtu(int mtu, int window) { - return tiny_fd_buffer_size_by_mtu_ex(0, mtu, window, HDLC_CRC_16); + return tiny_fd_buffer_size_by_mtu_ex(0, mtu, window, HDLC_CRC_16, 1); } /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_buffer_size_by_mtu_ex(uint8_t peers_count, int mtu, int window, hdlc_crc_t crc_type) +int tiny_fd_buffer_size_by_mtu_ex(uint8_t peers_count, int mtu, int tx_window, hdlc_crc_t crc_type, int rx_window) { if ( !peers_count ) { @@ -1267,11 +1253,11 @@ int tiny_fd_buffer_size_by_mtu_ex(uint8_t peers_count, int mtu, int window, hdlc return sizeof(tiny_fd_data_t) + TINY_ALIGN_STRUCT_VALUE - 1 + peers_count * sizeof(tiny_fd_peer_info_t) + // RX side - hdlc_ll_get_buf_size_ex(mtu + sizeof(tiny_frame_header_t), crc_type) + + hdlc_ll_get_buf_size_ex(mtu + sizeof(tiny_frame_header_t), crc_type, rx_window) + // TX side (sizeof(tiny_fd_frame_info_t *) + sizeof(tiny_fd_frame_info_t) + mtu - sizeof(((tiny_fd_frame_info_t *)0)->payload)) * - window + + tx_window + (sizeof(tiny_fd_frame_info_t *) + sizeof(tiny_fd_frame_info_t)) * TINY_FD_U_QUEUE_MAX_SIZE; } @@ -1291,14 +1277,14 @@ int tiny_fd_get_mtu(tiny_fd_handle_t handle) /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_send_to(tiny_fd_handle_t handle, uint8_t address, const void *data, int len) +int tiny_fd_send_to(tiny_fd_handle_t handle, uint8_t address, const void *data, int len, uint32_t timeout) { const uint8_t *ptr = (const uint8_t *)data; int left = len; while ( left > 0 ) { int size = left < tiny_fd_queue_get_mtu( &handle->frames.i_queue ) ? left : tiny_fd_queue_get_mtu( &handle->frames.i_queue ); - int result = tiny_fd_send_packet_to(handle, address, ptr, size); + int result = tiny_fd_send_packet_to(handle, address, ptr, size, timeout); if ( result != TINY_SUCCESS ) { break; @@ -1310,9 +1296,9 @@ int tiny_fd_send_to(tiny_fd_handle_t handle, uint8_t address, const void *data, /////////////////////////////////////////////////////////////////////////////// -int tiny_fd_send(tiny_fd_handle_t handle, const void *data, int len) +int tiny_fd_send(tiny_fd_handle_t handle, const void *data, int len, uint32_t timeout) { - return tiny_fd_send_to(handle, (HDLC_PRIMARY_ADDR >> 2), data, len); + return tiny_fd_send_to(handle, (HDLC_PRIMARY_ADDR >> 2), data, len, timeout); } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/proto/fd/tiny_fd.h b/src/proto/fd/tiny_fd.h index a011ea20..6cbfac88 100644 --- a/src/proto/fd/tiny_fd.h +++ b/src/proto/fd/tiny_fd.h @@ -95,10 +95,10 @@ extern "C" void *pdata; /// callback function to process incoming frames. Callback is called from tiny_fd_run_rx() context. - on_frame_cb_t on_frame_cb; + on_frame_read_cb_t on_read_cb; /// Callback to get notification of sent frames. Callback is called from tiny_fd_run_tx() context. - on_frame_cb_t on_sent_cb; + on_frame_send_cb_t on_send_cb; /** * buffer to store data during full-duplex protocol operating. @@ -174,12 +174,6 @@ extern "C" */ uint8_t mode; - /// callback function to process incoming frames. Callback is called from tiny_fd_run_rx() context. - on_frame_read_cb_t on_read_cb; - - /// Callback to get notification of sent frames. Callback is called from tiny_fd_run_tx() context. - on_frame_send_cb_t on_send_cb; - } tiny_fd_init_t; /** @@ -236,9 +230,10 @@ extern "C" * @param handle handle of full-duplex protocol * @param data pointer to buffer to fill with tx data * @param len maximum size of specified buffer + * @param timeout in milliseconds to wait for the data to be ready for sending * @return number of bytes written to specified buffer */ - extern int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len); + extern int tiny_fd_get_tx_data(tiny_fd_handle_t handle, void *data, int len, uint32_t timeout); /** * @brief sends tx data to the communication channel via user callback `write_func()`. @@ -276,7 +271,7 @@ extern "C" * Reads rx data from the communication channel via user callback `read_func()`. * Internally this function has 4-byte buffer, and tries to read 4 bytes from the channel. * Then received bytes are processed by the protocol. If FD protocol detects new incoming - * message then it calls on_sent_callback. + * message then it calls on_send_callback. * If no data available in the channel, the function returns immediately after read_func() callback * returns control. * @@ -305,6 +300,7 @@ extern "C" * @param address address of remote peer. For primary device, please use TINY_FD_PRIMARY_ADDR * @param buf data to send * @param len length of data to send + * @param timeout timeout in milliseconds to wait until data are placed to outgoing queue * * @return Success result or error code: * * TINY_SUCCESS if user data are put to internal queue. @@ -313,7 +309,7 @@ extern "C" * * TINY_ERR_UNKNOWN_PEER if peer is not known to the system. * * TINY_ERR_DATA_TOO_LARGE if user data are too big to fit in tx buffer. */ - extern int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void *buf, int len); + extern int tiny_fd_send_packet_to(tiny_fd_handle_t handle, uint8_t address, const void *buf, int len, uint32_t timeout); /** * Returns minimum required buffer size for specified parameters. @@ -330,10 +326,11 @@ extern "C" * * @param peers_count maximum number of peers supported by the primary. Use 0 or 1 for secondary devices * @param mtu size of desired user payload in bytes. - * @param window maximum tx queue size of I-frames. + * @param tx_window maximum tx queue size of I-frames. * @param crc_type crc type to be used with FD protocol + * @param rx_window number of RX ring buffer in frames */ - extern int tiny_fd_buffer_size_by_mtu_ex(uint8_t peers_count, int mtu, int window, hdlc_crc_t crc_type); + extern int tiny_fd_buffer_size_by_mtu_ex(uint8_t peers_count, int mtu, int tx_window, hdlc_crc_t crc_type, int rx_window); /** * @brief returns max packet size in bytes. @@ -360,10 +357,11 @@ extern "C" * @param address address of remote peer. For primary device, please use TINY_FD_PRIMARY_ADDR * @param buf data to send * @param len length of data to send + * @param timeout timeout in milliseconds, will be used for each block sending * * @return Number of bytes sent */ - extern int tiny_fd_send_to(tiny_fd_handle_t handle, uint8_t address, const void *buf, int len); + extern int tiny_fd_send_to(tiny_fd_handle_t handle, uint8_t address, const void *buf, int len, uint32_t timeout); /** * Sets keep alive timeout in milliseconds. This timeout is used to send special RR @@ -396,10 +394,11 @@ extern "C" * @param handle tiny_fd_handle_t handle * @param buf data to send * @param len length of data to send + * @param timeout timeout in milliseconds, will be used for each block sending * * @return Number of bytes sent */ - extern int tiny_fd_send(tiny_fd_handle_t handle, const void *buf, int len); + extern int tiny_fd_send(tiny_fd_handle_t handle, const void *buf, int len, uint32_t timeout); /** * Sends packet to primary station. For details, please, refer to tiny_fd_send_packet_to(). @@ -407,10 +406,11 @@ extern "C" * @param handle tiny_fd_handle_t handle * @param buf data to send * @param len length of data to send + * @param timeout timeout in milliseconds to wait until data are placed to outgoing queue * * @return Success result or error code */ - extern int tiny_fd_send_packet(tiny_fd_handle_t handle, const void *buf, int len); + extern int tiny_fd_send_packet(tiny_fd_handle_t handle, const void *buf, int len, uint32_t timeout); /** * @} diff --git a/src/proto/fd/tiny_fd_int.h b/src/proto/fd/tiny_fd_int.h index c744c348..0cd8e71a 100644 --- a/src/proto/fd/tiny_fd_int.h +++ b/src/proto/fd/tiny_fd_int.h @@ -45,13 +45,21 @@ extern "C" #define FD_PEER_BUF_SIZE() ( sizeof(tiny_fd_peer_info_t) ) - -#define FD_MIN_BUF_SIZE(mtu, window) ( sizeof(tiny_fd_data_t) + TINY_ALIGN_STRUCT_VALUE - 1 + \ - HDLC_MIN_BUF_SIZE( mtu + sizeof(tiny_frame_header_t), HDLC_CRC_16 ) + \ - ( 1 * FD_PEER_BUF_SIZE() ) + \ - ( sizeof(tiny_fd_frame_info_t *) + sizeof(tiny_fd_frame_info_t) + mtu \ - - sizeof(((tiny_fd_frame_info_t *)0)->payload) ) * window + \ - ( sizeof(tiny_fd_frame_info_t) + sizeof(tiny_fd_frame_info_t *) ) * TINY_FD_U_QUEUE_MAX_SIZE ) +#define FD_MIN_BUF_SIZE(mtu, window) \ + (sizeof(tiny_fd_data_t) + TINY_ALIGN_STRUCT_VALUE - 1 + \ + HDLC_MIN_BUF_SIZE(mtu + sizeof(tiny_frame_header_t), HDLC_CRC_16) + \ + ( 1 * FD_PEER_BUF_SIZE() ) + \ + (sizeof(tiny_fd_frame_info_t *) + sizeof(tiny_fd_frame_info_t) + mtu \ + - sizeof(((tiny_fd_frame_info_t *)0)->payload) ) * window + \ + ( sizeof(tiny_fd_frame_info_t) + sizeof(tiny_fd_frame_info_t *) ) * TINY_FD_U_QUEUE_MAX_SIZE ) + +#define FD_BUF_SIZE_EX(mtu, tx_window, crc, rx_window) \ + (sizeof(tiny_fd_data_t) + TINY_ALIGN_STRUCT_VALUE - 1 + \ + HDLC_BUF_SIZE_EX(mtu + sizeof(tiny_frame_header_t), crc, rx_window) + \ + ( 1 * FD_PEER_BUF_SIZE() ) + \ + (sizeof(tiny_fd_frame_info_t *) + sizeof(tiny_i_frame_info_t) + mtu \ + - sizeof(((tiny_fd_frame_info_t *)0)->payload)) * tx_window + \ + ( sizeof(tiny_fd_frame_info_t) + sizeof(tiny_fd_frame_info_t *) ) * TINY_FD_U_QUEUE_MAX_SIZE) typedef enum { @@ -103,10 +111,6 @@ extern "C" typedef struct tiny_fd_data_t { - /// Callback to process received frames - on_frame_cb_t on_frame_cb; - /// Callback to get notification of sent frames - on_frame_cb_t on_sent_cb; /// Callback to process received frames on_frame_read_cb_t on_read_cb; /// Callback to process received frames diff --git a/src/proto/hdlc/high_level/hdlc.c b/src/proto/hdlc/high_level/hdlc.c index 379e4c2d..b6a96cc5 100644 --- a/src/proto/hdlc/high_level/hdlc.c +++ b/src/proto/hdlc/high_level/hdlc.c @@ -56,8 +56,8 @@ enum RX_DATA_READY_BIT = 0x08, }; -static int on_frame_read(void *user_data, void *data, int len); -static int on_frame_sent(void *user_data, const void *data, int len); +static void on_frame_read(void *user_data, uint8_t *data, int len); +static void on_frame_send(void *user_data, const uint8_t *data, int len); //////////////////////////////////////////////////////////////////////////////////////// @@ -66,7 +66,7 @@ hdlc_handle_t hdlc_init(hdlc_struct_t *hdlc_info) hdlc_ll_init_t init = { 0 }; init.crc_type = hdlc_info->crc_type; init.on_frame_read = on_frame_read; - init.on_frame_sent = on_frame_sent; + init.on_frame_send = on_frame_send; init.buf = hdlc_info->rx_buf; init.buf_size = hdlc_info->rx_buf_size; init.user_data = hdlc_info; @@ -103,16 +103,15 @@ void hdlc_reset(hdlc_handle_t handle) //////////////////////////////////////////////////////////////////////////////////////// -static int on_frame_sent(void *user_data, const void *data, int len) +static void on_frame_send(void *user_data, const uint8_t *data, int len) { hdlc_handle_t handle = (hdlc_handle_t)user_data; - if ( handle->on_frame_sent ) + if ( handle->on_frame_send ) { - handle->on_frame_sent(handle->user_data, data, len); + handle->on_frame_send(handle->user_data, data, len); } tiny_events_set(&handle->events, TX_DATA_SENT_BIT); tiny_events_set(&handle->events, TX_ACCEPT_BIT); - return TINY_SUCCESS; } static void hdlc_send_terminate(hdlc_handle_t handle) @@ -270,7 +269,7 @@ int hdlc_send(hdlc_handle_t handle, const void *data, int len, uint32_t timeout) //////////////////////////////////////////////////////////////////////////////////////////// -static int on_frame_read(void *user_data, void *data, int len) +static void on_frame_read(void *user_data, uint8_t *data, int len) { hdlc_handle_t handle = (hdlc_handle_t)user_data; if ( handle->on_frame_read ) @@ -280,7 +279,6 @@ static int on_frame_read(void *user_data, void *data, int len) // Set bit indicating that we have read and processed the frame handle->rx_len = len; tiny_events_set(&handle->events, RX_DATA_READY_BIT); - return TINY_SUCCESS; } //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/proto/hdlc/high_level/hdlc.h b/src/proto/hdlc/high_level/hdlc.h index 4f0e6c6a..4c334312 100644 --- a/src/proto/hdlc/high_level/hdlc.h +++ b/src/proto/hdlc/high_level/hdlc.h @@ -89,7 +89,7 @@ extern "C" * @return user callback must return negative value in case of error * or 0 value if packet is successfully processed. */ - int (*on_frame_sent)(void *user_data, const void *data, int len); + int (*on_frame_send)(void *user_data, const void *data, int len); /** * Buffer to be used by hdlc level to receive data to diff --git a/src/proto/hdlc/low_level/hdlc.c b/src/proto/hdlc/low_level/hdlc.c index 9c108387..c352be57 100644 --- a/src/proto/hdlc/low_level/hdlc.c +++ b/src/proto/hdlc/low_level/hdlc.c @@ -78,15 +78,17 @@ int hdlc_ll_init(hdlc_ll_handle_t *handle, hdlc_ll_init_t *init) { LOG(TINY_LOG_ERR, "[HDLC] failed to init hdlc. buf=%p, size=%i (%i required)\n", init->buf, init->buf_size, (int)(sizeof(hdlc_ll_data_t) + TINY_ALIGN_STRUCT_VALUE - 1)); - return TINY_ERR_FAILED; + return TINY_ERR_OUT_OF_MEMORY; } *handle = (hdlc_ll_handle_t)buf; (*handle)->rx_buf = (uint8_t *)buf + sizeof(hdlc_ll_data_t); (*handle)->rx_buf_size = buf_size - sizeof(hdlc_ll_data_t); (*handle)->crc_type = init->crc_type == HDLC_CRC_OFF ? 0 : init->crc_type; (*handle)->on_frame_read = init->on_frame_read; - (*handle)->on_frame_sent = init->on_frame_sent; + (*handle)->on_frame_send = init->on_frame_send; (*handle)->user_data = init->user_data; + (*handle)->phys_mtu = init->mtu ? (init->mtu + get_crc_field_size((*handle)->crc_type)): ((*handle)->rx_buf_size); + (*handle)->rx.frame_buf = (*handle)->rx_buf; // Must be last hdlc_ll_reset(*handle, HDLC_LL_RESET_BOTH); @@ -99,9 +101,9 @@ int hdlc_ll_close(hdlc_ll_handle_t handle) { if ( handle && handle->tx.data ) { - if ( handle->on_frame_sent ) + if ( handle->on_frame_send ) { - handle->on_frame_sent(handle->user_data, handle->tx.origin_data, + handle->on_frame_send(handle->user_data, handle->tx.origin_data, (int)(handle->tx.data - handle->tx.origin_data) + handle->tx.len); } } @@ -167,7 +169,7 @@ static int hdlc_ll_send_start(hdlc_ll_handle_t handle) static int hdlc_ll_send_data(hdlc_ll_handle_t handle) { // This commented out code is never reachable because of implementation of hdlc_ll_put() - it check for zero length - //if ( handle->tx.len == 0 ) + // if ( handle->tx.len == 0 ) //{ // LOG(TINY_LOG_DEB, "[HDLC:%p] hdlc_ll_send_crc\n", handle); // handle->tx.state = hdlc_ll_send_crc; @@ -271,9 +273,9 @@ static int hdlc_ll_send_end(hdlc_ll_handle_t handle) const void *ptr = handle->tx.origin_data; handle->tx.origin_data = NULL; handle->tx.data = NULL; - if ( handle->on_frame_sent ) + if ( handle->on_frame_send ) { - handle->on_frame_sent(handle->user_data, ptr, len); + handle->on_frame_send(handle->user_data, ptr, len); } } return result; @@ -343,9 +345,9 @@ int hdlc_ll_run_tx(hdlc_ll_handle_t handle, void *data, int len) int hdlc_ll_put(hdlc_ll_handle_t handle, const void *data, int len) { - LOG(TINY_LOG_DEB, "[HDLC:%p] hdlc_ll_put\n", handle); - if ( !len || !data || !handle ) + if ( !handle ) { + LOG(TINY_LOG_ERR, "[HDLC:%p] hdlc_ll_put invalid handle passed \n", handle); return TINY_ERR_INVALID_DATA; } // Check if TX thread is ready to accept new data @@ -354,6 +356,10 @@ int hdlc_ll_put(hdlc_ll_handle_t handle, const void *data, int len) LOG(TINY_LOG_WRN, "[HDLC:%p] hdlc_ll_put FAILED\n", handle); return TINY_ERR_BUSY; } + if ( !len || !data ) + { + return TINY_SUCCESS; + } LOG(TINY_LOG_DEB, "[HDLC:%p] hdlc_ll_put SUCCESS\n", handle); handle->tx.origin_data = data; handle->tx.data = data; @@ -379,7 +385,7 @@ static int hdlc_ll_read_start(hdlc_ll_handle_t handle, const uint8_t *data, int } LOG(TINY_LOG_DEB, "[HDLC:%p] RX: %02X\n", handle, data[0]); handle->rx.escape = 0; - handle->rx.data = (uint8_t *)handle->rx_buf; + handle->rx.data = handle->rx.frame_buf; handle->rx.state = hdlc_ll_read_data; return 1; } @@ -403,7 +409,7 @@ static int hdlc_ll_read_data(hdlc_ll_handle_t handle, const uint8_t *data, int l { handle->rx.escape = 1; } - else if ( handle->rx.data - (uint8_t *)handle->rx_buf < handle->rx_buf_size ) + else if ( handle->rx.data - handle->rx.frame_buf < handle->phys_mtu ) { if ( handle->rx.escape ) { @@ -415,7 +421,11 @@ static int hdlc_ll_read_data(hdlc_ll_handle_t handle, const uint8_t *data, int l *handle->rx.data = byte; } handle->rx.data++; - // LOG(TINY_LOG_DEB, "%02X\n", handle->rx.data[ handle->rx.len ]); + } + else + { + LOG(TINY_LOG_WRN, "[HDLC:%p] No space for incoming byte: len=%i (mtu = %i)\n", + handle, (int)(handle->rx.data - handle->rx.frame_buf), handle->phys_mtu); } result++; data++; @@ -428,7 +438,7 @@ static int hdlc_ll_read_data(hdlc_ll_handle_t handle, const uint8_t *data, int l static int hdlc_ll_read_end(hdlc_ll_handle_t handle, const uint8_t *data, int len_bytes) { - if ( handle->rx.data == handle->rx_buf ) + if ( handle->rx.data == handle->rx.frame_buf ) { // Impossible, maybe frame alignment is wrong, go to read data again LOG(TINY_LOG_WRN, "[HDLC:%p] RX: error in frame alignment, recovering...\n", handle); @@ -437,8 +447,8 @@ static int hdlc_ll_read_end(hdlc_ll_handle_t handle, const uint8_t *data, int le return 0; // That's OK, we actually didn't process anything from user bytes } handle->rx.state = hdlc_ll_read_start; - int len = (int)(handle->rx.data - (uint8_t *)handle->rx_buf); - if ( len > handle->rx_buf_size ) + int len = (int)(handle->rx.data - handle->rx.frame_buf); + if ( len > handle->phys_mtu ) { // Buffer size issue, too long packet LOG(TINY_LOG_ERR, "[HDLC:%p] RX: tool long frame\n", handle); @@ -456,19 +466,19 @@ static int hdlc_ll_read_end(hdlc_ll_handle_t handle, const uint8_t *data, int le { #ifdef CONFIG_ENABLE_CHECKSUM case HDLC_CRC_8: - calc_crc = tiny_chksum(INITCHECKSUM, handle->rx_buf, len - 1) & 0x00FF; + calc_crc = tiny_chksum(INITCHECKSUM, handle->rx.frame_buf, len - 1) & 0x00FF; read_crc = handle->rx.data[-1]; break; #endif #ifdef CONFIG_ENABLE_FCS16 case HDLC_CRC_16: - calc_crc = tiny_crc16(PPPINITFCS16, handle->rx_buf, len - 2); + calc_crc = tiny_crc16(PPPINITFCS16, handle->rx.frame_buf, len - 2); read_crc = handle->rx.data[-2] | ((uint16_t)handle->rx.data[-1] << 8); break; #endif #ifdef CONFIG_ENABLE_FCS32 case HDLC_CRC_32: - calc_crc = tiny_crc32(PPPINITFCS32, handle->rx_buf, len - 4); + calc_crc = tiny_crc32(PPPINITFCS32, handle->rx.frame_buf, len - 4); read_crc = handle->rx.data[-4] | ((uint32_t)handle->rx.data[-3] << 8) | ((uint32_t)handle->rx.data[-2] << 16) | ((uint32_t)handle->rx.data[-1] << 24); break; @@ -482,22 +492,26 @@ static int hdlc_ll_read_end(hdlc_ll_handle_t handle, const uint8_t *data, int le LOG(TINY_LOG_ERR, "[HDLC:%p] RX: WRONG CRC (calc:%08X != %08X)\n", handle, calc_crc, read_crc); if ( TINY_LOG_DEB < g_tiny_log_level ) for ( int i = 0; i < len; i++ ) - fprintf(stderr, " %c ", (char)((uint8_t *)handle->rx_buf)[i]); - LOG(TINY_LOG_DEB, "\n"); + fprintf(stderr, " %c ", (char)(handle->rx.frame_buf)[i]); + LOG(TINY_LOG_DEB, "[%s]", "\n"); if ( TINY_LOG_DEB < g_tiny_log_level ) for ( int i = 0; i < len; i++ ) - fprintf(stderr, " %02X ", ((uint8_t *)handle->rx_buf)[i]); - LOG(TINY_LOG_DEB, "\n-----------\n"); + fprintf(stderr, " %02X ", (handle->rx.frame_buf)[i]); + LOG(TINY_LOG_DEB, "\n%s\n","------------"); #endif return TINY_ERR_WRONG_CRC; } - len -= (uint8_t)handle->crc_type / 8; // Shift back data pointer, pointing to the last byte after payload - handle->rx.data -= (uint8_t)handle->crc_type / 8; + len -= (uint8_t)handle->crc_type / 8; LOG(TINY_LOG_INFO, "[HDLC:%p] RX: Frame success: %d bytes\n", handle, len); if ( handle->on_frame_read ) { - handle->on_frame_read(handle->user_data, handle->rx_buf, len); + handle->on_frame_read(handle->user_data, handle->rx.frame_buf, len); + } + handle->rx.frame_buf += handle->phys_mtu; + if ( handle->rx.frame_buf - handle->rx_buf + handle->phys_mtu > handle->rx_buf_size ) + { + handle->rx.frame_buf = handle->rx_buf; } return TINY_SUCCESS; } @@ -539,10 +553,10 @@ int hdlc_ll_get_buf_size(int mtu) //////////////////////////////////////////////////////////////////////////////////////////// -int hdlc_ll_get_buf_size_ex(int mtu, hdlc_crc_t crc_type) +int hdlc_ll_get_buf_size_ex(int mtu, hdlc_crc_t crc_type, int rx_window) { // TINY_ALIGN_STRUCT_VALUE is added to satisfy alignment requirements - return get_crc_field_size(crc_type) + sizeof(hdlc_ll_data_t) + mtu + TINY_ALIGN_STRUCT_VALUE - 1; + return (get_crc_field_size(crc_type) + mtu) * rx_window + sizeof(hdlc_ll_data_t) + TINY_ALIGN_STRUCT_VALUE - 1; } //////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/proto/hdlc/low_level/hdlc.h b/src/proto/hdlc/low_level/hdlc.h index 8ad36c2e..ad0a5a39 100644 --- a/src/proto/hdlc/low_level/hdlc.h +++ b/src/proto/hdlc/low_level/hdlc.h @@ -81,11 +81,9 @@ extern "C" * @param user_data user-defined data * @param data pointer to received data * @param len size of received data in bytes - * @return user callback must return negative value in case of error - * or 0 value if packet is successfully processed. */ - int (*on_frame_read)(void *user_data, void *data, int len); + on_frame_cb_t on_frame_read; /** * User-defined callback, which is called when the packet is sent to TX @@ -94,10 +92,8 @@ extern "C" * @param user_data user-defined data * @param data pointer to sent data * @param len size of sent data in bytes - * @return user callback must return negative value in case of error - * or 0 value if packet is successfully processed. */ - int (*on_frame_sent)(void *user_data, const void *data, int len); + on_tx_frame_cb_t on_frame_send; /** * Buffer to be used by hdlc level to receive data to. @@ -119,6 +115,9 @@ extern "C" /** User data, which will be passed to user-defined callback as first argument */ void *user_data; + + /** mtu size, can be 0 */ + int mtu; } hdlc_ll_init_t; //------------------------ GENERIC FUNCIONS ------------------------------ @@ -233,10 +232,11 @@ extern "C" * * @param mtu size of desired max payload in bytes * @param crc_type type of crc validation to use for the protocol + * @param rx_window number of RX frames in the RX ring buffer * * @return size of the buffer required */ - int hdlc_ll_get_buf_size_ex(int mtu, hdlc_crc_t crc_type); + int hdlc_ll_get_buf_size_ex(int mtu, hdlc_crc_t crc_type, int rx_window); /** * @} diff --git a/src/proto/hdlc/low_level/hdlc_int.h b/src/proto/hdlc/low_level/hdlc_int.h index 276d67d8..6926d456 100644 --- a/src/proto/hdlc/low_level/hdlc_int.h +++ b/src/proto/hdlc/low_level/hdlc_int.h @@ -52,6 +52,11 @@ extern "C" */ #define HDLC_MIN_BUF_SIZE(mtu, crc) (sizeof(hdlc_ll_data_t) + (int)(crc) / 8 + (mtu) + TINY_ALIGN_STRUCT_VALUE - 1) +/** + * Macro calculating buffer size required for specific packet size in bytes, and window + */ +#define HDLC_BUF_SIZE_EX(mtu, crc, window) (sizeof(hdlc_ll_data_t) + ((int)(crc) / 8 + (mtu)) * (window) + TINY_ALIGN_STRUCT_VALUE - 1) + /** * Structure describes configuration of lowest HDLC level * Initialize this structure by 0 before passing to hdlc_ll_init() @@ -66,11 +71,8 @@ extern "C" * @param user_data user-defined data * @param data pointer to received data * @param len size of received data in bytes - * @return user callback must return negative value in case of error - * or 0 value if packet is successfully processed. */ - - int (*on_frame_read)(void *user_data, void *data, int len); + on_frame_cb_t on_frame_read; /** * User-defined callback, which is called when the packet is sent to TX @@ -79,15 +81,13 @@ extern "C" * @param user_data user-defined data * @param data pointer to sent data * @param len size of sent data in bytes - * @return user callback must return negative value in case of error - * or 0 value if packet is successfully processed. */ - int (*on_frame_sent)(void *user_data, const void *data, int len); + on_tx_frame_cb_t on_frame_send; /** * Buffer to be used by hdlc level to receive data to */ - void *rx_buf; + uint8_t *rx_buf; /** * size of hdlc buffer @@ -106,11 +106,13 @@ extern "C" #ifndef DOXYGEN_SHOULD_SKIP_THIS /** Parameters in DOXYGEN_SHOULD_SKIP_THIS section should not be modified by a user */ + int phys_mtu; struct { int (*state)(hdlc_ll_handle_t handle, const uint8_t *data, int len); uint8_t *data; uint8_t escape; + uint8_t *frame_buf; } rx; struct { diff --git a/src/proto/light/tiny_light.c b/src/proto/light/tiny_light.c index 432b7000..19da511d 100644 --- a/src/proto/light/tiny_light.c +++ b/src/proto/light/tiny_light.c @@ -60,8 +60,8 @@ * ***************************************************************/ -static int on_frame_read(void *user_data, void *data, int len); -static int on_frame_sent(void *user_data, const void *data, int len); +static void on_frame_read(void *user_data, uint8_t *data, int len); +static void on_frame_send(void *user_data, const uint8_t *data, int len); int tiny_light_init(STinyLightData *handle, write_block_cb_t write_func, read_block_cb_t read_func, void *pdata) { @@ -72,7 +72,7 @@ int tiny_light_init(STinyLightData *handle, write_block_cb_t write_func, read_bl hdlc_ll_init_t init = { 0 }; init.user_data = handle; init.on_frame_read = on_frame_read; - init.on_frame_sent = on_frame_sent; + init.on_frame_send = on_frame_send; init.buf = &handle->buffer[0]; init.buf_size = LIGHT_BUF_SIZE; init.crc_type = ((STinyLightData *)handle)->crc_type; @@ -104,9 +104,8 @@ int tiny_light_close(STinyLightData *handle) ///////////////////////////////////////////////////////////////////////////////////////// -static int on_frame_sent(void *user_data, const void *data, int len) +static void on_frame_send(void *user_data, const uint8_t *data, int len) { - return len; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -148,11 +147,10 @@ int tiny_light_send(STinyLightData *handle, const uint8_t *pbuf, int len) ///////////////////////////////////////////////////////////////////////////////////////// -static int on_frame_read(void *user_data, void *data, int len) +static void on_frame_read(void *user_data, uint8_t *data, int len) { STinyLightData *handle = (STinyLightData *)user_data; handle->rx_len = len; - return len; } ///////////////////////////////////////////////////////////////////////////////////////// @@ -160,9 +158,11 @@ static int on_frame_read(void *user_data, void *data, int len) int tiny_light_read(STinyLightData *handle, uint8_t *pbuf, int len) { uint32_t ts = tiny_millis(); - int result = 0; + int result = TINY_SUCCESS; handle->_hdlc->rx_buf = pbuf; + handle->_hdlc->rx.frame_buf = pbuf; handle->_hdlc->rx_buf_size = len; + handle->_hdlc->phys_mtu = len; handle->rx_len = 0; do { diff --git a/src/proto/light/tiny_light.h b/src/proto/light/tiny_light.h index 0fb1b00e..6ca5beb7 100644 --- a/src/proto/light/tiny_light.h +++ b/src/proto/light/tiny_light.h @@ -57,7 +57,7 @@ extern "C" /** * This macro defines buffer size required for tiny light protocol */ -#define LIGHT_BUF_SIZE (sizeof(uintptr_t) * 16) +#define LIGHT_BUF_SIZE (sizeof(uintptr_t) * 18) /** * This structure contains information about communication channel and its state. diff --git a/src/tinyproto.h b/src/tinyproto.h new file mode 100644 index 00000000..819aa2cb --- /dev/null +++ b/src/tinyproto.h @@ -0,0 +1,22 @@ +/* + Copyright 2021 (C) Alexey Dynda + + This file is part of Tiny Protocol Library. + + Protocol Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Protocol Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with Protocol Library. If not, see . +*/ + +#pragma once + +#include "TinyProtocol.h" diff --git a/unittest/fd_tests.cpp b/unittest/fd_tests.cpp index d3f8a8a3..a0407b61 100644 --- a/unittest/fd_tests.cpp +++ b/unittest/fd_tests.cpp @@ -96,7 +96,7 @@ TEST(FD, arduino_to_pc) TinyHelperFd pc(&conn.endpoint1(), 4096, nullptr, 4, 400); TinyHelperFd arduino( &conn.endpoint2(), tiny_fd_buffer_size_by_mtu(64, 4), - [&arduino, &arduino_timedout_frames](uint8_t *b, int s) -> void { + [&arduino, &arduino_timedout_frames](uint8_t a, uint8_t *b, int s) -> void { if ( arduino.send(b, s) == TINY_ERR_TIMEOUT ) arduino_timedout_frames++; }, @@ -297,17 +297,19 @@ TEST(FD, on_connect_callback) helper1.run(true); helper2.run(true); + // Give some time to initialize threads + tiny_sleep( 10 ); uint8_t data[1] = {0xAA}; helper1.send( data, sizeof(data) ); - for (int i = 0; i < 200 && !connected; i++) + for (int i = 0; i < 1000 && !connected; i++) { tiny_sleep( 1 ); } CHECK_EQUAL(true, connected); helper2.stop(); - for (int i = 0; i < 200 && connected; i++) + for (int i = 0; i < 1000 && connected; i++) { tiny_sleep( 1 ); } diff --git a/unittest/hdlc_tests.cpp b/unittest/hdlc_tests.cpp index 5405e6dc..6091f1b0 100644 --- a/unittest/hdlc_tests.cpp +++ b/unittest/hdlc_tests.cpp @@ -238,11 +238,11 @@ TEST(HDLC, hdlc_ll_missalignment) conn.endpoint1().setTimeout(0); conn.endpoint2().setTimeout(10); TinyHdlcHelper helper2(&conn.endpoint2(), nullptr, nullptr, 1024, HDLC_CRC_OFF); - const uint8_t data[] = { 0x7E, 0x7E, 0x01, 0x02, 0x03, 0x7E }; + const uint8_t data[] = {0x7E, 0x7E, 0x01, 0x02, 0x03, 0x7E}; conn.endpoint1().write(data, sizeof(data)); helper2.wait_until_rx_count(2, 50); // Never 2 messages should be received, alway exit on timeout 50ms - CHECK_EQUAL( 1, helper2.rx_count() ); + CHECK_EQUAL(1, helper2.rx_count()); } TEST(HDLC, hdlc_send_escape_chars_encode) @@ -250,11 +250,11 @@ TEST(HDLC, hdlc_send_escape_chars_encode) FakeSetup conn; conn.endpoint1().setTimeout(0); conn.endpoint2().setTimeout(10); - TinyHdlcHelper helper2( &conn.endpoint2(), nullptr, nullptr, 1024, HDLC_CRC_8 ); - const uint8_t frame[] = { 0x7E, 0x7D, 0x87 }; + TinyHdlcHelper helper2(&conn.endpoint2(), nullptr, nullptr, 1024, HDLC_CRC_8); + const uint8_t frame[] = {0x7E, 0x7D, 0x87}; helper2.send(frame, sizeof(frame), 100); - const uint8_t data[] = { 0x7E, 0x7D, 0x5E, 0x7D, 0x5D, 0x87, 0x7D, 0x5D, 0x7E }; + const uint8_t data[] = {0x7E, 0x7D, 0x5E, 0x7D, 0x5D, 0x87, 0x7D, 0x5D, 0x7E}; if ( !conn.endpoint1().wait_until_rx_count(sizeof(data), 50) ) { FAIL("Timeout"); @@ -270,18 +270,17 @@ TEST(HDLC, hdlc_recv_escape_chars_decode) conn.endpoint1().setTimeout(0); conn.endpoint2().setTimeout(10); TinyHdlcHelper helper2(&conn.endpoint2(), nullptr, nullptr, 1024, HDLC_CRC_OFF); - const uint8_t data[] = { 0x7E, 0x7D, 0x5E, 0x7D, 0x5D, 0x7E }; + const uint8_t data[] = {0x7E, 0x7D, 0x5E, 0x7D, 0x5D, 0x7E}; conn.endpoint1().write(data, sizeof(data)); // Use single thread recv() implementation, that's why helper2.run( true ) is commented out // helper2.run(true); - uint8_t frame[] = { 0x7E, 0x7D }; - uint8_t actual_frame[] = { 0x00, 0x00, 0x00 }; - int len = helper2.recv( actual_frame, sizeof(actual_frame), 50 ); - CHECK_EQUAL(sizeof(frame), len ); - MEMCMP_EQUAL( frame, actual_frame, len); + uint8_t frame[] = {0x7E, 0x7D}; + uint8_t actual_frame[] = {0x00, 0x00, 0x00}; + int len = helper2.recv(actual_frame, sizeof(actual_frame), 50); + CHECK_EQUAL(sizeof(frame), len); + MEMCMP_EQUAL(frame, actual_frame, len); } - TEST(HDLC, hdlc_incomplete_send_on_close) { FakeSetup conn; @@ -289,21 +288,23 @@ TEST(HDLC, hdlc_incomplete_send_on_close) conn.endpoint2().setTimeout(10); int bytes_sent = 0; uint8_t *buffer_sent = nullptr; - const uint8_t frame[] = { 0x7E, 0x7D }; + const uint8_t frame[] = {0x7E, 0x7D}; { - TinyHdlcHelper helper2( &conn.endpoint2(), nullptr, - [&bytes_sent, &buffer_sent](uint8_t *buf, int len) -> void { bytes_sent = len; buffer_sent = buf; } ); + TinyHdlcHelper helper2(&conn.endpoint2(), nullptr, [&bytes_sent, &buffer_sent](uint8_t *buf, int len) -> void { + bytes_sent = len; + buffer_sent = buf; + }); helper2.send(frame, sizeof(frame), 0); } - CHECK_EQUAL( frame, buffer_sent ); - CHECK_EQUAL( sizeof(frame), bytes_sent ); + CHECK_EQUAL(frame, buffer_sent); + CHECK_EQUAL(sizeof(frame), bytes_sent); } TEST(HDLC, check_buf_size_calculations) { CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 13 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size(10) ); - CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 9 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_OFF) ); - CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 10 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_8) ); - CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 11 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_16) ); - CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 13 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_32) ); + CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 9 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_OFF, 1) ); + CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 10 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_8, 1) ); + CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 11 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_16, 1) ); + CHECK_EQUAL( sizeof(hdlc_ll_data_t) + 13 + TINY_ALIGN_STRUCT_VALUE, hdlc_ll_get_buf_size_ex(10, HDLC_CRC_32, 1) ); } diff --git a/unittest/helpers/fake_wire.cpp b/unittest/helpers/fake_wire.cpp index 5be76c17..65ee864b 100644 --- a/unittest/helpers/fake_wire.cpp +++ b/unittest/helpers/fake_wire.cpp @@ -272,7 +272,7 @@ void FakeWire::TransferData(int bytes) break; } // fprintf(stderr, "*T: %02X\n", data); - if ( data == 0x7E ) { if ( ++cnt >=3 ) *((uint8_t *)0) = 1; } else cnt = 0; + //if ( data == 0x7E ) { if ( ++cnt >=3 ) *((uint8_t *)0) = 1; } else cnt = 0; m_byte_counter++; bool error_happened = false; for ( auto &err : m_errors ) diff --git a/unittest/helpers/tiny_fd_helper.cpp b/unittest/helpers/tiny_fd_helper.cpp index abb6cfd6..bb1dcb7d 100644 --- a/unittest/helpers/tiny_fd_helper.cpp +++ b/unittest/helpers/tiny_fd_helper.cpp @@ -30,7 +30,7 @@ #include TinyHelperFd::TinyHelperFd(FakeEndpoint *endpoint, int rxBufferSize, - const std::function &onRxFrameCb, int window_frames, int timeout) + const std::function &onRxFrameCb, int window_frames, int timeout) : IBaseHelper(endpoint, rxBufferSize) , m_onRxFrameCb(onRxFrameCb) , m_rxBufferSize(rxBufferSize) @@ -41,7 +41,7 @@ TinyHelperFd::TinyHelperFd(FakeEndpoint *endpoint, int rxBufferSize, } TinyHelperFd::TinyHelperFd(FakeEndpoint *endpoint, int rxBufferSize, uint8_t mode, - const std::function &onRxFrameCb) + const std::function &onRxFrameCb) : IBaseHelper(endpoint, rxBufferSize) , m_onRxFrameCb(onRxFrameCb) , m_rxBufferSize(rxBufferSize) @@ -70,12 +70,13 @@ int TinyHelperFd::init() { tiny_fd_init_t init{}; init.pdata = this; - init.on_frame_cb = onRxFrame; - init.on_sent_cb = onTxFrame; + init.on_read_cb = onRxFrame; + init.on_send_cb = onTxFrame; init.on_connect_event_cb = onConnect; init.buffer = m_buffer; init.buffer_size = m_rxBufferSize; init.window_frames = m_window ? m_window : 7; + m_timeout = m_timeout < 0 ? 2000 : m_timeout; init.send_timeout = m_timeout < 0 ? 2000 : m_timeout; init.retry_timeout = init.send_timeout ? (init.send_timeout / 2) : 200; init.retries = 2; @@ -99,12 +100,12 @@ int TinyHelperFd::registerPeer(uint8_t address) int TinyHelperFd::send(uint8_t *buf, int len) { - return tiny_fd_send_packet(m_handle, buf, len); + return tiny_fd_send_packet(m_handle, buf, len, m_timeout); } int TinyHelperFd::sendto(uint8_t address, uint8_t *buf, int len) { - return tiny_fd_send_packet_to(m_handle, address, buf, len); + return tiny_fd_send_packet_to(m_handle, address, buf, len, m_timeout); } void TinyHelperFd::MessageSender(TinyHelperFd *helper, int count, std::string msg) @@ -140,7 +141,7 @@ int TinyHelperFd::send(int count, const std::string &msg) int TinyHelperFd::run_tx() { uint8_t buf[16]; - int len = tiny_fd_get_tx_data(m_handle, buf, sizeof(buf)); + int len = tiny_fd_get_tx_data(m_handle, buf, sizeof(buf), 0); uint8_t *ptr = buf; while ( len > 0 ) { @@ -167,17 +168,17 @@ void TinyHelperFd::wait_until_rx_count(int count, uint32_t timeout) usleep(1000); } -void TinyHelperFd::onRxFrame(void *handle, uint8_t *buf, int len) +void TinyHelperFd::onRxFrame(void *handle, uint8_t address, uint8_t *buf, int len) { TinyHelperFd *helper = reinterpret_cast(handle); helper->m_rx_count++; if ( helper->m_onRxFrameCb ) { - helper->m_onRxFrameCb(buf, len); + helper->m_onRxFrameCb(address, buf, len); } } -void TinyHelperFd::onTxFrame(void *handle, uint8_t *buf, int len) +void TinyHelperFd::onTxFrame(void *handle, uint8_t address, const uint8_t *buf, int len) { TinyHelperFd *helper = reinterpret_cast(handle); helper->m_tx_count++; diff --git a/unittest/helpers/tiny_fd_helper.h b/unittest/helpers/tiny_fd_helper.h index 9f73a011..8e85a7dd 100644 --- a/unittest/helpers/tiny_fd_helper.h +++ b/unittest/helpers/tiny_fd_helper.h @@ -41,11 +41,11 @@ class TinyHelperFd: public IBaseHelper public: // default constructor for ABM mode TinyHelperFd(FakeEndpoint *endpoint, int rxBufferSize, - const std::function &onRxFrameCb = nullptr, int window_frames = 7, + const std::function &onRxFrameCb = nullptr, int window_frames = 7, int timeout = -1); // default constructor for any FD mode TinyHelperFd(FakeEndpoint *endpoint, int rxBufferSize, uint8_t mode, - const std::function &onRxFrameCb = nullptr); + const std::function &onRxFrameCb = nullptr); virtual ~TinyHelperFd(); // Must be called if non-ABM constructor was used @@ -85,7 +85,7 @@ class TinyHelperFd: public IBaseHelper int m_rx_count = 0; int m_tx_count = 0; std::thread *m_message_sender = nullptr; - std::function m_onRxFrameCb; + std::function m_onRxFrameCb; std::function m_onConnectCb = nullptr; bool m_stop_sender = false; uint8_t m_mode = TINY_FD_MODE_ABM; @@ -95,8 +95,8 @@ class TinyHelperFd: public IBaseHelper int m_window; int m_timeout; - static void onRxFrame(void *handle, uint8_t *buf, int len); - static void onTxFrame(void *handle, uint8_t *buf, int len); + static void onRxFrame(void *handle, uint8_t address, uint8_t *buf, int len); + static void onTxFrame(void *handle, uint8_t address, const uint8_t *buf, int len); static void onConnect(void *handle, uint8_t addr, bool connected); static void MessageSender(TinyHelperFd *helper, int count, std::string message); }; diff --git a/unittest/helpers/tiny_hdlc_helper.cpp b/unittest/helpers/tiny_hdlc_helper.cpp index ef2c4195..3946e947 100644 --- a/unittest/helpers/tiny_hdlc_helper.cpp +++ b/unittest/helpers/tiny_hdlc_helper.cpp @@ -1,5 +1,5 @@ /* - Copyright 2019-2022 (C) Alexey Dynda + Copyright 2019-2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -39,7 +39,7 @@ TinyHdlcHelper::TinyHdlcHelper(FakeEndpoint *endpoint, const std::function(handle); tiny_mutex_lock(&helper->m_read_mutex); helper->m_rx_count++; - if (helper->m_user_rx_buf != nullptr) + if ( helper->m_user_rx_buf != nullptr ) { - int sz = helper->m_user_rx_buf_size > len ? len: helper->m_user_rx_buf_size; - memcpy( helper->m_user_rx_buf, buf, sz ); + int sz = helper->m_user_rx_buf_size > len ? len : helper->m_user_rx_buf_size; + memcpy(helper->m_user_rx_buf, buf, sz); helper->m_user_rx_buf_size = sz; } if ( helper->m_onRxFrameCb ) @@ -148,11 +148,11 @@ int TinyHdlcHelper::recv(uint8_t *buf, int len, int timeout) m_user_rx_buf = buf; m_user_rx_buf_size = len; tiny_mutex_unlock(&m_read_mutex); - while (timeout >= 0 && cnt == m_rx_count) + while ( timeout >= 0 && cnt == m_rx_count ) { if ( m_receiveThread == nullptr ) { - run( false ); + run(false); } usleep(1000); timeout--; @@ -160,7 +160,7 @@ int TinyHdlcHelper::recv(uint8_t *buf, int len, int timeout) tiny_mutex_lock(&m_read_mutex); m_user_rx_buf = nullptr; tiny_mutex_unlock(&m_read_mutex); - return timeout < 0 ? 0: m_user_rx_buf_size; + return timeout < 0 ? 0 : m_user_rx_buf_size; } void TinyHdlcHelper::wait_until_rx_count(int count, uint32_t timeout) diff --git a/unittest/helpers/tiny_hdlc_helper.h b/unittest/helpers/tiny_hdlc_helper.h index 8219f1d0..014b4c2d 100644 --- a/unittest/helpers/tiny_hdlc_helper.h +++ b/unittest/helpers/tiny_hdlc_helper.h @@ -1,5 +1,5 @@ /* - Copyright 2019-2020,2022 (C) Alexey Dynda + Copyright 2019-2020,2022 (,2022 (C) Alexey Dynda This file is part of Tiny Protocol Library. @@ -73,10 +73,10 @@ class TinyHdlcHelper: public IBaseHelper tiny_mutex_t m_read_mutex; bool m_stop_sender = false; std::thread *m_sender_thread = nullptr; - std::atomic m_rx_count{ 0 }; - std::atomic m_tx_count{ 0 }; + std::atomic m_rx_count{0}; + std::atomic m_tx_count{0}; uint8_t *m_user_rx_buf = nullptr; - int m_user_rx_buf_size = 0; + int m_user_rx_buf_size = 0; bool m_tx_from_main = false; static int onRxFrame(void *handle, void *buf, int len);