diff --git a/components/channel/CMakeLists.txt b/components/channel/CMakeLists.txt new file mode 100644 index 0000000..585b309 --- /dev/null +++ b/components/channel/CMakeLists.txt @@ -0,0 +1,8 @@ +idf_component_register( + SRC_DIRS + src + INCLUDE_DIRS + include + REQUIRES + linux +) \ No newline at end of file diff --git a/components/channel/Kconfig b/components/channel/Kconfig new file mode 100644 index 0000000..b5cb114 --- /dev/null +++ b/components/channel/Kconfig @@ -0,0 +1,47 @@ +menu "Channel" + menuconfig CHANNEL_TEST_ENABLE + bool "Enable unit test" + default y + select CHANNEL_INTERNAL_EXPORT_SYMBOLS + if CHANNEL_TEST_ENABLE + config CHANNEL_TEST_ENABLE_INIT + bool "init" + default y + config CHANNEL_TEST_ENABLE_SETCONTEXT + bool "setContext" + default y + config CHANNEL_TEST_ENABLE_SETCALLBACK + bool "setCallback" + default y + config CHANNEL_TEST_ENABLE_REGISTER + bool "register" + default y + config CHANNEL_TEST_ENABLE_UNREGISTER + bool "unregister" + default y + config CHANNEL_TEST_ENABLE_SEND + bool "send" + default y + config CHANNEL_TEST_ENABLE_BROADCAST_INIT + bool "broadcast_init" + default y + config CHANNEL_TEST_ENABLE_BROADCAST_FINISHED + bool "broadcast_finished" + default y + config CHANNEL_TEST_ENABLE_RESETROOT + bool "resetRoot" + default y + config CHANNEL_TEST_QUEUE_CONSUMER + bool "Channel queue" + default y + config CHANNEL_TEST_QUEUE_TIMEOUT + bool "Channel queue timeout" + default y + config CHANNEL_TEST_QUEUE_LOOP + bool "Channel queue timeout" + default y + endif + config CHANNEL_INTERNAL_EXPORT_SYMBOLS + bool "Export internal functions" + default n +endmenu \ No newline at end of file diff --git a/components/channel/README.md b/components/channel/README.md new file mode 100644 index 0000000..e88881c --- /dev/null +++ b/components/channel/README.md @@ -0,0 +1,105 @@ +# Channel component + +Channels are objects which enable iteration over other Channel objects with the same name/identifier. + +Each Channel object contains a context pointer, a callback function and +an additional flags field. +The callback is issued with a data pointer and a timeout value by the caller and the callee specific context and flags. +This interface is intended to enable dynamic management of FreeRTOS queues, +where a queue can be registered as a context +of a Channel and therefore fed by the Channels callback. + +## Concept + +This implementation resembles a map, mapping identifiers to lists. +Channels are implemented as an item of two separate lists. +The "unique" list holds items with different identifiers, +the "same" list holds items that share the same identifier. +Each list item itself is a valid list containing only itself; +multiple items can be combined to a larger list. + +This implementation constructs a single valid "unique" list, +starting with a globally defined root node. +All "same" lists of all items are considered valid. +Adding to this structure implies iterating the "unique" list, +until either the same identifier is found (and the new item is added to the "same" list of the found item) or the end of the "unique" list reached (and it's appended as new "unique" list item). +Each added Channel object is now part of a "same" list which can be directly iterated to find other items. + +exemplary structure: +``` + root node---. O O + v | | +unique identifier list-> O--O--O--O--O + | | + O O + ^ + same identifier list---' +``` +## Examples + + +### Simple Input channel +```c +#include "channel.h" + +QueueHandle_t queue; +Channel_in input; + +void app_main() { + queue = xQueueCreateStatic(/*...*/); + channel_init_input(&input, "input", queue); +} +``` + +### Custom Input channel +```c +#include "channel.h" + +static void *latest_data; + +BaseType_t callback (void *ctx, + const void *data, + const TickType_t timeout, + const BaseType_t flags) { + latest_data = data; +} + +Channel_in input; + +void app_main() { + channel_init(&input, "input", data, 0, &callback); +} +``` + +### Output channel +```c +#include "channel.h" + +Channel_out output; + +void app_main() { + channel_init_output(&output, "output"); +} +``` + +### Simple broadcast without error handling +```c +#include "channel.h" + +Channel_out output; +char data[] = "test"; + +void app_main() { + channel_init_output(&output, "output"); + + Channel_broadcast broadcast; + channel_broadcast_init(&broadcast, &output, data, 0); + while(!channel_broadcast_finished(&broadcast)) { + channel_broadcast(&broadcast); + } +} +``` + + + + diff --git a/components/channel/include/channel.h b/components/channel/include/channel.h new file mode 100644 index 0000000..d71e773 --- /dev/null +++ b/components/channel/include/channel.h @@ -0,0 +1,232 @@ +// Channels are objects which enable iteration over other Channel objects with +// the same name/identifier. +// This implementation resembles a map, mapping identifiers to lists. +// Channels are implemented as an item of two separate lists. +// The "unique" list holds items with different identifiers, +// the "same" list holds items that share the same identifier. +// Each list item itself is a valid list containing only itself; +// multiple items can be combined to a larger list. +// +// This implementation constructs a single valid "unique" list, +// starting with a globally defined root node. All "same" lists of all items +// are considered valid. +// +// Adding to this structure implies iterating the "unique" list, until either +// the same identifier is found (and the new item is added to the "same" list +// of the found item) or the end of the "unique" list reached (and it's +// appended as new "unique" list item). +// +// Each added Channel object is now part of a "same" list which can be directly +// iterated to find other items. +// +// exemplary structure: +// +// root node---. O O +// v | | +// unique identifier list-> O--O--O--O--O +// | | +// O O +// ^ +// same identifier list---' +// +// Each Channel object contains a context pointer, a callback function and +// an additional flags field. A broadcast function is provided to iterate +// through all "same" list items of a Channel object. The callback is issued +// with a data pointer and a timeout value by the caller and the callee +// specific context and flags. This interface is intended to enable dynamic +// management of FreeRTOS queues, where a queue can be registered as a context +// of a Channel and therefore fed by the Channels callback. + +#ifndef __CHANNEL__ +#define __CHANNEL__ + +#include +#include + +#include "list.h" + +typedef BaseType_t (*Channel_callback) (void *ctx, const void *data, const TickType_t timeout, const BaseType_t flags); + +struct channel { + const char *identifier; + struct list_head same; + struct list_head unique; + void *ctx; + BaseType_t flags; + Channel_callback callback; +}; + +typedef struct channel Channel; +typedef struct channel Channel_consumer; +typedef struct channel Channel_producer; + +typedef struct broadcast { + Channel *ch; + Channel *pos; + void *data; + TickType_t timeout; + TickType_t elapsed; +} Channel_broadcast; + +#ifndef STR +#define STR(X) _STR(X) +#define _STR(X) #X +#endif + +/** + * channel_init - initialize & register channel object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @ctx: pointer to context of the callback + * @flags: additional flags for the callback to modify behaviour + * @callback: function to call on new data + */ +void +channel_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback); + +/** + * channel_init_consumer - helper function to initialize channel object as consumer + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @queue: queue handle to send input to + */ +static +void +inline +channel_init_consumer +(Channel_consumer *ch, const char *identifier, QueueHandle_t queue) +{ + return channel_init(ch, identifier, queue, queueSEND_TO_BACK, &xQueueGenericSend); +} + +/** + * channel_init_producer - helper function to initialize channel object as producer + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + */ +static +void +inline +channel_init_producer +(Channel_producer *ch, const char *identifier) +{ + return channel_init(ch, identifier, NULL, 0, NULL); +} + +/** + * channel_setContext - helper function to change the context the callback acts on + * @ch: pointer to channel object to modify + * @ctx: pointer to new context + */ +static +void +inline +channel_setContext +(Channel *ch, void *ctx) +{ + ch->ctx = ctx; +} + +/** + * channel_setCallback - helper function to change the callback + * @ch: pointer to channel object to modify + * @callback: function to register as callback + */ +static +void +inline +channel_setCallback +(Channel *ch, const Channel_callback callback) +{ + ch->callback = callback; +} + +/** + * reset_channel - removes a channel input or output from the message structure + * @ch: channel element to reset + */ +void +channel_reset +(Channel *ch); + +/** + * channel_send - sends a message to a single channel object + * @ch: the channel structure that holds informations for this channel + * @data: the pointer to the message + * @timeout: how many time-slices the callback may block before aborting + * @return: returns pdPass (=1) if send was successful, if non-one value is returned, callback failed + * + * Calls the callback function on the channel. + */ +BaseType_t +channel_send +(const Channel_consumer *ch, const void *data, const TickType_t timeout); + +/** + * channel_broadcast_init - helper function to initialize broadcast handler + * @handle: pointer to broadcast object + * @ch: pointer to channel object initiating the broadcast + * @data: pointer to data to be transferred + * @timeout: number of time-slices each receiver may block + */ +static +void +inline +channel_broadcast_init +(Channel_broadcast *handle, const Channel_producer *ch, const void *data, const TickType_t timeout) +{ + handle->ch = (void*) ch; + handle->pos = (void*) ch; + handle->data = (void*) data; + handle->timeout = timeout; + handle->elapsed = 0; +} + +/** + * channel_broadcast_timeout - helper function to determine if broadcast timed out + * @handle: pointer to broadcast object + */ +static +bool +inline +channel_broadcast_timeout +(Channel_broadcast *handle) +{ + if (handle->timeout == portMAX_DELAY) { + return false; + } + if (handle->elapsed >= handle->timeout) { + return true; + } + return false; +} + +/** + * channel_broadcast_finished - helper function to determine if broadcast finished + * Does not provide information if broadcast was successful! + * @ch: pointer to channel object + */ +static +bool +inline +channel_broadcast_finished +(const Channel_broadcast *handle) +{ + return handle->pos == NULL; +} + +/** + * channel_broadcast - send a message to all who registered on this channels identifier + * @handle: the broadcast handle to act on + * @return: returns callback return value each time iteration was stopped + * + * Iterates over all elements in the same list, that registered on this channels identifier + * and calls the callback function on it. May return with error_code before all elements were notified, + * allowing for custom error handling. + */ +BaseType_t +channel_broadcast +(Channel_broadcast *handle); + +#endif diff --git a/components/channel/include/channel_debug.h b/components/channel/include/channel_debug.h new file mode 100644 index 0000000..32ef523 --- /dev/null +++ b/components/channel/include/channel_debug.h @@ -0,0 +1,138 @@ +#ifndef CHANNEL_DEBUG_H_ +#define CHANNEL_DEBUG_H_ + +#include "channel.h" + +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" +#include + +/** + * channel_debug_print - print channel attributes + * @prefix: prefix string in debug output + * @ch: channel object to print + */ +static +void +__attribute__((unused)) +channel_debug_print +(const char *prefix, const Channel *ch) +{ + ESP_LOGD(prefix, "@%p identifier:%s", + ch, ch->identifier); + ESP_LOGD(prefix, "@%p same.next:%p", + ch, list_entry(ch->same.next, struct channel, same)); + ESP_LOGD(prefix, "@%p same.prev:%p", + ch, list_entry(ch->same.prev, struct channel, same)); + ESP_LOGD(prefix, "@%p uniq.next:%p", + ch, list_entry(ch->unique.next, struct channel, unique)); + ESP_LOGD(prefix, "@%p uniq.prev:%p", + ch, list_entry(ch->unique.prev, struct channel, unique)); +} + +/** + * channel_debug_print_dot - print channel attributes in dot format + * @prefix: prefix string in debug output + * @ch: channel object to print + */ +static +void +__attribute__((unused)) +channel_debug_print_dot +(const char *prefix, const Channel *ch) +{ + if (strcmp("",ch->identifier) == 0) { + ESP_LOGD(prefix, "x%p [label=\"%p\\n%s\", shape=box];", + ch, ch, ch->identifier); + } else { + if (ch->ctx == NULL) { + ESP_LOGD(prefix, "x%p [label=\"producer\\n%p\\n%s\" shape=house];", + ch, ch, ch->identifier); + } else { + ESP_LOGD(prefix, "x%p [label=\"consumer\\n%p\\n%s\" shape=invhouse];", + ch, ch, ch->identifier); + } + } + ESP_LOGD(prefix, "x%p -> x%p [label=sn, color=red];", + ch, list_entry(ch->same.next, struct channel, same)); + ESP_LOGD(prefix, "x%p -> x%p [label=sp, color=red];", + ch, list_entry(ch->same.prev, struct channel, same)); + ESP_LOGD(prefix, "x%p -> x%p [label=un, color=blue];", + ch, list_entry(ch->unique.next, struct channel, unique)); + ESP_LOGD(prefix, "x%p -> x%p [label=up, color=blue];", + ch, list_entry(ch->unique.prev, struct channel, unique)); +} + +/** + * channel_debug_printAll - print all reachable channel attributes + * @prefix: prefix string in debug output + * @ch: channel object to start search from + */ +static +void +__attribute__((unused)) +channel_debug_printAll +(const char *prefix, const Channel *ch) +{ + channel_debug_print(prefix, ch); + Channel *chUniq; + list_for_each_entry(chUniq, &ch->unique, unique) { + channel_debug_print(prefix, chUniq); + Channel *currSame; + list_for_each_entry(currSame, &chUniq->same, same) { + channel_debug_print(prefix, currSame); + } + } + Channel *chSame; + list_for_each_entry(chSame, &ch->same, same) { + channel_debug_print(prefix, chSame); + Channel *currUniq; + list_for_each_entry(currUniq, &chSame->unique, unique) { + channel_debug_print(prefix, currUniq); + Channel *currSame; + list_for_each_entry(currSame, &currUniq->same, same) { + channel_debug_print(prefix, currSame); + } + } + } +} + +/** + * channel_debug_printAll_dot - print all reachable channel attributes in dot format + * @prefix: prefix string in debug output + * @ch: channel object to start search from + */ +static +void +__attribute__((unused)) +channel_debug_printAll_dot +(const char *prefix, const Channel *ch) +{ + ESP_LOGD(prefix,"digraph G {"); + ESP_LOGD(prefix,"layout=neato;"); + ESP_LOGD(prefix,"overlap=false;"); + channel_debug_print_dot(prefix, ch); + Channel *chUniq; + list_for_each_entry(chUniq, &ch->unique, unique) { + channel_debug_print_dot(prefix, chUniq); + Channel *currSame; + list_for_each_entry(currSame, &chUniq->same, same) { + channel_debug_print_dot(prefix, currSame); + } + } + Channel *chSame; + list_for_each_entry(chSame, &ch->same, same) { + channel_debug_print_dot(prefix, chSame); + Channel *currUniq; + list_for_each_entry(currUniq, &chSame->unique, unique) { + channel_debug_print_dot(prefix, currUniq); + Channel *currSame; + list_for_each_entry(currSame, &currUniq->same, same) { + channel_debug_print_dot(prefix, currSame); + } + } + } + ESP_LOGD(prefix,"}"); +} + +#endif diff --git a/components/channel/include/channel_internal.h b/components/channel/include/channel_internal.h new file mode 100644 index 0000000..360d8d7 --- /dev/null +++ b/components/channel/include/channel_internal.h @@ -0,0 +1,58 @@ +#ifndef CHANNEL_INTERNAL_H_ +#define CHANNEL_INTERNAL_H_ + +#include "channel.h" + +#ifndef CONFIG_CHANNEL_INTERNAL_EXPORT_SYMBOLS +#define EXPORT static +#else +#define EXPORT +#endif + +/** + * channel_internal_init - initialize channel object + * @ch: pointer to channel object to initialize + * @identifier: identifier string of the channel + * @ctx: pointer to context of the callback + * @flags: additional flags for the callback to modify behaviour + * @callback: function to call on new data + */ +EXPORT +void +channel_internal_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback); + +/** + * channel_internal_register - register a channel + * @ch: channel to register + * + * This function adds channel to the message passing structure. + * It doesn't make a difference whether this is a message + * provider or consumer. The difference is only made by the channels + * callback implementation. + */ +EXPORT +void +channel_internal_register +(Channel *ch); + +/** + * channel_debug_unregister - removes a channel input or output from the message structure + * @ch: channel element to unregister + */ +EXPORT +void +channel_internal_unregister +(Channel *ch); + +/** + * channel_internal_resetRoot - reset global root node + * ATTENTION: THIS ONLY RESETS THE GLOBAL ROOT NODE! + * ALL CHANNELS OBJECTS WILL BE BROKEN! + */ +EXPORT +void +channel_internal_resetRoot +(void); + +#endif \ No newline at end of file diff --git a/components/channel/src/channel.c b/components/channel/src/channel.c new file mode 100644 index 0000000..584d89d --- /dev/null +++ b/components/channel/src/channel.c @@ -0,0 +1,163 @@ +#include "channel.h" +#include "channel_internal.h" + +#include +#include + +/** + * CHANNEL_INIT - Macro to provide initialization for channel object + */ +#define CHANNEL_INIT(name, identifier, ctx, flags, callback) { "" identifier "", LIST_HEAD_INIT(name.same), LIST_HEAD_INIT(name.unique), ctx, flags, callback } + +/** + * CHANNEL - Macro to create and initialize channel object + */ +#define CHANNEL(name, identifier, ctx, flags, callback) Channel name = CHANNEL_INIT(name, "" identifier "", ctx, flags, callback) + +static CHANNEL(root_channel, "", NULL, 0, NULL); + +void +channel_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback) +{ + channel_internal_init(ch, identifier, ctx, flags, callback); + channel_internal_register(ch); +} + +EXPORT +void +channel_internal_init +(Channel *ch, const char *identifier, void *ctx, const BaseType_t flags, Channel_callback callback) +{ + ch->identifier = identifier; + INIT_LIST_HEAD(&ch->unique); + INIT_LIST_HEAD(&ch->same); + ch->ctx = ctx; + ch->flags = flags; + ch->callback = callback; +} + +void +channel_reset +(Channel *ch) +{ + channel_internal_unregister(ch); +} + +EXPORT +void +channel_internal_register +(Channel *ch) +{ + Channel *curr; + list_for_each_entry(curr, &root_channel.unique, unique) { + if (strcmp(ch->identifier, curr->identifier) == 0) { + list_add(&ch->same, &curr->same); + return; + } + } + + list_add(&ch->unique, &root_channel.unique); +} + +EXPORT +void +channel_internal_unregister +(Channel *ch) +{ + int is_unique = ! list_empty(&ch->unique); + int is_same = ! list_empty(&ch->same); + + //channel is not registered + if (!is_unique && !is_same) { + return; + } + //channel is not in the unique chain so just delete it + if (!is_unique && is_same) { + list_del(&ch->same); + INIT_LIST_HEAD(&ch->same); + return; + } + + //channel is in the unique list, but single element so also just delete it + if (is_unique && !is_same) { + list_del(&ch->unique); + INIT_LIST_HEAD(&ch->unique); + return; + } + + //channel is in unique list and there are others in same list, so replace channel + if (is_unique && is_same) { + Channel *next_same = list_entry(ch->same.next, struct channel, same); + Channel *next_uniq = list_entry(ch->unique.next, struct channel, unique); + list_del(&ch->same); + INIT_LIST_HEAD(&ch->same); + list_del(&ch->unique); + INIT_LIST_HEAD(&ch->unique); + list_add(&next_same->unique, &next_uniq->unique); + return; + } +} + +BaseType_t +channel_send +(const Channel *ch, const void *data, const TickType_t timeout) +{ + if (!ch->callback) { + return pdPASS; + } + return ch->callback(ch->ctx, data, timeout, ch->flags); +} + +BaseType_t +channel_broadcast +(Channel_broadcast *handle) +{ + Channel *ch = handle->ch; + Channel *pos = handle->pos; + void *data = handle->data; + TickType_t start = xTaskGetTickCount(); + TickType_t timeout = handle->timeout; + BaseType_t status; + list_for_each_entry_continue(pos, (&ch->same), same) { + if (pos->callback) { + if (channel_broadcast_timeout(handle)) { + handle->pos = pos; + return pdFAIL; + } + if (timeout != portMAX_DELAY) { + timeout = handle->timeout - handle->elapsed; + } + status = pos->callback(pos->ctx, + data, + timeout, + pos->flags); + handle->elapsed += xTaskGetTickCount() - start; + if (!status) { + handle->pos = pos; + return status; + }; + } + } + if (channel_broadcast_timeout(handle)) { + handle->pos = pos; + return pdFAIL; + } + if (timeout != portMAX_DELAY) { + timeout = handle->timeout - handle->elapsed; + } + handle->pos = NULL; + status = channel_send(handle->ch, handle->data, timeout); + handle->elapsed += xTaskGetTickCount() - start; + return status; +} + +EXPORT +void +__attribute__((unused)) +channel_internal_resetRoot +(void) +{ + INIT_LIST_HEAD(&root_channel.unique); + INIT_LIST_HEAD(&root_channel.same); +} diff --git a/components/channel/test/CMakeLists.txt b/components/channel/test/CMakeLists.txt new file mode 100644 index 0000000..cd1502a --- /dev/null +++ b/components/channel/test/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register( + SRC_DIRS + "." + INCLUDE_DIRS + "." + REQUIRES + unity + channel + linux +) \ No newline at end of file diff --git a/components/channel/test/channel_test.c b/components/channel/test/channel_test.c new file mode 100644 index 0000000..8187ce8 --- /dev/null +++ b/components/channel/test/channel_test.c @@ -0,0 +1,755 @@ +#include "unity.h" +#include +#include +#include "channel.h" +#include "channel_internal.h" + +static void *callback_ctx; +static void *callback_data; +static TickType_t callback_timeout; +static BaseType_t callback_flags; + +BaseType_t +returnPdPASS +(void *ctx, + const void *data, + TickType_t timeout, + const BaseType_t flags) +{ + callback_ctx = ctx; + callback_data = (void*) data; + callback_timeout = timeout; + callback_flags = flags; + return pdPASS; +} + +BaseType_t +returnErrQUEUE_FULL +(void *ctx, + const void *data, + TickType_t timeout, + const BaseType_t flags) +{ + callback_ctx = ctx; + callback_data = (void*) data; + callback_timeout = timeout; + callback_flags = flags; + return errQUEUE_FULL; +} + +#define TEST_ASSERT_LIST_LENGTH(head, expect) \ +{ \ + uint length = 1; \ + struct list_head *pos; \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + list_for_each(pos,(head)) { \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + length++; \ + } \ + TEST_ASSERT_EQUAL_UINT((expect),length); \ +} + +#define TEST_ASSERT_LIST_OCCURRENCE(head,item,expect) \ +{ \ + uint count = 0; \ + struct list_head *pos; \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + TEST_ASSERT_NOT_NULL((item)->next); \ + TEST_ASSERT_NOT_NULL((item)->prev); \ + list_for_each(pos,(head)) { \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + if ((head) == (item)) { \ + count++; \ + } \ + } \ + TEST_ASSERT_EQUAL_UINT((expect),count); \ +} + +#define TEST_ASSERT_LIST_EMPTY(head) \ +{ \ + TEST_ASSERT_NOT_NULL((head)->next); \ + TEST_ASSERT_NOT_NULL((head)->prev); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)); \ + TEST_ASSERT_EQUAL_PTR((head)->next, (head)->prev); \ +} + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INIT +TEST_CASE("channel_init", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_internal_init(&ch, identifier, ctx, flags, callback); + + TEST_ASSERT_NOT_NULL(ch.identifier); + TEST_ASSERT_EQUAL_STRING(identifier, ch.identifier); + + TEST_ASSERT_LIST_EMPTY(&ch.same); + TEST_ASSERT_LIST_EMPTY(&ch.unique); + + TEST_ASSERT_NOT_NULL(ch.ctx); + TEST_ASSERT_EQUAL_PTR(ch.ctx, ctx); + + TEST_ASSERT_EQUAL_INT(ch.flags, flags); + + TEST_ASSERT_NOT_NULL(ch.callback); + TEST_ASSERT_EQUAL_PTR(ch.callback, callback); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SETCONTEXT +TEST_CASE("channel_setContext", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx0 = (void*)0x1; + void *ctx1 = (void*)0x2; + BaseType_t flags = 0x3; + Channel_callback callback = &returnPdPASS; + channel_internal_init(&ch, identifier, ctx0, flags, callback); + + channel_setContext(&ch, ctx1); + + TEST_ASSERT_NOT_NULL(ch.ctx); + TEST_ASSERT_EQUAL_PTR(ch.ctx, ctx1); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SETCONTEXT +TEST_CASE("channel_setCallback", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback0 = &returnPdPASS; + Channel_callback callback1 = &returnErrQUEUE_FULL; + channel_internal_init(&ch, identifier, ctx, flags, callback0); + + channel_setCallback(&ch, callback1); + + TEST_ASSERT_NOT_NULL(ch.callback); + TEST_ASSERT_EQUAL_PTR(ch.callback, callback1); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INTERNAL_REGISTER +TEST_CASE(INCREMENT"channel_internal_register", "[channel]") +{ + channel_internal_resetRoot(); + Channel ch0, ch1, ch2, ch3, ch4, ch5; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + char identifier2[] = "test_channel2"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_internal_init(&ch0, identifier0, ctx, flags, callback); + channel_internal_init(&ch1, identifier0, ctx, flags, callback); + channel_internal_init(&ch2, identifier0, ctx, flags, callback); + channel_internal_init(&ch3, identifier1, ctx, flags, callback); + channel_internal_init(&ch4, identifier1, ctx, flags, callback); + channel_internal_init(&ch5, identifier2, ctx, flags, callback); + + channel_internal_register(&ch0); + TEST_ASSERT_LIST_EMPTY(&ch0.same); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + + channel_internal_register(&ch1); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + + channel_internal_register(&ch2); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 2); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + + channel_internal_register(&ch3); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 3); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + + channel_internal_register(&ch4); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + + channel_internal_register(&ch5); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + channel_internal_resetRoot(); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_INTERNAL_UNREGISTER +TEST_CASE("channel_internal_unregister", "[channel]") +{ + channel_internal_resetRoot(); + Channel ch0, ch1, ch2, ch3, ch4, ch5; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + char identifier2[] = "test_channel2"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_init(&ch0, identifier0, ctx, flags, callback); + channel_init(&ch1, identifier0, ctx, flags, callback); + channel_init(&ch2, identifier0, ctx, flags, callback); + channel_init(&ch3, identifier1, ctx, flags, callback); + channel_init(&ch4, identifier1, ctx, flags, callback); + channel_init(&ch5, identifier2, ctx, flags, callback); + + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_LENGTH(&ch3.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch4.unique); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch3.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch4.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + // unregister unique& same channel + channel_internal_unregister(&ch3); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + //ch3.unique should be empty + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + //ch4 should now be unique + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 3); + TEST_ASSERT_LIST_LENGTH(&ch2.same, 3); + //ch3.same should be empty + TEST_ASSERT_LIST_EMPTY (&ch3.same); + //ch4.same should be empty + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + // unregister same channel + channel_internal_unregister(&ch2); + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 4); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 4); + TEST_ASSERT_LIST_LENGTH(&ch5.unique, 4); + //ch0 & ch1 same should decrement + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + //ch2 same should be empty + TEST_ASSERT_LIST_EMPTY (&ch2.same); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + // unregister unique channel + channel_internal_unregister(&ch5); + //ch0 unique should decrement + TEST_ASSERT_LIST_LENGTH(&ch0.unique, 3); + TEST_ASSERT_LIST_EMPTY (&ch1.unique); + TEST_ASSERT_LIST_EMPTY (&ch2.unique); + TEST_ASSERT_LIST_EMPTY (&ch3.unique); + //ch4 unique should decrement + TEST_ASSERT_LIST_LENGTH(&ch4.unique, 3); + //ch5 uniq should be empty + TEST_ASSERT_LIST_EMPTY (&ch5.unique); + TEST_ASSERT_LIST_LENGTH(&ch0.same, 2); + TEST_ASSERT_LIST_LENGTH(&ch1.same, 2); + TEST_ASSERT_LIST_EMPTY (&ch2.same); + TEST_ASSERT_LIST_EMPTY (&ch3.same); + TEST_ASSERT_LIST_EMPTY (&ch4.same); + TEST_ASSERT_LIST_EMPTY (&ch5.same); + + channel_internal_resetRoot(); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_SEND +TEST_CASE("channel_send", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*)0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_internal_init(&ch, identifier, ctx, flags, callback); + + callback_ctx = NULL; + callback_data = NULL; + callback_timeout = 0; + callback_flags = 0; + + void *data = (void*)0x3; + TickType_t timeout = 0x4; + channel_send(&ch, data, timeout); + + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx); + TEST_ASSERT_EQUAL_PTR(callback_data, data); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST_INIT +TEST_CASE("channel_broadcast_init", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*) 0x1; + BaseType_t flags = 0x2; + Channel_callback callback = &returnPdPASS; + channel_internal_init(&ch, identifier, ctx, flags, callback); + + Channel_broadcast handle; + void *data = (void*) 0x3; + TickType_t timeout = 0x4; + channel_broadcast_init(&handle, &ch, data, timeout); + + TEST_ASSERT_EQUAL_PTR(handle.ch,&ch); + TEST_ASSERT_EQUAL_PTR(handle.pos,&ch); + TEST_ASSERT_EQUAL_PTR(handle.data,data); + TEST_ASSERT_EQUAL_UINT(handle.timeout,timeout); + TEST_ASSERT_EQUAL_UINT(handle.elapsed,0); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST_FINISHED +TEST_CASE("channel_broadcast_finished", "[channel]") +{ + Channel ch; + char identifier[] = "test_channel"; + void *ctx = (void*) 0x1; + BaseType_t flags = 0x2; + Channel_callback callback = NULL; + channel_internal_init(&ch, identifier, ctx, flags, callback); + + Channel_broadcast handle; + void *data = (void*) 0x3; + TickType_t timeout = 0x4; + channel_broadcast_init(&handle, &ch, data, timeout); + + TEST_ASSERT_FALSE(channel_broadcast_finished(&handle)); + channel_broadcast(&handle); + TEST_ASSERT_TRUE(channel_broadcast_finished(&handle)); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_ENABLE_BROADCAST +TEST_CASE("channel_broadcast", "[channel]") +{ + channel_internal_resetRoot(); + Channel ch0, ch1, ch2; + char identifier0[] = "test_channel0"; + char identifier1[] = "test_channel1"; + void *ctx0 = (void*)0x1; + void *ctx1 = (void*)0x2; + void *ctx2 = (void*)0x3; + BaseType_t flags0 = 0x4; + BaseType_t flags1 = 0x5; + BaseType_t flags2 = 0x6; + channel_init(&ch0, identifier0, ctx0, flags0, &returnErrQUEUE_FULL); + channel_init(&ch1, identifier1, ctx1, flags1, &returnPdPASS); + channel_init(&ch2, identifier1, ctx2, flags2, &returnErrQUEUE_FULL); + + callback_ctx = NULL; + callback_data = NULL; + callback_timeout = 0; + callback_flags = 0; + + void *data0 = (void*)0x7; + void *data1 = (void*)0x8; + void *data2 = (void*)0x9; + + TickType_t timeout0 = 0xA; + TickType_t timeout1 = 0xB; + TickType_t timeout2 = 0xC; + + BaseType_t result; + Channel_broadcast handle; + channel_broadcast_init(&handle, &ch0, data0, timeout0); + result = channel_broadcast(&handle); + + TEST_ASSERT(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, errQUEUE_FULL); + TEST_ASSERT_EQUAL_PTR(handle.pos, NULL); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx0); + TEST_ASSERT_EQUAL_PTR(callback_data, data0); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout0); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags0); + + channel_broadcast_init(&handle, &ch1, data2, timeout2); + result = channel_broadcast(&handle); + + TEST_ASSERT_FALSE(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, errQUEUE_FULL); + TEST_ASSERT_EQUAL_PTR(handle.pos, &ch2); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx2); + TEST_ASSERT_EQUAL_PTR(callback_data, data2); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout2); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags2); + + handle.data = data1; + handle.timeout = timeout1; + result = channel_broadcast(&handle); + + TEST_ASSERT(channel_broadcast_finished(&handle)); + TEST_ASSERT_EQUAL_INT(result, pdPASS); + TEST_ASSERT_EQUAL_PTR(handle.pos, NULL); + TEST_ASSERT_EQUAL_PTR(callback_ctx, ctx1); + TEST_ASSERT_EQUAL_PTR(callback_data, data1); + TEST_ASSERT_EQUAL_UINT(callback_timeout, timeout1); + TEST_ASSERT_EQUAL_UINT(callback_flags, flags1); + + channel_internal_resetRoot(); +} +#endif + + +#ifdef CONFIG_CHANNEL_TEST_QUEUE_CONSUMER + +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_QUEUE_TEST_QUEUE_SIZE 2 +#define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" + +static int data; +static Channel_consumer cc; + + +static void channel_consumer_task(void *pvParameters) +{ + for (;;) { + xQueueReceive((QueueHandle_t)cc.ctx, &data, portMAX_DELAY); + } +} + +TEST_CASE("channel_queue", "[channel]") +{ + static StaticQueue_t cc_queue; + static QueueHandle_t cc_queue_handle; + static uint8_t cc_queue_buffer[CHANNEL_QUEUE_TEST_QUEUE_SIZE*sizeof(int)]; + + static StackType_t cc_task_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t cc_task; + static TaskHandle_t cc_task_handle; + + data = 0; + + cc_queue_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(int), + cc_queue_buffer, + &cc_queue + ); + configASSERT(cc_queue_handle); + + channel_init_consumer(&cc, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_handle); + + static Channel_producer cp; + channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); + + cc_task_handle = xTaskCreateStatic( + channel_consumer_task, + "channel_consumer_task", + MP_TEST_STACK_SIZE, + NULL, + 1, + cc_task_stack, + &cc_task + ); + configASSERT(cc_task_handle); + + Channel_broadcast br; + int test_data = 3; + channel_broadcast_init(&br, &cp, &test_data, 1); + UBaseType_t res = channel_broadcast(&br); + TEST_ASSERT_EQUAL_INT(pdPASS, res); + vTaskDelay(5); + TEST_ASSERT(channel_broadcast_finished(&br)); + + TEST_ASSERT_EQUAL_INT(data, test_data); + vTaskDelete(cc_task_handle); + vQueueDelete(cc_queue_handle); + channel_internal_resetRoot(); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_QUEUE_TIMEOUT + +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_QUEUE_TEST_QUEUE_1_SIZE 4 +#define CHANNEL_QUEUE_TEST_QUEUE_2_SIZE 1 +#define CHANNEL_QUEUE_TEST_IDENTIFIER_C1 "test_c1" + +static int data; +static Channel_consumer cc_1, cc_2; + + +TEST_CASE("channel_queue timout", "[channel]") +{ + static StaticQueue_t cc_queue_1; + static QueueHandle_t cc_queue_1_handle; + static uint8_t cc_queue_1_buffer[CHANNEL_QUEUE_TEST_QUEUE_1_SIZE*sizeof(int)]; + + cc_queue_1_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_1_SIZE, + sizeof(int), + cc_queue_1_buffer, + &cc_queue_1 + ); + configASSERT(cc_queue_1_handle); + channel_init_consumer(&cc_1, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_1_handle); + + static StaticQueue_t cc_queue_2; + static QueueHandle_t cc_queue_2_handle; + static uint8_t cc_queue_2_buffer[CHANNEL_QUEUE_TEST_QUEUE_2_SIZE*sizeof(int)]; + + cc_queue_2_handle = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_2_SIZE, + sizeof(int), + cc_queue_2_buffer, + &cc_queue_2 + ); + configASSERT(cc_queue_2_handle); + channel_init_consumer(&cc_2, CHANNEL_QUEUE_TEST_IDENTIFIER_C1, cc_queue_2_handle); + + static Channel_producer cp; + channel_init_producer(&cp, CHANNEL_QUEUE_TEST_IDENTIFIER_C1); + + Channel_broadcast br; + int test_data = 5; + channel_broadcast_init(&br, &cp, &test_data, 1); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdPASS); + + unsigned int timeout = 10; + channel_broadcast_init(&br, &cp, &test_data, timeout); + TickType_t start = xTaskGetTickCount(); + TEST_ASSERT_EQUAL_INT(channel_broadcast(&br), pdFAIL); + TEST_ASSERT_FALSE(channel_broadcast_finished(&br)); + TickType_t stop = xTaskGetTickCount(); + TEST_ASSERT_EQUAL_UINT(start + timeout, stop); + TEST_ASSERT_EQUAL_UINT(timeout, br.elapsed); + TEST_ASSERT_EQUAL_PTR(&cc_2, br.pos); + TEST_ASSERT(channel_broadcast_timeout(&br)); + vQueueDelete(cc_queue_1_handle); + vQueueDelete(cc_queue_2_handle); + channel_internal_resetRoot(); +} +#endif + +#ifdef CONFIG_CHANNEL_TEST_QUEUE_LOOP + +#define MP_TEST_STACK_SIZE 0x1000 +#define CHANNEL_LOOP_TEST_QUEUE_SIZE 10 +#define CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE 10 +#define CHANNEL_LOOP_TEST_IDENTIFIER_C1 "c_loop1" +#define CHANNEL_LOOP_TEST_IDENTIFIER_C2 "c_loop2" +#define CHANNEL_LOOP_TEST_LOOP_COUNT 10 + +typedef struct { + Channel_consumer *in; + Channel_producer *out; + unsigned int id; +} task_data; + +static Channel_consumer t1_ch1_co; +static Channel_producer t1_ch2_pr; + +static task_data t1_data = { + .in = &t1_ch1_co, + .out = &t1_ch2_pr, + .id = 1 +}; + +static Channel_consumer t2_ch2_co; +static Channel_producer t2_ch1_pr; + +static task_data t2_data = { + .in = &t2_ch2_co, + .out = &t2_ch1_pr, + .id = 2 +}; + +static void channel_loop_task(void *pvParameters) +{ + task_data *tdata = (task_data *)pvParameters; + static Channel_broadcast bc; + int data = -1; + + for (;;) { + xQueueReceive((QueueHandle_t)tdata->in->ctx, &data, portMAX_DELAY); + + data +=1; + channel_broadcast_init(&bc, tdata->out, &data, portMAX_DELAY); + BaseType_t res = channel_broadcast(&bc); + TEST_ASSERT_EQUAL(pdPASS, res); + + if (data > CHANNEL_LOOP_TEST_LOOP_COUNT) + { + while (1) {} + } + } +} + +TEST_CASE("channel_queue loop", "[channel]") +{ + static StaticQueue_t queue_1; + static QueueHandle_t queue_handle_1; + static uint8_t queue_buffer_1[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(int)]; + + queue_handle_1 = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(int), + queue_buffer_1, + &queue_1 + ); + configASSERT(queue_handle_1); + channel_init_consumer(t1_data.in, CHANNEL_LOOP_TEST_IDENTIFIER_C1, queue_handle_1); + channel_init_producer(t1_data.out, CHANNEL_LOOP_TEST_IDENTIFIER_C2); + + static StaticQueue_t queue_2; + static QueueHandle_t queue_handle_2; + static uint8_t queue_buffer_2[CHANNEL_LOOP_TEST_QUEUE_SIZE*sizeof(int)]; + + queue_handle_2 = xQueueCreateStatic( + CHANNEL_QUEUE_TEST_QUEUE_SIZE, + sizeof(int), + queue_buffer_2, + &queue_2 + ); + configASSERT(queue_handle_2); + channel_init_consumer(t2_data.in, CHANNEL_LOOP_TEST_IDENTIFIER_C2, queue_handle_2); + channel_init_producer(t2_data.out, CHANNEL_LOOP_TEST_IDENTIFIER_C1); + + static StaticQueue_t queue_11; + static QueueHandle_t queue_11_handle; + static uint8_t queue_11_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(int)]; + + + queue_11_handle = xQueueCreateStatic( + CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, + sizeof(int), + queue_11_buffer, + &queue_11 + ); + configASSERT(queue_11_handle); + + static StaticQueue_t queue_22; + static QueueHandle_t queue_22_handle; + static uint8_t queue_22_buffer[CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE*sizeof(int)]; + + + queue_22_handle = xQueueCreateStatic( + CHANNEL_LOOP_TEST_LOG_QUEUE_SIZE, + sizeof(int), + queue_22_buffer, + &queue_22 + ); + configASSERT(queue_22_handle); + + static Channel_consumer ch1_co; + static Channel_consumer ch2_co; + channel_init_consumer(&ch1_co, CHANNEL_LOOP_TEST_IDENTIFIER_C1, queue_11_handle); + channel_init_consumer(&ch2_co, CHANNEL_LOOP_TEST_IDENTIFIER_C2, queue_22_handle); + + static Channel_producer ch1_pr; + static Channel_producer ch2_pr; + channel_init_producer(&ch1_pr, CHANNEL_LOOP_TEST_IDENTIFIER_C1); + channel_init_producer(&ch2_pr, CHANNEL_LOOP_TEST_IDENTIFIER_C2); + + static StackType_t t1_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t t1; + static TaskHandle_t t1_handle; + + t1_handle = xTaskCreateStatic( + channel_loop_task, + "channel_loop_task_1", + MP_TEST_STACK_SIZE, + &t1_data, + 1, + t1_stack, + &t1 + ); + configASSERT(t1_handle); + + static StackType_t t2_stack[MP_TEST_STACK_SIZE]; + static StaticTask_t t2; + static TaskHandle_t t2_handle; + + t2_handle = xTaskCreateStatic( + channel_loop_task, + "channel_loop_task_2", + MP_TEST_STACK_SIZE, + &t2_data, + 1, + t2_stack, + &t2 + ); + configASSERT(t2_handle); + + Channel_broadcast br; + int test_data = 0; + channel_broadcast_init(&br, &ch1_pr, &test_data, 10); + TEST_ASSERT_EQUAL(pdPASS, channel_broadcast(&br)); + TEST_ASSERT(channel_broadcast_finished(&br)); + + int d; + for (int i = 0; i < CHANNEL_LOOP_TEST_LOOP_COUNT + 1; i += 2) + { + xQueueReceive(queue_11_handle, &d, portMAX_DELAY); + + TEST_ASSERT_EQUAL_INT(i, d); + xQueueReceive(queue_22_handle, &d, portMAX_DELAY); + + TEST_ASSERT_EQUAL_INT(i+1, d); + } + + vTaskDelete(t1_handle); + vTaskDelete(t2_handle); + vQueueDelete(queue_11_handle); + vQueueDelete(queue_22_handle); + vQueueDelete(queue_handle_1); + vQueueDelete(queue_handle_2); + channel_internal_resetRoot(); +} +#endif diff --git a/components/linux/CMakeLists.txt b/components/linux/CMakeLists.txt new file mode 100644 index 0000000..351ce70 --- /dev/null +++ b/components/linux/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( +# SRCS + INCLUDE_DIRS + include +# REQUIRES +) \ No newline at end of file diff --git a/components/linux/include/list.h b/components/linux/include/list.h new file mode 100644 index 0000000..e51f8fa --- /dev/null +++ b/components/linux/include/list.h @@ -0,0 +1,521 @@ +/** + * http://www.mcs.anl.gov/~kazutomo/list/list.h + * + * I grub it from linux kernel source code and fix it for user space + * program. Of course, this is a GPL licensed header file. + * + * Here is a recipe to cook list.h for user space program + * + * 1. copy list.h from linux/include/list.h + * 2. remove + * - #ifdef __KERNE__ and its #endif + * - all #include line + * - prefetch() and rcu related functions + * 3. add macro offsetof() and container_of + * + * - kazutomo@mcs.anl.gov + */ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +/** + * @name from other kernel headers + */ +/*@{*/ + +/** + * Get offset of a member + */ +#ifndef offsetof +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif + +/** + * Casts a member of a structure out to the containing structure + * @param ptr the pointer to the member. + * @param type the type of the container struct this is embedded in. + * @param member the name of the member within the struct. + * + */ +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +/*@}*/ + + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/** + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + + + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use as a start point in + * list_for_each_entry_continue + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - iterate over list of given type + * continuing after existing point + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue - iterate over list of given type + * continuing after existing point safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against + * removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + + + + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + + + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \ + pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \ + pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from existing point + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ prefetch(pos->next); 1;}) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop counter. + * @pos: the &struct hlist_node to use as a loop counter. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +#endif \ No newline at end of file