diff --git a/TinyFrame/TF_Config.h b/TinyFrame/TF_Config.h index ce86235..837b742 100644 --- a/TinyFrame/TF_Config.h +++ b/TinyFrame/TF_Config.h @@ -7,48 +7,18 @@ #include "platform.h" #include -//#include // when using with esphttpd -//----------------------------- FRAME FORMAT --------------------------------- -// The format can be adjusted to fit your particular application needs - -// If the connection is reliable, you can disable the SOF byte and checksums. -// That can save up to 9 bytes of overhead. - -// ,-----+----+-----+------+------------+- - - -+------------, -// | SOF | ID | LEN | TYPE | HEAD_CKSUM | DATA | PLD_CKSUM | -// | 1 | ? | ? | ? | ? | ... | ? | <- size (bytes) -// '-----+----+-----+------+------------+- - - -+------------' - -// !!! BOTH SIDES MUST USE THE SAME SETTINGS !!! - -// Adjust sizes as desired (1,2,4) #define TF_ID_BYTES 2 #define TF_LEN_BYTES 2 #define TF_TYPE_BYTES 1 - -// Checksum type -//#define TF_CKSUM_TYPE TF_CKSUM_NONE #define TF_CKSUM_TYPE TF_CKSUM_XOR -//#define TF_CKSUM_TYPE TF_CKSUM_CRC16 -//#define TF_CKSUM_TYPE TF_CKSUM_CRC32 - -// Use a SOF byte to mark the start of a frame #define TF_USE_SOF_BYTE 1 -// Value of the SOF byte (if TF_USE_SOF_BYTE == 1) #define TF_SOF_BYTE 0x01 - -//----------------------- PLATFORM COMPATIBILITY ---------------------------- - -// used for timeout tick counters - should be large enough for all used timeouts typedef uint16_t TF_TICKS; - -// used in loops iterating over listeners typedef uint8_t TF_COUNT; +#define TF_USE_MUTEX 1 -//----------------------------- PARAMETERS ---------------------------------- - -// buffers, counts and timeout are defined in plat_compat.h +// buffer sizes and listener counts are in plat_compat.h #define TF_Error(format, ...) dbg("[TF] " format, ##__VA_ARGS__) diff --git a/TinyFrame/TF_Integration.c b/TinyFrame/TF_Integration.c index 4f38df7..1828963 100644 --- a/TinyFrame/TF_Integration.c +++ b/TinyFrame/TF_Integration.c @@ -14,7 +14,7 @@ extern osSemaphoreId semVcomTxReadyHandle; extern osMutexId mutTinyFrameTxHandle; -void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len) +void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, uint32_t len) { (void) tf; #define CHUNK 64 // same as TF_SENDBUF_LEN, so we should always have only one run of the loop @@ -30,13 +30,15 @@ void TF_WriteImpl(TinyFrame *tf, const uint8_t *buff, size_t len) } /** Claim the TX interface before composing and sending a frame */ -void TF_ClaimTx(TinyFrame *tf) +bool TF_ClaimTx(TinyFrame *tf) { (void) tf; assert_param(osThreadGetId() != tskMainHandle); assert_param(!inIRQ()); assert_param(osOK == osMutexWait(mutTinyFrameTxHandle, 5000)); + + return true; } /** Free the TX interface after composing and sending a frame */ diff --git a/TinyFrame/TinyFrame.c b/TinyFrame/TinyFrame.c index 5d4c247..3489942 100644 --- a/TinyFrame/TinyFrame.c +++ b/TinyFrame/TinyFrame.c @@ -1,6 +1,6 @@ //--------------------------------------------------------------------------- #include "TinyFrame.h" -#include +#include // - for malloc() if dynamic constructor is used //--------------------------------------------------------------------------- // Compatibility with ESP8266 SDK @@ -10,144 +10,213 @@ #define _TF_FN #endif + // Helper macros -#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) + + +// Type-dependent masks for bit manipulation in the ID field +#define TF_ID_MASK (TF_ID)(((TF_ID)1 << (sizeof(TF_ID)*8 - 1)) - 1) +#define TF_ID_PEERBIT (TF_ID)((TF_ID)1 << ((sizeof(TF_ID)*8) - 1)) + + +#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 bool TF_ClaimTx(TinyFrame *tf) { + if (tf->soft_lock) { + TF_Error("TF already locked for tx!"); + return false; + } + + tf->soft_lock = true; + return true; + } -// 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 + /** Free the TX interface after composing and sending a frame */ + static void TF_ReleaseTx(TinyFrame *tf) + { + tf->soft_lock = false; + } +#endif //region Checksums #if TF_CKSUM_TYPE == TF_CKSUM_NONE -// NONE -#define CKSUM_RESET(cksum) -#define CKSUM_ADD(cksum, byte) -#define CKSUM_FINALIZE(cksum) + static TF_CKSUM TF_CksumStart(void) + { return 0; } + + static TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) + { return cksum; } + + static TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) + { return cksum; } #elif TF_CKSUM_TYPE == TF_CKSUM_XOR -// ~XOR -#define CKSUM_RESET(cksum) do { (cksum) = 0; } while (0) -#define CKSUM_ADD(cksum, byte) do { (cksum) ^= (byte); } while(0) -#define CKSUM_FINALIZE(cksum) do { (cksum) = (TF_CKSUM)~cksum; } while(0) + static TF_CKSUM TF_CksumStart(void) + { return 0; } + + static TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) + { return cksum ^ byte; } + + static TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) + { return (TF_CKSUM) ~cksum; } + +#elif TF_CKSUM_TYPE == TF_CKSUM_CRC8 + + static inline uint8_t crc8_bits(uint8_t data) + { + uint8_t crc = 0; + if(data & 1) crc ^= 0x5e; + if(data & 2) crc ^= 0xbc; + if(data & 4) crc ^= 0x61; + if(data & 8) crc ^= 0xc2; + if(data & 0x10) crc ^= 0x9d; + if(data & 0x20) crc ^= 0x23; + if(data & 0x40) crc ^= 0x46; + if(data & 0x80) crc ^= 0x8c; + return crc; + } + + static TF_CKSUM TF_CksumStart(void) + { return 0; } + + static TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) + { return crc8_bits(byte ^ cksum); } + + static TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) + { return cksum; } #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) */ -static const uint16_t crc16_table[256] = { - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 -}; - -static inline uint16_t crc16_byte(uint16_t cksum, const uint8_t byte) -{ - return (cksum >> 8) ^ crc16_table[(cksum ^ byte) & 0xff]; -} - -#define CKSUM_RESET(cksum) do { (cksum) = 0; } while (0) -#define CKSUM_ADD(cksum, byte) do { (cksum) = crc16_byte((cksum), (byte)); } while(0) -#define CKSUM_FINALIZE(cksum) + // TODO try to replace with an algorithm + /** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */ + static const uint16_t crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 + }; + + static TF_CKSUM TF_CksumStart(void) + { return 0; } + + static TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) + { return (cksum >> 8) ^ crc16_table[(cksum ^ byte) & 0xff]; } + + static TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) + { return cksum; } #elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 -// TODO try to replace with an algorithm -static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, - 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, - 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, - 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, - 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, - 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, - 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, - 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, - 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, - 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, - 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, - 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, - 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, - 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, - 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, - 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, - 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, - 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, - 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, - 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, - 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, - 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - -static inline uint32_t crc32_byte(uint32_t cksum, const uint8_t byte) -{ - return (crc32_table[((cksum) ^ ((uint8_t)byte)) & 0xff] ^ ((cksum) >> 8)); -} - -#define CKSUM_RESET(cksum) do { (cksum) = (TF_CKSUM)0xFFFFFFFF; } while (0) -#define CKSUM_ADD(cksum, byte) do { (cksum) = crc32_byte(cksum, byte); } while(0) -#define CKSUM_FINALIZE(cksum) do { (cksum) = (TF_CKSUM)~(cksum); } while(0) + // TODO try to replace with an algorithm + static const uint32_t crc32_table[] = { /* CRC polynomial 0xedb88320 */ + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, + 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, + 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, + 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, + 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, + 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, + 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, + 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, + 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, + 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, + 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d + }; + + static TF_CKSUM TF_CksumStart(void) + { return (TF_CKSUM)0xFFFFFFFF; } + + static TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte) + { return crc32_table[((cksum) ^ ((uint8_t)byte)) & 0xff] ^ ((cksum) >> 8); } + + static TF_CKSUM TF_CksumEnd(TF_CKSUM cksum) + { return (TF_CKSUM) ~cksum; } #endif +#define CKSUM_RESET(cksum) do { (cksum) = TF_CksumStart(); } while (0) +#define CKSUM_ADD(cksum, byte) do { (cksum) = TF_CksumAdd((cksum), (byte)); } while (0) +#define CKSUM_FINALIZE(cksum) do { (cksum) = TF_CksumEnd((cksum)); } while (0) + //endregion + +//region Init + /** 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; @@ -159,12 +228,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; } @@ -176,6 +251,9 @@ void TF_DeInit(TinyFrame *tf) free(tf); } +//endregion Init + + //region Listeners /** Reset ID listener's timeout to the original value */ @@ -191,7 +269,7 @@ static void _TF_FN cleanup_id_listener(TinyFrame *tf, TF_COUNT i, struct TF_IdLi if (lst->fn == NULL) return; // Make user clean up their data - only if not NULL - if (lst->userdata != NULL) { + if (lst->userdata != NULL || lst->userdata2 != NULL) { msg.userdata = lst->userdata; msg.userdata2 = lst->userdata2; msg.data = NULL; // this is a signal that the listener should clean up @@ -444,12 +522,33 @@ static void _TF_FN TF_HandleReceivedMessage(TinyFrame *tf) TF_Error("Unhandled message, type %d", (int)msg.type); } +/** Externally renew an ID listener */ +bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id) +{ + TF_COUNT i; + struct TF_IdListener_ *lst; + for (i = 0; i < tf->count_id_lst; i++) { + lst = &tf->id_listeners[i]; + // test if live & matching + if (lst->fn != NULL && lst->id == id) { + renew_id_listener(lst); + return true; + } + } + + TF_Error("Renew listener: not found (id %d)", (int)id); + return false; +} + //endregion Listeners + +//region Parser + /** 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]); } @@ -586,8 +685,8 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c) if (tf->rxi == tf->len) { #if TF_CKSUM_TYPE == TF_CKSUM_NONE // All done - TF_HandleReceivedMessage(); - TF_ResetParser(); + TF_HandleReceivedMessage(tf); + TF_ResetParser(tf); #else // Enter DATA_CKSUM state tf->state = TFState_DATA_CKSUM; @@ -614,18 +713,17 @@ void _TF_FN TF_AcceptChar(TinyFrame *tf, unsigned char c) break; } //@formatter:on - - // we get here after finishing HEAD, if no data are to be received - handle and clear - // TODO verify - this seems unreachable under normal circumstances - if (tf->len == 0 && tf->state == TFState_DATA) { - TF_HandleReceivedMessage(tf); - TF_ResetParser(tf); - } } +//endregion Parser + + +//region Compose and send + // Helper macros for the Compose functions // use variables: si - signed int, b - byte, outbuff - target buffer, pos - count of bytes in buffer + /** * Write a number to the output buffer. * @@ -669,15 +767,15 @@ 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; - (void)cksum; + (void)cksum; // suppress "unused" warning if checksums are disabled CKSUM_RESET(cksum); @@ -724,13 +822,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 inline 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]; @@ -748,11 +846,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 inline 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); @@ -762,60 +860,109 @@ static size_t _TF_FN TF_ComposeTail(uint8_t *outbuff, TF_CKSUM *cksum) } /** - * Send a message + * Begin building and sending 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_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; - len = TF_ComposeHead(tf, tf->sendbuf, msg); - if (listener) TF_AddIdListener(tf, msg, listener, timeout); + if (listener) { + TF_TRY(TF_AddIdListener(tf, msg, listener, timeout)); + } - CKSUM_RESET(cksum); + CKSUM_RESET(tf->tx_cksum); + return true; +} + +/** + * Build and send a part (or all) of a frame body. + * Caution: this does not check the total length against the length specified in the frame head + * + * @param tf - instance + * @param buff - bytes to write + * @param length - count + */ +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; } } +} +/** + * End a multi-part frame. This sends the checksum and releases mutex. + * + * @param tf - instance + */ +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)); + if (msg->len == 0 || msg->data != NULL) { + // Send the payload and checksum only if we're not starting a multi-part frame. + // A multi-part frame is identified by passing NULL to the data field and setting the length. + // User then needs to call those functions manually + TF_SendFrame_Chunk(tf, msg->data, msg->len); + TF_SendFrame_End(tf); + } return true; } +//endregion Compose and send + + +//region Sending API funcs + /** send without listener */ bool _TF_FN TF_Send(TinyFrame *tf, TF_Msg *msg) { @@ -857,28 +1004,56 @@ bool _TF_FN TF_Respond(TinyFrame *tf, TF_Msg *msg) return TF_Send(tf, msg); } -/** Externally renew an ID listener */ -bool _TF_FN TF_RenewIdListener(TinyFrame *tf, TF_ID id) +//endregion Sending API funcs + + +//region Sending API funcs - multipart + +bool _TF_FN TF_Send_Multipart(TinyFrame *tf, TF_Msg *msg) { - TF_COUNT i; - struct TF_IdListener_ *lst; - for (i = 0; i < tf->count_id_lst; i++) { - lst = &tf->id_listeners[i]; - // test if live & matching - if (lst->fn != NULL && lst->id == id) { - renew_id_listener(lst); - return true; - } - } + msg->data = NULL; + return TF_Send(tf, msg); +} - TF_Error("Renew listener: not found (id %d)", (int)id); - return false; +bool _TF_FN TF_SendSimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len) +{ + return TF_SendSimple(tf, type, NULL, len); +} + +bool _TF_FN TF_QuerySimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len, TF_Listener listener, TF_TICKS timeout) +{ + return TF_QuerySimple(tf, type, NULL, len, listener, timeout); +} + +bool _TF_FN TF_Query_Multipart(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) +{ + msg->data = NULL; + return TF_Query(tf, msg, listener, timeout); +} + +void _TF_FN TF_Respond_Multipart(TinyFrame *tf, TF_Msg *msg) +{ + msg->data = NULL; + TF_Respond(tf, msg); } +void _TF_FN TF_Multipart_Payload(TinyFrame *tf, const uint8_t *buff, uint32_t length) +{ + TF_SendFrame_Chunk(tf, buff, length); +} + +void _TF_FN TF_Multipart_Close(TinyFrame *tf) +{ + TF_SendFrame_End(tf); +} + +//endregion Sending API funcs - multipart + + /** 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) @@ -898,19 +1073,3 @@ void _TF_FN TF_Tick(TinyFrame *tf) } } } - -/** Default impl for claiming write mutex; can be specific to the instance */ -void __attribute__((weak)) TF_ClaimTx(TinyFrame *tf) -{ - (void) tf; - - // do nothing -} - -/** Default impl for releasing write mutex; can be specific to the instance */ -void __attribute__((weak)) TF_ReleaseTx(TinyFrame *tf) -{ - (void) tf; - - // do nothing -} diff --git a/TinyFrame/TinyFrame.h b/TinyFrame/TinyFrame.h index d90c1da..5f6943c 100644 --- a/TinyFrame/TinyFrame.h +++ b/TinyFrame/TinyFrame.h @@ -4,13 +4,13 @@ /** * TinyFrame protocol library * - * (c) Ondřej Hruška 2017, MIT License + * (c) Ondřej Hruška 2017-2018, MIT License * no liability/warranty, free for any use, must retain this notice & license * * Upstream URL: https://github.com/MightyPork/TinyFrame */ -#define TF_VERSION "2.1.0" +#define TF_VERSION "2.3.0" //--------------------------------------------------------------------------- #include // for uint8_t etc @@ -19,78 +19,77 @@ #include // for memset() //--------------------------------------------------------------------------- -// Select checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32) -#define TF_CKSUM_NONE 0 -#define TF_CKSUM_XOR 8 -#define TF_CKSUM_CRC16 16 -#define TF_CKSUM_CRC32 32 +// Checksum type (0 = none, 8 = ~XOR, 16 = CRC16 0x8005, 32 = CRC32) +#define TF_CKSUM_NONE 0 // no checksums +#define TF_CKSUM_XOR 8 // inverted xor of all payload bytes +#define TF_CKSUM_CRC8 9 // Dallas/Maxim CRC8 (1-wire) +#define TF_CKSUM_CRC16 16 // CRC16 with the polynomial 0x8005 (x^16 + x^15 + x^2 + 1) +#define TF_CKSUM_CRC32 32 // CRC32 with the polynomial 0xedb88320 +#define TF_CKSUM_CUSTOM8 1 // Custom 8-bit checksum +#define TF_CKSUM_CUSTOM16 2 // Custom 16-bit checksum +#define TF_CKSUM_CUSTOM32 3 // Custom 32-bit checksum #include "TF_Config.h" //region Resolve data types #if TF_LEN_BYTES == 1 -typedef uint8_t TF_LEN; + typedef uint8_t TF_LEN; #elif TF_LEN_BYTES == 2 -typedef uint16_t TF_LEN; + typedef uint16_t TF_LEN; #elif TF_LEN_BYTES == 4 -typedef uint32_t TF_LEN; + typedef uint32_t TF_LEN; #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 #if TF_TYPE_BYTES == 1 -typedef uint8_t TF_TYPE; + typedef uint8_t TF_TYPE; #elif TF_TYPE_BYTES == 2 -typedef uint16_t TF_TYPE; + typedef uint16_t TF_TYPE; #elif TF_TYPE_BYTES == 4 -typedef uint32_t TF_TYPE; + typedef uint32_t TF_TYPE; #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 #if TF_ID_BYTES == 1 -typedef uint8_t TF_ID; + typedef uint8_t TF_ID; #elif TF_ID_BYTES == 2 -typedef uint16_t TF_ID; + typedef uint16_t TF_ID; #elif TF_ID_BYTES == 4 -typedef uint32_t TF_ID; + typedef uint32_t TF_ID; #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 -#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) -typedef uint8_t TF_CKSUM; -#elif TF_CKSUM_TYPE == TF_CKSUM_CRC16 -// CRC16 -typedef uint16_t TF_CKSUM; -#elif TF_CKSUM_TYPE == TF_CKSUM_CRC32 -// CRC32 -typedef uint32_t TF_CKSUM; +#if (TF_CKSUM_TYPE == TF_CKSUM_XOR) || (TF_CKSUM_TYPE == TF_CKSUM_NONE) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM8) || (TF_CKSUM_TYPE == TF_CKSUM_CRC8) + // ~XOR (if 0, still use 1 byte - it won't be used) + typedef uint8_t TF_CKSUM; +#elif (TF_CKSUM_TYPE == TF_CKSUM_CRC16) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM16) + // CRC16 + typedef uint16_t TF_CKSUM; +#elif (TF_CKSUM_TYPE == TF_CKSUM_CRC32) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM32) + // CRC32 + typedef uint32_t TF_CKSUM; #else -#error Bad value for TF_CKSUM_TYPE, must be 8, 16 or 32 + #error Bad value for TF_CKSUM_TYPE #endif //endregion //--------------------------------------------------------------------------- -// Type-dependent masks for bit manipulation in the ID field -#define TF_ID_MASK (TF_ID)(((TF_ID)1 << (sizeof(TF_ID)*8 - 1)) - 1) -#define TF_ID_PEERBIT (TF_ID)((TF_ID)1 << ((sizeof(TF_ID)*8) - 1)) - -//--------------------------------------------------------------------------- - /** Peer bit enum (used for init) */ typedef enum { TF_SLAVE = 0, TF_MASTER = 1, } TF_Peer; + /** Response from listeners */ typedef enum { TF_NEXT = 0, //!< Not handled, let other listeners handle it @@ -99,117 +98,59 @@ typedef enum { TF_CLOSE = 3, //!< Handled, remove self } TF_Result; + /** Data structure for sending / receiving messages */ -typedef struct _TF_MSG_STRUCT_ { +typedef struct TF_Msg_ { TF_ID frame_id; //!< message ID bool is_response; //!< internal flag, set when using the Respond function. frame_id is then kept unchanged. TF_TYPE type; //!< received or sent message type - const uint8_t *data; //!< buffer of received data or data to send. NULL = listener timed out, free userdata! - TF_LEN len; //!< length of the buffer - void *userdata; //!< here's a place for custom data; this data will be stored with the listener + + /** + * Buffer of received data, or data to send. + * + * - If (data == NULL) in an ID listener, that means the listener timed out and + * the user should free any userdata and take other appropriate actions. + * + * - If (data == NULL) and length is not zero when sending a frame, that starts a multi-part frame. + * This call then must be followed by sending the payload and closing the frame. + */ + const uint8_t *data; + TF_LEN len; //!< length of the payload + + /** + * Custom user data for the ID listener. + * + * This data will be stored in the listener slot and passed to the ID callback + * via those same fields on the received message. + */ + void *userdata; void *userdata2; } TF_Msg; /** * Clear message struct + * + * @param msg - message to clear in-place */ static inline void TF_ClearMsg(TF_Msg *msg) { memset(msg, 0, sizeof(TF_Msg)); } +/** TinyFrame struct typedef */ typedef struct TinyFrame_ TinyFrame; /** * TinyFrame Type Listener callback * - * @param frame_id - ID of the received frame - * @param type - type field from the message - * @param data - byte buffer with the application data - * @param len - number of bytes in the buffer + * @param tf - instance + * @param msg - the received message, userdata is populated inside the object * @return listener result */ 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 - -// ------------------------------------------------------------------- +// ---------------------------------- INIT ------------------------------ /** * Initialize the TinyFrame engine. @@ -221,7 +162,9 @@ struct TinyFrame_ { * This function is a wrapper around TF_InitStatic that calls malloc() to obtain * the instance. * + * @param tf - instance * @param peer_bit - peer bit to use for self + * @return TF instance or NULL */ TinyFrame *TF_Init(TF_Peer peer_bit); @@ -231,26 +174,66 @@ TinyFrame *TF_Init(TF_Peer peer_bit); * * The .userdata / .usertag field is preserved when TF_InitStatic is called. * + * @param tf - instance * @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 * - * @param tf + * @param tf - instance */ void TF_DeInit(TinyFrame *tf); + +// ---------------------------------- API CALLS -------------------------------------- + +/** + * Accept incoming bytes & parse frames + * + * @param tf - instance + * @param buffer - byte buffer to process + * @param count - nr of bytes in the buffer + */ +void TF_Accept(TinyFrame *tf, const uint8_t *buffer, uint32_t count); + +/** + * Accept a single incoming byte + * + * @param tf - instance + * @param c - a received char + */ +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. + * + * A common place to call this from is the SysTick handler. + * + * @param tf - instance + */ +void TF_Tick(TinyFrame *tf); + /** * Reset the frame parser state machine. * This does not affect registered listeners. + * + * @param tf - instance */ void TF_ResetParser(TinyFrame *tf); + +// ---------------------------- MESSAGE LISTENERS ------------------------------- + /** * Register a frame type listener. * + * @param tf - instance * @param msg - message (contains frame_id and userdata) * @param cb - callback * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) @@ -261,6 +244,7 @@ bool TF_AddIdListener(TinyFrame *tf, TF_Msg *msg, TF_Listener cb, TF_TICKS timeo /** * Remove a listener by the message ID it's registered for * + * @param tf - instance * @param frame_id - the frame we're listening for */ bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id); @@ -268,6 +252,7 @@ bool TF_RemoveIdListener(TinyFrame *tf, TF_ID frame_id); /** * Register a frame type listener. * + * @param tf - instance * @param frame_type - frame type to listen for * @param cb - callback * @return slot index (for removing), or TF_ERROR (-1) @@ -277,6 +262,7 @@ bool TF_AddTypeListener(TinyFrame *tf, TF_TYPE frame_type, TF_Listener cb); /** * Remove a listener by type. * + * @param tf - instance * @param type - the type it's registered for */ bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type); @@ -284,6 +270,7 @@ bool TF_RemoveTypeListener(TinyFrame *tf, TF_TYPE type); /** * Register a generic listener. * + * @param tf - instance * @param cb - callback * @return slot index (for removing), or TF_ERROR (-1) */ @@ -292,13 +279,27 @@ bool TF_AddGenericListener(TinyFrame *tf, TF_Listener cb); /** * Remove a generic listener by function pointer * + * @param tf - instance * @param cb - callback function to remove */ bool TF_RemoveGenericListener(TinyFrame *tf, TF_Listener cb); +/** + * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener) + * + * @param tf - instance + * @param id - listener ID to renew + * @return true if listener was found and renewed + */ +bool TF_RenewIdListener(TinyFrame *tf, TF_ID id); + + +// ---------------------------- FRAME TX FUNCTIONS ------------------------------ + /** * Send a frame, no listener * + * @param tf - instance * @param msg - message struct. ID is stored in the frame_id field * @return success */ @@ -312,73 +313,214 @@ bool TF_SendSimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len) /** * Send a frame, and optionally attach an ID listener. * + * @param tf - instance * @param msg - message struct. ID is stored in the frame_id field * @param listener - listener waiting for the response (can be NULL) * @param timeout - listener expiry time in ticks * @return success */ -bool TF_Query(TinyFrame *tf, 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 + * Like TF_Query(), but without the struct */ -bool TF_QuerySimple(TinyFrame *tf, TF_TYPE type, const uint8_t *data, TF_LEN len, +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. * + * @param tf - instance * @param msg - message struct. ID is read from frame_id. set ->renew to reset listener timeout * @return success */ bool TF_Respond(TinyFrame *tf, TF_Msg *msg); + +// ------------------------ MULTIPART FRAME TX FUNCTIONS ----------------------------- +// Those routines are used to send long frames without having all the data available +// at once (e.g. capturing it from a peripheral or reading from a large memory buffer) + /** - * Renew an ID listener timeout externally (as opposed to by returning TF_RENEW from the ID listener) - * - * @param id - listener ID to renew - * @return true if listener was found and renewed + * TF_Send() with multipart payload. + * msg.data is ignored and set to NULL */ -bool TF_RenewIdListener(TinyFrame *tf, TF_ID id); +bool TF_Send_Multipart(TinyFrame *tf, TF_Msg *msg); /** - * Accept incoming bytes & parse frames - * - * @param buffer - byte buffer to process - * @param count - nr of bytes in the buffer + * TF_SendSimple() with multipart payload. */ -void TF_Accept(TinyFrame *tf, const uint8_t *buffer, size_t count); +bool TF_SendSimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len); /** - * Accept a single incoming byte - * - * @param c - a received char + * TF_QuerySimple() with multipart payload. */ -void TF_AcceptChar(TinyFrame *tf, uint8_t c); +bool TF_QuerySimple_Multipart(TinyFrame *tf, TF_TYPE type, TF_LEN len, TF_Listener listener, TF_TICKS timeout); /** - * This function should be called periodically. + * TF_Query() with multipart payload. + * msg.data is ignored and set to NULL + */ +bool TF_Query_Multipart(TinyFrame *tf, TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); + +/** + * TF_Respond() with multipart payload. + * msg.data is ignored and set to NULL + */ +void TF_Respond_Multipart(TinyFrame *tf, TF_Msg *msg); + +/** + * Send the payload for a started multipart frame. This can be called multiple times + * if needed, until the full length is transmitted. * - * The time base is used to time-out partial frames in the parser and - * automatically reset it. + * @param tf - instance + * @param buff - buffer to send bytes from + * @param length - number of bytes to send + */ +void TF_Multipart_Payload(TinyFrame *tf, const uint8_t *buff, uint32_t length); + +/** + * Close the multipart message, generating chekcsum and releasing the Tx lock. * - * (suggestion - call this in a SysTick handler) + * @param tf - instance */ -void TF_Tick(TinyFrame *tf); +void TF_Multipart_Close(TinyFrame *tf); + + +// ---------------------------------- INTERNAL ---------------------------------- +// This is publicly visible only to allow static init. + +enum TF_State_ { + 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 (0 = no timeout) + void *userdata; + void *userdata2; +}; + +struct TF_TypeListener_ { + TF_TYPE type; + TF_Listener fn; +}; -// --- TO BE IMPLEMENTED BY USER --- +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 TF_State_ 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. + + /* Tx state */ + // Buffer for building frames + uint8_t sendbuf[TF_SENDBUF_LEN]; //!< Transmit temporary buffer + + uint32_t tx_pos; //!< Next write position in the Tx buffer (used for multipart) + uint32_t tx_len; //!< Total expected Tx length + TF_CKSUM tx_cksum; //!< Transmit checksum accumulator + +#if !TF_USE_MUTEX + bool soft_lock; //!< Tx lock flag used if the mutex feature is not enabled. +#endif + + /* --- 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; +}; + + +// ------------------------ 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); +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); + /** Claim the TX interface before composing and sending a frame */ + extern bool TF_ClaimTx(TinyFrame *tf); -/** Free the TX interface after composing and sending a frame */ -extern void TF_ReleaseTx(TinyFrame *tf); + /** Free the TX interface after composing and sending a frame */ + extern void TF_ReleaseTx(TinyFrame *tf); + +#endif + +// Custom checksum functions +#if (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM8) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM16) || (TF_CKSUM_TYPE == TF_CKSUM_CUSTOM32) + + /** + * Initialize a checksum + * + * @return initial checksum value + */ + extern TF_CKSUM TF_CksumStart(void); + + /** + * Update a checksum with a byte + * + * @param cksum - previous checksum value + * @param byte - byte to add + * @return updated checksum value + */ + extern TF_CKSUM TF_CksumAdd(TF_CKSUM cksum, uint8_t byte); + + /** + * Finalize the checksum calculation + * + * @param cksum - previous checksum value + * @return final checksum value + */ + extern TF_CKSUM TF_CksumEnd(TF_CKSUM cksum); + +#endif #endif diff --git a/platform/irq_dispatcher.c b/platform/irq_dispatcher.c index 74e2fee..6935efe 100644 --- a/platform/irq_dispatcher.c +++ b/platform/irq_dispatcher.c @@ -146,7 +146,10 @@ static struct cbslot *get_slot_for_periph(void *periph) void irqd_attach(void *periph, IrqCallback callback, void *arg) { struct cbslot *slot = get_slot_for_periph(periph); - assert_param(slot->callback == NULL); + if (slot->callback != NULL) { + trap("Attach IRQ %p() to %p but %p() already bound", callback, periph, slot->callback); + } + slot->callback = callback; slot->arg = arg; } @@ -157,6 +160,8 @@ void irqd_detach(void *periph, IrqCallback callback) if (slot->callback == callback) { slot->callback = NULL; slot->arg = NULL; + } else { + trap("Detach IRQ %p() from %p but %p() bound instead", callback, periph, slot->callback); } } diff --git a/units/usart/_dmas.c b/units/usart/_dmas.c index 12adaab..5c51694 100644 --- a/units/usart/_dmas.c +++ b/units/usart/_dmas.c @@ -433,8 +433,8 @@ void UUSART_DeInitDMAs(Unit *unit) struct priv *priv = unit->data; assert_param(priv); - irqd_detach(priv->dma_tx, UUSART_DMA_RxHandler); - irqd_detach(priv->dma_rx, UUSART_DMA_TxHandler); + irqd_detach(priv->dma_tx, UUSART_DMA_TxHandler); + irqd_detach(priv->dma_rx, UUSART_DMA_RxHandler); LL_DMA_DeInit(priv->dma, priv->dma_rx_chnum); LL_DMA_DeInit(priv->dma, priv->dma_tx_chnum);