diff --git a/TinyFrame.c b/TinyFrame.c index b8af068..dac526b 100644 --- a/TinyFrame.c +++ b/TinyFrame.c @@ -23,7 +23,7 @@ enum TFState { typedef struct _IdListener_struct_ { TF_ID id; - TF_LISTENER fn; + 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; @@ -31,11 +31,11 @@ typedef struct _IdListener_struct_ { typedef struct _TypeListener_struct_ { TF_TYPE type; - TF_LISTENER fn; + TF_Listener fn; } TypeListener; typedef struct _GenericListener_struct_ { - TF_LISTENER fn; + TF_Listener fn; } GenericListener; /** @@ -43,7 +43,7 @@ typedef struct _GenericListener_struct_ { */ static struct TinyFrameStruct { /* Own state */ - TF_PEER peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash) + TF_Peer peer_bit; //!< Own peer bit (unqiue to avoid msg ID clash) TF_ID next_id; //!< Next frame / frame chain ID /* Parser state */ @@ -201,7 +201,7 @@ static struct TinyFrameStruct { //endregion -void _TF_FN TF_Init(TF_PEER peer_bit) +void _TF_FN TF_Init(TF_Peer peer_bit) { // Zero it out memset(&tf, 0, sizeof(struct TinyFrameStruct)); @@ -211,6 +211,11 @@ void _TF_FN TF_Init(TF_PEER peer_bit) //region Listeners +static void _TF_FN renew_id_listener(IdListener *lst) +{ + lst->timeout = lst->timeout_max; +} + /** * Notify callback about ID listener demise & clean it * @@ -218,12 +223,16 @@ void _TF_FN TF_Init(TF_PEER peer_bit) */ static void _TF_FN cleanup_id_listener(TF_COUNT i, IdListener *lst) { - TF_MSG msg; + TF_Msg msg; if (lst->fn == NULL) return; - msg.userdata = lst->userdata; - msg.data = NULL; // this is a signal that the listener should clean up - lst->fn(&msg); + // Make user clean up their data - only if not NULL + if (lst->userdata != NULL) { + msg.userdata = lst->userdata; + 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 = NULL; // Discard listener if (i == tf.count_id_lst - 1) { @@ -257,7 +266,7 @@ static inline void _TF_FN cleanup_generic_listener(TF_COUNT i, GenericListener * } } -bool _TF_FN TF_AddIdListener(TF_MSG *msg, TF_LISTENER cb, TF_TICKS timeout) +bool _TF_FN TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout) { TF_COUNT i; IdListener *lst; @@ -278,7 +287,7 @@ bool _TF_FN TF_AddIdListener(TF_MSG *msg, TF_LISTENER cb, TF_TICKS timeout) return false; } -bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_LISTENER cb) +bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb) { TF_COUNT i; TypeListener *lst; @@ -297,7 +306,7 @@ bool _TF_FN TF_AddTypeListener(TF_TYPE frame_type, TF_LISTENER cb) return false; } -bool _TF_FN TF_AddGenericListener(TF_LISTENER cb) +bool _TF_FN TF_AddGenericListener(TF_Listener cb) { TF_COUNT i; GenericListener *lst; @@ -345,7 +354,7 @@ bool _TF_FN TF_RemoveTypeListener(TF_TYPE type) return false; } -bool _TF_FN TF_RemoveGenericListener(TF_LISTENER cb) +bool _TF_FN TF_RemoveGenericListener(TF_Listener cb) { TF_COUNT i; GenericListener *lst; @@ -367,9 +376,10 @@ static void _TF_FN TF_HandleReceivedMessage(void) IdListener *ilst; TypeListener *tlst; GenericListener *glst; + TF_Result res; // Prepare message object - TF_MSG msg; + TF_Msg msg; msg.frame_id = tf.id; msg.is_response = false; msg.type = tf.type; @@ -388,10 +398,19 @@ static void _TF_FN TF_HandleReceivedMessage(void) for (i = 0; i < tf.count_id_lst; i++) { ilst = &tf.id_listeners[i]; if (ilst->fn && ilst->id == msg.frame_id) { - msg.renew = false; msg.userdata = ilst->userdata; // pass userdata pointer to the callback - if (ilst->fn(&msg)) return; + res = ilst->fn(&msg); ilst->userdata = msg.userdata; // put it back (may have changed the pointer or set to NULL) + + if (res != TF_NEXT) { + if (res == TF_CLOSE) { + cleanup_id_listener(i, ilst); + } + else if (res == TF_RENEW) { + renew_id_listener(ilst); + } + return; + } } } msg.userdata = NULL; @@ -401,7 +420,14 @@ static void _TF_FN TF_HandleReceivedMessage(void) for (i = 0; i < tf.count_type_lst; i++) { tlst = &tf.type_listeners[i]; if (tlst->fn && tlst->type == msg.type) { - if (tlst->fn(&msg)) return; + res = tlst->fn(&msg); + + if (res != TF_NEXT) { + if (res == TF_CLOSE) { + cleanup_type_listener(i, tlst); + } + return; + } } } @@ -409,7 +435,14 @@ static void _TF_FN TF_HandleReceivedMessage(void) for (i = 0; i < tf.count_generic_lst; i++) { glst = &tf.generic_listeners[i]; if (glst->fn) { - if (glst->fn(&msg)) return; + res = glst->fn(&msg); + + if (res != TF_NEXT) { + if (res == TF_CLOSE) { + cleanup_generic_listener(i, glst); + } + return; + } } } } @@ -671,7 +704,7 @@ static inline size_t _TF_FN TF_Compose(uint8_t *outbuff, TF_ID *id_ptr, } // send without listener -bool _TF_FN TF_Send(TF_MSG *msg) +bool _TF_FN TF_Send(TF_Msg *msg) { return TF_Query(msg, NULL, 0); } @@ -679,7 +712,7 @@ bool _TF_FN TF_Send(TF_MSG *msg) // send without listener and struct bool _TF_FN TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len) { - TF_MSG msg; + TF_Msg msg; TF_ClearMsg(&msg); msg.type = type; msg.data = data; @@ -688,9 +721,9 @@ bool _TF_FN TF_SendSimple(TF_TYPE type, const uint8_t *data, TF_LEN len) } // send without listener and struct -bool _TF_FN TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_LISTENER listener, TF_TICKS timeout) +bool _TF_FN TF_QuerySimple(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); msg.type = type; msg.data = data; @@ -699,7 +732,7 @@ bool _TF_FN TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_LIS } // send with listener -bool _TF_FN TF_Query(TF_MSG *msg, TF_LISTENER listener, TF_TICKS timeout) +bool _TF_FN TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout) { size_t len; len = TF_Compose(tf.sendbuf, @@ -719,13 +752,10 @@ bool _TF_FN TF_Query(TF_MSG *msg, TF_LISTENER listener, TF_TICKS timeout) } // Like TF_Send, but with explicit frame ID -bool _TF_FN TF_Respond(TF_MSG *msg) +bool _TF_FN TF_Respond(TF_Msg *msg) { msg->is_response = true; - bool suc = TF_Send(msg); - - if (suc && msg->renew) TF_RenewIdListener(msg->frame_id); - return suc; + return TF_Send(msg); } bool _TF_FN TF_RenewIdListener(TF_ID id) @@ -736,7 +766,7 @@ bool _TF_FN TF_RenewIdListener(TF_ID id) lst = &tf.id_listeners[i]; // test if live & matching if (lst->fn != NULL && lst->id == id) { - lst->timeout = lst->timeout_max; + renew_id_listener(lst); return true; } } diff --git a/TinyFrame.h b/TinyFrame.h index 1ada271..c724a24 100644 --- a/TinyFrame.h +++ b/TinyFrame.h @@ -87,8 +87,15 @@ /** Peer bit enum (used for init) */ typedef enum { TF_SLAVE = 0, - TF_MASTER = 1 -} TF_PEER; + TF_MASTER, +} TF_Peer; + +typedef enum { + TF_NEXT = 0, //!< Not handled, let other listeners handle it + TF_STAY = 1, //!< Handled, stay + TF_RENEW = 2, //!< Handled, stay, renew - useful only with listener timeout + TF_CLOSE = 3, //!< Handled, remove self +} TF_Result; /** Data structure for sending / receiving messages */ typedef struct _TF_MSG_STRUCT_ { @@ -98,13 +105,12 @@ typedef struct _TF_MSG_STRUCT_ { 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 - bool renew; //!< Renew the ID listener - if using timeout -} TF_MSG; +} TF_Msg; /** * Clear message struct */ -static inline void TF_ClearMsg(TF_MSG *msg) +static inline void TF_ClearMsg(TF_Msg *msg) { msg->frame_id = 0; msg->is_response = false; @@ -112,7 +118,6 @@ static inline void TF_ClearMsg(TF_MSG *msg) msg->data = NULL; msg->len = 0; msg->userdata = NULL; - msg->renew = false; } /** @@ -122,9 +127,9 @@ static inline void TF_ClearMsg(TF_MSG *msg) * @param type - type field from the message * @param data - byte buffer with the application data * @param len - number of bytes in the buffer - * @return true if the frame was consumed + * @return listener result */ -typedef bool (*TF_LISTENER)(TF_MSG *msg); +typedef TF_Result (*TF_Listener)(TF_Msg *msg); /** * Initialize the TinyFrame engine. @@ -132,7 +137,7 @@ typedef bool (*TF_LISTENER)(TF_MSG *msg); * * @param peer_bit - peer bit to use for self */ -void TF_Init(TF_PEER peer_bit); +void TF_Init(TF_Peer peer_bit); /** * Reset the frame parser state machine. @@ -148,7 +153,7 @@ void TF_ResetParser(void); * @param timeout - timeout in ticks to auto-remove the listener (0 = keep forever) * @return slot index (for removing), or TF_ERROR (-1) */ -bool TF_AddIdListener(TF_MSG *msg, TF_LISTENER cb, TF_TICKS timeout); +bool TF_AddIdListener(TF_Msg *msg, TF_Listener cb, TF_TICKS timeout); /** * Remove a listener by the message ID it's registered for @@ -164,7 +169,7 @@ bool TF_RemoveIdListener(TF_ID frame_id); * @param cb - callback * @return slot index (for removing), or TF_ERROR (-1) */ -bool TF_AddTypeListener(TF_TYPE frame_type, TF_LISTENER cb); +bool TF_AddTypeListener(TF_TYPE frame_type, TF_Listener cb); /** * Remove a listener by type. @@ -179,14 +184,14 @@ bool TF_RemoveTypeListener(TF_TYPE type); * @param cb - callback * @return slot index (for removing), or TF_ERROR (-1) */ -bool TF_AddGenericListener(TF_LISTENER cb); +bool TF_AddGenericListener(TF_Listener cb); /** * Remove a generic listener by function pointer * * @param cb - callback function to remove */ -bool TF_RemoveGenericListener(TF_LISTENER cb); +bool TF_RemoveGenericListener(TF_Listener cb); /** * Send a frame, no listener @@ -194,7 +199,7 @@ bool TF_RemoveGenericListener(TF_LISTENER cb); * @param msg - message struct. ID is stored in the frame_id field * @return success */ -bool TF_Send(TF_MSG *msg); +bool TF_Send(TF_Msg *msg); /** * Like TF_Send, but without the struct @@ -204,7 +209,7 @@ bool TF_SendSimple(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); +bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_Listener listener, TF_TICKS timeout); /** * Send a frame, and optionally attach an ID listener. @@ -214,7 +219,7 @@ bool TF_QuerySimple(TF_TYPE type, const uint8_t *data, TF_LEN len, TF_LISTENER l * @param timeout - listener expiry time in ticks * @return success */ -bool TF_Query(TF_MSG *msg, TF_LISTENER listener, TF_TICKS timeout); +bool TF_Query(TF_Msg *msg, TF_Listener listener, TF_TICKS timeout); /** * Send a response to a received message. @@ -222,7 +227,7 @@ 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 * @return success */ -bool TF_Respond(TF_MSG *msg); +bool TF_Respond(TF_Msg *msg); /** * Renew ID listener timeout diff --git a/demo/demo.c b/demo/demo.c index 503e382..4b234ff 100644 --- a/demo/demo.c +++ b/demo/demo.c @@ -2,10 +2,9 @@ // Created by MightyPork on 2017/10/15. // -// http://www.thegeekstuff.com/2011/12/c-socket-programming/?utm_source=feedburner - #include "demo.h" +// those magic defines are needed so we can use clone() #define _GNU_SOURCE #define __USE_GNU #include @@ -20,15 +19,24 @@ volatile int sockfd = -1; volatile bool conn_disband = false; +/** + * Close socket + */ void demo_disconn(void) { conn_disband = true; if (sockfd >= 0) close(sockfd); } +/** + * Demo WriteImpl - send stuff to our peer + * + * @param buff + * @param len + */ void TF_WriteImpl(const uint8_t *buff, size_t len) { - printf("\033[32m--- TX %ld bytes ---\033[0m\n", len); + printf("\033[32mTF_WriteImpl - sending frame:\033[0m\n"); dumpFrame(buff, len); usleep(1000); @@ -39,6 +47,13 @@ void TF_WriteImpl(const uint8_t *buff, size_t len) } } + +/** + * Client bg thread + * + * @param unused + * @return unused + */ static int demo_client(void* unused) { (void)unused; @@ -81,6 +96,12 @@ static int demo_client(void* unused) return 0; } +/** + * Server bg thread + * + * @param unused + * @return unused + */ static int demo_server(void* unused) { (void)unused; @@ -136,20 +157,36 @@ static int demo_server(void* unused) return 0; } -void signal_handler(int sig) +/** + * Trap - clean up + * + * @param sig - signal that caused this + */ +static void signal_handler(int sig) { (void)sig; printf("Shutting down..."); demo_disconn(); - exit(sig); + + exit(sig); // pass the signal through - this is nonstandard behavior but useful for debugging } +/** + * Sleaping Beauty's fave function + */ void demo_sleep(void) { while(1) usleep(10); } -void demo_init(TF_PEER peer) +/** + * Start the background thread + * + * Slave is started first and doesn't normally init transactions - but it could + * + * @param peer what peer we are + */ +void demo_init(TF_Peer peer) { signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); @@ -163,6 +200,9 @@ void demo_init(TF_PEER peer) } printf("Starting %s...\n", peer == TF_MASTER ? "MASTER" : "SLAVE"); + + // CLONE_VM --- share heap + // CLONE_FILES --- share stdout and stderr if (peer == TF_MASTER) { retc = clone(&demo_client, (char *)stack+8192, CLONE_VM|CLONE_FILES, 0); } else { diff --git a/demo/demo.h b/demo/demo.h index d5292f4..4caa068 100644 --- a/demo/demo.h +++ b/demo/demo.h @@ -15,9 +15,9 @@ void demo_sleep(void); /** Init server - DOES NOT init TinyFrame! */ -void demo_init(TF_PEER peer); +void demo_init(TF_Peer peer); -/** Disconnect client from the server - claled by a server-side callback */ +/** Disconnect client from the server - can be called by a server-side callback */ void demo_disconn(void); #endif //TF_DEMO_H diff --git a/demo/hello/master.c b/demo/hello/master.c index 5bffbf6..22e5d58 100644 --- a/demo/hello/master.c +++ b/demo/hello/master.c @@ -5,18 +5,18 @@ #include #include "../demo.h" -bool testIdListener(TF_MSG *msg) +TF_Result testIdListener(TF_Msg *msg) { printf("testIdListener()\n"); dumpFrameInfo(msg); - return true; + return TF_CLOSE; } -bool testGenericListener(TF_MSG *msg) +TF_Result testGenericListener(TF_Msg *msg) { printf("testGenericListener()\n"); dumpFrameInfo(msg); - return true; + return TF_STAY; } int main(void) diff --git a/demo/hello/slave.c b/demo/hello/slave.c index 200b3e2..3493bd1 100644 --- a/demo/hello/slave.c +++ b/demo/hello/slave.c @@ -6,14 +6,14 @@ #include "../demo.h" #include -bool helloListener(TF_MSG *msg) +TF_Result helloListener(TF_Msg *msg) { printf("helloListener()\n"); dumpFrameInfo(msg); - return true; + return TF_STAY; } -bool replyListener(TF_MSG *msg) +TF_Result replyListener(TF_Msg *msg) { printf("replyListener()\n"); dumpFrameInfo(msg); @@ -21,8 +21,14 @@ bool replyListener(TF_MSG *msg) msg->len = (TF_LEN) strlen((const char *) msg->data); TF_Respond(msg); + // unsolicted reply - will not be handled + msg->data = (const uint8_t *) "SPAM"; + msg->len = 5; + TF_Respond(msg); + + // unrelated message TF_SendSimple(77, (const uint8_t *) "NAZDAR", 7); - return true; + return TF_STAY; } int main(void) diff --git a/demo/test/test.c b/demo/test/test.c index dc686cd..90c9371 100644 --- a/demo/test/test.c +++ b/demo/test/test.c @@ -19,22 +19,22 @@ void TF_WriteImpl(const uint8_t *buff, size_t len) } /** An example listener function */ -bool myListener(TF_MSG *msg) +TF_Result myListener(TF_Msg *msg) { dumpFrameInfo(msg); - return true; + return TF_STAY; } -bool testIdListener(TF_MSG *msg) +TF_Result testIdListener(TF_Msg *msg) { printf("OK - ID Listener triggered for msg!\n"); dumpFrameInfo(msg); - return true; + return TF_CLOSE; } void main(void) { - TF_MSG msg; + TF_Msg msg; const char *longstr = "Lorem ipsum dolor sit amet."; // Set up the TinyFrame library diff --git a/demo/utils.c b/demo/utils.c index 3454cbf..2454c2c 100644 --- a/demo/utils.c +++ b/demo/utils.c @@ -21,7 +21,7 @@ void dumpFrame(const uint8_t *buff, size_t len) printf("--- end of frame ---\n\n"); } -void dumpFrameInfo(TF_MSG *msg) +void dumpFrameInfo(TF_Msg *msg) { printf("\033[33mFrame info\n" " type: %02Xh\n" diff --git a/demo/utils.h b/demo/utils.h index 06c4af5..f45a807 100644 --- a/demo/utils.h +++ b/demo/utils.h @@ -8,8 +8,19 @@ #include #include "../TinyFrame.h" +/** pointer to unsigned char */ typedef unsigned char* pu8; + +/** + * Dump a binary frame as hex, dec and ASCII + */ void dumpFrame(const uint8_t *buff, size_t len); -void dumpFrameInfo(TF_MSG *msg); + +/** + * Dump message metadata (not the content) + * + * @param msg + */ +void dumpFrameInfo(TF_Msg *msg); #endif //TF_UTILS_H