8 Commits
Author SHA1 Message Date
MightyPorkandGitHub db0ae30f41 Add re-entrancy support / multiple instances (#9)
* done with changes but now segfaulting

* fixed the bug

* fix some bad indents

* some cleanings

* updated readme
2017-12-14 00:28:06 +01:00
MightyPork 77f60e1876 removed no longer used TF_OVERHEAD_BYTES 2017-12-12 21:09:07 +01:00
MightyPork 978eabfb92 Added utilities 2017-12-12 20:54:47 +01:00
MightyPork aadbb0f89e add TF_VERSION define 2017-12-12 20:22:40 +01:00
MightyPork a27798188d Updated code with new improvements - mutex locks 2017-12-12 20:19:02 +01:00
MightyPork 970d6f7ced fix corrupt indentation 2017-11-21 23:17:12 +01:00
MightyPork a966345816 updated readme a little 2017-11-21 22:20:28 +01:00
MightyPork ed389d530b make Tx support multi-chunk payload (exceeding buffer size) 2017-11-21 22:17:06 +01:00
27 changed files with 2068 additions and 1011 deletions
+8 -6
View File
@@ -4,11 +4,13 @@ project(tf)
set(CMAKE_CXX_STANDARD GNU89) set(CMAKE_CXX_STANDARD GNU89)
set(SOURCE_FILES set(SOURCE_FILES
demo/test/test.c demo/simple/test.c
demo/test/TF_Config.h demo/simple/TF_Config.h
demo/hello/master.c demo/socket_demo/master.c
demo/hello/slave.c demo/socket_demo/slave.c
demo/hello/TF_Config.h demo/socket_demo/TF_Config.h
demo/multipart_tx/test.c
demo/multipart_tx/TF_Config.h
demo/demo.c demo/demo.c
demo/demo.h demo/demo.h
TinyFrame.c TinyFrame.c
@@ -18,6 +20,6 @@ set(SOURCE_FILES
demo/utils.h demo/utils.h
) )
include_directories(demo/test) include_directories(demo/simple)
add_executable(tf ${SOURCE_FILES}) add_executable(tf ${SOURCE_FILES})
+9 -3
View File
@@ -19,6 +19,10 @@ The library lets you register listeners (callback functions) to wait for (1) any
a particular frame Type, or (3) a specific message ID. This high-level API lets you a particular frame Type, or (3) a specific message ID. This high-level API lets you
easily implement various async communication patterns. easily implement various async communication patterns.
TinyFrame is re-entrant and supports creating multiple instances with the limitation
that their structure (field sizes and checksum type) must be the same. There is a support
for adding multi-threaded access to a shared instance using a mutex (via a callback stub).
## Frame structure ## Frame structure
All fields in the message frame have a configurable size (see the top of the header file). All fields in the message frame have a configurable size (see the top of the header file).
@@ -49,10 +53,12 @@ DATA_CKSUM .. checksum, implemented as XOR of all preceding bytes in the message
- All TinyFrame functions, typedefs and macros start with the `TF_` prefix. - All TinyFrame functions, typedefs and macros start with the `TF_` prefix.
- Both peers must include the library with the same parameters (configured at the top of the header file) - Both peers must include the library with the same parameters (configured at the top of the header file)
- Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument - Start by calling `TF_Init()` with `TF_MASTER` or `TF_SLAVE` as the argument. This creates a handle.
Use `TF_InitStatic()` to avoid the use of malloc(). If multiple instances are used, you can tag them
using the `tf.userdata` / `tf.usertag` field.
- Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`. - Implement `TF_WriteImpl()` - declared at the bottom of the header file as `extern`.
This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer). This function is used by `TF_Send()` and others to write bytes to your UART (or other physical layer).
Presently, always a full frame is sent to this function. A frame can be sent in it's entirety, or in multiple parts, depending on its size.
- If you wish to use timeouts, periodically call `TF_Tick()`. The calling period determines - If you wish to use timeouts, periodically call `TF_Tick()`. The calling period determines
the length of 1 tick. This is used to time-out the parser in case it gets stuck the length of 1 tick. This is used to time-out the parser in case it gets stuck
in a bad state (such as receiving a partial frame) and can also time-out ID listeners. in a bad state (such as receiving a partial frame) and can also time-out ID listeners.
@@ -83,6 +89,6 @@ keep it, renew it's timeout, or let some other listener handle the message.
You'll find various examples in the `demo/` folder. Each example has it's own Makefile, You'll find various examples in the `demo/` folder. Each example has it's own Makefile,
read it to see what options are available. read it to see what options are available.
The demos are written for Linux, using sockets and `clone()` for background processing. The demos are written for Linux, some using sockets and `clone()` for background processing.
They try to simulate real TinyFrame behavior in an embedded system with asynchronous They try to simulate real TinyFrame behavior in an embedded system with asynchronous
Rx and Tx. If you can't run the demos, the source files are still good as examples. Rx and Tx. If you can't run the demos, the source files are still good as examples.
+7 -3
View File
@@ -1,4 +1,6 @@
// //
// TinyFrame configuration file.
//
// Rename to TF_Config.h // Rename to TF_Config.h
// //
@@ -19,7 +21,7 @@
// | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes) // | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes)
// '-----+----+-----+------+------------+- - - -+------------' // '-----+----+-----+------+------------+- - - -+------------'
// !!! BOTH SIDES MUST USE THE SAME SETTINGS !!! // !!! BOTH PEERS MUST USE THE SAME SETTINGS !!!
// Adjust sizes as desired (1,2,4) // Adjust sizes as desired (1,2,4)
#define TF_ID_BYTES 1 #define TF_ID_BYTES 1
@@ -47,10 +49,12 @@ typedef uint8_t TF_COUNT;
//----------------------------- PARAMETERS ---------------------------------- //----------------------------- PARAMETERS ----------------------------------
// Maximum send / receive payload size (static buffers size) // Maximum received payload size (static buffer)
// Larger payloads will be rejected. // Larger payloads will be rejected.
#define TF_MAX_PAYLOAD_RX 1024 #define TF_MAX_PAYLOAD_RX 1024
#define TF_MAX_PAYLOAD_TX 1024 // Size of the sending buffer. Larger payloads will be split to pieces and sent
// in multiple calls to the write function. This can be lowered to reduce RAM usage.
#define TF_SENDBUF_LEN 128
// --- Listener counts - determine sizes of the static slot tables --- // --- Listener counts - determine sizes of the static slot tables ---
+28
View File
@@ -0,0 +1,28 @@
#include "TinyFrame.h"
/**
* This is an example of integrating TinyFrame into the application.
*
* TF_WriteImpl() is required, the mutex functions are weak and can
* be removed if not used. They are called from all TF_Send/Respond functions.
*
* Also remember to periodically call TF_Tick() if you wish to use the
* listener timeout feature.
*/
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{
// send to UART
}
/** Claim the TX interface before composing and sending a frame */
void TF_ClaimTx(TinyFrame *tf)
{
// take mutex
}
/** Free the TX interface after composing and sending a frame */
void TF_ReleaseTx(TinyFrame *tf)
{
// release mutex
}
+380 -321
View File
File diff suppressed because it is too large Load Diff
+156 -51
View File
@@ -1,10 +1,22 @@
#ifndef TinyFrameH #ifndef TinyFrameH
#define TinyFrameH #define TinyFrameH
/**
* TinyFrame protocol library
*
* (c) Ondřej Hruška 2017, MIT License
* no liability/warranty, free for any use, must retain this notice & license
*
* Upstream URL: https://github.com/MightyPork/TinyFrame
*/
#define TF_VERSION "2.0.0"
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <stdint.h> // for uint8_t etc #include <stdint.h> // for uint8_t etc
#include <stdbool.h> // for bool #include <stdbool.h> // for bool
#include <stdlib.h> // for NULL #include <stddef.h> // for NULL
#include <string.h> // for memset()
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32) // Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32)
@@ -13,7 +25,7 @@
#define TF_CKSUM_CRC16 16 #define TF_CKSUM_CRC16 16
#define TF_CKSUM_CRC32 32 #define TF_CKSUM_CRC32 32
#include <TF_Config.h> #include "TF_Config.h"
//region Resolve data types //region Resolve data types
@@ -63,17 +75,6 @@
#error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32 #error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32
#endif #endif
// Bytes added to TF_MAX_PAYLOAD for the send buffer size.
#define TF_OVERHEAD_BYTES \
(1*TF_USE_SOF_BYTE + \
sizeof(TF_ID) + \
sizeof(TF_LEN) + \
sizeof(TF_CKSUM) + \
sizeof(TF_TYPE) + \
sizeof(TF_CKSUM) \
)
//endregion //endregion
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@@ -87,9 +88,10 @@
/** Peer bit enum (used for init) */ /** Peer bit enum (used for init) */
typedef enum { typedef enum {
TF_SLAVE = 0, TF_SLAVE = 0,
TF_MASTER, TF_MASTER = 1,
} TF_Peer; } TF_Peer;
/** Response from listeners */
typedef enum { typedef enum {
TF_NEXT = 0, //!< Not handled, let other listeners handle it TF_NEXT = 0, //!< Not handled, let other listeners handle it
TF_STAY = 1, //!< Handled, stay TF_STAY = 1, //!< Handled, stay
@@ -105,6 +107,7 @@ typedef struct _TF_MSG_STRUCT_ {
const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata! const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata!
TF_LEN len; //!< length of the buffer TF_LEN len; //!< length of the buffer
void *userdata; //!< here's a place for custom data; this data will be stored with the listener void *userdata; //!< here's a place for custom data; this data will be stored with the listener
void *userdata2;
} TF_Msg; } TF_Msg;
/** /**
@@ -112,14 +115,11 @@ typedef struct _TF_MSG_STRUCT_ {
*/ */
static inline void TF_ClearMsg(TF_Msg *msg) static inline void TF_ClearMsg(TF_Msg *msg)
{ {
msg->frame_id = 0; memset(msg, 0, sizeof(TF_Msg));
msg->is_response = false;
msg->type = 0;
msg->data = NULL;
msg->len = 0;
msg->userdata = NULL;
} }
typedef struct TinyFrame_ TinyFrame;
/** /**
* TinyFrame Type Listener callback * TinyFrame Type Listener callback
* *
@@ -129,21 +129,117 @@ static inline void TF_ClearMsg(TF_Msg *msg)
* @param len - number of bytes in the buffer * @param len - number of bytes in the buffer
* @return listener result * @return listener result
*/ */
typedef TF_Result (*TF_Listener)(TF_Msg *msg); typedef TF_Result (*TF_Listener)(TinyFrame *tf, TF_Msg *msg);
// -------------------------------------------------------------------
// region Internal
enum TFState_ {
TFState_SOF = 0, //!< Wait for SOF
TFState_LEN, //!< Wait for Number Of Bytes
TFState_HEAD_CKSUM, //!< Wait for header Checksum
TFState_ID, //!< Wait for ID
TFState_TYPE, //!< Wait for message type
TFState_DATA, //!< Receive payload
TFState_DATA_CKSUM //!< Wait for Checksum
};
struct TF_IdListener_ {
TF_ID id;
TF_Listener fn;
TF_TICKS timeout; // nr of ticks remaining to disable this listener
TF_TICKS timeout_max; // the original timeout is stored here
void *userdata;
void *userdata2;
};
struct TF_TypeListener_ {
TF_TYPE type;
TF_Listener fn;
};
struct TF_GenericListener_ {
TF_Listener fn;
};
/**
* Frame parser internal state.
*/
struct TinyFrame_ {
/* Public user data */
void *userdata;
uint32_t usertag;
// --- the rest of the struct is internal, do not access directly ---
/* Own state */
TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash)
TF_ID next_id; //!< Next frame / frame chain ID
/* Parser state */
enum TFState_ state;
TF_TICKS parser_timeout_ticks;
TF_ID id; //!< Incoming packet ID
TF_LEN len; //!< Payload length
uint8_t data[TF_MAX_PAYLOAD_RX]; //!< Data byte buffer
TF_LEN rxi; //!< Field size byte counter
TF_CKSUM cksum; //!< Checksum calculated of the data stream
TF_CKSUM ref_cksum; //!< Reference checksum read from the message
TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data.
/* --- Callbacks --- */
/* Transaction callbacks */
struct TF_IdListener_ id_listeners[TF_MAX_ID_LST];
struct TF_TypeListener_ type_listeners[TF_MAX_TYPE_LST];
struct TF_GenericListener_ generic_listeners[TF_MAX_GEN_LST];
// Those counters are used to optimize look-up times.
// They point to the highest used slot number,
// or close to it, depending on the removal order.
TF_COUNT count_id_lst;
TF_COUNT count_type_lst;
TF_COUNT count_generic_lst;
// Buffer for building frames
uint8_t sendbuf[TF_SENDBUF_LEN];
};
// endregion
// -------------------------------------------------------------------
/** /**
* Initialize the TinyFrame engine. * Initialize the TinyFrame engine.
* This can also be used to completely reset it (removing all listeners etc) * This can also be used to completely reset it (removing all listeners etc).
*
* The field .userdata (or .usertag) can be used to identify different instances
* in the TF_WriteImpl() function etc. Set this field after the init.
*
* This function is a wrapper around TF_InitStatic that calls malloc() to obtain
* the instance.
* *
* @param peer_bit - peer bit to use for self * @param peer_bit - peer bit to use for self
*/ */
void TF_Init(TF_Peer peer_bit); TinyFrame *TF_Init(TF_Peer peer_bit);
/**
* Initialize the TinyFrame engine using a statically allocated instance struct.
*
* The .userdata / .usertag field is preserved when TF_InitStatic is called.
*
* @param peer_bit - peer bit to use for self
*/
void TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit);
/** /**
* Reset the frame parser state machine. * Reset the frame parser state machine.
* This does not affect registered listeners. * This does not affect registered listeners.
*/ */
void TF_ResetParser(void); void TF_ResetParser(TinyFrame *tf);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -153,14 +249,14 @@ void TF_ResetParser(void);
* @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever)
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout); bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout);
/** /**
* Remove a listener by the message ID it's registered for * Remove a listener by the message ID it's registered for
* *
* @param frame_id - the frame we're listening for * @param frame_id - the frame we're listening for
*/ */
bool TF_RemoveIdListener(TF_ID frame_id); bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id);
/** /**
* Register a frame type listener. * Register a frame type listener.
@@ -169,14 +265,14 @@ bool TF_RemoveIdListener(TF_ID frame_id);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb); bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb);
/** /**
* Remove a listener by type. * Remove a listener by type.
* *
* @param type - the type it's registered for * @param type - the type it's registered for
*/ */
bool TF_RemoveTypeListener(TF_TYPE type); bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type);
/** /**
* Register a generic listener. * Register a generic listener.
@@ -184,14 +280,14 @@ bool TF_RemoveTypeListener(TF_TYPE type);
* @param cb - callback * @param cb - callback
* @return slot index (for removing), or TF_ERROR (-1) * @return slot index (for removing), or TF_ERROR (-1)
*/ */
bool TF_AddGenericListener(TF_Listener cb); bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Remove a generic listener by function pointer * Remove a generic listener by function pointer
* *
* @param cb - callback function to remove * @param cb - callback function to remove
*/ */
bool TF_RemoveGenericListener(TF_Listener cb); bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb);
/** /**
* Send a frame, no listener * Send a frame, no listener
@@ -199,17 +295,12 @@ bool TF_RemoveGenericListener(TF_Listener cb);
* @param msg - message struct. ID is stored in the frame_id field * @param msg - message struct. ID is stored in the frame_id field
* @return success * @return success
*/ */
bool TF_Send(TF_Msg *msg); bool TF_Send(TinyFrame *tf, TF_Msg *msg);
/** /**
* Like TF_Send, but without the struct * Like TF_Send, but without the struct
*/ */
bool TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len); bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout, void *userdata);
/** /**
* Send a frame, and optionally attach an ID listener. * Send a frame, and optionally attach an ID listener.
@@ -219,7 +310,13 @@ bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener l
* @param timeout - listener expiry time in ticks * @param timeout - listener expiry time in ticks
* @return success * @return success
*/ */
bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); bool TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
/**
* Like TF_Query, but without the struct
*/
bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len,
TF_Listener listener, TF_TICKS timeout);
/** /**
* Send a response to a received message. * Send a response to a received message.
@@ -227,15 +324,15 @@ bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout);
* @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout * @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout
* @return success * @return success
*/ */
bool TF_Respond(TF_Msg *msg); bool TF_Respond(TinyFrame *tf, TF_Msg *msg);
/** /**
* Renew ID listener timeout * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener)
* *
* @param id - listener ID to renew * @param id - listener ID to renew
* @return true if listener was found and renewed * @return true if listener was found and renewed
*/ */
bool TF_RenewIdListener(TF_ID id); bool TF_RenewIdListener(TinyFrame *tf, TF_ID id);
/** /**
* Accept incoming bytes & parse frames * Accept incoming bytes & parse frames
@@ -243,21 +340,14 @@ bool TF_RenewIdListener(TF_ID id);
* @param buffer - byte buffer to process * @param buffer - byte buffer to process
* @param count - nr of bytes in the buffer * @param count - nr of bytes in the buffer
*/ */
void TF_Accept(const uint8_t *buffer, size_t count); void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count);
/** /**
* Accept a single incoming byte * Accept a single incoming byte
* *
* @param c - a received char * @param c - a received char
*/ */
void TF_AcceptChar(uint8_t c); void TF_AcceptChar(TinyFrame *tf, uint8_t c);
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(const uint8_t *buff, size_t len);
/** /**
* This function should be called periodically. * This function should be called periodically.
@@ -267,6 +357,21 @@ extern void TF_WriteImpl(const uint8_t *buff, size_t len);
* *
* (suggestion - call this in a SysTick handler) * (suggestion - call this in a SysTick handler)
*/ */
void TF_Tick(void); void TF_Tick(TinyFrame *tf);
// --- TO BE IMPLEMENTED BY USER ---
/**
* 'Write bytes' function that sends data to UART
*
* ! Implement this in your application code !
*/
extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len);
/** Claim the TX interface before composing and sending a frame */
extern void TF_ClaimTx(TinyFrame *tf);
/** Free the TX interface after composing and sending a frame */
extern void TF_ReleaseTx(TinyFrame *tf);
#endif #endif
+12 -5
View File
@@ -7,6 +7,7 @@
// those magic defines are needed so we can use clone() // those magic defines are needed so we can use clone()
#define _GNU_SOURCE #define _GNU_SOURCE
#define __USE_GNU #define __USE_GNU
#include <sched.h> #include <sched.h>
#include <unistd.h> #include <unistd.h>
@@ -15,10 +16,14 @@
#include <string.h> #include <string.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#include <signal.h> #include <signal.h>
#include <malloc.h>
#include <stdlib.h>
volatile int sockfd = -1; volatile int sockfd = -1;
volatile bool conn_disband = false; volatile bool conn_disband = false;
TinyFrame *demo_tf;
/** /**
* Close socket * Close socket
*/ */
@@ -34,7 +39,7 @@ void demo_disconn(void)
* @param buff * @param buff
* @param len * @param len
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
@@ -42,7 +47,8 @@ void TF_WriteImpl(const uint8_t *buff, size_t len)
if (sockfd != -1) { if (sockfd != -1) {
write(sockfd, buff, len); write(sockfd, buff, len);
} else { }
else {
printf("\nNo peer!\n"); printf("\nNo peer!\n");
} }
} }
@@ -91,7 +97,7 @@ static int demo_client(void* unused)
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, (size_t) n); dumpFrame(recvBuff, (size_t) n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
return 0; return 0;
} }
@@ -143,7 +149,7 @@ static int demo_server(void* unused)
while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) { while ((n = read(sockfd, recvBuff, sizeof(recvBuff) - 1)) > 0 && !conn_disband) {
printf("\033[36m--- RX %ld bytes ---\033[0m\n", n); printf("\033[36m--- RX %ld bytes ---\033[0m\n", n);
dumpFrame(recvBuff, n); dumpFrame(recvBuff, n);
TF_Accept(recvBuff, (size_t) n); TF_Accept(demo_tf, recvBuff, (size_t) n);
} }
if (n < 0) { if (n < 0) {
@@ -205,7 +211,8 @@ void demo_init(TF_Peer peer)
// CLONE_FILES --- share stdout and stderr // CLONE_FILES --- share stdout and stderr
if (peer == TF_MASTER) { if (peer == TF_MASTER) {
retc = clone(&demo_client, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0); retc = clone(&demo_client, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} else { }
else {
retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0); retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} }
+2
View File
@@ -11,6 +11,8 @@
#define PORT 9798 #define PORT 9798
extern TinyFrame *demo_tf;
/** Sleep and wait for ^C */ /** Sleep and wait for ^C */
void demo_sleep(void); void demo_sleep(void);
-35
View File
@@ -1,35 +0,0 @@
//
// Created by MightyPork on 2017/10/15.
//
#include <stdio.h>
#include "../demo.h"
TF_Result testIdListener(TF_Msg *msg)
{
printf("testIdListener()\n");
dumpFrameInfo(msg);
return TF_CLOSE;
}
TF_Result testGenericListener(TF_Msg *msg)
{
printf("testGenericListener()\n");
dumpFrameInfo(msg);
return TF_STAY;
}
int main(void)
{
TF_Init(TF_MASTER);
TF_AddGenericListener(testGenericListener);
demo_init(TF_MASTER);
TF_SendSimple(1, (pu8)"Ahoj", 5);
TF_SendSimple(1, (pu8)"Hello", 6);
TF_QuerySimple(2, (pu8)"Query!", 6, testIdListener, 0, NULL);
demo_sleep();
}
-42
View File
@@ -1,42 +0,0 @@
//
// Created by MightyPork on 2017/10/15.
//
#include <stdio.h>
#include "../demo.h"
#include <memory.h>
TF_Result helloListener(TF_Msg *msg)
{
printf("helloListener()\n");
dumpFrameInfo(msg);
return TF_STAY;
}
TF_Result replyListener(TF_Msg *msg)
{
printf("replyListener()\n");
dumpFrameInfo(msg);
msg->data = (const uint8_t *) "response to query";
msg->len = (TF_LEN) strlen((const char *) msg->data);
TF_Respond(msg);
// unsolicited reply - will not be handled by the ID listener, which is already gone
msg->data = (const uint8_t *) "SPAM";
msg->len = 5;
TF_Respond(msg);
// unrelated message
TF_SendSimple(77, (const uint8_t *) "NAZDAR", 7);
return TF_STAY;
}
int main(void)
{
TF_Init(TF_SLAVE);
TF_AddTypeListener(1, helloListener);
TF_AddTypeListener(2, replyListener);
demo_init(TF_SLAVE);
demo_sleep();
}
+12
View File
@@ -0,0 +1,12 @@
CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: test.bin
run: test.bin
./test.bin
test.bin: test.c $(CFILES)
gcc test.c $(CFLAGS) -o test.bin
+25
View File
@@ -0,0 +1,25 @@
//
// Created by MightyPork on 2017/10/15.
//
#ifndef TF_CONFIG_H
#define TF_CONFIG_H
#include <stdint.h>
#define TF_ID_BYTES 1
#define TF_LEN_BYTES 2
#define TF_TYPE_BYTES 1
#define TF_CKSUM_TYPE TF_CKSUM_CRC16
#define TF_USE_SOF_BYTE 1
#define TF_SOF_BYTE 0x01
typedef uint16_t TF_TICKS;
typedef uint8_t TF_COUNT;
#define TF_MAX_PAYLOAD_RX 10240
#define TF_SENDBUF_LEN 64
#define TF_MAX_ID_LST 10
#define TF_MAX_TYPE_LST 10
#define TF_MAX_GEN_LST 5
#define TF_PARSER_TIMEOUT_TICKS 10
#endif //TF_CONFIG_H
+300
View File
@@ -0,0 +1,300 @@
#include <stdio.h>
#include <string.h>
#include "../../TinyFrame.h"
#include "../utils.h"
TinyFrame *demo_tf;
extern const char *romeo;
/**
* This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other)
*/
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{
printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len);
// Send it back as if we received it
TF_Accept(tf, buff, len);
}
/** An example listener function */
TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{
dumpFrameInfo(msg);
if (strcmp((const char *) msg->data, romeo) == 0) {
printf("FILE TRANSFERRED OK!\r\n");
}
else {
printf("FAIL!!!!\r\n");
}
return TF_STAY;
}
void main(void)
{
TF_Msg msg;
// Set up the TinyFrame library
demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a LOOONG message --------\n");
TF_ClearMsg(&msg);
msg.type = 0x22;
msg.data = (pu8) romeo;
msg.len = (TF_LEN) strlen(romeo);
TF_Send(demo_tf, &msg);
}
const char *romeo = "THE TRAGEDY OF ROMEO AND JULIET\n"
"\n"
"by William Shakespeare\n"
"\n"
"\n"
"\n"
"Dramatis Personae\n"
"\n"
" Chorus.\n"
"\n"
"\n"
" Escalus, Prince of Verona.\n"
"\n"
" Paris, a young Count, kinsman to the Prince.\n"
"\n"
" Montague, heads of two houses at variance with each other.\n"
"\n"
" Capulet, heads of two houses at variance with each other.\n"
"\n"
" An old Man, of the Capulet family.\n"
"\n"
" Romeo, son to Montague.\n"
"\n"
" Tybalt, nephew to Lady Capulet.\n"
"\n"
" Mercutio, kinsman to the Prince and friend to Romeo.\n"
"\n"
" Benvolio, nephew to Montague, and friend to Romeo\n"
"\n"
" Tybalt, nephew to Lady Capulet.\n"
"\n"
" Friar Laurence, Franciscan.\n"
"\n"
" Friar John, Franciscan.\n"
"\n"
" Balthasar, servant to Romeo.\n"
"\n"
" Abram, servant to Montague.\n"
"\n"
" Sampson, servant to Capulet.\n"
"\n"
" Gregory, servant to Capulet.\n"
"\n"
" Peter, servant to Juliet's nurse.\n"
"\n"
" An Apothecary.\n"
"\n"
" Three Musicians.\n"
"\n"
" An Officer.\n"
"\n"
"\n"
" Lady Montague, wife to Montague.\n"
"\n"
" Lady Capulet, wife to Capulet.\n"
"\n"
" Juliet, daughter to Capulet.\n"
"\n"
" Nurse to Juliet.\n"
"\n"
"\n"
" Citizens of Verona; Gentlemen and Gentlewomen of both houses;\n"
" Maskers, Torchbearers, Pages, Guards, Watchmen, Servants, and\n"
" Attendants.\n"
"\n"
" SCENE.--Verona; Mantua.\n"
"\n"
"\n"
"\n"
" THE PROLOGUE\n"
"\n"
" Enter Chorus.\n"
"\n"
"\n"
" Chor. Two households, both alike in dignity,\n"
" In fair Verona, where we lay our scene,\n"
" From ancient grudge break to new mutiny,\n"
" Where civil blood makes civil hands unclean.\n"
" From forth the fatal loins of these two foes\n"
" A pair of star-cross'd lovers take their life;\n"
" Whose misadventur'd piteous overthrows\n"
" Doth with their death bury their parents' strife.\n"
" The fearful passage of their death-mark'd love,\n"
" And the continuance of their parents' rage,\n"
" Which, but their children's end, naught could remove,\n"
" Is now the two hours' traffic of our stage;\n"
" The which if you with patient ears attend,\n"
" What here shall miss, our toil shall strive to mend.\n"
" [Exit.]\n"
"\n"
"\n"
"\n"
"\n"
"ACT I. Scene I.\n"
"Verona. A public place.\n"
"\n"
"Enter Sampson and Gregory (with swords and bucklers) of the house\n"
"of Capulet.\n"
"\n"
"\n"
" Samp. Gregory, on my word, we'll not carry coals.\n"
"\n"
" Greg. No, for then we should be colliers.\n"
"\n"
" Samp. I mean, an we be in choler, we'll draw.\n"
"\n"
" Greg. Ay, while you live, draw your neck out of collar.\n"
"\n"
" Samp. I strike quickly, being moved.\n"
"\n"
" Greg. But thou art not quickly moved to strike.\n"
"\n"
" Samp. A dog of the house of Montague moves me.\n"
"\n"
" Greg. To move is to stir, and to be valiant is to stand.\n"
" Therefore, if thou art moved, thou runn'st away.\n"
"\n"
" Samp. A dog of that house shall move me to stand. I will take\n"
" the wall of any man or maid of Montague's.\n"
"\n"
" Greg. That shows thee a weak slave; for the weakest goes to the\n"
" wall.\n"
"\n"
" Samp. 'Tis true; and therefore women, being the weaker vessels,\n"
" are ever thrust to the wall. Therefore I will push Montague's men\n"
" from the wall and thrust his maids to the wall.\n"
"\n"
" Greg. The quarrel is between our masters and us their men.\n"
"\n"
" Samp. 'Tis all one. I will show myself a tyrant. When I have\n"
" fought with the men, I will be cruel with the maids- I will cut off\n"
" their heads.\n"
"\n"
" Greg. The heads of the maids?\n"
"\n"
" Samp. Ay, the heads of the maids, or their maidenheads.\n"
" Take it in what sense thou wilt.\n"
"\n"
" Greg. They must take it in sense that feel it.\n"
"\n"
" Samp. Me they shall feel while I am able to stand; and 'tis known I\n"
" am a pretty piece of flesh.\n"
"\n"
" Greg. 'Tis well thou art not fish; if thou hadst, thou hadst\n"
" been poor-John. Draw thy tool! Here comes two of the house of\n"
" Montagues.\n"
"\n"
" Enter two other Servingmen [Abram and Balthasar].\n"
"\n"
"\n"
" Samp. My naked weapon is out. Quarrel! I will back thee.\n"
"\n"
" Greg. How? turn thy back and run?\n"
"\n"
" Samp. Fear me not.\n"
"\n"
" Greg. No, marry. I fear thee!\n"
"\n"
" Samp. Let us take the law of our sides; let them begin.\n"
"\n"
" Greg. I will frown as I pass by, and let them take it as they list.\n"
"\n"
" Samp. Nay, as they dare. I will bite my thumb at them; which is\n"
" disgrace to them, if they bear it.\n"
"\n"
" Abr. Do you bite your thumb at us, sir?\n"
"\n"
" Samp. I do bite my thumb, sir.\n"
"\n"
" Abr. Do you bite your thumb at us, sir?\n"
"\n"
" Samp. [aside to Gregory] Is the law of our side if I say ay?\n"
"\n"
" Greg. [aside to Sampson] No.\n"
"\n"
" Samp. No, sir, I do not bite my thumb at you, sir; but I bite my\n"
" thumb, sir.\n"
"\n"
" Greg. Do you quarrel, sir?\n"
"\n"
" Abr. Quarrel, sir? No, sir.\n"
"\n"
" Samp. But if you do, sir, am for you. I serve as good a man as\n"
" you.\n"
"\n"
" Abr. No better.\n"
"\n"
" Samp. Well, sir.\n"
"\n"
" Enter Benvolio.\n"
"\n"
"\n"
" Greg. [aside to Sampson] Say 'better.' Here comes one of my\n"
" master's kinsmen.\n"
"\n"
" Samp. Yes, better, sir.\n"
"\n"
" Abr. You lie.\n"
"\n"
" Samp. Draw, if you be men. Gregory, remember thy swashing blow.\n"
" They fight.\n"
"\n"
" Ben. Part, fools! [Beats down their swords.]\n"
" Put up your swords. You know not what you do.\n"
"\n"
" Enter Tybalt.\n"
"\n"
"\n"
" Tyb. What, art thou drawn among these heartless hinds?\n"
" Turn thee Benvolio! look upon thy death.\n"
"\n"
" Ben. I do but keep the peace. Put up thy sword,\n"
" Or manage it to part these men with me.\n"
"\n"
" Tyb. What, drawn, and talk of peace? I hate the word\n"
" As I hate hell, all Montagues, and thee.\n"
" Have at thee, coward! They fight.\n"
"\n"
" Enter an officer, and three or four Citizens with clubs or\n"
" partisans.\n"
"\n"
"\n"
" Officer. Clubs, bills, and partisans! Strike! beat them down!\n"
"\n"
" Citizens. Down with the Capulets! Down with the Montagues!\n"
"\n"
" Enter Old Capulet in his gown, and his Wife.\n"
"\n"
"\n"
" Cap. What noise is this? Give me my long sword, ho!\n"
"\n"
" Wife. A crutch, a crutch! Why call you for a sword?\n"
"\n"
" Cap. My sword, I say! Old Montague is come\n"
" And flourishes his blade in spite of me.\n"
"\n"
" Enter Old Montague and his Wife.\n"
"\n"
"\n"
" Mon. Thou villain Capulet!- Hold me not, let me go.\n"
"\n"
" M. Wife. Thou shalt not stir one foot to seek a foe.\n"
"\n"
" Enter Prince Escalus, with his Train.\n"
"\n"
"\n"
"END OF FILE\n";
+1 -1
View File
@@ -1,6 +1,6 @@
CFILES=../utils.c ../../TinyFrame.c CFILES=../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wno-unused -Wall -Wextra $(CFILES) $(INCLDIRS)
run: test.bin run: test.bin
./test.bin ./test.bin
@@ -16,7 +16,7 @@
typedef uint16_t TF_TICKS; typedef uint16_t TF_TICKS;
typedef uint8_t TF_COUNT; typedef uint8_t TF_COUNT;
#define TF_MAX_PAYLOAD_RX 1024 #define TF_MAX_PAYLOAD_RX 1024
#define TF_MAX_PAYLOAD_TX 1024 #define TF_SENDBUF_LEN 1024
#define TF_MAX_ID_LST 10 #define TF_MAX_ID_LST 10
#define TF_MAX_TYPE_LST 10 #define TF_MAX_TYPE_LST 10
#define TF_MAX_GEN_LST 5 #define TF_MAX_GEN_LST 5
+66
View File
@@ -0,0 +1,66 @@
#include <stdio.h>
#include <string.h>
#include "../../TinyFrame.h"
#include "../utils.h"
TinyFrame *demo_tf;
/**
* This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other)
*/
void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{
printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len);
// Send it back as if we received it
TF_Accept(tf, buff, len);
}
/** An example listener function */
TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{
dumpFrameInfo(msg);
return TF_STAY;
}
TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{
printf("OK - ID Listener triggered for msg!\n");
dumpFrameInfo(msg);
return TF_CLOSE;
}
void main(void)
{
TF_Msg msg;
const char *longstr = "Lorem ipsum dolor sit amet.";
// Set up the TinyFrame library
demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a message --------\n");
TF_ClearMsg(&msg);
msg.type = 0x22;
msg.data = (pu8) "Hello TinyFrame";
msg.len = 16;
TF_Send(demo_tf, &msg);
msg.type = 0x33;
msg.data = (pu8) longstr;
msg.len = (TF_LEN) (strlen(longstr) + 1); // add the null byte
TF_Send(demo_tf, &msg);
msg.type = 0x44;
msg.data = (pu8) "Hello2";
msg.len = 7;
TF_Send(demo_tf, &msg);
msg.len = 0;
msg.type = 0x77;
TF_Query(demo_tf, &msg, testIdListener, 0);
}
@@ -1,6 +1,6 @@
CFILES=../demo.c ../utils.c ../../TinyFrame.c CFILES=../demo.c ../utils.c ../../TinyFrame.c
INCLDIRS=-I. -I.. -I../.. INCLDIRS=-I. -I.. -I../..
CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wextra $(CFILES) $(INCLDIRS) CFLAGS=-O0 -ggdb --std=gnu99 -Wno-main -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
build: master.bin slave.bin build: master.bin slave.bin
@@ -16,7 +16,7 @@
typedef uint16_t TF_TICKS; typedef uint16_t TF_TICKS;
typedef uint8_t TF_COUNT; typedef uint8_t TF_COUNT;
#define TF_MAX_PAYLOAD_RX 1024 #define TF_MAX_PAYLOAD_RX 1024
#define TF_MAX_PAYLOAD_TX 1024 #define TF_SENDBUF_LEN 1024
#define TF_MAX_ID_LST 10 #define TF_MAX_ID_LST 10
#define TF_MAX_TYPE_LST 10 #define TF_MAX_TYPE_LST 10
#define TF_MAX_GEN_LST 5 #define TF_MAX_GEN_LST 5
+35
View File
@@ -0,0 +1,35 @@
//
// Created by MightyPork on 2017/10/15.
//
#include <stdio.h>
#include "../demo.h"
TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{
printf("testIdListener()\n");
dumpFrameInfo(msg);
return TF_CLOSE;
}
TF_Result testGenericListener(TinyFrame *tf, TF_Msg *msg)
{
printf("testGenericListener()\n");
dumpFrameInfo(msg);
return TF_STAY;
}
int main(void)
{
demo_tf = TF_Init(TF_MASTER);
TF_AddGenericListener(demo_tf, testGenericListener);
demo_init(TF_MASTER);
TF_SendSimple(demo_tf, 1, (pu8) "Ahoj", 5);
TF_SendSimple(demo_tf, 1, (pu8) "Hello", 6);
TF_QuerySimple(demo_tf, 2, (pu8) "Query!", 6, testIdListener, 0);
demo_sleep();
}
+41
View File
@@ -0,0 +1,41 @@
//
// Created by MightyPork on 2017/10/15.
//
#include <stdio.h>
#include "../demo.h"
TF_Result helloListener(TinyFrame *tf, TF_Msg *msg)
{
printf("helloListener()\n");
dumpFrameInfo(msg);
return TF_STAY;
}
TF_Result replyListener(TinyFrame *tf, TF_Msg *msg)
{
printf("replyListener()\n");
dumpFrameInfo(msg);
msg->data = (const uint8_t *) "response to query";
msg->len = (TF_LEN) strlen((const char *) msg->data);
TF_Respond(tf, msg);
// unsolicited reply - will not be handled by the ID listener, which is already gone
msg->data = (const uint8_t *) "SPAM";
msg->len = 5;
TF_Respond(tf, msg);
// unrelated message
TF_SendSimple(tf, 77, (const uint8_t *) "NAZDAR", 7);
return TF_STAY;
}
int main(void)
{
demo_tf = TF_Init(TF_SLAVE);
TF_AddTypeListener(demo_tf, 1, helloListener);
TF_AddTypeListener(demo_tf, 2, replyListener);
demo_init(TF_SLAVE);
demo_sleep();
}
-65
View File
@@ -1,65 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "../../TinyFrame.h"
#include "../utils.h"
/**
* This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other)
*/
void TF_WriteImpl(const uint8_t *buff, size_t len)
{
printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len);
// Send it back as if we received it
TF_Accept(buff, len);
}
/** An example listener function */
TF_Result myListener(TF_Msg *msg)
{
dumpFrameInfo(msg);
return TF_STAY;
}
TF_Result testIdListener(TF_Msg *msg)
{
printf("OK - ID Listener triggered for msg!\n");
dumpFrameInfo(msg);
return TF_CLOSE;
}
void main(void)
{
TF_Msg msg;
const char *longstr = "Lorem ipsum dolor sit amet.";
// Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener);
printf("------ Simulate sending a message --------\n");
TF_ClearMsg(&msg);
msg.type = 0x22;
msg.data = (pu8)"Hello TinyFrame";
msg.len = 16;
TF_Send(&msg);
msg.type = 0x33;
msg.data = (pu8)longstr;
msg.len = (TF_LEN) (strlen(longstr)+1); // add the null byte
TF_Send(&msg);
msg.type = 0x44;
msg.data = (pu8)"Hello2";
msg.len = 7;
TF_Send(&msg);
msg.len = 0;
msg.type = 0x77;
TF_Query(&msg, testIdListener, 0);
}
+2 -1
View File
@@ -13,7 +13,8 @@ void dumpFrame(const uint8_t *buff, size_t len)
printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]); printf("%3u \033[94m%02X\033[0m", buff[i], buff[i]);
if (buff[i] >= 0x20 && buff[i] < 127) { if (buff[i] >= 0x20 && buff[i] < 127) {
printf(" %c", buff[i]); printf(" %c", buff[i]);
} else { }
else {
printf(" \033[31m.\033[0m"); printf(" \033[31m.\033[0m");
} }
printf("\n"); printf("\n");
+100
View File
@@ -0,0 +1,100 @@
#include <string.h>
#include "payload_builder.h"
#define pb_check_capacity(pb, needed) \
if ((pb)->current + (needed) > (pb)->end) { \
if ((pb)->full_handler == NULL || !(pb)->full_handler(pb, needed)) (pb)->ok = 0; \
}
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len)
{
pb_check_capacity(pb, len);
if (!pb->ok) return false;
memcpy(pb->current, buf, len);
pb->current += len;
return true;
}
/** Write s zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str)
{
uint32_t len = (uint32_t) strlen(str);
pb_check_capacity(pb, len+1);
if (!pb->ok) return false;
memcpy(pb->current, str, len+1);
pb->current += len+1;
return true;
}
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte)
{
pb_check_capacity(pb, 1);
if (!pb->ok) return false;
*pb->current++ = byte;
return true;
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word)
{
pb_check_capacity(pb, 2);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
}
return true;
}
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word)
{
pb_check_capacity(pb, 4);
if (!pb->ok) return false;
if (pb->bigendian) {
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) (word & 0xFF);
} else {
*pb->current++ = (uint8_t) (word & 0xFF);
*pb->current++ = (uint8_t) ((word >> 8) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 16) & 0xFF);
*pb->current++ = (uint8_t) ((word >> 24) & 0xFF);
}
return true;
}
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte)
{
return pb_u8(pb, ((union conv8){.i8 = byte}).u8);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word)
{
return pb_u16(pb, ((union conv16){.i16 = word}).u16);
}
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word)
{
return pb_u32(pb, ((union conv32){.i32 = word}).u32);
}
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f)
{
return pb_u32(pb, ((union conv32){.f32 = f}).u32);
}
+110
View File
@@ -0,0 +1,110 @@
#ifndef PAYLOAD_BUILDER_H
#define PAYLOAD_BUILDER_H
/**
* PayloadBuilder, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2014-2017. MIT license.
*
* The builder supports big and little endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* This module helps you with building payloads (not only for TinyFrame)
*
* The builder performs bounds checking and calls the provided handler when
* the requested write wouldn't fit. Use the handler to realloc / flush the buffer
* or report an error.
*/
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "type_coerce.h"
typedef struct PayloadBuilder_ PayloadBuilder;
/**
* Full buffer handler.
*
* 'needed' more bytes should be written but the end of the buffer was reached.
*
* Return true if the problem was solved (e.g. buffer was flushed and the
* 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following writes are discarded.
*/
typedef bool (*pb_full_handler)(PayloadBuilder *pb, uint32_t needed);
struct PayloadBuilder_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pb_full_handler full_handler; //!< Callback for buffer overrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the builder. */
#define pb_start_e(buf, capacity, bigendian, full_handler) ((PayloadBuilder){buf, buf, (buf)+(capacity), full_handler, bigendian, 1})
/** Start the builder in big-endian mode */
#define pb_start_be(buf, capacity, full_handler) pb_start_e(buf, capacity, 1, full_handler)
/** Start the builder in little-endian mode */
#define pb_start_le(buf, capacity, full_handler) pb_start_e(buf, capacity, 0, full_handler)
/** Start the parser in little-endian mode (default) */
#define pb_start(buf, capacity, full_handler) pb_start_le(buf, capacity, full_handler)
// --- utilities ---
/** Get already used bytes count */
#define pb_length(pb) ((pb)->current - (pb)->start)
/** Reset the current pointer to start */
#define pb_rewind(pb) do { pb->current = pb->start; } while (0)
/** Write from a buffer */
bool pb_buf(PayloadBuilder *pb, const uint8_t *buf, uint32_t len);
/** Write a zero terminated string */
bool pb_string(PayloadBuilder *pb, const char *str);
/** Write uint8_t to the buffer */
bool pb_u8(PayloadBuilder *pb, uint8_t byte);
/** Write boolean to the buffer. */
static inline bool pb_bool(PayloadBuilder *pb, bool b)
{
return pb_u8(pb, (uint8_t) b);
}
/** Write uint16_t to the buffer. */
bool pb_u16(PayloadBuilder *pb, uint16_t word);
/** Write uint32_t to the buffer. */
bool pb_u32(PayloadBuilder *pb, uint32_t word);
/** Write int8_t to the buffer. */
bool pb_i8(PayloadBuilder *pb, int8_t byte);
/** Write char (int8_t) to the buffer. */
static inline bool pb_char(PayloadBuilder *pb, char c)
{
return pb_i8(pb, c);
}
/** Write int16_t to the buffer. */
bool pb_i16(PayloadBuilder *pb, int16_t word);
/** Write int32_t to the buffer. */
bool pb_i32(PayloadBuilder *pb, int32_t word);
/** Write 4-byte float to the buffer. */
bool pb_float(PayloadBuilder *pb, float f);
#endif // PAYLOAD_BUILDER_H
+121
View File
@@ -0,0 +1,121 @@
#include "payload_parser.h"
#define pp_check_capacity(pp, needed) \
if ((pp)->current + (needed) > (pp)->end) { \
if ((pp)->empty_handler == NULL || !(pp)->empty_handler(pp, needed)) {(pp)->ok = 0;} ; \
}
void pp_skip(PayloadParser *pp, uint32_t num)
{
pp->current += num;
}
uint8_t pp_u8(PayloadParser *pp)
{
pp_check_capacity(pp, 1);
if (!pp->ok) return 0;
return *pp->current++;
}
uint16_t pp_u16(PayloadParser *pp)
{
pp_check_capacity(pp, 2);
if (!pp->ok) return 0;
uint16_t x = 0;
if (pp->bigendian) {
x |= *pp->current++ << 8;
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= *pp->current++ << 8;
}
return x;
}
uint32_t pp_u32(PayloadParser *pp)
{
pp_check_capacity(pp, 4);
if (!pp->ok) return 0;
uint32_t x = 0;
if (pp->bigendian) {
x |= (uint32_t) (*pp->current++ << 24);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 8);
x |= *pp->current++;
} else {
x |= *pp->current++;
x |= (uint32_t) (*pp->current++ << 8);
x |= (uint32_t) (*pp->current++ << 16);
x |= (uint32_t) (*pp->current++ << 24);
}
return x;
}
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length)
{
int32_t len = (int) (pp->end - pp->current);
if (!pp->ok || len <= 0) {
if (length != NULL) *length = 0;
return NULL;
}
if (length != NULL) {
*length = (uint32_t) len;
}
return pp->current;
}
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp)
{
return ((union conv8) {.u8 = pp_u8(pp)}).i8;
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp)
{
return ((union conv16) {.u16 = pp_u16(pp)}).i16;
}
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).i32;
}
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp)
{
return ((union conv32) {.u32 = pp_u32(pp)}).f32;
}
/** Read a zstring */
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen)
{
pp_check_capacity(pp, 1);
uint32_t len = 0;
while (len < maxlen-1 && pp->current != pp->end) {
char c = *buffer++ = *pp->current++;
if (c == 0) break;
len++;
}
*buffer = 0;
return len;
}
/** Read a buffer */
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen)
{
uint32_t len = 0;
while (len < maxlen && pp->current != pp->end) {
*buffer++ = *pp->current++;
len++;
}
return len;
}
+143
View File
@@ -0,0 +1,143 @@
#ifndef PAYLOAD_PARSER_H
#define PAYLOAD_PARSER_H
/**
* PayloadParser, part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This module helps you with parsing payloads (not only from TinyFrame).
*
* The parser supports big and little-endian which is selected when
* initializing it or by accessing the bigendian struct field.
*
* The parser performs bounds checking and calls the provided handler when
* the requested read doesn't have enough data. Use the callback to take
* appropriate action, e.g. report an error.
*
* If the handler function is not defined, the pb->ok flag is set to false
* (use this to check for success), and further reads won't have any effect
* and always result in 0 or empty array.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#include "type_coerce.h"
typedef struct PayloadParser_ PayloadParser;
/**
* Empty buffer handler.
*
* 'needed' more bytes should be read but the end was reached.
*
* Return true if the problem was solved (e.g. new data loaded into
* the buffer and the 'current' pointer moved to the beginning).
*
* If false is returned, the 'ok' flag on the struct is set to false
* and all following reads will fail / return 0.
*/
typedef bool (*pp_empty_handler)(PayloadParser *pp, uint32_t needed);
struct PayloadParser_ {
uint8_t *start; //!< Pointer to the beginning of the buffer
uint8_t *current; //!< Pointer to the next byte to be read
uint8_t *end; //!< Pointer to the end of the buffer (start + length)
pp_empty_handler empty_handler; //!< Callback for buffer underrun
bool bigendian; //!< Flag to use big-endian parsing
bool ok; //!< Indicates that all reads were successful
};
// --- initializer helper macros ---
/** Start the parser. */
#define pp_start_e(buf, length, bigendian, empty_handler) ((PayloadParser){buf, buf, (buf)+(length), empty_handler, bigendian, 1})
/** Start the parser in big-endian mode */
#define pp_start_be(buf, length, empty_handler) pp_start_e(buf, length, 1, empty_handler)
/** Start the parser in little-endian mode */
#define pp_start_le(buf, length, empty_handler) pp_start_e(buf, length, 0, empty_handler)
/** Start the parser in little-endian mode (default) */
#define pp_start(buf, length, empty_handler) pp_start_le(buf, length, empty_handler)
// --- utilities ---
/** Get remaining length */
#define pp_length(pp) ((pp)->end - (pp)->current)
/** Reset the current pointer to start */
#define pp_rewind(pp) do { pp->current = pp->start; } while (0)
/**
* @brief Get the remainder of the buffer.
*
* Returns NULL and sets 'length' to 0 if there are no bytes left.
*
* @param pp
* @param length : here the buffer length will be stored. NULL to do not store.
* @return the remaining portion of the input buffer
*/
const uint8_t *pp_tail(PayloadParser *pp, uint32_t *length);
/** Read uint8_t from the payload. */
uint8_t pp_u8(PayloadParser *pp);
/** Read bool from the payload. */
static inline int8_t pp_bool(PayloadParser *pp)
{
return pp_u8(pp) != 0;
}
/** Skip bytes */
void pp_skip(PayloadParser *pp, uint32_t num);
/** Read uint16_t from the payload. */
uint16_t pp_u16(PayloadParser *pp);
/** Read uint32_t from the payload. */
uint32_t pp_u32(PayloadParser *pp);
/** Read int8_t from the payload. */
int8_t pp_i8(PayloadParser *pp);
/** Read char (int8_t) from the payload. */
static inline int8_t pp_char(PayloadParser *pp)
{
return pp_i8(pp);
}
/** Read int16_t from the payload. */
int16_t pp_i16(PayloadParser *pp);
/** Read int32_t from the payload. */
int32_t pp_i32(PayloadParser *pp);
/** Read 4-byte float from the payload. */
float pp_float(PayloadParser *pp);
/**
* Parse a zero-terminated string
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_string(PayloadParser *pp, char *buffer, uint32_t maxlen);
/**
* Parse a buffer
*
* @param pp - parser
* @param buffer - target buffer
* @param maxlen - buffer size
* @return actual number of bytes, excluding terminator
*/
uint32_t pp_buf(PayloadParser *pp, uint8_t *buffer, uint32_t maxlen);
#endif // PAYLOAD_PARSER_H
+32
View File
@@ -0,0 +1,32 @@
#ifndef TYPE_COERCE_H
#define TYPE_COERCE_H
/**
* Structs for conversion between types,
* part of the TinyFrame utilities collection
*
* (c) Ondřej Hruška, 2016-2017. MIT license.
*
* This is a support header file for PayloadParser and PayloadBuilder.
*/
#include <stdint.h>
#include <stddef.h>
union conv8 {
uint8_t u8;
int8_t i8;
};
union conv16 {
uint16_t u16;
int16_t i16;
};
union conv32 {
uint32_t u32;
int32_t i32;
float f32;
};
#endif // TYPE_COERCE_H