Add re-entrancy support / multiple instances (#9)

* done with changes but now segfaulting

* fixed the bug

* fix some bad indents

* some cleanings

* updated readme
pull/14/head 2.0.0
Ondřej Hruška 7 years ago committed by GitHub
parent 77f60e1876
commit db0ae30f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      README.md
  2. 6
      TF_Integration.example.c
  3. 560
      TinyFrame.c
  4. 223
      TinyFrame.h
  5. 37
      demo/demo.c
  6. 2
      demo/demo.h
  7. 7
      demo/multipart_tx/Makefile
  8. 517
      demo/multipart_tx/test.c
  9. 2
      demo/simple/Makefile
  10. 29
      demo/simple/test.c
  11. 2
      demo/socket_demo/Makefile
  12. 14
      demo/socket_demo/master.c
  13. 17
      demo/socket_demo/slave.c
  14. 5
      demo/utils.c

@ -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,7 +53,9 @@ 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).
A frame can be sent in it's entirety, or in multiple parts, depending on its size. A frame can be sent in it's entirety, or in multiple parts, depending on its size.

@ -10,19 +10,19 @@
* listener timeout feature. * listener timeout feature.
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
// send to UART // send to UART
} }
/** Claim the TX interface before composing and sending a frame */ /** Claim the TX interface before composing and sending a frame */
void TF_ClaimTx(void) void TF_ClaimTx(TinyFrame *tf)
{ {
// take mutex // take mutex
} }
/** Free the TX interface after composing and sending a frame */ /** Free the TX interface after composing and sending a frame */
void TF_ReleaseTx(void) void TF_ReleaseTx(TinyFrame *tf)
{ {
// release mutex // release mutex
} }

@ -1,6 +1,6 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "TinyFrame.h" #include "TinyFrame.h"
#include <string.h> #include <malloc.h>
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Compatibility with ESP8266 SDK // Compatibility with ESP8266 SDK
@ -14,71 +14,9 @@
#define TF_MAX(a, b) ((a)>(b)?(a):(b)) #define TF_MAX(a, b) ((a)>(b)?(a):(b))
#define TF_MIN(a, b) ((a)<(b)?(a):(b)) #define TF_MIN(a, b) ((a)<(b)?(a):(b))
enum TFState { // TODO It would be nice to have per-instance configurable checksum types, but that would
TFState_SOF = 0, //!< Wait for SOF // mandate configurable field sizes unless we use u32 everywhere (and possibly shorten
TFState_LEN, //!< Wait for Number Of Bytes // it when encoding to the buffer). I don't really like this idea so much. -MP
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
};
typedef struct _IdListener_struct_ {
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;
} IdListener;
typedef struct _TypeListener_struct_ {
TF_TYPE type;
TF_Listener fn;
} TypeListener;
typedef struct _GenericListener_struct_ {
TF_Listener fn;
} GenericListener;
/**
* Frame parser internal state
*/
static struct TinyFrameStruct {
/* 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 */
IdListener id_listeners[TF_MAX_ID_LST];
TypeListener type_listeners[TF_MAX_TYPE_LST];
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];
} tf;
//region Checksums //region Checksums
@ -98,6 +36,7 @@ static struct TinyFrameStruct {
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC16
// TODO try to replace with an algorithm
/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
static const uint16_t crc16_table[256] = { static const uint16_t crc16_table[256] = {
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
@ -145,6 +84,7 @@ static inline uint16_t crc16_byte(uint16_t cksum, const uint8_t byte)
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC32
// TODO try to replace with an algorithm
static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@ -204,28 +144,41 @@ static inline uint32_t crc32_byte(uint32_t cksum, const uint8_t byte)
//endregion //endregion
/** Init with a user-allocated buffer */
void _TF_FN TF_Init(TF_Peer peer_bit) void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit)
{ {
// Zero it out if (tf == NULL) return;
memset(&tf, 0, sizeof(struct TinyFrameStruct));
tf.peer_bit = peer_bit; // Zero it out, keeping user config
uint32_t usertag = tf->usertag;
void * userdata = tf->userdata;
memset(tf, 0, sizeof(struct TinyFrame_));
tf->usertag = usertag;
tf->userdata = userdata;
tf->peer_bit = peer_bit;
}
/** Init with malloc */
TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit)
{
TinyFrame *tf = malloc(sizeof(TinyFrame));
TF_InitStatic(tf, peer_bit);
return tf;
} }
//region Listeners //region Listeners
static void _TF_FN renew_id_listener(IdListener *lst) /** Reset ID listener's timeout to the original value */
static inline void _TF_FN renew_id_listener(struct TF_IdListener_ *lst)
{ {
lst->timeout = lst->timeout_max; lst->timeout = lst->timeout_max;
} }
/** /** Notify callback about ID listener's demise & let it free any resources in userdata */
* Notify callback about ID listener demise & clean it static void _TF_FN cleanup_id_listener(TinyFrame *tf, TF_COUNT i, struct TF_IdListener_ *lst)
*
* @param lst - listener to clean
*/
static void _TF_FN cleanup_id_listener(TF_COUNT i, IdListener *lst)
{ {
TF_Msg msg; TF_Msg msg;
if (lst->fn == NULL) return; if (lst->fn == NULL) return;
@ -235,48 +188,41 @@ static void _TF_FN cleanup_id_listener(TF_COUNT i, IdListener *lst)
msg.userdata = lst->userdata; msg.userdata = lst->userdata;
msg.userdata2 = lst->userdata2; msg.userdata2 = lst->userdata2;
msg.data = NULL; // this is a signal that the listener should clean up msg.data = NULL; // this is a signal that the listener should clean up
lst->fn(&msg); // return value is ignored here - use TF_STAY or TF_CLOSE lst->fn(tf, &msg); // return value is ignored here - use TF_STAY or TF_CLOSE
} }
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_id_lst - 1) { if (i == tf->count_id_lst - 1) {
tf.count_id_lst--; tf->count_id_lst--;
} }
} }
/** /** Clean up Type listener */
* Clean up Type listener static inline void _TF_FN cleanup_type_listener(TinyFrame *tf, TF_COUNT i, struct TF_TypeListener_ *lst)
*
* @param lst - listener to clean
*/
static inline void _TF_FN cleanup_type_listener(TF_COUNT i, TypeListener *lst)
{ {
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_type_lst - 1) { if (i == tf->count_type_lst - 1) {
tf.count_type_lst--; tf->count_type_lst--;
} }
} }
/** /** Clean up Generic listener */
* Clean up Generic listener static inline void _TF_FN cleanup_generic_listener(TinyFrame *tf, TF_COUNT i, struct TF_GenericListener_ *lst)
*
* @param lst - listener to clean
*/
static inline void _TF_FN cleanup_generic_listener(TF_COUNT i, GenericListener *lst)
{ {
lst->fn = NULL; // Discard listener lst->fn = NULL; // Discard listener
if (i == tf.count_generic_lst - 1) { if (i == tf->count_generic_lst - 1) {
tf.count_generic_lst--; tf->count_generic_lst--;
} }
} }
bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout) /** Add a new ID listener. Returns 1 on success. */
bool _TF_FN TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeout)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < TF_MAX_ID_LST; i++) { for (i = 0; i < TF_MAX_ID_LST; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
@ -284,8 +230,8 @@ bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout)
lst->userdata = msg->userdata; lst->userdata = msg->userdata;
lst->userdata2 = msg->userdata2; lst->userdata2 = msg->userdata2;
lst->timeout_max = lst->timeout = timeout; lst->timeout_max = lst->timeout = timeout;
if (i >= tf.count_id_lst) { if (i >= tf->count_id_lst) {
tf.count_id_lst = (TF_COUNT) (i + 1); tf->count_id_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@ -293,18 +239,19 @@ bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout)
return false; return false;
} }
bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb) /** Add a new Type listener. Returns 1 on success. */
bool _TF_FN TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
TypeListener *lst; struct TF_TypeListener_ *lst;
for (i = 0; i < TF_MAX_TYPE_LST; i++) { for (i = 0; i < TF_MAX_TYPE_LST; i++) {
lst = &tf.type_listeners[i]; lst = &tf->type_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
lst->type = frame_type; lst->type = frame_type;
if (i >= tf.count_type_lst) { if (i >= tf->count_type_lst) {
tf.count_type_lst = (TF_COUNT) (i + 1); tf->count_type_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@ -312,17 +259,18 @@ bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb)
return false; return false;
} }
bool _TF_FN TF_AddGenericListener(TF_Listener cb) /** Add a new Generic listener. Returns 1 on success. */
bool _TF_FN TF_AddGenericListener(TinyFrame *tf, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
GenericListener *lst; struct TF_GenericListener_ *lst;
for (i = 0; i < TF_MAX_GEN_LST; i++) { for (i = 0; i < TF_MAX_GEN_LST; i++) {
lst = &tf.generic_listeners[i]; lst = &tf->generic_listeners[i];
// test for empty slot // test for empty slot
if (lst->fn == NULL) { if (lst->fn == NULL) {
lst->fn = cb; lst->fn = cb;
if (i >= tf.count_generic_lst) { if (i >= tf->count_generic_lst) {
tf.count_generic_lst = (TF_COUNT) (i + 1); tf->count_generic_lst = (TF_COUNT) (i + 1);
} }
return true; return true;
} }
@ -330,45 +278,48 @@ bool _TF_FN TF_AddGenericListener(TF_Listener cb)
return false; return false;
} }
bool _TF_FN TF_RemoveIdListener(TF_ID frame_id) /** Remove a ID listener by its frame ID. Returns 1 on success. */
bool _TF_FN TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->id == frame_id) { if (lst->fn != NULL && lst->id == frame_id) {
cleanup_id_listener(i, lst); cleanup_id_listener(tf, i, lst);
return true; return true;
} }
} }
return false; return false;
} }
bool _TF_FN TF_RemoveTypeListener(TF_TYPE type) /** Remove a type listener by its type. Returns 1 on success. */
bool _TF_FN TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type)
{ {
TF_COUNT i; TF_COUNT i;
TypeListener *lst; struct TF_TypeListener_ *lst;
for (i = 0; i < tf.count_type_lst; i++) { for (i = 0; i < tf->count_type_lst; i++) {
lst = &tf.type_listeners[i]; lst = &tf->type_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->type == type) { if (lst->fn != NULL && lst->type == type) {
cleanup_type_listener(i, lst); cleanup_type_listener(tf, i, lst);
return true; return true;
} }
} }
return false; return false;
} }
bool _TF_FN TF_RemoveGenericListener(TF_Listener cb) /** Remove a generic listener by its function pointer. Returns 1 on success. */
bool _TF_FN TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb)
{ {
TF_COUNT i; TF_COUNT i;
GenericListener *lst; struct TF_GenericListener_ *lst;
for (i = 0; i < tf.count_generic_lst; i++) { for (i = 0; i < tf->count_generic_lst; i++) {
lst = &tf.generic_listeners[i]; lst = &tf->generic_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn == cb) { if (lst->fn == cb) {
cleanup_generic_listener(i, lst); cleanup_generic_listener(tf, i, lst);
return true; return true;
} }
} }
@ -376,80 +327,78 @@ bool _TF_FN TF_RemoveGenericListener(TF_Listener cb)
} }
/** Handle a message that was just collected & verified by the parser */ /** Handle a message that was just collected & verified by the parser */
static void _TF_FN TF_HandleReceivedMessage(void) static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *ilst; struct TF_IdListener_ *ilst;
TypeListener *tlst; struct TF_TypeListener_ *tlst;
GenericListener *glst; struct TF_GenericListener_ *glst;
TF_Result res; TF_Result res;
// Prepare message object // Prepare message object
TF_Msg msg; TF_Msg msg;
msg.frame_id = tf.id; TF_ClearMsg(&msg);
msg.frame_id = tf->id;
msg.is_response = false; msg.is_response = false;
msg.type = tf.type; msg.type = tf->type;
msg.data = tf.data; msg.data = tf->data;
msg.len = tf.len; msg.len = tf->len;
//dumpFrameInfo(&msg); // Any listener can consume the message, or let someone else handle it.
// Any listener can consume the message (return true),
// or let someone else handle it.
// The loop upper bounds are the highest currently used slot index // The loop upper bounds are the highest currently used slot index
// (or close to it, depending on the order of listener removals) // (or close to it, depending on the order of listener removals).
// ID listeners first // ID listeners first
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
ilst = &tf.id_listeners[i]; ilst = &tf->id_listeners[i];
if (ilst->fn && ilst->id == msg.frame_id) { if (ilst->fn && ilst->id == msg.frame_id) {
msg.userdata = ilst->userdata; // pass userdata pointer to the callback msg.userdata = ilst->userdata; // pass userdata pointer to the callback
msg.userdata2 = ilst->userdata2; msg.userdata2 = ilst->userdata2;
res = ilst->fn(&msg); res = ilst->fn(tf, &msg);
ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL) ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL)
ilst->userdata2 = msg.userdata2; // put it back (may have changed the pointer or set to NULL) ilst->userdata2 = msg.userdata2; // put it back (may have changed the pointer or set to NULL)
if (res != TF_NEXT) { if (res != TF_NEXT) {
if (res == TF_CLOSE) { // if it's TF_CLOSE, we assume user already cleaned up userdata
cleanup_id_listener(i, ilst); if (res == TF_RENEW) {
}
else if (res == TF_RENEW) {
renew_id_listener(ilst); renew_id_listener(ilst);
} }
return; return;
} }
} }
} }
// clean up for the following listeners that don't use userdata (this avoids data from
// an ID listener that returned TF_NEXT from leaking into Type and Generic listeners)
msg.userdata = NULL; msg.userdata = NULL;
msg.userdata2 = NULL; msg.userdata2 = NULL;
// clean up for the following listeners that don't use userdata
// Type listeners // Type listeners
for (i = 0; i < tf.count_type_lst; i++) { for (i = 0; i < tf->count_type_lst; i++) {
tlst = &tf.type_listeners[i]; tlst = &tf->type_listeners[i];
if (tlst->fn && tlst->type == msg.type) { if (tlst->fn && tlst->type == msg.type) {
res = tlst->fn(&msg); res = tlst->fn(tf, &msg);
if (res != TF_NEXT) { if (res != TF_NEXT) {
if (res == TF_CLOSE) { // if it's TF_CLOSE, we assume user already cleaned up userdata
cleanup_type_listener(i, tlst); // TF_RENEW doesn't make sense here because type listeners don't expire
}
return; return;
} }
} }
} }
// Generic listeners // Generic listeners
for (i = 0; i < tf.count_generic_lst; i++) { for (i = 0; i < tf->count_generic_lst; i++) {
glst = &tf.generic_listeners[i]; glst = &tf->generic_listeners[i];
if (glst->fn) { if (glst->fn) {
res = glst->fn(&msg); res = glst->fn(tf, &msg);
if (res != TF_NEXT) { if (res != TF_NEXT) {
if (res == TF_CLOSE) { // if it's TF_CLOSE, we assume user already cleaned up userdata
cleanup_generic_listener(i, glst); // TF_RENEW doesn't make sense here because generic listeners don't expire
}
return; return;
} }
} }
@ -458,173 +407,206 @@ static void _TF_FN TF_HandleReceivedMessage(void)
//endregion Listeners //endregion Listeners
void _TF_FN TF_Accept(const uint8_t *buffer, size_t count) /** Handle a received byte buffer */
void _TF_FN TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count)
{ {
size_t i; size_t i;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
TF_AcceptChar(buffer[i]); TF_AcceptChar(tf, buffer[i]);
} }
} }
void _TF_FN TF_ResetParser(void) /** Reset the parser's internal state. */
void _TF_FN TF_ResetParser(TinyFrame *tf)
{ {
tf.state = TFState_SOF; tf->state = TFState_SOF;
// more init will be done by the parser when the first byte is received
} }
/** SOF was received */ /** SOF was received - prepare for the frame */
static void _TF_FN TF_ParsBeginFrame(void) { static void _TF_FN pars_begin_frame(TinyFrame *tf) {
// Reset state vars // Reset state vars
CKSUM_RESET(tf.cksum); CKSUM_RESET(tf->cksum);
#if TF_USE_SOF_BYTE #if TF_USE_SOF_BYTE
CKSUM_ADD(tf.cksum, TF_SOF_BYTE); CKSUM_ADD(tf->cksum, TF_SOF_BYTE);
#endif #endif
tf.discard_data = false; tf->discard_data = false;
// Enter ID state // Enter ID state
tf.state = TFState_ID; tf->state = TFState_ID;
tf.rxi = 0; tf->rxi = 0;
} }
void _TF_FN TF_AcceptChar(unsigned char c) /** Handle a received char - here's the main state machine */
void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c)
{ {
// Parser timeout - clear // Parser timeout - clear
if (tf.parser_timeout_ticks >= TF_PARSER_TIMEOUT_TICKS) { if (tf->parser_timeout_ticks >= TF_PARSER_TIMEOUT_TICKS) {
TF_ResetParser(); TF_ResetParser(tf);
} }
tf.parser_timeout_ticks = 0; tf->parser_timeout_ticks = 0;
// DRY snippet - collect multi-byte number from the input stream // DRY snippet - collect multi-byte number from the input stream, byte by byte
// This is a little dirty, but makes the code easier to read. It's used like e.g. if(),
// the body is run only after the entire number (of data type 'type') was received
// and stored to 'dest'
#define COLLECT_NUMBER(dest, type) dest = (type)(((dest) << 8) | c); \ #define COLLECT_NUMBER(dest, type) dest = (type)(((dest) << 8) | c); \
if (++tf.rxi == sizeof(type)) if (++tf->rxi == sizeof(type))
#if !TF_USE_SOF_BYTE #if !TF_USE_SOF_BYTE
if (tf.state == TFState_SOF) { if (tf->state == TFState_SOF) {
TF_ParsBeginFrame(); TF_ParsBeginFrame();
} }
#endif #endif
switch (tf.state) { switch (tf->state) {
case TFState_SOF: case TFState_SOF:
if (c == TF_SOF_BYTE) { if (c == TF_SOF_BYTE) {
TF_ParsBeginFrame(); pars_begin_frame(tf);
} }
break; break;
case TFState_ID: case TFState_ID:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.id, TF_ID) { COLLECT_NUMBER(tf->id, TF_ID) {
// Enter LEN state // Enter LEN state
tf.state = TFState_LEN; tf->state = TFState_LEN;
tf.rxi = 0; tf->rxi = 0;
} }
break; break;
case TFState_LEN: case TFState_LEN:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.len, TF_LEN) { COLLECT_NUMBER(tf->len, TF_LEN) {
// Enter TYPE state // Enter TYPE state
tf.state = TFState_TYPE; tf->state = TFState_TYPE;
tf.rxi = 0; tf->rxi = 0;
} }
break; break;
case TFState_TYPE: case TFState_TYPE:
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
COLLECT_NUMBER(tf.type, TF_TYPE) { COLLECT_NUMBER(tf->type, TF_TYPE) {
#if TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_NONE
tf.state = TFState_DATA; tf->state = TFState_DATA;
tf.rxi = 0; tf->rxi = 0;
#else #else
// enter HEAD_CKSUM state // enter HEAD_CKSUM state
tf.state = TFState_HEAD_CKSUM; tf->state = TFState_HEAD_CKSUM;
tf.rxi = 0; tf->rxi = 0;
tf.ref_cksum = 0; tf->ref_cksum = 0;
#endif #endif
} }
break; break;
case TFState_HEAD_CKSUM: case TFState_HEAD_CKSUM:
COLLECT_NUMBER(tf.ref_cksum, TF_CKSUM) { COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) {
// Check the header checksum against the computed value // Check the header checksum against the computed value
CKSUM_FINALIZE(tf.cksum); CKSUM_FINALIZE(tf->cksum);
if (tf.cksum != tf.ref_cksum) { if (tf->cksum != tf->ref_cksum) {
TF_ResetParser(); TF_ResetParser(tf);
break; break;
} }
if (tf.len == 0) { if (tf->len == 0) {
TF_HandleReceivedMessage(); TF_HandleReceivedMessage(tf);
TF_ResetParser(); TF_ResetParser(tf);
break; break;
} }
// Enter DATA state // Enter DATA state
tf.state = TFState_DATA; tf->state = TFState_DATA;
tf.rxi = 0; tf->rxi = 0;
CKSUM_RESET(tf.cksum); // Start collecting the payload CKSUM_RESET(tf->cksum); // Start collecting the payload
if (tf.len >= TF_MAX_PAYLOAD_RX) { if (tf->len >= TF_MAX_PAYLOAD_RX) {
// ERROR - frame too long. Consume, but do not store. // ERROR - frame too long. Consume, but do not store.
tf.discard_data = true; tf->discard_data = true;
} }
} }
break; break;
case TFState_DATA: case TFState_DATA:
if (tf.discard_data) { if (tf->discard_data) {
tf.rxi++; tf->rxi++;
} else { } else {
CKSUM_ADD(tf.cksum, c); CKSUM_ADD(tf->cksum, c);
tf.data[tf.rxi++] = c; tf->data[tf->rxi++] = c;
} }
if (tf.rxi == tf.len) { if (tf->rxi == tf->len) {
#if TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_NONE
// All done // All done
TF_HandleReceivedMessage(); TF_HandleReceivedMessage();
TF_ResetParser(); TF_ResetParser();
#else #else
// Enter DATA_CKSUM state // Enter DATA_CKSUM state
tf.state = TFState_DATA_CKSUM; tf->state = TFState_DATA_CKSUM;
tf.rxi = 0; tf->rxi = 0;
tf.ref_cksum = 0; tf->ref_cksum = 0;
#endif #endif
} }
break; break;
case TFState_DATA_CKSUM: case TFState_DATA_CKSUM:
COLLECT_NUMBER(tf.ref_cksum, TF_CKSUM) { COLLECT_NUMBER(tf->ref_cksum, TF_CKSUM) {
// Check the header checksum against the computed value // Check the header checksum against the computed value
CKSUM_FINALIZE(tf.cksum); CKSUM_FINALIZE(tf->cksum);
if (!tf.discard_data && tf.cksum == tf.ref_cksum) { if (!tf->discard_data && tf->cksum == tf->ref_cksum) {
TF_HandleReceivedMessage(); TF_HandleReceivedMessage(tf);
} }
TF_ResetParser(); TF_ResetParser(tf);
} }
break; break;
} }
// we get here after finishing HEAD, if no data are to be received - handle and clear // we get here after finishing HEAD, if no data are to be received - handle and clear
if (tf.len == 0 && tf.state == TFState_DATA) { if (tf->len == 0 && tf->state == TFState_DATA) {
TF_HandleReceivedMessage(); TF_HandleReceivedMessage(tf);
TF_ResetParser(); TF_ResetParser(tf);
} }
} }
// Helper macros for the Compose functions // Helper macros for the Compose functions
// use variables: si - signed int, b - byte, outbuf - target buffer, pos - count of bytes in buffer // use variables: si - signed int, b - byte, outbuff - target buffer, pos - count of bytes in buffer
/**
* Write a number to the output buffer.
*
* @param type - data type
* @param num - number to write
* @param xtra - extra callback run after each byte, 'b' now contains the byte.
*/
#define WRITENUM_BASE(type, num, xtra) \ #define WRITENUM_BASE(type, num, xtra) \
for (si = sizeof(type)-1; si>=0; si--) { \ for (si = sizeof(type)-1; si>=0; si--) { \
b = (uint8_t)((num) >> (si*8) & 0xFF); \ b = (uint8_t)((num) >> (si*8) & 0xFF); \
outbuff[pos++] = b; \ outbuff[pos++] = b; \
xtra; \ xtra; \
} }
/**
* Do nothing
*/
#define _NOOP() #define _NOOP()
/**
* Write a number without adding its bytes to the checksum
*
* @param type - data type
* @param num - number to write
*/
#define WRITENUM(type, num) WRITENUM_BASE(type, num, _NOOP()) #define WRITENUM(type, num) WRITENUM_BASE(type, num, _NOOP())
/**
* Write a number AND add its bytes to the checksum
*
* @param type - data type
* @param num - number to write
*/
#define WRITENUM_CKSUM(type, num) WRITENUM_BASE(type, num, CKSUM_ADD(cksum, b)) #define WRITENUM_CKSUM(type, num) WRITENUM_BASE(type, num, CKSUM_ADD(cksum, b))
/** /**
@ -632,16 +614,10 @@ void _TF_FN TF_AcceptChar(unsigned char c)
* The frame can be sent using TF_WriteImpl(), or received by TF_Accept() * The frame can be sent using TF_WriteImpl(), or received by TF_Accept()
* *
* @param outbuff - buffer to store the result in * @param outbuff - buffer to store the result in
* @param msgid - message ID is stored here, if not NULL * @param msg - message written to the buffer
* @param type - message type
* @param len - payload size in bytes
* @param explicit_id - ID to use in the frame (8-bit)
* @param use_expl_id - whether to use the previous param
* @return nr of bytes in outbuff used by the frame, 0 on failure * @return nr of bytes in outbuff used by the frame, 0 on failure
*/ */
static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr, static inline size_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_Msg *msg)
TF_TYPE type, TF_LEN data_len,
TF_ID explicit_id, bool use_expl_id)
{ {
int8_t si = 0; // signed small int int8_t si = 0; // signed small int
uint8_t b = 0; uint8_t b = 0;
@ -654,17 +630,17 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
// Gen ID // Gen ID
if (use_expl_id) { if (msg->is_response) {
id = explicit_id; id = msg->frame_id;
} }
else { else {
id = (TF_ID) (tf.next_id++ & TF_ID_MASK); id = (TF_ID) (tf->next_id++ & TF_ID_MASK);
if (tf.peer_bit) { if (tf->peer_bit) {
id |= TF_ID_PEERBIT; id |= TF_ID_PEERBIT;
} }
} }
if (id_ptr != NULL) *id_ptr = id; msg->frame_id = id; // put the resolved ID into the message object for later use
// --- Start --- // --- Start ---
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
@ -675,8 +651,8 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
#endif #endif
WRITENUM_CKSUM(TF_ID, id); WRITENUM_CKSUM(TF_ID, id);
WRITENUM_CKSUM(TF_LEN, data_len); WRITENUM_CKSUM(TF_LEN, msg->len);
WRITENUM_CKSUM(TF_TYPE, type); WRITENUM_CKSUM(TF_TYPE, msg->type);
#if TF_CKSUM_TYPE != TF_CKSUM_NONE #if TF_CKSUM_TYPE != TF_CKSUM_NONE
CKSUM_FINALIZE(cksum); CKSUM_FINALIZE(cksum);
@ -696,7 +672,9 @@ static inline size_t _TF_FN TF_ComposeHead(uint8_t *outbuff, TF_ID *id_ptr,
* @param cksum - checksum variable, used for all calls to TF_ComposeBody. Must be reset before first use! (CKSUM_RESET(cksum);) * @param cksum - checksum variable, used for all calls to TF_ComposeBody. Must be reset before first use! (CKSUM_RESET(cksum);)
* @return nr of bytes in outbuff used * @return nr of bytes in outbuff used
*/ */
static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, const uint8_t *data, TF_LEN data_len, TF_CKSUM *cksum) static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff,
const uint8_t *data, TF_LEN data_len,
TF_CKSUM *cksum)
{ {
TF_LEN i = 0; TF_LEN i = 0;
uint8_t b = 0; uint8_t b = 0;
@ -718,7 +696,7 @@ static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, const uint8_t *data, TF_LE
* @param cksum - checksum variable used for the body * @param cksum - checksum variable used for the body
* @return nr of bytes in outbuff used * @return nr of bytes in outbuff used
*/ */
static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, const TF_CKSUM *cksum)
{ {
int8_t si = 0; // signed small int int8_t si = 0; // signed small int
uint8_t b = 0; uint8_t b = 0;
@ -731,104 +709,106 @@ static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum)
return pos; return pos;
} }
// send with listener /**
static bool _TF_FN TF_SendFrame(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) * Send a message
*
* @param tf - instance
* @param msg - message object
* @param listener - ID listener, or NULL
* @param timeout - listener timeout, 0 is none
* @return true if sent
*/
static bool _TF_FN TF_SendFrame(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout)
{ {
size_t len = 0; size_t len = 0;
size_t remain = 0; size_t remain = 0;
size_t sent = 0; size_t sent = 0;
TF_CKSUM cksum = 0; TF_CKSUM cksum = 0;
TF_ClaimTx(); TF_ClaimTx(tf);
len = TF_ComposeHead(tf.sendbuf, len = TF_ComposeHead(tf, tf->sendbuf, msg);
&msg->frame_id, if (listener) TF_AddIdListener(tf, msg, listener, timeout);
msg->type,
msg->len,
msg->frame_id,
msg->is_response);
if (listener) TF_AddIdListener(msg, listener, timeout);
CKSUM_RESET(cksum); CKSUM_RESET(cksum);
remain = msg->len; remain = msg->len;
while (remain > 0) { while (remain > 0) {
size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain); size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain);
len += TF_ComposeBody(tf.sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum); len += TF_ComposeBody(tf->sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum);
remain -= chunk; remain -= chunk;
sent += chunk; sent += chunk;
// Flush if the buffer is full and we have more to send // Flush if the buffer is full and we have more to send
if (remain > 0 && len == TF_SENDBUF_LEN) { if (remain > 0 && len == TF_SENDBUF_LEN) {
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
len = 0; len = 0;
} }
} }
// Flush if checksum wouldn't fit in the buffer // Flush if checksum wouldn't fit in the buffer
if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) { if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) {
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
len = 0; len = 0;
} }
// Add checksum, flush what remains to be sent // Add checksum, flush what remains to be sent
len += TF_ComposeTail(tf.sendbuf+len, &cksum); len += TF_ComposeTail(tf->sendbuf+len, &cksum);
TF_WriteImpl((const uint8_t *) tf.sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len);
TF_ReleaseTx(); TF_ReleaseTx(tf);
return true; return true;
} }
// send without listener /** send without listener */
bool _TF_FN TF_Send(TF_Msg *msg) bool _TF_FN TF_Send(TinyFrame *tf, TF_Msg *msg)
{ {
return TF_SendFrame(msg, NULL, 0); return TF_SendFrame(tf, msg, NULL, 0);
} }
// send without listener and struct /** send without listener and struct */
bool _TF_FN TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len) bool _TF_FN TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len)
{ {
TF_Msg msg; TF_Msg msg;
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = type; msg.type = type;
msg.data = data; msg.data = data;
msg.len = len; msg.len = len;
return TF_Send(&msg); return TF_Send(tf, &msg);
} }
// send without listener and struct /** send with a listener waiting for a reply, without the struct */
bool _TF_FN TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout, void *userdata) bool _TF_FN TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout)
{ {
TF_Msg msg; TF_Msg msg;
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = type; msg.type = type;
msg.data = data; msg.data = data;
msg.len = len; msg.len = len;
msg.userdata = userdata; return TF_SendFrame(tf, &msg, listener, timeout);
return TF_SendFrame(&msg, listener, timeout);
} }
// send with listener /** send with a listener waiting for a reply */
bool _TF_FN TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) bool _TF_FN TF_Query(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout)
{ {
return TF_SendFrame(msg, listener, timeout); return TF_SendFrame(tf, msg, listener, timeout);
} }
// Like TF_Send, but with explicit frame ID /** Like TF_Send, but with explicit frame ID (set inside the msg object), use for responses */
bool _TF_FN TF_Respond(TF_Msg *msg) bool _TF_FN TF_Respond(TinyFrame *tf, TF_Msg *msg)
{ {
msg->is_response = true; msg->is_response = true;
return TF_Send(msg); return TF_Send(tf, msg);
} }
bool _TF_FN TF_RenewIdListener(TF_ID id) /** Externally renew an ID listener */
bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id)
{ {
TF_COUNT i; TF_COUNT i;
IdListener *lst; struct TF_IdListener_ *lst;
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
// test if live & matching // test if live & matching
if (lst->fn != NULL && lst->id == id) { if (lst->fn != NULL && lst->id == id) {
renew_id_listener(lst); renew_id_listener(lst);
@ -839,34 +819,40 @@ bool _TF_FN TF_RenewIdListener(TF_ID id)
} }
/** Timebase hook - for timeouts */ /** Timebase hook - for timeouts */
void _TF_FN TF_Tick(void) void _TF_FN TF_Tick(TinyFrame *tf)
{ {
TF_COUNT i = 0; TF_COUNT i = 0;
IdListener *lst; struct TF_IdListener_ *lst;
// increment parser timeout (timeout is handled when receiving next byte) // increment parser timeout (timeout is handled when receiving next byte)
if (tf.parser_timeout_ticks < TF_PARSER_TIMEOUT_TICKS) { if (tf->parser_timeout_ticks < TF_PARSER_TIMEOUT_TICKS) {
tf.parser_timeout_ticks++; tf->parser_timeout_ticks++;
} }
// decrement and expire ID listeners // decrement and expire ID listeners
for (i = 0; i < tf.count_id_lst; i++) { for (i = 0; i < tf->count_id_lst; i++) {
lst = &tf.id_listeners[i]; lst = &tf->id_listeners[i];
if (!lst->fn || lst->timeout == 0) continue; if (!lst->fn || lst->timeout == 0) continue;
// count down... // count down...
if (--lst->timeout == 0) { if (--lst->timeout == 0) {
// Listener has expired // Listener has expired
cleanup_id_listener(i, lst); cleanup_id_listener(tf, i, lst);
} }
} }
} }
void __attribute__((weak)) TF_ClaimTx(void) /** Default impl for claiming write mutex; can be specific to the instance */
void __attribute__((weak)) TF_ClaimTx(TinyFrame *tf)
{ {
(void) tf;
// do nothing // do nothing
} }
void __attribute__((weak)) TF_ReleaseTx(void) /** Default impl for releasing write mutex; can be specific to the instance */
void __attribute__((weak)) TF_ReleaseTx(TinyFrame *tf)
{ {
(void) tf;
// do nothing // do nothing
} }

@ -10,12 +10,13 @@
* Upstream URL: https://github.com/MightyPork/TinyFrame * Upstream URL: https://github.com/MightyPork/TinyFrame
*/ */
#define TF_VERSION "1.2.0" #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)
@ -24,54 +25,54 @@
#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
#if TF_LEN_BYTES == 1 #if TF_LEN_BYTES == 1
typedef uint8_t TF_LEN; typedef uint8_t TF_LEN;
#elif TF_LEN_BYTES == 2 #elif TF_LEN_BYTES == 2
typedef uint16_t TF_LEN; typedef uint16_t TF_LEN;
#elif TF_LEN_BYTES == 4 #elif TF_LEN_BYTES == 4
typedef uint32_t TF_LEN; typedef uint32_t TF_LEN;
#else #else
#error Bad value of TF_LEN_BYTES, must be 1, 2 or 4 #error Bad value of TF_LEN_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_TYPE_BYTES == 1 #if TF_TYPE_BYTES == 1
typedef uint8_t TF_TYPE; typedef uint8_t TF_TYPE;
#elif TF_TYPE_BYTES == 2 #elif TF_TYPE_BYTES == 2
typedef uint16_t TF_TYPE; typedef uint16_t TF_TYPE;
#elif TF_TYPE_BYTES == 4 #elif TF_TYPE_BYTES == 4
typedef uint32_t TF_TYPE; typedef uint32_t TF_TYPE;
#else #else
#error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4 #error Bad value of TF_TYPE_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_ID_BYTES == 1 #if TF_ID_BYTES == 1
typedef uint8_t TF_ID; typedef uint8_t TF_ID;
#elif TF_ID_BYTES == 2 #elif TF_ID_BYTES == 2
typedef uint16_t TF_ID; typedef uint16_t TF_ID;
#elif TF_ID_BYTES == 4 #elif TF_ID_BYTES == 4
typedef uint32_t TF_ID; typedef uint32_t TF_ID;
#else #else
#error Bad value of TF_ID_BYTES, must be 1, 2 or 4 #error Bad value of TF_ID_BYTES, must be 1, 2 or 4
#endif #endif
#if TF_CKSUM_TYPE == TF_CKSUM_XOR || TF_CKSUM_TYPE == TF_CKSUM_NONE #if TF_CKSUM_TYPE == TF_CKSUM_XOR || TF_CKSUM_TYPE == TF_CKSUM_NONE
// ~XOR (if 0, still use 1 byte - it won't be used) // ~XOR (if 0, still use 1 byte - it won't be used)
typedef uint8_t TF_CKSUM; typedef uint8_t TF_CKSUM;
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC16
// CRC16 // CRC16
typedef uint16_t TF_CKSUM; typedef uint16_t TF_CKSUM;
#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 #elif TF_CKSUM_TYPE == TF_CKSUM_CRC32
// CRC32 // CRC32
typedef uint32_t TF_CKSUM; typedef uint32_t TF_CKSUM;
#else #else
#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
//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
@ -113,15 +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;
msg->userdata2 = NULL;
} }
typedef struct TinyFrame_ TinyFrame;
/** /**
* TinyFrame Type Listener callback * TinyFrame Type Listener callback
* *
@ -131,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.
@ -155,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.
@ -171,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.
@ -186,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
@ -201,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.
@ -221,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.
@ -229,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
@ -245,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.
@ -269,12 +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 */ /** Claim the TX interface before composing and sending a frame */
extern void TF_ClaimTx(void); extern void TF_ClaimTx(TinyFrame *tf);
/** Free the TX interface after composing and sending a frame */ /** Free the TX interface after composing and sending a frame */
extern void TF_ReleaseTx(void); extern void TF_ReleaseTx(TinyFrame *tf);
#endif #endif

@ -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");
} }
} }
@ -54,9 +60,9 @@ void TF_WriteImpl(const uint8_t *buff, size_t len)
* @param unused * @param unused
* @return unused * @return unused
*/ */
static int demo_client(void* unused) static int demo_client(void *unused)
{ {
(void)unused; (void) unused;
ssize_t n = 0; ssize_t n = 0;
uint8_t recvBuff[1024]; uint8_t recvBuff[1024];
@ -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;
} }
@ -102,9 +108,9 @@ static int demo_client(void* unused)
* @param unused * @param unused
* @return unused * @return unused
*/ */
static int demo_server(void* unused) static int demo_server(void *unused)
{ {
(void)unused; (void) unused;
ssize_t n; ssize_t n;
int listenfd = 0; int listenfd = 0;
uint8_t recvBuff[1024]; uint8_t recvBuff[1024];
@ -117,7 +123,7 @@ static int demo_server(void* unused)
memset(&serv_addr, '0', sizeof(serv_addr)); memset(&serv_addr, '0', sizeof(serv_addr));
option = 1; option = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option)); setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof(option));
serv_addr.sin_family = AF_INET; serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
@ -136,14 +142,14 @@ static int demo_server(void* unused)
while (1) { while (1) {
printf("\nWaiting for client...\n"); printf("\nWaiting for client...\n");
sockfd = accept(listenfd, (struct sockaddr *) NULL, NULL); sockfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof(option)); setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof(option));
printf("\nClient connected\n"); printf("\nClient connected\n");
conn_disband = false; conn_disband = false;
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) {
@ -164,7 +170,7 @@ static int demo_server(void* unused)
*/ */
static void signal_handler(int sig) static void signal_handler(int sig)
{ {
(void)sig; (void) sig;
printf("Shutting down..."); printf("Shutting down...");
demo_disconn(); demo_disconn();
@ -176,7 +182,7 @@ static void signal_handler(int sig)
*/ */
void demo_sleep(void) void demo_sleep(void)
{ {
while(1) usleep(10); while (1) usleep(10);
} }
/** /**
@ -204,9 +210,10 @@ void demo_init(TF_Peer peer)
// CLONE_VM --- share heap // CLONE_VM --- share heap
// 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 { }
retc = clone(&demo_server, (char *)stack+8192, CLONE_VM|CLONE_FILES, 0); else {
retc = clone(&demo_server, (char *) stack + 8192, CLONE_VM | CLONE_FILES, 0);
} }
if (retc == 0) { if (retc == 0) {

@ -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);

@ -1,11 +1,12 @@
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 -Wall -Wno-unused -Wextra $(CFILES) $(INCLDIRS)
run: test.bin
./test.bin
build: test.bin build: test.bin
run: test.bin
./test.bin
test.bin: test.c $(CFILES) test.bin: test.c $(CFILES)
gcc test.c $(CFLAGS) -o test.bin gcc test.c $(CFLAGS) -o test.bin

@ -3,29 +3,32 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
extern const char *romeo; extern const char *romeo;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
if (strcmp(msg->data, romeo) == 0) { if (strcmp((const char *) msg->data, romeo) == 0) {
printf("FILE TRANSFERRED OK!\r\n"); printf("FILE TRANSFERRED OK!\r\n");
} else { }
else {
printf("FAIL!!!!\r\n"); printf("FAIL!!!!\r\n");
} }
return TF_STAY; return TF_STAY;
@ -36,262 +39,262 @@ void main(void)
TF_Msg msg; TF_Msg msg;
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a LOOONG message --------\n"); printf("------ Simulate sending a LOOONG message --------\n");
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8)romeo; msg.data = (pu8) romeo;
msg.len = strlen(romeo); msg.len = (TF_LEN) strlen(romeo);
TF_Send(&msg); TF_Send(demo_tf, &msg);
} }
const char *romeo ="THE TRAGEDY OF ROMEO AND JULIET\n" const char *romeo = "THE TRAGEDY OF ROMEO AND JULIET\n"
"\n" "\n"
"by William Shakespeare\n" "by William Shakespeare\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"Dramatis Personae\n" "Dramatis Personae\n"
"\n" "\n"
" Chorus.\n" " Chorus.\n"
"\n" "\n"
"\n" "\n"
" Escalus, Prince of Verona.\n" " Escalus, Prince of Verona.\n"
"\n" "\n"
" Paris, a young Count, kinsman to the Prince.\n" " Paris, a young Count, kinsman to the Prince.\n"
"\n" "\n"
" Montague, heads of two houses at variance with each other.\n" " Montague, heads of two houses at variance with each other.\n"
"\n" "\n"
" Capulet, heads of two houses at variance with each other.\n" " Capulet, heads of two houses at variance with each other.\n"
"\n" "\n"
" An old Man, of the Capulet family.\n" " An old Man, of the Capulet family.\n"
"\n" "\n"
" Romeo, son to Montague.\n" " Romeo, son to Montague.\n"
"\n" "\n"
" Tybalt, nephew to Lady Capulet.\n" " Tybalt, nephew to Lady Capulet.\n"
"\n" "\n"
" Mercutio, kinsman to the Prince and friend to Romeo.\n" " Mercutio, kinsman to the Prince and friend to Romeo.\n"
"\n" "\n"
" Benvolio, nephew to Montague, and friend to Romeo\n" " Benvolio, nephew to Montague, and friend to Romeo\n"
"\n" "\n"
" Tybalt, nephew to Lady Capulet.\n" " Tybalt, nephew to Lady Capulet.\n"
"\n" "\n"
" Friar Laurence, Franciscan.\n" " Friar Laurence, Franciscan.\n"
"\n" "\n"
" Friar John, Franciscan.\n" " Friar John, Franciscan.\n"
"\n" "\n"
" Balthasar, servant to Romeo.\n" " Balthasar, servant to Romeo.\n"
"\n" "\n"
" Abram, servant to Montague.\n" " Abram, servant to Montague.\n"
"\n" "\n"
" Sampson, servant to Capulet.\n" " Sampson, servant to Capulet.\n"
"\n" "\n"
" Gregory, servant to Capulet.\n" " Gregory, servant to Capulet.\n"
"\n" "\n"
" Peter, servant to Juliet's nurse.\n" " Peter, servant to Juliet's nurse.\n"
"\n" "\n"
" An Apothecary.\n" " An Apothecary.\n"
"\n" "\n"
" Three Musicians.\n" " Three Musicians.\n"
"\n" "\n"
" An Officer.\n" " An Officer.\n"
"\n" "\n"
"\n" "\n"
" Lady Montague, wife to Montague.\n" " Lady Montague, wife to Montague.\n"
"\n" "\n"
" Lady Capulet, wife to Capulet.\n" " Lady Capulet, wife to Capulet.\n"
"\n" "\n"
" Juliet, daughter to Capulet.\n" " Juliet, daughter to Capulet.\n"
"\n" "\n"
" Nurse to Juliet.\n" " Nurse to Juliet.\n"
"\n" "\n"
"\n" "\n"
" Citizens of Verona; Gentlemen and Gentlewomen of both houses;\n" " Citizens of Verona; Gentlemen and Gentlewomen of both houses;\n"
" Maskers, Torchbearers, Pages, Guards, Watchmen, Servants, and\n" " Maskers, Torchbearers, Pages, Guards, Watchmen, Servants, and\n"
" Attendants.\n" " Attendants.\n"
"\n" "\n"
" SCENE.--Verona; Mantua.\n" " SCENE.--Verona; Mantua.\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
" THE PROLOGUE\n" " THE PROLOGUE\n"
"\n" "\n"
" Enter Chorus.\n" " Enter Chorus.\n"
"\n" "\n"
"\n" "\n"
" Chor. Two households, both alike in dignity,\n" " Chor. Two households, both alike in dignity,\n"
" In fair Verona, where we lay our scene,\n" " In fair Verona, where we lay our scene,\n"
" From ancient grudge break to new mutiny,\n" " From ancient grudge break to new mutiny,\n"
" Where civil blood makes civil hands unclean.\n" " Where civil blood makes civil hands unclean.\n"
" From forth the fatal loins of these two foes\n" " From forth the fatal loins of these two foes\n"
" A pair of star-cross'd lovers take their life;\n" " A pair of star-cross'd lovers take their life;\n"
" Whose misadventur'd piteous overthrows\n" " Whose misadventur'd piteous overthrows\n"
" Doth with their death bury their parents' strife.\n" " Doth with their death bury their parents' strife.\n"
" The fearful passage of their death-mark'd love,\n" " The fearful passage of their death-mark'd love,\n"
" And the continuance of their parents' rage,\n" " And the continuance of their parents' rage,\n"
" Which, but their children's end, naught could remove,\n" " Which, but their children's end, naught could remove,\n"
" Is now the two hours' traffic of our stage;\n" " Is now the two hours' traffic of our stage;\n"
" The which if you with patient ears attend,\n" " The which if you with patient ears attend,\n"
" What here shall miss, our toil shall strive to mend.\n" " What here shall miss, our toil shall strive to mend.\n"
" [Exit.]\n" " [Exit.]\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"\n" "\n"
"ACT I. Scene I.\n" "ACT I. Scene I.\n"
"Verona. A public place.\n" "Verona. A public place.\n"
"\n" "\n"
"Enter Sampson and Gregory (with swords and bucklers) of the house\n" "Enter Sampson and Gregory (with swords and bucklers) of the house\n"
"of Capulet.\n" "of Capulet.\n"
"\n" "\n"
"\n" "\n"
" Samp. Gregory, on my word, we'll not carry coals.\n" " Samp. Gregory, on my word, we'll not carry coals.\n"
"\n" "\n"
" Greg. No, for then we should be colliers.\n" " Greg. No, for then we should be colliers.\n"
"\n" "\n"
" Samp. I mean, an we be in choler, we'll draw.\n" " Samp. I mean, an we be in choler, we'll draw.\n"
"\n" "\n"
" Greg. Ay, while you live, draw your neck out of collar.\n" " Greg. Ay, while you live, draw your neck out of collar.\n"
"\n" "\n"
" Samp. I strike quickly, being moved.\n" " Samp. I strike quickly, being moved.\n"
"\n" "\n"
" Greg. But thou art not quickly moved to strike.\n" " Greg. But thou art not quickly moved to strike.\n"
"\n" "\n"
" Samp. A dog of the house of Montague moves me.\n" " Samp. A dog of the house of Montague moves me.\n"
"\n" "\n"
" Greg. To move is to stir, and to be valiant is to stand.\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" " Therefore, if thou art moved, thou runn'st away.\n"
"\n" "\n"
" Samp. A dog of that house shall move me to stand. I will take\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" " the wall of any man or maid of Montague's.\n"
"\n" "\n"
" Greg. That shows thee a weak slave; for the weakest goes to the\n" " Greg. That shows thee a weak slave; for the weakest goes to the\n"
" wall.\n" " wall.\n"
"\n" "\n"
" Samp. 'Tis true; and therefore women, being the weaker vessels,\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" " 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" " from the wall and thrust his maids to the wall.\n"
"\n" "\n"
" Greg. The quarrel is between our masters and us their men.\n" " Greg. The quarrel is between our masters and us their men.\n"
"\n" "\n"
" Samp. 'Tis all one. I will show myself a tyrant. When I have\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" " fought with the men, I will be cruel with the maids- I will cut off\n"
" their heads.\n" " their heads.\n"
"\n" "\n"
" Greg. The heads of the maids?\n" " Greg. The heads of the maids?\n"
"\n" "\n"
" Samp. Ay, the heads of the maids, or their maidenheads.\n" " Samp. Ay, the heads of the maids, or their maidenheads.\n"
" Take it in what sense thou wilt.\n" " Take it in what sense thou wilt.\n"
"\n" "\n"
" Greg. They must take it in sense that feel it.\n" " Greg. They must take it in sense that feel it.\n"
"\n" "\n"
" Samp. Me they shall feel while I am able to stand; and 'tis known I\n" " Samp. Me they shall feel while I am able to stand; and 'tis known I\n"
" am a pretty piece of flesh.\n" " am a pretty piece of flesh.\n"
"\n" "\n"
" Greg. 'Tis well thou art not fish; if thou hadst, thou hadst\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" " been poor-John. Draw thy tool! Here comes two of the house of\n"
" Montagues.\n" " Montagues.\n"
"\n" "\n"
" Enter two other Servingmen [Abram and Balthasar].\n" " Enter two other Servingmen [Abram and Balthasar].\n"
"\n" "\n"
"\n" "\n"
" Samp. My naked weapon is out. Quarrel! I will back thee.\n" " Samp. My naked weapon is out. Quarrel! I will back thee.\n"
"\n" "\n"
" Greg. How? turn thy back and run?\n" " Greg. How? turn thy back and run?\n"
"\n" "\n"
" Samp. Fear me not.\n" " Samp. Fear me not.\n"
"\n" "\n"
" Greg. No, marry. I fear thee!\n" " Greg. No, marry. I fear thee!\n"
"\n" "\n"
" Samp. Let us take the law of our sides; let them begin.\n" " Samp. Let us take the law of our sides; let them begin.\n"
"\n" "\n"
" Greg. I will frown as I pass by, and let them take it as they list.\n" " Greg. I will frown as I pass by, and let them take it as they list.\n"
"\n" "\n"
" Samp. Nay, as they dare. I will bite my thumb at them; which is\n" " Samp. Nay, as they dare. I will bite my thumb at them; which is\n"
" disgrace to them, if they bear it.\n" " disgrace to them, if they bear it.\n"
"\n" "\n"
" Abr. Do you bite your thumb at us, sir?\n" " Abr. Do you bite your thumb at us, sir?\n"
"\n" "\n"
" Samp. I do bite my thumb, sir.\n" " Samp. I do bite my thumb, sir.\n"
"\n" "\n"
" Abr. Do you bite your thumb at us, sir?\n" " Abr. Do you bite your thumb at us, sir?\n"
"\n" "\n"
" Samp. [aside to Gregory] Is the law of our side if I say ay?\n" " Samp. [aside to Gregory] Is the law of our side if I say ay?\n"
"\n" "\n"
" Greg. [aside to Sampson] No.\n" " Greg. [aside to Sampson] No.\n"
"\n" "\n"
" Samp. No, sir, I do not bite my thumb at you, sir; but I bite my\n" " Samp. No, sir, I do not bite my thumb at you, sir; but I bite my\n"
" thumb, sir.\n" " thumb, sir.\n"
"\n" "\n"
" Greg. Do you quarrel, sir?\n" " Greg. Do you quarrel, sir?\n"
"\n" "\n"
" Abr. Quarrel, sir? No, sir.\n" " Abr. Quarrel, sir? No, sir.\n"
"\n" "\n"
" Samp. But if you do, sir, am for you. I serve as good a man as\n" " Samp. But if you do, sir, am for you. I serve as good a man as\n"
" you.\n" " you.\n"
"\n" "\n"
" Abr. No better.\n" " Abr. No better.\n"
"\n" "\n"
" Samp. Well, sir.\n" " Samp. Well, sir.\n"
"\n" "\n"
" Enter Benvolio.\n" " Enter Benvolio.\n"
"\n" "\n"
"\n" "\n"
" Greg. [aside to Sampson] Say 'better.' Here comes one of my\n" " Greg. [aside to Sampson] Say 'better.' Here comes one of my\n"
" master's kinsmen.\n" " master's kinsmen.\n"
"\n" "\n"
" Samp. Yes, better, sir.\n" " Samp. Yes, better, sir.\n"
"\n" "\n"
" Abr. You lie.\n" " Abr. You lie.\n"
"\n" "\n"
" Samp. Draw, if you be men. Gregory, remember thy swashing blow.\n" " Samp. Draw, if you be men. Gregory, remember thy swashing blow.\n"
" They fight.\n" " They fight.\n"
"\n" "\n"
" Ben. Part, fools! [Beats down their swords.]\n" " Ben. Part, fools! [Beats down their swords.]\n"
" Put up your swords. You know not what you do.\n" " Put up your swords. You know not what you do.\n"
"\n" "\n"
" Enter Tybalt.\n" " Enter Tybalt.\n"
"\n" "\n"
"\n" "\n"
" Tyb. What, art thou drawn among these heartless hinds?\n" " Tyb. What, art thou drawn among these heartless hinds?\n"
" Turn thee Benvolio! look upon thy death.\n" " Turn thee Benvolio! look upon thy death.\n"
"\n" "\n"
" Ben. I do but keep the peace. Put up thy sword,\n" " Ben. I do but keep the peace. Put up thy sword,\n"
" Or manage it to part these men with me.\n" " Or manage it to part these men with me.\n"
"\n" "\n"
" Tyb. What, drawn, and talk of peace? I hate the word\n" " Tyb. What, drawn, and talk of peace? I hate the word\n"
" As I hate hell, all Montagues, and thee.\n" " As I hate hell, all Montagues, and thee.\n"
" Have at thee, coward! They fight.\n" " Have at thee, coward! They fight.\n"
"\n" "\n"
" Enter an officer, and three or four Citizens with clubs or\n" " Enter an officer, and three or four Citizens with clubs or\n"
" partisans.\n" " partisans.\n"
"\n" "\n"
"\n" "\n"
" Officer. Clubs, bills, and partisans! Strike! beat them down!\n" " Officer. Clubs, bills, and partisans! Strike! beat them down!\n"
"\n" "\n"
" Citizens. Down with the Capulets! Down with the Montagues!\n" " Citizens. Down with the Capulets! Down with the Montagues!\n"
"\n" "\n"
" Enter Old Capulet in his gown, and his Wife.\n" " Enter Old Capulet in his gown, and his Wife.\n"
"\n" "\n"
"\n" "\n"
" Cap. What noise is this? Give me my long sword, ho!\n" " Cap. What noise is this? Give me my long sword, ho!\n"
"\n" "\n"
" Wife. A crutch, a crutch! Why call you for a sword?\n" " Wife. A crutch, a crutch! Why call you for a sword?\n"
"\n" "\n"
" Cap. My sword, I say! Old Montague is come\n" " Cap. My sword, I say! Old Montague is come\n"
" And flourishes his blade in spite of me.\n" " And flourishes his blade in spite of me.\n"
"\n" "\n"
" Enter Old Montague and his Wife.\n" " Enter Old Montague and his Wife.\n"
"\n" "\n"
"\n" "\n"
" Mon. Thou villain Capulet!- Hold me not, let me go.\n" " Mon. Thou villain Capulet!- Hold me not, let me go.\n"
"\n" "\n"
" M. Wife. Thou shalt not stir one foot to seek a foe.\n" " M. Wife. Thou shalt not stir one foot to seek a foe.\n"
"\n" "\n"
" Enter Prince Escalus, with his Train.\n" " Enter Prince Escalus, with his Train.\n"
"\n" "\n"
"\n" "\n"
"END OF FILE\n"; "END OF FILE\n";

@ -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

@ -3,29 +3,30 @@
#include "../../TinyFrame.h" #include "../../TinyFrame.h"
#include "../utils.h" #include "../utils.h"
TinyFrame *demo_tf;
/** /**
* This function should be defined in the application code. * This function should be defined in the application code.
* It implements the lowest layer - sending bytes to UART (or other) * It implements the lowest layer - sending bytes to UART (or other)
*/ */
void TF_WriteImpl(const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len)
{ {
printf("--------------------\n"); printf("--------------------\n");
printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n");
dumpFrame(buff, len); dumpFrame(buff, len);
// Send it back as if we received it // Send it back as if we received it
TF_Accept(buff, len); TF_Accept(tf, buff, len);
} }
/** An example listener function */ /** An example listener function */
TF_Result myListener(TF_Msg *msg) TF_Result myListener(TinyFrame *tf, TF_Msg *msg)
{ {
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("OK - ID Listener triggered for msg!\n"); printf("OK - ID Listener triggered for msg!\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@ -38,28 +39,28 @@ void main(void)
const char *longstr = "Lorem ipsum dolor sit amet."; const char *longstr = "Lorem ipsum dolor sit amet.";
// Set up the TinyFrame library // Set up the TinyFrame library
TF_Init(TF_MASTER); // 1 = master, 0 = slave demo_tf = TF_Init(TF_MASTER); // 1 = master, 0 = slave
TF_AddGenericListener(myListener); TF_AddGenericListener(demo_tf, myListener);
printf("------ Simulate sending a message --------\n"); printf("------ Simulate sending a message --------\n");
TF_ClearMsg(&msg); TF_ClearMsg(&msg);
msg.type = 0x22; msg.type = 0x22;
msg.data = (pu8)"Hello TinyFrame"; msg.data = (pu8) "Hello TinyFrame";
msg.len = 16; msg.len = 16;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x33; msg.type = 0x33;
msg.data = (pu8)longstr; msg.data = (pu8) longstr;
msg.len = (TF_LEN) (strlen(longstr)+1); // add the null byte msg.len = (TF_LEN) (strlen(longstr) + 1); // add the null byte
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.type = 0x44; msg.type = 0x44;
msg.data = (pu8)"Hello2"; msg.data = (pu8) "Hello2";
msg.len = 7; msg.len = 7;
TF_Send(&msg); TF_Send(demo_tf, &msg);
msg.len = 0; msg.len = 0;
msg.type = 0x77; msg.type = 0x77;
TF_Query(&msg, testIdListener, 0); 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

@ -5,14 +5,14 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
TF_Result testIdListener(TF_Msg *msg) TF_Result testIdListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testIdListener()\n"); printf("testIdListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_CLOSE; return TF_CLOSE;
} }
TF_Result testGenericListener(TF_Msg *msg) TF_Result testGenericListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("testGenericListener()\n"); printf("testGenericListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
@ -21,15 +21,15 @@ TF_Result testGenericListener(TF_Msg *msg)
int main(void) int main(void)
{ {
TF_Init(TF_MASTER); demo_tf = TF_Init(TF_MASTER);
TF_AddGenericListener(testGenericListener); TF_AddGenericListener(demo_tf, testGenericListener);
demo_init(TF_MASTER); demo_init(TF_MASTER);
TF_SendSimple(1, (pu8)"Ahoj", 5); TF_SendSimple(demo_tf, 1, (pu8) "Ahoj", 5);
TF_SendSimple(1, (pu8)"Hello", 6); TF_SendSimple(demo_tf, 1, (pu8) "Hello", 6);
TF_QuerySimple(2, (pu8)"Query!", 6, testIdListener, 0, NULL); TF_QuerySimple(demo_tf, 2, (pu8) "Query!", 6, testIdListener, 0);
demo_sleep(); demo_sleep();
} }

@ -4,38 +4,37 @@
#include <stdio.h> #include <stdio.h>
#include "../demo.h" #include "../demo.h"
#include <memory.h>
TF_Result helloListener(TF_Msg *msg) TF_Result helloListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("helloListener()\n"); printf("helloListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
return TF_STAY; return TF_STAY;
} }
TF_Result replyListener(TF_Msg *msg) TF_Result replyListener(TinyFrame *tf, TF_Msg *msg)
{ {
printf("replyListener()\n"); printf("replyListener()\n");
dumpFrameInfo(msg); dumpFrameInfo(msg);
msg->data = (const uint8_t *) "response to query"; msg->data = (const uint8_t *) "response to query";
msg->len = (TF_LEN) strlen((const char *) msg->data); msg->len = (TF_LEN) strlen((const char *) msg->data);
TF_Respond(msg); TF_Respond(tf, msg);
// unsolicited reply - will not be handled by the ID listener, which is already gone // unsolicited reply - will not be handled by the ID listener, which is already gone
msg->data = (const uint8_t *) "SPAM"; msg->data = (const uint8_t *) "SPAM";
msg->len = 5; msg->len = 5;
TF_Respond(msg); TF_Respond(tf, msg);
// unrelated message // unrelated message
TF_SendSimple(77, (const uint8_t *) "NAZDAR", 7); TF_SendSimple(tf, 77, (const uint8_t *) "NAZDAR", 7);
return TF_STAY; return TF_STAY;
} }
int main(void) int main(void)
{ {
TF_Init(TF_SLAVE); demo_tf = TF_Init(TF_SLAVE);
TF_AddTypeListener(1, helloListener); TF_AddTypeListener(demo_tf, 1, helloListener);
TF_AddTypeListener(2, replyListener); TF_AddTypeListener(demo_tf, 2, replyListener);
demo_init(TF_SLAVE); demo_init(TF_SLAVE);
demo_sleep(); demo_sleep();

@ -9,11 +9,12 @@
void dumpFrame(const uint8_t *buff, size_t len) void dumpFrame(const uint8_t *buff, size_t len)
{ {
size_t i; size_t i;
for(i = 0; i < len; i++) { for (i = 0; i < len; i++) {
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");

Loading…
Cancel
Save