From 137797cc7d97b123e844b1177f4cdc99b21b02e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Fri, 26 Jan 2018 09:49:04 +0100 Subject: [PATCH] Replace size_t with uint32_t, add default mutex impl, preparing internals for multipart Tx --- TF_Integration.example.c | 31 ++++++++- TinyFrame.c | 134 +++++++++++++++++++++++++++------------ TinyFrame.h | 23 +++++-- demo/simple/test.c | 24 ++++++- 4 files changed, 160 insertions(+), 52 deletions(-) diff --git a/TF_Integration.example.c b/TF_Integration.example.c index 4c5934b..1e1b357 100644 --- a/TF_Integration.example.c +++ b/TF_Integration.example.c @@ -10,15 +10,20 @@ * listener timeout feature. */ -void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len) +void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len) { // send to UART } +// --------- Mutex callbacks ---------- +// Needed only if TF_USE_MUTEX is 1 in the config file. +// DELETE if mutex is not used + /** Claim the TX interface before composing and sending a frame */ -void TF_ClaimTx(TinyFrame *tf) +bool TF_ClaimTx(TinyFrame *tf) { // take mutex + return true; // we succeeded } /** Free the TX interface after composing and sending a frame */ @@ -26,3 +31,25 @@ void TF_ReleaseTx(TinyFrame *tf) { // release mutex } + +// --------- Custom checksums --------- +// This should be defined here only if a custom checksum type is used. +// DELETE those if you use one of the built-in checksum types + +/** Initialize a checksum */ +TF_CKSUM TF_CksumStart(void) +{ + return 0; +} + +/** Update a checksum with a byte */ +TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) +{ + return cksum ^ byte; +} + +/** Finalize the checksum calculation */ +TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) +{ + return cksum; +} diff --git a/TinyFrame.c b/TinyFrame.c index 0307925..922d189 100644 --- a/TinyFrame.c +++ b/TinyFrame.c @@ -14,16 +14,33 @@ #define TF_MAX(a, b) ((a)>(b)?(a):(b)) #define TF_MIN(a, b) ((a)<(b)?(a):(b)) +#define TF_TRY(func) do { if(!(func)) return false; } while (0) + // TODO It would be nice to have per-instance configurable checksum types, but that would // mandate configurable field sizes unless we use u32 everywhere (and possibly shorten // it when encoding to the buffer). I don't really like this idea so much. -MP -#if TF_USE_MUTEX==0 +#if !TF_USE_MUTEX + // Not thread safe lock implementation, used if user did not provide a better one. + // This is less reliable than a real mutex, but will catch most bugs caused by + // inappropriate use fo the API. + /** Claim the TX interface before composing and sending a frame */ - static inline void TF_ClaimTx(TinyFrame *tf) { (void)tf; } + static bool TF_ClaimTx(TinyFrame *tf) { + if (tf->soft_lock) { + TF_Error("TF already locked for tx!"); + return false; + } + + tf->soft_lock = true; + return true; + } /** Free the TX interface after composing and sending a frame */ - static inline void TF_ReleaseTx(TinyFrame *tf) { (void)tf; } + static void TF_ReleaseTx(TinyFrame *tf) + { + tf->soft_lock = false; + } #endif //region Checksums @@ -190,9 +207,12 @@ //endregion /** Init with a user-allocated buffer */ -void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit) +bool _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit) { - if (tf == NULL) return; + if (tf == NULL) { + TF_Error("TF_InitStatic() failed, tf is null."); + return false; + } // Zero it out, keeping user config uint32_t usertag = tf->usertag; @@ -204,12 +224,18 @@ void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit) tf->userdata = userdata; tf->peer_bit = peer_bit; + return true; } /** Init with malloc */ TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit) { TinyFrame *tf = malloc(sizeof(TinyFrame)); + if (!tf) { + TF_Error("TF_Init() failed, out of memory."); + return NULL; + } + TF_InitStatic(tf, peer_bit); return tf; } @@ -492,9 +518,9 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf) //endregion Listeners /** Handle a received byte buffer */ -void _TF_FN TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count) +void _TF_FN TF_Accept(TinyFrame *tf, const uint8_t *buffer, uint32_t count) { - size_t i; + uint32_t i; for (i = 0; i < count; i++) { TF_AcceptChar(tf, buffer[i]); } @@ -714,13 +740,13 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c) * @param msg - message written to the buffer * @return nr of bytes in outbuff used by the frame, 0 on failure */ -static inline size_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_Msg *msg) +static inline uint32_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_Msg *msg) { int8_t si = 0; // signed small int uint8_t b = 0; TF_ID id = 0; TF_CKSUM cksum = 0; - size_t pos = 0; // can be needed to grow larger than TF_LEN + uint32_t pos = 0; // can be needed to grow larger than TF_LEN (void)cksum; @@ -769,13 +795,13 @@ static inline size_t _TF_FN TF_ComposeHead(TinyFrame *tf, uint8_t *outbuff, TF_M * @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 */ -static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, +static uint32_t _TF_FN TF_ComposeBody(uint8_t *outbuff, const uint8_t *data, TF_LEN data_len, TF_CKSUM *cksum) { TF_LEN i = 0; uint8_t b = 0; - size_t pos = 0; + uint32_t pos = 0; for (i = 0; i < data_len; i++) { b = data[i]; @@ -793,11 +819,11 @@ static size_t _TF_FN TF_ComposeBody(uint8_t *outbuff, * @param cksum - checksum variable used for the body * @return nr of bytes in outbuff used */ -static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) +static uint32_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) { int8_t si = 0; // signed small int uint8_t b = 0; - size_t pos = 0; + uint32_t pos = 0; #if TF_CKSUM_TYPE != TF_CKSUM_NONE CKSUM_FINALIZE(*cksum); @@ -807,57 +833,83 @@ static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) } /** - * Send a message + * Begin building a frame * * @param tf - instance - * @param msg - message object - * @param listener - ID listener, or NULL - * @param timeout - listener timeout, 0 is none - * @return true if sent + * @param msg - message to send + * @param listener - response listener or NULL + * @param timeout - listener timeout ticks, 0 = indefinite + * @return success (mutex claimed and listener added, if any) */ -static bool _TF_FN TF_SendFrame(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) +static bool _TF_FN TF_SendFrame_Begin(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) { - size_t len = 0; - size_t remain = 0; - size_t sent = 0; - TF_CKSUM cksum = 0; + TF_TRY(TF_ClaimTx(tf)); + + tf->tx_pos = (uint32_t) TF_ComposeHead(tf, tf->sendbuf, msg); // frame ID is incremented here if it's not a response + tf->tx_len = msg->len; - TF_ClaimTx(tf); + if (listener) { + TF_TRY(TF_AddIdListener(tf, msg, listener, timeout)); + } - len = TF_ComposeHead(tf, tf->sendbuf, msg); - if (listener) TF_AddIdListener(tf, msg, listener, timeout); + CKSUM_RESET(tf->tx_cksum); + return true; +} - CKSUM_RESET(cksum); +static void _TF_FN TF_SendFrame_Chunk(TinyFrame *tf, const uint8_t *buff, uint32_t length) +{ + uint32_t remain; + uint32_t chunk; + uint32_t sent = 0; - remain = msg->len; + remain = length; while (remain > 0) { - size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain); - len += TF_ComposeBody(tf->sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum); + // Write what can fit in the tx buffer + chunk = TF_MIN(TF_SENDBUF_LEN - tf->tx_pos, remain); + tf->tx_pos += TF_ComposeBody(tf->sendbuf+tf->tx_pos, buff+sent, (TF_LEN) chunk, &tf->tx_cksum); remain -= chunk; sent += chunk; - // Flush if the buffer is full and we have more to send - if (remain > 0 && len == TF_SENDBUF_LEN) { - TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); - len = 0; + // Flush if the buffer is full + if (tf->tx_pos == TF_SENDBUF_LEN) { + TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos); + tf->tx_pos = 0; } } +} +static void _TF_FN TF_SendFrame_End(TinyFrame *tf) +{ // Checksum only if message had a body - if (msg->len > 0) { + if (tf->tx_len > 0) { // Flush if checksum wouldn't fit in the buffer - if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) { - TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); - len = 0; + if (TF_SENDBUF_LEN - tf->tx_pos < sizeof(TF_CKSUM)) { + TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos); + tf->tx_pos = 0; } // Add checksum, flush what remains to be sent - len += TF_ComposeTail(tf->sendbuf + len, &cksum); + tf->tx_pos += TF_ComposeTail(tf->sendbuf + tf->tx_pos, &tf->tx_cksum); } - TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); + TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos); TF_ReleaseTx(tf); +} +/** + * 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) +{ + TF_TRY(TF_SendFrame_Begin(tf, msg, listener, timeout)); + TF_SendFrame_Chunk(tf, msg->data, msg->len); + TF_SendFrame_End(tf); return true; } @@ -923,7 +975,7 @@ bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id) /** Timebase hook - for timeouts */ void _TF_FN TF_Tick(TinyFrame *tf) { - TF_COUNT i = 0; + TF_COUNT i; struct TF_IdListener_ *lst; // increment parser timeout (timeout is handled when receiving next byte) diff --git a/TinyFrame.h b/TinyFrame.h index 11eb083..ad6507d 100644 --- a/TinyFrame.h +++ b/TinyFrame.h @@ -193,6 +193,15 @@ struct TinyFrame_ { TF_TYPE type; //!< Collected message type number bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data. + /* Tx state */ + uint32_t tx_pos; + uint32_t tx_len; + TF_CKSUM tx_cksum; + +#if !TF_USE_MUTEX + bool soft_lock; //!< Lock used if the mutex feature is not enabled. +#endif + /* --- Callbacks --- */ /* Transaction callbacks */ @@ -226,6 +235,7 @@ struct TinyFrame_ { * the instance. * * @param peer_bit - peer bit to use for self + * @return TF instance or NULL */ TinyFrame *TF_Init(TF_Peer peer_bit); @@ -236,8 +246,9 @@ TinyFrame *TF_Init(TF_Peer peer_bit); * The .userdata / .usertag field is preserved when TF_InitStatic is called. * * @param peer_bit - peer bit to use for self + * @return success */ -void TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit); +bool TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit); /** * De-init the dynamically allocated TF instance @@ -353,7 +364,7 @@ bool TF_RenewIdListener(TinyFrame *tf, TF_ID id); * @param buffer - byte buffer to process * @param count - nr of bytes in the buffer */ -void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count); +void TF_Accept(TinyFrame *tf, const uint8_t *buffer, uint32_t count); /** * Accept a single incoming byte @@ -364,11 +375,11 @@ void TF_AcceptChar(TinyFrame *tf, uint8_t c); /** * This function should be called periodically. - * * The time base is used to time-out partial frames in the parser and * automatically reset it. + * It's also used to expire ID listeners if a timeout is set when registering them. * - * (suggestion - call this in a SysTick handler) + * A common place to call this from is the SysTick handler. */ void TF_Tick(TinyFrame *tf); @@ -379,13 +390,13 @@ void TF_Tick(TinyFrame *tf); * * ! Implement this in your application code ! */ -extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len); +extern void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len); // Mutex functions #if TF_USE_MUTEX /** Claim the TX interface before composing and sending a frame */ - extern void TF_ClaimTx(TinyFrame *tf); + extern bool TF_ClaimTx(TinyFrame *tf); /** Free the TX interface after composing and sending a frame */ extern void TF_ReleaseTx(TinyFrame *tf); diff --git a/demo/simple/test.c b/demo/simple/test.c index ca07b83..ba2798e 100644 --- a/demo/simple/test.c +++ b/demo/simple/test.c @@ -5,18 +5,27 @@ TinyFrame *demo_tf; +bool do_corrupt = false; + /** * 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) +void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len) { printf("--------------------\n"); printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); - dumpFrame(buff, len); + + uint8_t *xbuff = (uint8_t *)buff; + if (do_corrupt) { + printf("(corrupting to test checksum checking...)\n"); + xbuff[8]++; + } + + dumpFrame(xbuff, len); // Send it back as if we received it - TF_Accept(tf, buff, len); + TF_Accept(tf, xbuff, len); } /** An example listener function */ @@ -63,4 +72,13 @@ void main(void) msg.len = 0; msg.type = 0x77; TF_Query(demo_tf, &msg, testIdListener, 0); + + printf("This should fail:\n"); + + // test checksums are tested + do_corrupt = true; + msg.type = 0x44; + msg.data = (pu8) "Hello2"; + msg.len = 7; + TF_Send(demo_tf, &msg); }