Replace size_t with uint32_t, add default mutex impl, preparing internals for multipart Tx

pull/14/head
Ondřej Hruška 6 years ago
parent e24296598e
commit 137797cc7d
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 31
      TF_Integration.example.c
  2. 134
      TinyFrame.c
  3. 23
      TinyFrame.h
  4. 24
      demo/simple/test.c

@ -10,15 +10,20 @@
* listener timeout feature. * 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 // 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 */ /** Claim the TX interface before composing and sending a frame */
void TF_ClaimTx(TinyFrame *tf) bool TF_ClaimTx(TinyFrame *tf)
{ {
// take mutex // take mutex
return true; // we succeeded
} }
/** Free the TX interface after composing and sending a frame */ /** Free the TX interface after composing and sending a frame */
@ -26,3 +31,25 @@ void TF_ReleaseTx(TinyFrame *tf)
{ {
// release mutex // 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;
}

@ -14,16 +14,33 @@
#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))
#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 // 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 // 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 // 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 */ /** 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 */ /** 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 #endif
//region Checksums //region Checksums
@ -190,9 +207,12 @@
//endregion //endregion
/** Init with a user-allocated buffer */ /** 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 // Zero it out, keeping user config
uint32_t usertag = tf->usertag; uint32_t usertag = tf->usertag;
@ -204,12 +224,18 @@ void _TF_FN TF_InitStatic(TinyFrame *tf, TF_Peer peer_bit)
tf->userdata = userdata; tf->userdata = userdata;
tf->peer_bit = peer_bit; tf->peer_bit = peer_bit;
return true;
} }
/** Init with malloc */ /** Init with malloc */
TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit) TinyFrame * _TF_FN TF_Init(TF_Peer peer_bit)
{ {
TinyFrame *tf = malloc(sizeof(TinyFrame)); TinyFrame *tf = malloc(sizeof(TinyFrame));
if (!tf) {
TF_Error("TF_Init() failed, out of memory.");
return NULL;
}
TF_InitStatic(tf, peer_bit); TF_InitStatic(tf, peer_bit);
return tf; return tf;
} }
@ -492,9 +518,9 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf)
//endregion Listeners //endregion Listeners
/** Handle a received byte buffer */ /** 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++) { for (i = 0; i < count; i++) {
TF_AcceptChar(tf, buffer[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 * @param msg - message written to the buffer
* @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(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 int8_t si = 0; // signed small int
uint8_t b = 0; uint8_t b = 0;
TF_ID id = 0; TF_ID id = 0;
TF_CKSUM cksum = 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; (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);) * @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, static uint32_t _TF_FN TF_ComposeBody(uint8_t *outbuff,
const uint8_t *data, TF_LEN data_len, const uint8_t *data, TF_LEN data_len,
TF_CKSUM *cksum) TF_CKSUM *cksum)
{ {
TF_LEN i = 0; TF_LEN i = 0;
uint8_t b = 0; uint8_t b = 0;
size_t pos = 0; uint32_t pos = 0;
for (i = 0; i < data_len; i++) { for (i = 0; i < data_len; i++) {
b = data[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 * @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 uint32_t _TF_FN TF_ComposeTail(uint8_t *outbuff, 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;
size_t pos = 0; uint32_t pos = 0;
#if TF_CKSUM_TYPE != TF_CKSUM_NONE #if TF_CKSUM_TYPE != TF_CKSUM_NONE
CKSUM_FINALIZE(*cksum); 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 tf - instance
* @param msg - message object * @param msg - message to send
* @param listener - ID listener, or NULL * @param listener - response listener or NULL
* @param timeout - listener timeout, 0 is none * @param timeout - listener timeout ticks, 0 = indefinite
* @return true if sent * @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; TF_TRY(TF_ClaimTx(tf));
size_t remain = 0;
size_t sent = 0; tf->tx_pos = (uint32_t) TF_ComposeHead(tf, tf->sendbuf, msg); // frame ID is incremented here if it's not a response
TF_CKSUM cksum = 0; 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); CKSUM_RESET(tf->tx_cksum);
if (listener) TF_AddIdListener(tf, msg, listener, timeout); 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) { while (remain > 0) {
size_t chunk = TF_MIN(TF_SENDBUF_LEN - len, remain); // Write what can fit in the tx buffer
len += TF_ComposeBody(tf->sendbuf+len, msg->data+sent, (TF_LEN) chunk, &cksum); 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; remain -= chunk;
sent += chunk; sent += chunk;
// Flush if the buffer is full and we have more to send // Flush if the buffer is full
if (remain > 0 && len == TF_SENDBUF_LEN) { if (tf->tx_pos == TF_SENDBUF_LEN) {
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
len = 0; tf->tx_pos = 0;
} }
} }
}
static void _TF_FN TF_SendFrame_End(TinyFrame *tf)
{
// Checksum only if message had a body // 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 // Flush if checksum wouldn't fit in the buffer
if (TF_SENDBUF_LEN - len < sizeof(TF_CKSUM)) { if (TF_SENDBUF_LEN - tf->tx_pos < sizeof(TF_CKSUM)) {
TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, len); TF_WriteImpl(tf, (const uint8_t *) tf->sendbuf, tf->tx_pos);
len = 0; tf->tx_pos = 0;
} }
// Add checksum, flush what remains to be sent // 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); 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; return true;
} }
@ -923,7 +975,7 @@ bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id)
/** Timebase hook - for timeouts */ /** Timebase hook - for timeouts */
void _TF_FN TF_Tick(TinyFrame *tf) void _TF_FN TF_Tick(TinyFrame *tf)
{ {
TF_COUNT i = 0; TF_COUNT i;
struct TF_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)

@ -193,6 +193,15 @@ struct TinyFrame_ {
TF_TYPE type; //!< Collected message type number TF_TYPE type; //!< Collected message type number
bool discard_data; //!< Set if (len > TF_MAX_PAYLOAD) to read the frame, but ignore the data. 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 --- */ /* --- Callbacks --- */
/* Transaction callbacks */ /* Transaction callbacks */
@ -226,6 +235,7 @@ struct TinyFrame_ {
* the instance. * the instance.
* *
* @param peer_bit - peer bit to use for self * @param peer_bit - peer bit to use for self
* @return TF instance or NULL
*/ */
TinyFrame *TF_Init(TF_Peer peer_bit); 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. * The .userdata / .usertag field is preserved when TF_InitStatic is called.
* *
* @param peer_bit - peer bit to use for self * @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 * 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 buffer - byte buffer to process
* @param count - nr of bytes in the buffer * @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 * Accept a single incoming byte
@ -364,11 +375,11 @@ void TF_AcceptChar(TinyFrame *tf, uint8_t c);
/** /**
* This function should be called periodically. * This function should be called periodically.
*
* The time base is used to time-out partial frames in the parser and * The time base is used to time-out partial frames in the parser and
* automatically reset it. * 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); void TF_Tick(TinyFrame *tf);
@ -379,13 +390,13 @@ void TF_Tick(TinyFrame *tf);
* *
* ! Implement this in your application code ! * ! 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 // Mutex functions
#if TF_USE_MUTEX #if TF_USE_MUTEX
/** Claim the TX interface before composing and sending a frame */ /** 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 */ /** Free the TX interface after composing and sending a frame */
extern void TF_ReleaseTx(TinyFrame *tf); extern void TF_ReleaseTx(TinyFrame *tf);

@ -5,18 +5,27 @@
TinyFrame *demo_tf; TinyFrame *demo_tf;
bool do_corrupt = false;
/** /**
* 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(TinyFrame *tf, const uint8_t *buff, size_t len) void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_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);
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 // Send it back as if we received it
TF_Accept(tf, buff, len); TF_Accept(tf, xbuff, len);
} }
/** An example listener function */ /** An example listener function */
@ -63,4 +72,13 @@ void main(void)
msg.len = 0; msg.len = 0;
msg.type = 0x77; msg.type = 0x77;
TF_Query(demo_tf, &msg, testIdListener, 0); 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);
} }

Loading…
Cancel
Save