diff --git a/.astyleignore b/.astyleignore index e9a2664..ff4722c 100644 --- a/.astyleignore +++ b/.astyleignore @@ -20,6 +20,7 @@ ^hal/storage_abstraction ^platform/cxxsupport ^platform/tests/UNITTESTS +^events/tests/UNITTESTS ^rtos/source/TARGET_CORTEX/rtx4 ^rtos/source/TARGET_CORTEX/rtx5 ^targets diff --git a/.travis.yml b/.travis.yml index e1e6995..ab261ba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -100,7 +100,8 @@ - | ! git grep '^#include\s["'"']mbed.h['"'"]$' -- '*.c' '*.h' '*.cpp' '*.hpp' \ ':!*platform_mbed.h' ':!*TESTS/*' ':!TEST_APPS/' ':!UNITTESTS/' \ - ':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*' + ':!*tests/*' ':!*targets/*' ':!*TARGET_*' ':!*unsupported/*' \ + ':!*events/tests/*' ### Docs Tests ### @@ -252,10 +253,8 @@ connectivity/libraries/ppp connectivity/netsocket features/nanostack features/lwipstack features/frameworks/greentea-client \ features/frameworks/utest features/frameworks/unity components BUILD - python tools/make.py -t GCC_ARM -m NUCLEO_F103RB --source=. --build=BUILD/NUCLEO_F103RB/GCC_ARM -j0 - # Run local equeue tests - - make -C ${EVENTS}/source test # Run profiling tests - - make -C ${EVENTS}/source prof | tee prof + - make -C ${EVENTS}/tests/unit prof | tee prof after_success: # Update status, comparing with master if possible. - | diff --git a/TESTS/events/equeue/main.cpp b/TESTS/events/equeue/main.cpp deleted file mode 100644 index 061b2d9..0000000 --- a/TESTS/events/equeue/main.cpp +++ /dev/null @@ -1,1093 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBED_EXTENDED_TESTS -#error [NOT_SUPPORTED] When running on CI this test is disabled due to limiting testing time. -#else - -#include "utest/utest.h" -#include "unity/unity.h" -#include "greentea-client/test_env.h" - -#include "equeue.h" -#include "mbed.h" - -using namespace utest::v1; - -#define TEST_EQUEUE_SIZE (4*EVENTS_EVENT_SIZE) -#define TEST_THREAD_STACK_SIZE 512 -#define DISPATCH_INFINITE -1 - -// Test functions -static void pass_func(void *eh) -{ -} - -static void simple_func(void *p) -{ - uint8_t *d = reinterpret_cast(p); - if (*d < 255) { - (*d)++; - } -} - -static void sloth_func(void *p) -{ - ThisThread::sleep_for(10); - (*(reinterpret_cast(p)))++; -} - -struct indirect { - uint8_t *touched; - uint8_t buffer[7]; -}; - -static void indirect_func(void *p) -{ - struct indirect *i = reinterpret_cast(p); - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -static void timing_func(void *p) -{ - struct timing *timing = reinterpret_cast(p); - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - TEST_ASSERT_UINT_WITHIN(10, t2, t1); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -static void fragment_func(void *p) -{ - struct fragment *fragment = reinterpret_cast(p); - timing_func(&fragment->timing); - - struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); - TEST_ASSERT_NOT_NULL(nfragment); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - TEST_ASSERT_NOT_EQUAL(0, id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -static void cancel_func(void *p) -{ - struct cancel *ccel = reinterpret_cast(p); - equeue_cancel(ccel->q, ccel->id); -} - -struct nest { - equeue_t *q; - void (*cb)(void *); - void *data; -}; - -static void nest_func(void *p) -{ - struct nest *nst = reinterpret_cast(p); - equeue_call(nst->q, nst->cb, nst->data); - - ThisThread::sleep_for(10); -} - -static void multithread_thread(equeue_t *p) -{ - equeue_dispatch(p, DISPATCH_INFINITE); -} - -static void background_func(void *p, int ms) -{ - *(reinterpret_cast(p)) = ms; -} - -struct ethread { - equeue_t *q; - int ms; -}; - -static void ethread_dispatch(void *p) -{ - struct ethread *t = reinterpret_cast(p); - equeue_dispatch(t->q, t->ms); -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -static void simple_breaker(void *p) -{ - struct count_and_queue *caq = reinterpret_cast(p); - equeue_break(caq->q); - ThisThread::sleep_for(10); - caq->p++; -} - -// Simple call tests - -/** Test that equeue executes function passed by equeue_call. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call is executed properly. - */ -static void test_equeue_simple_call() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_dispatch(&q, 10); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_in. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_in is executed properly. - */ -static void test_equeue_simple_call_in() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int id = equeue_call_in(&q, 10, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 15); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_dispatch(&q, 10); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_every. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_every is executed properly. - */ -static void test_equeue_simple_call_every() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int id = equeue_call_every(&q, 10, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 15); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_post. - * - * Given queue is initialized. - * When the event is posted and after that equeue_dispatch is called. - * Then function passed by equeue_post is executed properly. - */ -static void test_equeue_simple_post() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(i); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, *i->touched); - - equeue_destroy(&q); -} - - -// Misc tests - -/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. - * - * Given queue is initialized. - * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. - * Then functions attached to equeue events destructors are executed properly. - */ -static void test_equeue_destructor() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct indirect *e; - int ids[3]; - - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(3, touched); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - TEST_ASSERT_EQUAL_UINT8(3, touched); - - equeue_dispatch(&q, 0); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - TEST_ASSERT_NOT_NULL(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_destroy(&q); - TEST_ASSERT_EQUAL_UINT8(3, touched); -} - -/** Test that equeue_alloc returns 0 when equeue can not be allocated. - * - * Given queue is initialized. - * When equeue_alloc is called and equeue can not be allocated - * Then function equeue_alloc returns NULL. - */ -static void test_equeue_allocation_failure() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); - TEST_ASSERT_NULL(p); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - TEST_ASSERT_NULL(p); - - equeue_destroy(&q); -} - -/** Test that equeue does not execute evenets that has been canceled. - * - * Given queue is initialized. - * When events are canceled by equeue_cancel. - * Then they are not executed by calling equeue_dispatch. - */ -template -static void test_equeue_cancel() -{ - equeue_t q; - int err = equeue_create(&q, (N * EVENTS_EVENT_SIZE)); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - int ids[N]; - - for (int i = 0; i < N; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - TEST_ASSERT_NOT_EQUAL(0, ids[i]); - } - - for (int i = N - 1; i >= 0; i--) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT(0, touched); - - equeue_destroy(&q); -} - -/** Test that events can be cancelled by function executed by equeue_dispatch. - * - * Given queue is initialized. - * When event is cancelled by another event while dispatching. - * Then event that was cancelled is not being executed. - */ -static void test_equeue_cancel_inflight() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - - int id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); - TEST_ASSERT_NOT_NULL(ccel); - ccel->q = &q; - ccel->id = 0; - - id = equeue_post(&q, cancel_func, ccel); - TEST_ASSERT_NOT_EQUAL(0, id); - - ccel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - equeue_destroy(&q); -} - -/** Test that unnecessary canceling events would not affect executing other events. - * - * Given queue is initialized. - * When event is unnecessary canceled by equeue_cancel. - * Then other events are properly executed after calling equeue_dispatch. - */ -static void test_equeue_cancel_unnecessarily() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. - * - * Given queue is initialized. - * When events have 0 ms period time. - * Then dispatching would not end up in infinite loop. - */ -static void test_equeue_loop_protect() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - - touched1 = 0; - uint8_t touched2 = 0; - equeue_call_every(&q, 1, simple_func, &touched2); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break breaks event queue out of dispatching. - * - * Given queue is initialized. - * When equeue_break is called. - * Then event queue will stop dispatching after finisching current dispatching cycle. - */ -static void test_equeue_break() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - uint8_t touched2 = 0; - equeue_call_every(&q, 5, simple_func, &touched2); - - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break function breaks equeue dispatching only once. - * - * Given queue is initialized. - * When equeue_break is called several times. - * Then equeue is stopped only once. - */ -static void test_equeue_break_no_windup() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - touched = 0; - equeue_dispatch(&q, 55); - TEST_ASSERT(touched > 1); - - equeue_destroy(&q); -} - -/** Test that function passed by equeue_call_every is being executed periodically. - * - * Given queue is initialized. - * When function is passed by equeue_call_every with specified period. - * Then event is executed (dispatch time/period) times. - */ -static void test_equeue_period() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - TEST_ASSERT_EQUAL_UINT8(5, touched); - - equeue_destroy(&q); -} - -/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. - * - * Given queue is initialized. - * When nested function is added to enqueue. - * Then it is executed in the next dispatch, or after execution of "mother" function. - */ -static void test_equeue_nested() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - TEST_ASSERT_NOT_NULL(nst); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - int id = equeue_post(&q, nest_func, nst); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 5); - TEST_ASSERT_EQUAL_UINT8(0, touched); - - equeue_dispatch(&q, 1); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - touched = 0; - nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - TEST_ASSERT_NOT_NULL(nst); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - id = equeue_post(&q, nest_func, nst); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 20); - TEST_ASSERT_EQUAL_UINT8(1, touched); - - equeue_destroy(&q); -} - -/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. - * - * Given queue is initialized. - * When sloth function is being called before other functions. - * Then if it is possible all functions start according to predefined schedule correctly. - */ -static void test_equeue_sloth() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - uint8_t touched3 = 0; - int id = equeue_call(&q, sloth_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id); - - id = equeue_call_in(&q, 5, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id); - - id = equeue_call_in(&q, 15, simple_func, &touched3); - TEST_ASSERT_NOT_EQUAL(0, id); - - equeue_dispatch(&q, 20); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - TEST_ASSERT_EQUAL_UINT8(1, touched3); - - equeue_destroy(&q); -} - -/** Test that equeue can be broken of dispatching from a different thread. - * - * Given queue is initialized. - * When equeue starts dispatching in one thread. - * Then it can be stopped from another thread via equeue_break. - */ -static void test_equeue_multithread() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 1, simple_func, &touched); - - Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); - t1.start(callback(multithread_thread, &q)); - ThisThread::sleep_for(10); - equeue_break(&q); - err = t1.join(); - TEST_ASSERT_EQUAL_INT(0, err); - - TEST_ASSERT(touched > 1); - - equeue_destroy(&q); -} - -/** Test that variable referred via equeue_background shows value in ms to the next event. - * - * Given queue is initialized. - * When variable is referred via equeue_background. - * Then it depicts the time in ms to the next event. - */ -static void test_equeue_background() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - - int ms; - equeue_background(&q, background_func, &ms); - TEST_ASSERT_EQUAL_INT(20, ms); - - id = equeue_call_in(&q, 10, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - TEST_ASSERT_EQUAL_INT(10, ms); - - id = equeue_call(&q, pass_func, 0); - TEST_ASSERT_NOT_EQUAL(0, id); - TEST_ASSERT_EQUAL_INT(0, ms); - - equeue_dispatch(&q, 0); - TEST_ASSERT_EQUAL_INT(10, ms); - - equeue_destroy(&q); - TEST_ASSERT_EQUAL_INT(-1, ms); -} - -/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. - * - * Given queue is initialized. - * When target chained equeue is dispatched. - * Then events from both chained equeues are executed. - */ -static void test_equeue_chain() -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - - int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - id1 = equeue_call_in(&q1, 5, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call_in(&q2, 5, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_cancel(&q1, id1); - equeue_cancel(&q2, id2); - - id1 = equeue_call_in(&q1, 10, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call_in(&q2, 10, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 30); - - TEST_ASSERT_EQUAL_UINT8(3, touched1); - TEST_ASSERT_EQUAL_UINT8(3, touched2); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -/** Test that unchaining equeues makes them work on their own. - * - * Given queue is initialized. - * When equeue is unchained. - * Then it can be only dispatched by calling with reference to it. - */ -static void test_equeue_unchain() -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - int id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - int id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_chain(&q2, 0); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q1, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(0, touched2); - - equeue_dispatch(&q2, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_chain(&q1, &q2); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - TEST_ASSERT_NOT_EQUAL(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - TEST_ASSERT_NOT_EQUAL(0, id2); - - equeue_dispatch(&q2, 0); - TEST_ASSERT_EQUAL_UINT8(1, touched1); - TEST_ASSERT_EQUAL_UINT8(1, touched2); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests - -/** Test that equeue keeps good time at starting events. - * - * Given queue is initialized. - * When equeue is being dispatched. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_simple_barrage() -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - TEST_ASSERT_EQUAL_INT(0, err); - - for (int i = 0; i < N; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - TEST_ASSERT_NOT_NULL(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. - * - * Given queue is initialized. - * When equeue is being dispatched and new events are added via already placed in equeue. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_fragmenting_barrage() -{ - equeue_t q; - int err = equeue_create(&q, - 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); - TEST_ASSERT_EQUAL_INT(0, err); - - for (int i = 0; i < N; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); - TEST_ASSERT_NOT_NULL(fragment); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events even if it is working on different thread. - * - * Given queue is initialized. - * When equeue is being dispatched on different thread. - * Then events happen according to the schedule with an error within a specified range. - */ -template -static void test_equeue_multithreaded_barrage() -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - TEST_ASSERT_EQUAL_INT(0, err); - - struct ethread t; - t.q = &q; - t.ms = N * 100; - - Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); - - t1.start(callback(ethread_dispatch, &t)); - - for (int i = 0; i < N; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - TEST_ASSERT_NOT_NULL(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - TEST_ASSERT_NOT_EQUAL(0, id); - } - - err = t1.join(); - TEST_ASSERT_EQUAL_INT(0, err); - - equeue_destroy(&q); -} - -/** Test that break request flag is cleared when equeue stops dispatching timeouts. - * - * Given queue is initialized. - * When equeue break request flag is called but equeue stops dispatching because of timeout. - * Then next equeue dispatch is not stopped. - */ -static void test_equeue_break_request_cleared_on_timeout() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL(0, err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_in(&q, 1, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - - TEST_ASSERT_EQUAL_INT(1, pq.p); - - equeue_cancel(&q, id); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - TEST_ASSERT_EQUAL_UINT8(5, touched); - - equeue_destroy(&q); -} - -/** Test that siblings events don't have next pointers. - * - * Given queue is initialized. - * When events are scheduled on the same time. - * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. - */ -static void test_equeue_sibling() -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - TEST_ASSERT_EQUAL(0, err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - TEST_ASSERT_NULL(s->next); - } - } - equeue_cancel(&q, id0); - equeue_cancel(&q, id1); - equeue_cancel(&q, id2); - equeue_destroy(&q); -} - -struct user_allocated_event { - struct equeue_event e; - uint8_t touched; -}; - -/** Test that equeue executes user allocated events passed by equeue_post. - * - * Given queue is initialized and its size is set to store one event at max in its internal memory. - * When post events allocated in queues internal memory (what is done by calling equeue_call). - * Then only one event can be posted due to queue memory size. - * When post user allocated events. - * Then number of posted events is not limited by queue memory size. - * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. - * Then both types of events are executed properly. - */ -static void test_equeue_user_allocated_event_post() -{ - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - TEST_ASSERT_EQUAL_INT(0, err); - - uint8_t touched = 0; - user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - TEST_ASSERT_NOT_EQUAL(0, equeue_call_every(&q, 10, simple_func, &touched)); - TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); - TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - equeue_cancel_user_allocated(&q, &e3.e); - equeue_cancel_user_allocated(&q, &e3.e); - - equeue_dispatch(&q, 11); - - TEST_ASSERT_EQUAL_UINT8(1, touched); - TEST_ASSERT_EQUAL_UINT8(1, e1.touched); - TEST_ASSERT_EQUAL_UINT8(1, e2.touched); - TEST_ASSERT_EQUAL_UINT8(0, e3.touched); - TEST_ASSERT_EQUAL_UINT8(1, e4.touched); - TEST_ASSERT_EQUAL_UINT8(1, e5.touched); - - e3.e.target = 10; // set target as it's modified by equeue_call - e3.e.period = 10; // set period as it's reset by equeue_cancel - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_dispatch(&q, 101); - - TEST_ASSERT_EQUAL_UINT8(11, touched); - TEST_ASSERT_EQUAL_UINT8(1, e1.touched); - TEST_ASSERT_EQUAL_UINT8(11, e2.touched); - TEST_ASSERT_EQUAL_UINT8(10, e3.touched); - TEST_ASSERT_EQUAL_UINT8(11, e4.touched); - TEST_ASSERT_EQUAL_UINT8(1, e5.touched); - - equeue_destroy(&q); -} - -Case cases[] = { - Case("simple call test", test_equeue_simple_call), - Case("simple call in test", test_equeue_simple_call_in), - Case("simple call every test", test_equeue_simple_call_every), - Case("simple post test", test_equeue_simple_post), - - Case("destructor test", test_equeue_destructor), - Case("allocation failure test", test_equeue_allocation_failure), - Case("cancel test", test_equeue_cancel<20>), - Case("cancel inflight test", test_equeue_cancel_inflight), - Case("cancel unnecessarily test", test_equeue_cancel_unnecessarily), - Case("loop protect test", test_equeue_loop_protect), - Case("break test", test_equeue_break), - Case("break no windup test", test_equeue_break_no_windup), - Case("period test", test_equeue_period), - Case("nested test", test_equeue_nested), - Case("sloth test", test_equeue_sloth), - - Case("multithread test", test_equeue_multithread), - - Case("background test", test_equeue_background), - Case("chain test", test_equeue_chain), - Case("unchain test", test_equeue_unchain), - - Case("simple barrage test", test_equeue_simple_barrage<20>), - Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>), - Case("multithreaded barrage test", test_equeue_multithreaded_barrage<10>), - Case("break request cleared on timeout test", test_equeue_break_request_cleared_on_timeout), - Case("sibling test", test_equeue_sibling), - Case("user allocated event test", test_equeue_user_allocated_event_post) - -}; - -utest::v1::status_t greentea_test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(40, "default_auto"); - return greentea_test_setup_handler(number_of_cases); -} - -Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); - -int main() -{ - Harness::run(specification); -} - -#endif // MBED_EXTENDED_TESTS diff --git a/TESTS/events/queue/main.cpp b/TESTS/events/queue/main.cpp deleted file mode 100644 index 1b03793..0000000 --- a/TESTS/events/queue/main.cpp +++ /dev/null @@ -1,549 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed_events.h" -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported -#else - -using namespace utest::v1; - -// Assume that tolerance is 5% of measured time. -#define DELTA(ms) (ms / 20) - -// TEST_EQUEUE_SIZE was reduced below 1024B to fit this test to devices with small RAM (RAM <= 16kB) -// additionally TEST_EQUEUE_SIZE was expressed in EVENTS_EVENT_SIZE to increase readability -// (for more details about EVENTS_EVENT_SIZE see EventQueue constructor) -#define TEST_EQUEUE_SIZE (18*EVENTS_EVENT_SIZE) - -// By empirical, we take 80MHz CPU/2ms delay as base tolerance for time left test. -// For higher CPU frequency, tolerance is fixed to 2ms. -// For lower CPU frequency, tolerance is inversely proportional to CPU frequency. -// E.g.: -// 100MHz: 2ms -// 80MHz: 2ms -// 64MHz: 3ms -// 48MHz: 4ms -#define ALLOWED_TIME_LEFT_TOLERANCE_MS ((SystemCoreClock >= 80000000) ? 2 : ((80000000 * 2 + SystemCoreClock - 1) / SystemCoreClock)) - -// flag for called -volatile bool touched = false; - -// static functions -void func5(int a0, int a1, int a2, int a3, int a4) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3 | a4, 0x1f); -} - -void func4(int a0, int a1, int a2, int a3) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3, 0xf); -} - -void func3(int a0, int a1, int a2) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1 | a2, 0x7); -} - -void func2(int a0, int a1) -{ - touched = true; - TEST_ASSERT_EQUAL(a0 | a1, 0x3); -} - -void func1(int a0) -{ - touched = true; - TEST_ASSERT_EQUAL(a0, 0x1); -} - -void func0() -{ - touched = true; -} - -#define SIMPLE_POSTS_TEST(i, ...) \ -void simple_posts_test##i() { \ - EventQueue queue(TEST_EQUEUE_SIZE); \ - \ - touched = false; \ - queue.call(func##i,##__VA_ARGS__); \ - queue.dispatch(0); \ - TEST_ASSERT(touched); \ - \ - touched = false; \ - queue.call_in(1, func##i,##__VA_ARGS__); \ - queue.dispatch(2); \ - TEST_ASSERT(touched); \ - \ - touched = false; \ - queue.call_every(1, func##i,##__VA_ARGS__); \ - queue.dispatch(2); \ - TEST_ASSERT(touched); \ -} - -SIMPLE_POSTS_TEST(5, 0x01, 0x02, 0x04, 0x08, 0x010) -SIMPLE_POSTS_TEST(4, 0x01, 0x02, 0x04, 0x08) -SIMPLE_POSTS_TEST(3, 0x01, 0x02, 0x04) -SIMPLE_POSTS_TEST(2, 0x01, 0x02) -SIMPLE_POSTS_TEST(1, 0x01) -SIMPLE_POSTS_TEST(0) - - -void time_func(Timer *t, int ms) -{ - TEST_ASSERT_INT_WITHIN(DELTA(ms), ms, t->read_ms()); - t->reset(); -} - -template -void call_in_test() -{ - Timer tickers[N]; - - EventQueue queue(TEST_EQUEUE_SIZE); - - for (int i = 0; i < N; i++) { - tickers[i].start(); - queue.call_in((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); - } - - queue.dispatch(N * 100); -} - -template -void call_every_test() -{ - Timer tickers[N]; - - EventQueue queue(TEST_EQUEUE_SIZE); - - for (int i = 0; i < N; i++) { - tickers[i].start(); - queue.call_every((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); - } - - queue.dispatch(N * 100); -} - -void allocate_failure_test() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - int id; - - for (int i = 0; i < 100; i++) { - id = queue.call((void (*)())0); - } - - TEST_ASSERT(!id); -} - -void no() -{ - TEST_ASSERT(false); -} - -template -void cancel_test1() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - - int ids[N]; - - for (int i = 0; i < N; i++) { - ids[i] = queue.call_in(1000, no); - } - - for (int i = N - 1; i >= 0; i--) { - queue.cancel(ids[i]); - } - - queue.dispatch(0); -} - - -// Testing the dynamic arguments to the event class -unsigned counter = 0; - -void count5(unsigned a0, unsigned a1, unsigned a2, unsigned a3, unsigned a5) -{ - counter += a0 + a1 + a2 + a3 + a5; -} - -void count4(unsigned a0, unsigned a1, unsigned a2, unsigned a3) -{ - counter += a0 + a1 + a2 + a3; -} - -void count3(unsigned a0, unsigned a1, unsigned a2) -{ - counter += a0 + a1 + a2; -} - -void count2(unsigned a0, unsigned a1) -{ - counter += a0 + a1; -} - -void count1(unsigned a0) -{ - counter += a0; -} - -void count0() -{ - counter += 0; -} - -void event_class_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - Event e5(&queue, count5); - Event e4(&queue, count5, 1); - Event e3(&queue, count5, 1, 1); - Event e2(&queue, count5, 1, 1, 1); - Event e1(&queue, count5, 1, 1, 1, 1); - Event e0(&queue, count5, 1, 1, 1, 1, 1); - - e5.post(1, 1, 1, 1, 1); - e4.post(1, 1, 1, 1); - e3.post(1, 1, 1); - e2.post(1, 1); - e1.post(1); - e0.post(); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 30); -} - -void event_class_helper_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - Event e5 = queue.event(count5, 1, 1, 1, 1, 1); - Event e4 = queue.event(count4, 1, 1, 1, 1); - Event e3 = queue.event(count3, 1, 1, 1); - Event e2 = queue.event(count2, 1, 1); - Event e1 = queue.event(count1, 1); - Event e0 = queue.event(count0); - - e5.post(); - e4.post(); - e3.post(); - e2.post(); - e1.post(); - e0.post(); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 15); -} - -void event_inference_test() -{ - counter = 0; - EventQueue queue(TEST_EQUEUE_SIZE); - - queue.event(count5, 1, 1, 1, 1, 1).post(); - queue.event(count5, 1, 1, 1, 1).post(1); - queue.event(count5, 1, 1, 1).post(1, 1); - queue.event(count5, 1, 1).post(1, 1, 1); - queue.event(count5, 1).post(1, 1, 1, 1); - queue.event(count5).post(1, 1, 1, 1, 1); - - queue.event(callback(count5), 1, 1, 1, 1, 1).post(); - queue.event(callback(count5), 1, 1, 1, 1).post(1); - queue.event(callback(count5), 1, 1, 1).post(1, 1); - queue.event(callback(count5), 1, 1).post(1, 1, 1); - queue.event(callback(count5), 1).post(1, 1, 1, 1); - queue.event(callback(count5)).post(1, 1, 1, 1, 1); - - queue.dispatch(0); - - TEST_ASSERT_EQUAL(counter, 60); -} - -int timeleft_events[2]; - -void check_time_left(EventQueue *queue, int index, int expected) -{ - const int event_id = timeleft_events[index]; - TEST_ASSERT_INT_WITHIN(ALLOWED_TIME_LEFT_TOLERANCE_MS, expected, queue->time_left(event_id)); - touched = true; -} - -void time_left(EventQueue *queue, int index) -{ - const int event_id = timeleft_events[index]; - TEST_ASSERT_EQUAL(0, queue->time_left(event_id)); -} - -void time_left_test() -{ - EventQueue queue(TEST_EQUEUE_SIZE); - - // Enque check events - TEST_ASSERT(queue.call_in(50, check_time_left, &queue, 0, 100 - 50)); - TEST_ASSERT(queue.call_in(200, check_time_left, &queue, 1, 200 - 200)); - - // Enque events to be checked - timeleft_events[0] = queue.call_in(100, time_left, &queue, 0); - timeleft_events[1] = queue.call_in(200, time_left, &queue, 1); - TEST_ASSERT(timeleft_events[0]); - TEST_ASSERT(timeleft_events[1]); - - queue.dispatch(300); - - // Ensure check was called - TEST_ASSERT(touched); - touched = false; - - int id = queue.call(func0); - TEST_ASSERT(id); - TEST_ASSERT_EQUAL(0, queue.time_left(id)); - queue.dispatch(10); - - // Test invalid event id - TEST_ASSERT_EQUAL(-1, queue.time_left(0)); -} - -void f5(int a1, int a2, int a3, int a4, int a5) -{ - touched = true; -} - -class EventTest { -public: - EventTest() : counter() {} - void f0() - { - counter++; - } - void f1(int a) - { - counter += a; - } - void f5(int a, int b, int c, int d, int e) - { - counter += a + b + c + d + e; - } - uint32_t counter; -}; - -/** Test that queue executes both dynamic and user allocated events. - * - * Given queue is initialized and its size is set to store three Event at max in its internal memory. - * When post queue allocated event. - * Then only three event can be posted due to queue memory size. - * When post user allocated evens. - * Then number of posted events is not limited by queue memory size. - * When both Event and UserAllocatedEvent are posted and queue dispatch is called. - * Then both types of events are executed properly. - * - */ -void mixed_dynamic_static_events_queue_test() -{ - { - EventQueue queue(9 * EVENTS_EVENT_SIZE); - - EventTest e1_test; - Event e1 = queue.event(&e1_test, &EventTest::f0); - e1.delay(10); - e1.period(10); - int id1 = e1.post(); - TEST_ASSERT_NOT_EQUAL(0, id1); - EventTest e2_test; - Event e2 = queue.event(&e2_test, &EventTest::f1, 3); - e2.period(10); - int id2 = e2.post(); - TEST_ASSERT_NOT_EQUAL(0, id2); - EventTest e3_test; - Event e3 = queue.event(&e3_test, &EventTest::f5, 1, 2, 3, 4, 5); - e3.period(10); - int id3 = e3.post(); - TEST_ASSERT_NOT_EQUAL(0, id3); - - - auto ue0 = make_user_allocated_event(func0); - EventTest ue1_test; - auto ue1 = make_user_allocated_event(&ue1_test, &EventTest::f0); - EventTest ue2_test; - auto ue2 = make_user_allocated_event(&ue2_test, &EventTest::f1, 3); - EventTest ue3_test; - auto ue3 = make_user_allocated_event(&ue3_test, &EventTest::f5, 1, 2, 3, 4, 5); - EventTest ue4_test; - auto ue4 = make_user_allocated_event(&ue4_test, &EventTest::f5, 1, 2, 3, 4, 5); - - touched = false; - - ue0.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue0.try_call()); - ue1.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue1.try_call()); - ue2.period(10); - ue2.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue2.try_call()); - ue3.period(10); - ue3.delay(50); - ue3.call_on(&queue); - TEST_ASSERT_EQUAL(false, ue3.try_call()); - ue4.call_on(&queue); - ue4.cancel(); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - ue4.cancel(); - e2.cancel(); - - queue.dispatch(101); - - TEST_ASSERT_EQUAL(true, touched); - TEST_ASSERT_EQUAL(1, ue1_test.counter); - TEST_ASSERT_EQUAL(33, ue2_test.counter); - TEST_ASSERT_EQUAL(90, ue3_test.counter); - TEST_ASSERT_EQUAL(0, ue4_test.counter); - TEST_ASSERT_EQUAL(10, e1_test.counter); - TEST_ASSERT_EQUAL(0, e2_test.counter); - TEST_ASSERT_EQUAL(165, e3_test.counter); - - // user allocated event have to be canceled(removed from the queue) before destruction - // cancel all periodic user events - ue2.cancel(); - ue3.cancel(); - } -} - - -static EventQueue g_queue(0); -static auto ue0 = g_queue.make_user_allocated_event(func0); -static EventTest test1; -static auto ue1 = make_user_allocated_event(&test1, &EventTest::f0); -static EventTest test2; -static auto ue2 = g_queue.make_user_allocated_event(&test2, &EventTest::f1, 3); -static EventTest test3; -static auto ue3 = make_user_allocated_event(&test3, &EventTest::f5, 1, 2, 3, 4, 5); -static EventTest test4; -static auto ue4 = g_queue.make_user_allocated_event(&test4, &EventTest::f5, 1, 2, 3, 4, 5); - - -/** Test that static queue executes user allocated events. - * - * Given static queue is initialized - * When post user allocated evens. - * Then UserAllocatedEvent are posted and dispatched without any error. - */ -void static_events_queue_test() -{ - // check that no dynamic event can be posted - Event e0 = g_queue.event(func0); - TEST_ASSERT_EQUAL(0, e0.post()); - - ue0.delay(100); - ue0.period(200); - - ue1.delay(100); - ue1.period(200); - - ue2.delay(100); - ue2.period(200); - - ue3.delay(100); - ue3.period(200); - - ue4.delay(100); - ue4.period(200); - - ue0.call(); - TEST_ASSERT_EQUAL(false, ue0.try_call()); - ue1.call_on(&g_queue); - TEST_ASSERT_EQUAL(false, ue1.try_call()); - ue2(); - TEST_ASSERT_EQUAL(false, ue2.try_call()); - ue3.call_on(&g_queue); - TEST_ASSERT_EQUAL(false, ue3.try_call()); - ue4.call(); - ue4.cancel(); - ue4.cancel(); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - g_queue.cancel(&ue4); - g_queue.cancel(&ue4); - - g_queue.dispatch(400); - - TEST_ASSERT_EQUAL(2, test1.counter); - TEST_ASSERT_EQUAL(6, test2.counter); - TEST_ASSERT_EQUAL(30, test3.counter); - TEST_ASSERT_EQUAL(0, test4.counter); - - ue4.delay(1); - TEST_ASSERT_EQUAL(true, ue4.try_call()); - g_queue.dispatch(1); - - TEST_ASSERT_EQUAL(2, test1.counter); - TEST_ASSERT_EQUAL(6, test2.counter); - TEST_ASSERT_EQUAL(30, test3.counter); - TEST_ASSERT_EQUAL(15, test4.counter); -} - -// Test setup -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP(20, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -const Case cases[] = { - Case("Testing calls with 5 args", simple_posts_test5), - Case("Testing calls with 4 args", simple_posts_test4), - Case("Testing calls with 3 args", simple_posts_test3), - Case("Testing calls with 2 args", simple_posts_test2), - Case("Testing calls with 1 args", simple_posts_test1), - Case("Testing calls with 0 args", simple_posts_test0), - - Case("Testing call_in", call_in_test<20>), - Case("Testing call_every", call_every_test<20>), - - Case("Testing allocate failure", allocate_failure_test), - - Case("Testing event cancel 1", cancel_test1<20>), - Case("Testing the event class", event_class_test), - Case("Testing the event class helpers", event_class_helper_test), - Case("Testing the event inference", event_inference_test), - - Case("Testing time_left", time_left_test), - Case("Testing mixed dynamic & static events queue", mixed_dynamic_static_events_queue_test), - Case("Testing static events queue", static_events_queue_test) - -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/events/timing/main.cpp b/TESTS/events/timing/main.cpp deleted file mode 100644 index fb0b759..0000000 --- a/TESTS/events/timing/main.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* mbed Microcontroller Library - * Copyright (c) 2017 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "mbed_events.h" -#include "mbed.h" -#include "greentea-client/test_env.h" -#include "unity.h" -#include "utest.h" -#include -#include - -using namespace utest::v1; - -#if !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported -#else - -// Test delay -#ifndef TEST_EVENTS_TIMING_TIME -#define TEST_EVENTS_TIMING_TIME 20000 -#endif - -#ifndef TEST_EVENTS_TIMING_MEAN -#define TEST_EVENTS_TIMING_MEAN 25 -#endif - -#ifndef M_PI -#define M_PI 3.14159265358979323846264338327950288 -#endif - -// Random number generation to skew timing values -float gauss(float mu, float sigma) -{ - float x = (float)rand() / ((float)RAND_MAX + 1); - float y = (float)rand() / ((float)RAND_MAX + 1); - float x2pi = x * 2.0 * M_PI; - float g2rad = sqrt(-2.0 * log(1.0 - y)); - float z = cos(x2pi) * g2rad; - return mu + z * sigma; -} - -float chisq(float sigma) -{ - return pow(gauss(0, sqrt(sigma)), 2); -} - - -Timer timer; -DigitalOut led(LED1); - -equeue_sema_t sema; - -// Timer timing test -void timer_timing_test() -{ - timer.reset(); - timer.start(); - int prev = timer.read_us(); - - while (prev < TEST_EVENTS_TIMING_TIME * 1000) { - int next = timer.read_us(); - if (next < prev) { - printf("backwards drift %d -> %d (%08x -> %08x)\r\n", - prev, next, prev, next); - } - TEST_ASSERT(next >= prev); - prev = next; - } -} - -// equeue tick timing test -void tick_timing_test() -{ - unsigned start = equeue_tick(); - int prev = 0; - - while (prev < TEST_EVENTS_TIMING_TIME) { - int next = equeue_tick() - start; - if (next < prev) { - printf("backwards drift %d -> %d (%08x -> %08x)\r\n", - prev, next, prev, next); - } - TEST_ASSERT(next >= prev); - prev = next; - } -} - -// equeue semaphore timing test -void semaphore_timing_test() -{ - srand(0); - timer.reset(); - timer.start(); - - int err = equeue_sema_create(&sema); - TEST_ASSERT_EQUAL(0, err); - - while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) { - int delay = chisq(TEST_EVENTS_TIMING_MEAN); - - int start = timer.read_us(); - equeue_sema_wait(&sema, delay); - int taken = timer.read_us() - start; - - if (taken < (delay * 1000 - 5000) || taken > (delay * 1000 + 5000)) { - printf("delay %dms => error %dus\r\n", delay, abs(1000 * delay - taken)); - } - - TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000); - - led = !led; - } - - equeue_sema_destroy(&sema); -} - - -// Test setup -utest::v1::status_t test_setup(const size_t number_of_cases) -{ - GREENTEA_SETUP((number_of_cases + 1)*TEST_EVENTS_TIMING_TIME / 1000, "default_auto"); - return verbose_test_setup_handler(number_of_cases); -} - -const Case cases[] = { - Case("Testing accuracy of timer", timer_timing_test), - Case("Testing accuracy of equeue tick", tick_timing_test), - Case("Testing accuracy of equeue semaphore", semaphore_timing_test), -}; - -Specification specification(test_setup, cases); - -int main() -{ - return !Harness::run(specification); -} - -#endif // !DEVICE_USTICKER diff --git a/TESTS/mbed_hal/common_tickers_freq/main.cpp b/TESTS/mbed_hal/common_tickers_freq/main.cpp index 28fa61d..fb4a886 100644 --- a/TESTS/mbed_hal/common_tickers_freq/main.cpp +++ b/TESTS/mbed_hal/common_tickers_freq/main.cpp @@ -29,10 +29,14 @@ #include "hal/lp_ticker_api.h" #include "hal/mbed_lp_ticker_wrapper.h" -#if defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER -#error [NOT_SUPPORTED] test not supported +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] UsTicker need to be enabled for this test #else +#if defined(SKIP_TIME_DRIFT_TESTS) +#error [NOT_SUPPORTED] timing accuracy tests skipped +#endif // defined(SKIP_TIME_DRIFT_TESTS) + #define US_PER_S 1000000 using namespace utest::v1; @@ -208,4 +212,4 @@ Harness::run(specification); } -#endif // defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER +#endif // !DEVICE_USTICKER diff --git a/TESTS/usb_device/basic/USBEndpointTester.cpp b/TESTS/usb_device/basic/USBEndpointTester.cpp index c26bbbb..4b48e94 100644 --- a/TESTS/usb_device/basic/USBEndpointTester.cpp +++ b/TESTS/usb_device/basic/USBEndpointTester.cpp @@ -20,7 +20,7 @@ #include "stdint.h" #include "stdlib.h" #include "USBEndpointTester.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "EndpointResolver.h" #define DEFAULT_CONFIGURATION (1) diff --git a/TESTS/usb_device/basic/USBEndpointTester.h b/TESTS/usb_device/basic/USBEndpointTester.h index a1d02ea..cf649dd 100644 --- a/TESTS/usb_device/basic/USBEndpointTester.h +++ b/TESTS/usb_device/basic/USBEndpointTester.h @@ -21,7 +21,7 @@ /* These headers are included for child class. */ #include "USBDescriptor.h" #include "USBDevice_Types.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "EventFlags.h" #include "platform/NonCopyable.h" diff --git a/TESTS/usb_device/basic/USBTester.cpp b/TESTS/usb_device/basic/USBTester.cpp index 004d03c..743f217 100644 --- a/TESTS/usb_device/basic/USBTester.cpp +++ b/TESTS/usb_device/basic/USBTester.cpp @@ -19,7 +19,7 @@ #include "stdint.h" #include "USBTester.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "EndpointResolver.h" #define DEFAULT_CONFIGURATION (1) diff --git a/TESTS/usb_device/basic/USBTester.h b/TESTS/usb_device/basic/USBTester.h index afe1dfa..d131ab3 100644 --- a/TESTS/usb_device/basic/USBTester.h +++ b/TESTS/usb_device/basic/USBTester.h @@ -21,7 +21,7 @@ /* These headers are included for child class. */ #include "USBDescriptor.h" #include "USBDevice_Types.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "EventFlags.h" #include "platform/NonCopyable.h" diff --git a/UNITTESTS/CMakeLists.txt b/UNITTESTS/CMakeLists.txt index 8ca02ab..2a5b54a 100644 --- a/UNITTESTS/CMakeLists.txt +++ b/UNITTESTS/CMakeLists.txt @@ -103,8 +103,8 @@ # Set include dirs. set(unittest-includes-base "${PROJECT_SOURCE_DIR}/target_h" - "${PROJECT_SOURCE_DIR}/target_h/events" - "${PROJECT_SOURCE_DIR}/target_h/events/equeue" + "${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h" + "${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h/equeue" "${PROJECT_SOURCE_DIR}/target_h/platform" "${PROJECT_SOURCE_DIR}/target_h/platform/cxxsupport" "${PROJECT_SOURCE_DIR}/target_h/drivers" @@ -123,9 +123,9 @@ "${PROJECT_SOURCE_DIR}/../storage/kvstore/kv_config/include" "${PROJECT_SOURCE_DIR}/../drivers" "${PROJECT_SOURCE_DIR}/../hal" - "${PROJECT_SOURCE_DIR}/../events" + "${PROJECT_SOURCE_DIR}/../events/include" + "${PROJECT_SOURCE_DIR}/../events/include/events/internal" "${PROJECT_SOURCE_DIR}/../events/source" - "${PROJECT_SOURCE_DIR}/../events/internal" "${PROJECT_SOURCE_DIR}/../rtos/include" "${PROJECT_SOURCE_DIR}/../features/frameworks" "${PROJECT_SOURCE_DIR}/../features/frameworks/mbed-trace" diff --git a/UNITTESTS/events/equeue/test_equeue.cpp b/UNITTESTS/events/equeue/test_equeue.cpp deleted file mode 100644 index 5d90023..0000000 --- a/UNITTESTS/events/equeue/test_equeue.cpp +++ /dev/null @@ -1,1114 +0,0 @@ -/* - * Copyright (c) 2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "gtest/gtest.h" -#include "equeue.h" -#include "mbed.h" -#include -#include - -#define EVENTS_EVENT_SIZE (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) -#define TEST_EQUEUE_SIZE 2048 -#define TEST_THREAD_STACK_SIZE 512 -#define DISPATCH_INFINITE -1 -#define ITERATION_TIMES 10 - -extern unsigned int equeue_global_time; - -class TestEqueue : public testing::Test { - virtual void SetUp() - { - } - - virtual void TearDown() - { - } -}; - -// Test functions -static void pass_func(void *eh) -{ -} - -static void simple_func(void *p) -{ - uint8_t *d = reinterpret_cast(p); - if (*d < 255) { - (*d)++; - } -} - -static void sloth_func(void *p) -{ - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; - (*(reinterpret_cast(p)))++; -} - -struct indirect { - uint8_t *touched; - uint8_t buffer[7]; -}; - -static void indirect_func(void *p) -{ - struct indirect *i = reinterpret_cast(p); - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -static void timing_func(void *p) -{ - struct timing *timing = reinterpret_cast(p); - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - EXPECT_TRUE(t2 - 10 < t1 < t2 + 10); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -static void fragment_func(void *p) -{ - struct fragment *fragment = reinterpret_cast(p); - timing_func(&fragment->timing); - - struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); - ASSERT_TRUE(nfragment != NULL); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - ASSERT_NE(0, id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -static void cancel_func(void *p) -{ - struct cancel *ccel = reinterpret_cast(p); - equeue_cancel(ccel->q, ccel->id); -} - -struct nest { - equeue_t *q; - void (*cb)(void *); - void *data; -}; - -static void nest_func(void *p) -{ - struct nest *nst = reinterpret_cast(p); - equeue_call(nst->q, nst->cb, nst->data); - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; -} - -static void *multithread_thread(void *p) -{ - equeue_t *q = reinterpret_cast(p); - equeue_dispatch(q, DISPATCH_INFINITE); - return 0; -} - -class ecount { - mutable pthread_mutex_t mutex; - pthread_cond_t cond; - uint8_t count; -public: - ecount() : count(0) - { - int err = pthread_mutex_init(&mutex, NULL); - EXPECT_EQ(0, err); - err = pthread_cond_init(&cond, NULL); - EXPECT_EQ(0, err); - } - - ~ecount() - { - int err = pthread_mutex_destroy(&mutex); - EXPECT_EQ(0, err); - err = pthread_cond_destroy(&cond); - EXPECT_EQ(0, err); - } - - void lock() const - { - int err = pthread_mutex_lock(&mutex); - EXPECT_EQ(0, err); - } - - void unlock() const - { - int err = pthread_mutex_unlock(&mutex); - EXPECT_EQ(0, err); - } - - void touch() - { - lock(); - if (count < 200) { - count++; - } - unlock(); - int err = pthread_cond_broadcast(&cond); - EXPECT_EQ(0, err); - } - - void wait_for_touches(uint8_t n) - { - lock(); - while (count < n) { - int err = pthread_cond_wait(&cond, &mutex); - EXPECT_EQ(0, err); - } - unlock(); - } -}; - -static void multithread_func(void *p) -{ - static_cast(p)->touch(); -} - -static void background_func(void *p, int ms) -{ - *(reinterpret_cast(p)) = ms; -} - -struct ethread { - pthread_t thread; - equeue_t *q; - int ms; -}; - -static void *ethread_dispatch(void *p) -{ - struct ethread *t = reinterpret_cast(p); - equeue_dispatch(t->q, t->ms); - return 0; -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -static void simple_breaker(void *p) -{ - struct count_and_queue *caq = reinterpret_cast(p); - equeue_break(caq->q); - // adding to equeue_global_time becouse this simulates that this function takes some time - equeue_global_time += 10; - caq->p++; -} - -// Simple call tests - -/** Test that equeue executes function passed by equeue_call. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_call) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 10); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_in. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_in is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_call_in) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int id = equeue_call_in(&q, 10, simple_func, &touched); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 15); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 10); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_call_every. - * - * Given queue is initialized. - * When the event is scheduled and after that equeue_dispatch is called. - * Then function passed by equeue_call_every is executed properly. - */ - -TEST_F(TestEqueue, test_equeue_simple_call_every) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int id = equeue_call_every(&q, 10, simple_func, &touched); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 15); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that equeue executes function passed by equeue_post. - * - * Given queue is initialized. - * When the event is posted and after that equeue_dispatch is called. - * Then function passed by equeue_post is executed properly. - */ -TEST_F(TestEqueue, test_equeue_simple_post) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(i != NULL); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, *i->touched); - - equeue_destroy(&q); -} - -// Misc tests - -/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. - * - * Given queue is initialized. - * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. - * Then functions attached to equeue events destructors are executed properly. - */ -TEST_F(TestEqueue, test_equeue_destructor) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct indirect *e; - int ids[3]; - - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e != NULL); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(3, touched); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e != NULL); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - ASSERT_NE(0, ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - EXPECT_EQ(3, touched); - - equeue_dispatch(&q, 0); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); - ASSERT_TRUE(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - ASSERT_NE(0, id); - } - - equeue_destroy(&q); - EXPECT_EQ(3, touched); -} - -/** Test that equeue_alloc returns 0 when equeue can not be allocated. - * - * Given queue is initialized. - * When equeue_alloc is called and equeue can not be allocated - * Then function equeue_alloc returns NULL. - */ -TEST_F(TestEqueue, test_equeue_allocation_failure) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); - EXPECT_TRUE(p == NULL); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - EXPECT_TRUE(p == NULL); - - equeue_destroy(&q); -} - -/** Test that equeue does not execute evenets that has been canceled. - * - * Given queue is initialized. - * When events are canceled by equeue_cancel. - * Then they are not executed by calling equeue_dispatch. - */ -TEST_F(TestEqueue, test_equeue_cancel) -{ - equeue_t q; - int err = equeue_create(&q, (2 * ITERATION_TIMES * EVENTS_EVENT_SIZE)); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - int ids[2 * ITERATION_TIMES]; - - for (int i = 0; i < 2 * ITERATION_TIMES; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - ASSERT_NE(0, ids[i]); - } - - for (int i = 2 * ITERATION_TIMES - 1; i >= 0; i--) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that events can be cancelled by function executed by equeue_dispatch. - * - * Given queue is initialized. - * When event is cancelled by another event while dispatching. - * Then event that was cancelled is not being executed. - */ -TEST_F(TestEqueue, test_equeue_cancel_inflight) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - - int id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - id = equeue_call(&q, simple_func, &touched); - equeue_cancel(&q, id); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); - ASSERT_TRUE(ccel != NULL); - ccel->q = &q; - ccel->id = 0; - - id = equeue_post(&q, cancel_func, ccel); - ASSERT_NE(0, id); - - ccel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - EXPECT_EQ(0, touched); - - equeue_destroy(&q); -} - -/** Test that unnecessary canceling events would not affect executing other events. - * - * Given queue is initialized. - * When event is unnecessary canceled by equeue_cancel. - * Then other events are properly executed after calling equeue_dispatch. - */ -TEST_F(TestEqueue, test_equeue_cancel_unnecessarily) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - uint8_t touched = 0; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - equeue_cancel(&q, id); - } - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. - * - * Given queue is initialized. - * When events have 0 ms period time. - * Then dispatching would not end up in infinite loop. - */ -TEST_F(TestEqueue, test_equeue_loop_protect) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched1); - - touched1 = 0; - uint8_t touched2 = 0; - equeue_call_every(&q, 1, simple_func, &touched2); - - equeue_dispatch(&q, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break breaks event queue out of dispatching. - * - * Given queue is initialized. - * When equeue_break is called. - * Then event queue will stop dispatching after finisching current dispatching cycle. - */ -TEST_F(TestEqueue, test_equeue_break) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - equeue_call_every(&q, 0, simple_func, &touched1); - - uint8_t touched2 = 0; - equeue_call_every(&q, 5, simple_func, &touched2); - - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_destroy(&q); -} - -/** Test that equeue_break function breaks equeue dispatching only once. - * - * Given queue is initialized. - * When equeue_break is called several times. - * Then equeue is stopped only once. - */ -TEST_F(TestEqueue, test_equeue_break_no_windup) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, DISPATCH_INFINITE); - EXPECT_EQ(1, touched); - - touched = 0; - equeue_dispatch(&q, 55); - EXPECT_TRUE(touched > 1); - - equeue_destroy(&q); -} - -/** Test that function passed by equeue_call_every is being executed periodically. - * - * Given queue is initialized. - * When function is passed by equeue_call_every with specified period. - * Then event is executed (dispatch time/period) times. - */ - -TEST_F(TestEqueue, test_equeue_period) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - EXPECT_EQ(5, touched); - - equeue_destroy(&q); -} - -/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. - * - * Given queue is initialized. - * When nested function is added to enqueue. - * Then it is executed in the next dispatch, or after execution of "mother" function. - */ -TEST_F(TestEqueue, test_equeue_nested) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - ASSERT_TRUE(nst != NULL); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - int id = equeue_post(&q, nest_func, nst); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 5); - EXPECT_EQ(0, touched); - - equeue_dispatch(&q, 1); - EXPECT_EQ(1, touched); - - touched = 0; - nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); - ASSERT_TRUE(nst != NULL); - nst->q = &q; - nst->cb = simple_func; - nst->data = &touched; - - id = equeue_post(&q, nest_func, nst); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 20); - EXPECT_EQ(1, touched); - - equeue_destroy(&q); -} - -/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. - * - * Given queue is initialized. - * When sloth function is being called before other functions. - * Then if it is possible all functions start according to predefined schedule correctly. - */ -TEST_F(TestEqueue, test_equeue_sloth) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - uint8_t touched3 = 0; - int id = equeue_call(&q, sloth_func, &touched1); - ASSERT_NE(0, id); - - id = equeue_call_in(&q, 5, simple_func, &touched2); - ASSERT_NE(0, id); - - id = equeue_call_in(&q, 15, simple_func, &touched3); - ASSERT_NE(0, id); - - equeue_dispatch(&q, 20); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - EXPECT_EQ(1, touched3); - - equeue_destroy(&q); -} - -/** Test that equeue can be broken of dispatching from a different thread. - * - * Given queue is initialized. - * When equeue starts dispatching in one thread. - * Then it can be stopped from another thread via equeue_break. - */ -TEST_F(TestEqueue, test_equeue_multithread) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - ecount t; - equeue_call_every(&q, 1, multithread_func, &t); - - pthread_t thread; - err = pthread_create(&thread, 0, multithread_thread, &q); - ASSERT_EQ(0, err); - - t.wait_for_touches(1); - equeue_break(&q); - err = pthread_join(thread, 0); - ASSERT_EQ(0, err); - - equeue_destroy(&q); -} - -/** Test that variable referred via equeue_background shows value in ms to the next event. - * - * Given queue is initialized. - * When variable is referred via equeue_background. - * Then it depicts the time in ms to the next event. - */ -TEST_F(TestEqueue, test_equeue_background) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - ASSERT_NE(0, id); - - int ms; - equeue_background(&q, background_func, &ms); - EXPECT_EQ(20, ms); - - id = equeue_call_in(&q, 10, pass_func, 0); - ASSERT_NE(0, id); - EXPECT_EQ(10, ms); - - id = equeue_call(&q, pass_func, 0); - ASSERT_NE(0, id); - EXPECT_EQ(0, ms); - - equeue_dispatch(&q, 0); - EXPECT_EQ(10, ms); - - equeue_destroy(&q); - EXPECT_EQ(-1, ms); -} - -/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. - * - * Given queue is initialized. - * When target chained equeue is dispatched. - * Then events from both chained equeues are executed. - */ -TEST_F(TestEqueue, test_equeue_chain) -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - - int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); - ASSERT_NE(0, id1); - int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); - ASSERT_NE(0, id2); - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - id1 = equeue_call_in(&q1, 5, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call_in(&q2, 5, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_cancel(&q1, id1); - equeue_cancel(&q2, id2); - - id1 = equeue_call_in(&q1, 10, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call_in(&q2, 10, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 30); - - EXPECT_EQ(3, touched1); - EXPECT_EQ(3, touched2); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -/** Test that unchaining equeues makes them work on their own. - * - * Given queue is initialized. - * When equeue is unchained. - * Then it can be only dispatched by calling with reference to it. - */ -TEST_F(TestEqueue, test_equeue_unchain) -{ - equeue_t q1; - int err = equeue_create(&q1, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_t q2; - err = equeue_create(&q2, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - equeue_chain(&q2, &q1); - - uint8_t touched1 = 0; - uint8_t touched2 = 0; - int id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - int id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_chain(&q2, 0); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q1, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(0, touched2); - - equeue_dispatch(&q2, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_chain(&q1, &q2); - - touched1 = 0; - touched2 = 0; - - id1 = equeue_call(&q1, simple_func, &touched1); - ASSERT_NE(0, id1); - id2 = equeue_call(&q2, simple_func, &touched2); - ASSERT_NE(0, id2); - - equeue_dispatch(&q2, 0); - EXPECT_EQ(1, touched1); - EXPECT_EQ(1, touched2); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests - -/** Test that equeue keeps good time at starting events. - * - * Given queue is initialized. - * When equeue is being dispatched. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_simple_barrage) -{ - equeue_t q; - int err = equeue_create(&q, 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - ASSERT_EQ(0, err); - - for (int i = 0; i < 2 * ITERATION_TIMES; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - ASSERT_TRUE(timing != NULL); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, 2 * ITERATION_TIMES * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. - * - * Given queue is initialized. - * When equeue is being dispatched and new events are added via already placed in equeue. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_fragmenting_barrage) -{ - equeue_t q; - int err = equeue_create(&q, - 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + ITERATION_TIMES * sizeof(int))); - ASSERT_EQ(0, err); - - for (int i = 0; i < ITERATION_TIMES; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); - ASSERT_TRUE(fragment != NULL); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - ASSERT_NE(0, id); - } - - equeue_dispatch(&q, ITERATION_TIMES * 100); - - equeue_destroy(&q); -} - -/** Test that equeue keeps good time at starting events even if it is working on different thread. - * - * Given queue is initialized. - * When equeue is being dispatched on different thread. - * Then events happen according to the schedule with an error within a specified range. - */ -TEST_F(TestEqueue, test_equeue_multithreaded_barrage) -{ - equeue_t q; - int err = equeue_create(&q, ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - ASSERT_EQ(0, err); - - struct ethread t; - t.q = &q; - t.ms = ITERATION_TIMES * 100; - - err = pthread_create(&t.thread, 0, ethread_dispatch, &t); - ASSERT_EQ(0, err); - - for (int i = 0; i < ITERATION_TIMES; i++) { - struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); - ASSERT_TRUE(timing != NULL); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - ASSERT_NE(0, id); - } - - err = pthread_join(t.thread, 0); - ASSERT_EQ(0, err); - - equeue_destroy(&q); -} - -/** Test that break request flag is cleared when equeue stops dispatching timeouts. - * - * Given queue is initialized. - * When equeue break request flag is called but equeue stops dispatching because of timeout. - * Then next equeue dispatch is not stopped. - */ -TEST_F(TestEqueue, test_equeue_break_request_cleared_on_timeout) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_in(&q, 1, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - - EXPECT_EQ(1, pq.p); - - equeue_cancel(&q, id); - - uint8_t touched = 0; - equeue_call_every(&q, 10, simple_func, &touched); - - equeue_dispatch(&q, 55); - EXPECT_EQ(5, touched); - - equeue_destroy(&q); -} - -/** Test that siblings events don't have next pointers. - * - * Given queue is initialized. - * When events are scheduled on the same time. - * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. - */ -TEST_F(TestEqueue, test_equeue_sibling) -{ - equeue_t q; - int err = equeue_create(&q, TEST_EQUEUE_SIZE); - ASSERT_EQ(0, err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - EXPECT_TRUE(s->next == NULL); - } - } - equeue_cancel(&q, id0); - equeue_cancel(&q, id1); - equeue_cancel(&q, id2); - equeue_destroy(&q); -} - -/** Test that equeue executes user allocated events passed by equeue_post. - * - * Given queue is initialized and its size is set to store one event at max in its internal memory. - * When post events allocated in queues internal memory (what is done by calling equeue_call). - * Then only one event can be posted due to queue memory size. - * When post user allocated events. - * Then number of posted events is not limited by queue memory size. - * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. - * Then both types of events are executed properly. - */ -TEST_F(TestEqueue, test_equeue_user_allocated_event_post) -{ - struct user_allocated_event { - struct equeue_event e; - uint8_t touched; - }; - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - ASSERT_EQ(0, err); - - uint8_t touched = 0; - user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; - user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - EXPECT_NE(0, equeue_call_every(&q, 10, simple_func, &touched)); - EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); - EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - equeue_cancel_user_allocated(&q, &e3.e); - equeue_cancel_user_allocated(&q, &e3.e); - - equeue_dispatch(&q, 11); - - EXPECT_EQ(1, touched); - EXPECT_EQ(1, e1.touched); - EXPECT_EQ(1, e2.touched); - EXPECT_EQ(0, e3.touched); - EXPECT_EQ(1, e4.touched); - EXPECT_EQ(1, e5.touched); - - e3.e.target = 10; // set target as it's modified by equeue_call - e3.e.period = 10; // set period as it's reset by equeue_cancel - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_dispatch(&q, 101); - - EXPECT_EQ(11, touched); - EXPECT_EQ(1 , e1.touched); - EXPECT_EQ(11, e2.touched); - EXPECT_EQ(10 , e3.touched); - EXPECT_EQ(11, e4.touched); - EXPECT_EQ(1 , e5.touched); - - equeue_destroy(&q); -} diff --git a/UNITTESTS/events/equeue/unittest.cmake b/UNITTESTS/events/equeue/unittest.cmake deleted file mode 100644 index b685b91..0000000 --- a/UNITTESTS/events/equeue/unittest.cmake +++ /dev/null @@ -1,27 +0,0 @@ - -#################### -# UNIT TESTS -#################### - -list(REMOVE_ITEM unittest-includes ${PROJECT_SOURCE_DIR}/target_h/events ${PROJECT_SOURCE_DIR}/target_h/events/equeue) - -set(unittest-includes ${unittest-includes} - ../events/source - ../events - ../events/internal -) - -set(unittest-sources - ../events/source/equeue.c -) - -set(unittest-test-sources - events/equeue/test_equeue.cpp - stubs/EqueuePosix_stub.c -) - -set(unittest-test-flags - -pthread - -DEQUEUE_PLATFORM_POSIX -) - diff --git a/UNITTESTS/stubs/EqueuePosix_stub.c b/UNITTESTS/stubs/EqueuePosix_stub.c deleted file mode 100644 index e3d975e..0000000 --- a/UNITTESTS/stubs/EqueuePosix_stub.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2019, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "internal/equeue_platform.h" - -#if defined(EQUEUE_PLATFORM_POSIX) - -#include - -/* - * Using global variable as a simulation of passing time. Use of sleep functions may cause problems with thread preempting, which can lead to bad timings. - * This problem does not occur on targets. - * This variable is only increased in equeue_sema_wait function and in functions that simulate pass of time. - */ -unsigned int equeue_global_time = 0; - -// Tick operations -void equeue_tick_init(void) {} - -unsigned equeue_tick(void) -{ - return equeue_global_time; -} - - -// Mutex operations -int equeue_mutex_create(equeue_mutex_t *m) -{ - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - return pthread_mutex_init(m, &attr); -} - -void equeue_mutex_destroy(equeue_mutex_t *m) -{ - pthread_mutex_destroy(m); -} - -void equeue_mutex_lock(equeue_mutex_t *m) -{ - pthread_mutex_lock(m); -} - -void equeue_mutex_unlock(equeue_mutex_t *m) -{ - pthread_mutex_unlock(m); -} - - -// Semaphore operations -int equeue_sema_create(equeue_sema_t *s) -{ - int err = pthread_mutex_init(&s->mutex, 0); - if (err) { - return err; - } - - err = pthread_cond_init(&s->cond, 0); - if (err) { - return err; - } - - s->signal = false; - return 0; -} - -void equeue_sema_destroy(equeue_sema_t *s) -{ - pthread_cond_destroy(&s->cond); - pthread_mutex_destroy(&s->mutex); -} - -void equeue_sema_signal(equeue_sema_t *s) -{ - pthread_mutex_lock(&s->mutex); - s->signal = true; - pthread_cond_signal(&s->cond); - pthread_mutex_unlock(&s->mutex); -} - -bool equeue_sema_wait(equeue_sema_t *s, int ms) -{ - pthread_mutex_lock(&s->mutex); - if (!s->signal) { - if (ms < 0) { - pthread_cond_wait(&s->cond, &s->mutex); - } else { - for (int i = 0; i < ms; i++) { - equeue_global_time++; - } - // If ms == 0 increase time so functions don't get stuck in infinite loops. - if (ms == 0) { - equeue_global_time++; - } - } - } - - bool signal = s->signal; - s->signal = false; - pthread_mutex_unlock(&s->mutex); - - return signal; -} - -#endif diff --git a/UNITTESTS/stubs/equeue_stub.c b/UNITTESTS/stubs/equeue_stub.c index 8ae5ab4..125e57c 100644 --- a/UNITTESTS/stubs/equeue_stub.c +++ b/UNITTESTS/stubs/equeue_stub.c @@ -15,7 +15,7 @@ * limitations under the License. */ -#include "equeue.h" +#include "events/equeue.h" #include #include "equeue_stub.h" diff --git a/UNITTESTS/target_h/events/equeue/equeue_platform.h b/UNITTESTS/target_h/events/equeue/equeue_platform.h deleted file mode 100644 index 7d9f968..0000000 --- a/UNITTESTS/target_h/events/equeue/equeue_platform.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EQUEUE_PLATFORM_H -#define EQUEUE_PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef unsigned equeue_mutex_t; - -typedef struct equeue_sema { -} equeue_sema_t; - -#ifdef __cplusplus -} -#endif - -#endif //EQUEUE_PLATFORM_H diff --git a/UNITTESTS/target_h/events/mbed_events.h b/UNITTESTS/target_h/events/mbed_events.h deleted file mode 100644 index c104e73..0000000 --- a/UNITTESTS/target_h/events/mbed_events.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2017, Arm Limited and affiliates. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBED_EVENTS_H -#define MBED_EVENTS_H - -#include "events/EventQueue.h" - -#include "events/mbed_shared_queues.h" - -#endif diff --git a/connectivity/cellular/source/framework/device/CellularDevice.cpp b/connectivity/cellular/source/framework/device/CellularDevice.cpp index 266822c..50d900e 100644 --- a/connectivity/cellular/source/framework/device/CellularDevice.cpp +++ b/connectivity/cellular/source/framework/device/CellularDevice.cpp @@ -20,7 +20,7 @@ #include "CellularUtil.h" #include "CellularLog.h" #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" namespace mbed { diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp new file mode 100644 index 0000000..9ddc9cd --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp @@ -0,0 +1,1124 @@ +/* +* Copyright (c) 2016-2018 ARM Limited. All rights reserved. +* SPDX-License-Identifier: Apache-2.0 +* Licensed under the Apache License, Version 2.0 (the License); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an AS IS BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +#include "mbed_power_mgmt.h" +#include "common_functions.h" +#include "platform/arm_hal_interrupt.h" +#include "platform/arm_hal_phy.h" +#include "NanostackRfPhyKw41z.h" + +#include "fsl_xcvr.h" + +#define RF_THREAD_STACK_SIZE 1024 + +static void rf_thread_loop(); +Thread rf_thread(osPriorityRealtime, RF_THREAD_STACK_SIZE); + +#define PHY_MTU_SIZE 127 +#define CRC_LENGTH 0 +#define PHY_HEADER_LENGTH 0 + +#define BM_ZLL_IRQSTS_TMRxMSK (ZLL_IRQSTS_TMR1MSK_MASK | \ + ZLL_IRQSTS_TMR2MSK_MASK | \ + ZLL_IRQSTS_TMR3MSK_MASK | \ + ZLL_IRQSTS_TMR4MSK_MASK) + +#define RF_CCA_THRESHOLD 75 /* -75 dBm */ + +#define gPhyDefaultTxPowerLevel_d (22) +#define gPhyMaxTxPowerLevel_d (32) +#define gCcaED_c (0) +#define gCcaCCA_MODE1_c (1) + +#define gPhyTimeMask_c (0x00FFFFFF) + +#define KW41Z_SHR_PHY_TIME 12 +#define KW41Z_PER_BYTE_TIME 2 +#define KW41Z_ACK_WAIT_TIME 54 + +static int8_t rf_radio_driver_id = -1; +static uint8_t need_ack = 0; + +/* PHY states */ +typedef enum xcvrState_tag { + gIdle_c, + gRX_c, + gTX_c, + gCCA_c, + gTR_c, + gCCCA_c, +} xcvrState_t; + +static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel); +static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol); +static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr); +static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr); +static void rf_mac_hw_init(void); +static void rf_mac_ed_state_enable(void); +static void rf_mac_set_pending(uint8_t status); +static void rf_mac_set_shortAddress(uint8_t *valueAddress); +static void rf_mac_set_panId(uint8_t *valueAddress); +static void rf_mac_set_mac64(const uint8_t *valueAddress); +static uint8_t rf_convert_energy_level(uint8_t energyLevel); +static void rf_abort(void); +static void rf_ack_wait_timer_start(uint16_t time); +static void rf_get_timestamp(uint32_t *pRetClk); +static uint32_t rf_get_timeout(void); +static void rf_set_timeout(uint32_t timeout); +static void rf_promiscuous(uint8_t state); +static void handle_IRQ_events(void); +static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan); +static void rf_receive(void); +static void rf_handle_rx_end(void); + +static uint8_t MAC64_addr_default[8] = {1, 2, 3, 4, 5, 6, 7, 8}; +static uint8_t MAC64_addr[8]; + +static xcvrState_t mPhySeqState; +static uint8_t rf_mac_handle; +static volatile uint8_t rf_ed_value = 0; +static volatile bool rf_ack_pending_state = false; +static volatile bool sleep_blocked = false; + +static NanostackRfPhyKw41z *rf = NULL; + +#define MAC_PACKET_SIZE 127 //MAX MAC payload is 127 bytes +static uint8_t PHYPAYLOAD[MAC_PACKET_SIZE]; + +const phy_rf_channel_configuration_s phy_2_4ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK}; + +const phy_device_channel_page_s phy_channel_pages[] = { + {CHANNEL_PAGE_0, &phy_2_4ghz}, + {CHANNEL_PAGE_0, NULL} +}; + +static phy_device_driver_s device_driver = { + PHY_LINK_15_4_2_4GHZ_TYPE, + PHY_LAYER_PAYLOAD_DATA_FLOW, + MAC64_addr, + PHY_MTU_SIZE, + (char *)"NXP kw41z", + CRC_LENGTH, + PHY_HEADER_LENGTH, + &rf_interface_state_control, + &rf_start_cca, + &rf_address_write, + &rf_extension, + phy_channel_pages, + NULL, + NULL, + NULL, + NULL +}; + +static void rf_thread_loop() +{ + for (;;) { + ThisThread::flags_wait_all(1); + + platform_enter_critical(); + + handle_IRQ_events(); + + platform_exit_critical(); + NVIC_ClearPendingIRQ(Radio_1_IRQn); + NVIC_EnableIRQ(Radio_1_IRQn); + } +} + +static int8_t rf_device_register(void) +{ + if (rf_radio_driver_id < 0) { + rf_mac_hw_init(); + /** + * Read factory stored Mac address to RAM + */ + common_write_32_bit(ZLL->MACLONGADDRS0_MSB, MAC64_addr); + common_write_32_bit(ZLL->MACLONGADDRS0_LSB, MAC64_addr + 4); + + rf_radio_driver_id = arm_net_phy_register(&device_driver); + } + + return rf_radio_driver_id; +} + +static void rf_device_unregister(void) +{ + arm_net_phy_unregister(rf_radio_driver_id); + + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } +} + +/* + * \brief Function enables/disables Rx promiscuous mode. + * + * \param state of XCVR promiscuous mode + * + * \return none + */ +static void rf_promiscuous(uint8_t state) +{ + if (state) { + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_PROMISCUOUS_MASK; + /* FRM_VER[11:8] = b1111. Any FrameVersion accepted */ + ZLL->RX_FRAME_FILTER |= (ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | + ZLL_RX_FRAME_FILTER_ACK_FT_MASK | + ZLL_RX_FRAME_FILTER_NS_FT_MASK); + } else { + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_PROMISCUOUS_MASK; + /* FRM_VER[11:8] = b0011. Accept FrameVersion 0 and 1 packets, reject all others */ + /* Beacon, Data and MAC command frame types accepted */ + ZLL->RX_FRAME_FILTER &= ~(ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | + ZLL_RX_FRAME_FILTER_ACK_FT_MASK | + ZLL_RX_FRAME_FILTER_NS_FT_MASK | + ZLL_RX_FRAME_FILTER_ACTIVE_PROMISCUOUS_MASK); + ZLL->RX_FRAME_FILTER |= ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3); + } +} + +static void rf_mac_set_pending(uint8_t status) +{ + uint32_t reg = ZLL->SAM_TABLE; + + /* Disable the Source Address Matching feature and set FP manually */ + reg |= ZLL_SAM_TABLE_ACK_FRM_PND_CTRL_MASK; + if (status) { + reg |= ZLL_SAM_TABLE_ACK_FRM_PND_MASK; + rf_ack_pending_state = true; + } else { + reg &= ~ZLL_SAM_TABLE_ACK_FRM_PND_MASK; + rf_ack_pending_state = false; + } + ZLL->SAM_TABLE = reg; +} + +static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) +{ + platform_enter_critical(); + + switch (new_state) { + /*Reset PHY driver and set to idle*/ + case PHY_INTERFACE_RESET: + rf_abort(); + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } + break; + /*Disable PHY Interface driver*/ + case PHY_INTERFACE_DOWN: + rf_abort(); + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } + break; + /*Enable PHY Interface driver*/ + case PHY_INTERFACE_UP: + if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { + return 1; + } + if (!sleep_blocked) { + /* Disable enter to deep sleep when transfer active */ + sleep_manager_lock_deep_sleep(); + sleep_blocked = true; + } + rf_receive(); + break; + /*Enable wireless interface ED scan mode*/ + case PHY_INTERFACE_RX_ENERGY_STATE: + if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { + return 1; + } + if (!sleep_blocked) { + /* Disable enter to deep sleep when transfer active */ + sleep_manager_lock_deep_sleep(); + sleep_blocked = true; + } + rf_abort(); + rf_mac_ed_state_enable(); + break; + case PHY_INTERFACE_SNIFFER_STATE: /**< Enable Sniffer state */ + rf_promiscuous(1); + if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { + return 1; + } + if (!sleep_blocked) { + /* Disable enter to deep sleep when transfer active */ + sleep_manager_lock_deep_sleep(); + sleep_blocked = true; + } + rf_receive(); + break; + } + + platform_exit_critical(); + + return 0; +} + +/* + * \brief Function forces the XCVR to Idle state. + * + * \param none + * + * \return none + */ +static void rf_abort(void) +{ + /* Mask XCVR irq */ + NVIC_DisableIRQ(Radio_1_IRQn); + + mPhySeqState = gIdle_c; + + /* Mask SEQ interrupt */ + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_SEQMSK_MASK; + + /* Disable timer trigger (for scheduled XCVSEQ) */ + if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_TMRTRIGEN_MASK) { + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMRTRIGEN_MASK; + /* give the FSM enough time to start if it was triggered */ + while ((XCVR_MISC->XCVR_CTRL & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) == 0) {} + } + + /* If XCVR is not idle, abort current SEQ */ + if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) { + /* Abort current SEQ */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; + + /* Wait for Sequence Idle (if not already) */ + while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} + } + + /* Stop timers */ + ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR1CMP_EN_MASK | + ZLL_PHY_CTRL_TMR2CMP_EN_MASK | + ZLL_PHY_CTRL_TMR3CMP_EN_MASK | + ZLL_PHY_CTRL_TC3TMOUT_MASK); + + /* clear all IRQ bits to avoid unexpected interrupts */ + ZLL->IRQSTS = ZLL->IRQSTS; + + /* Unmask XCVR irq */ + NVIC_EnableIRQ(Radio_1_IRQn); +} + +static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol) +{ + uint32_t reg, tx_len; + uint32_t irqSts; + volatile uint8_t *pPB; + uint8_t i; + uint32_t tx_warmup_time; + + platform_enter_critical(); + + if (mPhySeqState == gRX_c) { + rf_abort(); + } + + /* Check if transmitter is busy*/ + if (mPhySeqState != gIdle_c) { + platform_exit_critical(); + /*Return busy*/ + return -1; + } + + /*Store TX handle*/ + rf_mac_handle = tx_handle; + + /* Check if transmitted data needs to be acked */ + need_ack = (*data_ptr & 0x20) == 0x20; + + /* Load data into Packet Buffer */ + pPB = (uint8_t *)ZLL->PKT_BUFFER_TX; + + tx_len = data_length + 2; + *pPB++ = tx_len; /* including 2 bytes of FCS */ + + for (i = 0; i < data_length; i++) { + *pPB++ = *data_ptr++; + } + + reg = ZLL->PHY_CTRL; + + /* Perform CCA before TX */ + reg |= ZLL_PHY_CTRL_CCABFRTX_MASK; + + /* Set CCA mode 1 */ + reg &= ~(ZLL_PHY_CTRL_CCATYPE_MASK); + reg |= ZLL_PHY_CTRL_CCATYPE(gCcaCCA_MODE1_c); + ZLL->PHY_CTRL = reg; + + /* Perform TxRxAck sequence if required by phyTxMode */ + if (need_ack) { + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_RXACKRQD_MASK; + mPhySeqState = gTR_c; + } else { + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RXACKRQD_MASK; + mPhySeqState = gTX_c; + } + + /* Ensure that no spurious interrupts are raised */ + irqSts = ZLL->IRQSTS; + irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); + irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; + ZLL->IRQSTS = irqSts; + + tx_warmup_time = (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_MASK) >> + XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_SHIFT; + + /* Compute warmup times (scaled to 16us) */ + if (tx_warmup_time & 0x0F) { + tx_warmup_time = 1 + (tx_warmup_time >> 4); + } else { + tx_warmup_time = tx_warmup_time >> 4; + } + + if (need_ack) { + rf_ack_wait_timer_start(tx_warmup_time + KW41Z_SHR_PHY_TIME + + tx_len * KW41Z_PER_BYTE_TIME + 10 + KW41Z_ACK_WAIT_TIME); + } + + /* Unmask SEQ interrupt */ + ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_SEQMSK_MASK); + + /* Start the TX / TRX */ + reg = ZLL->PHY_CTRL; + reg &= ~(ZLL_PHY_CTRL_XCVSEQ_MASK); + reg |= mPhySeqState; + ZLL->PHY_CTRL = reg; + + platform_exit_critical(); + + /*Return success*/ + return 0; +} + +static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) +{ + int8_t ret_val = 0; + + platform_enter_critical(); + + switch (address_type) { + case PHY_MAC_64BIT: + rf_mac_set_mac64(address_ptr); + break; + /*Set 16-bit address*/ + case PHY_MAC_16BIT: + rf_mac_set_shortAddress(address_ptr); + break; + /*Set PAN Id*/ + case PHY_MAC_PANID: + rf_mac_set_panId(address_ptr); + break; + default: + ret_val = -1; + } + + platform_exit_critical(); + + return ret_val; +} + +static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) +{ + int ret_value = 0; + + platform_enter_critical(); + + switch (extension_type) { + case PHY_EXTENSION_CTRL_PENDING_BIT: /**< Control MAC pending bit for indirect data. */ + rf_mac_set_pending(*data_ptr); + break; + /* Return frame Auto Ack frame pending status */ + case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: { + *data_ptr = rf_ack_pending_state; + break; + } + case PHY_EXTENSION_SET_CHANNEL: /**< Net library channel set. */ + break; + case PHY_EXTENSION_READ_CHANNEL_ENERGY: /**< RF interface ED scan energy read. */ + *data_ptr = rf_ed_value; + break; + case PHY_EXTENSION_READ_LINK_STATUS: /**< Net library could read link status. */ + case PHY_EXTENSION_CONVERT_SIGNAL_INFO: /**< Convert signal info. */ + default: + ret_value = -1; + } + + platform_exit_critical(); + + return ret_value; +} + +/* + * \brief Function converts the energy level from dBm to a 0-255 value. + * + * \param energyLevel in dBm + * + * \return energy level (0-255) + */ +static uint8_t rf_convert_energy_level(uint8_t energyLevel) +{ + int32_t temp = (int8_t)energyLevel; + + if (temp <= -82) { + temp = 0x00; + } else if (temp >= -3) { + temp = 0xFF; + } else { + /* Convert energy level from dbm into a 0x00-0xFF value */ + temp = (255 * temp + 20910) / 79; + } + + return (uint8_t)temp; +} + +/** + * SET MAC 16 address to Register + */ +static void rf_mac_set_shortAddress(uint8_t *valueAddress) +{ + ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK; + ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACSHORTADDRS0(common_read_16_bit(valueAddress)); +} + +/** + * SET PAN-ID to Register + */ +static void rf_mac_set_panId(uint8_t *valueAddress) +{ + ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACPANID0_MASK; + ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACPANID0(common_read_16_bit(valueAddress)); +} + +/** + * SET MAC64 address to register + */ +static void rf_mac_set_mac64(const uint8_t *valueAddress) +{ + ZLL->MACLONGADDRS0_MSB = common_read_32_bit(valueAddress); + valueAddress += 4; + ZLL->MACLONGADDRS0_LSB = common_read_32_bit(valueAddress); +} + +static void PhyPlmeSetPwrLevelRequest(uint8_t pwrStep) +{ + /* Do not exceed the Tx power limit for the current channel */ + if (pwrStep > gPhyMaxTxPowerLevel_d) { + pwrStep = gPhyMaxTxPowerLevel_d; + } + + if (pwrStep > 2) { + pwrStep = (pwrStep << 1) - 2; + } + + ZLL->PA_PWR = pwrStep; +} + +static uint8_t PhyPlmeGetPwrLevelRequest(void) +{ + uint8_t pwrStep = (uint8_t)ZLL->PA_PWR; + + if (pwrStep > 2) { + pwrStep = (pwrStep + 2) >> 1; + } + + return pwrStep; +} + +static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan) +{ + if ((channel < 11) || (channel > 26)) { + return 1; + } + + if (!pan) { + ZLL->CHANNEL_NUM0 = channel; + } else { + ZLL->CHANNEL_NUM1 = channel; + } + + /* Make sure the current Tx power doesn't exceed the Tx power limit for the new channel */ + if (PhyPlmeGetPwrLevelRequest() > gPhyMaxTxPowerLevel_d) { + PhyPlmeSetPwrLevelRequest(gPhyMaxTxPowerLevel_d); + } + + return 0; +} + +/* + * Function is a RF interrupt vector. + */ +static void PHY_InterruptHandler(void) +{ + /* Disable and clear transceiver(IRQ_B) interrupt */ + NVIC_DisableIRQ(Radio_1_IRQn); + rf_thread.flags_set(1); +} + +static void PhyIsrSeqCleanup(void) +{ + uint32_t irqStatus; + + /* Set the PHY sequencer back to IDLE */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; + /* Mask SEQ, RX, TX and CCA interrupts */ + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK | + ZLL_PHY_CTRL_RXMSK_MASK | + ZLL_PHY_CTRL_TXMSK_MASK | + ZLL_PHY_CTRL_SEQMSK_MASK; + + while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} + + irqStatus = ZLL->IRQSTS; + /* Mask TMR3 interrupt */ + irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK; + /* Clear transceiver interrupts except TMRxIRQ */ + irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | + ZLL_IRQSTS_TMR2IRQ_MASK | + ZLL_IRQSTS_TMR3IRQ_MASK | + ZLL_IRQSTS_TMR4IRQ_MASK); + ZLL->IRQSTS = irqStatus; +} + +static void PhyIsrTimeoutCleanup(void) +{ + uint32_t irqStatus; + + /* Set the PHY sequencer back to IDLE and disable TMR3 comparator and timeout */ + ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR3CMP_EN_MASK | + ZLL_PHY_CTRL_TC3TMOUT_MASK | + ZLL_PHY_CTRL_XCVSEQ_MASK); + /* Mask SEQ, RX, TX and CCA interrupts */ + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK | + ZLL_PHY_CTRL_RXMSK_MASK | + ZLL_PHY_CTRL_TXMSK_MASK | + ZLL_PHY_CTRL_SEQMSK_MASK; + + while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} + + irqStatus = ZLL->IRQSTS; + /* Mask TMR3 interrupt */ + irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK; + /* Clear transceiver interrupts except TMR1IRQ and TMR4IRQ. */ + irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | + ZLL_IRQSTS_TMR4IRQ_MASK); + ZLL->IRQSTS = irqStatus; + + /* The packet was transmitted successfully, but no ACK was received */ + if (device_driver.phy_tx_done_cb) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1); + } +} + +/* + * \brief Function get the time-out for the current sequence. + * + * \return sequence time-out value + */ +static uint32_t rf_get_timeout(void) +{ + return ZLL->T3CMP; +} + +/* + * \brief Function set a time-out to an XCVR sequence. + * + * \param pEndTime sequence time-out value [symbols] + * + * \return none + */ +static void rf_set_timeout(uint32_t pEndTime) +{ + uint32_t irqsts; + + platform_enter_critical(); + + /* disable TMR3 compare */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMR3CMP_EN_MASK; + ZLL->T3CMP = pEndTime & ZLL_T3CMP_T3CMP_MASK; + + /* acknowledge TMR3 IRQ */ + irqsts = ZLL->IRQSTS & BM_ZLL_IRQSTS_TMRxMSK; + irqsts |= ZLL_IRQSTS_TMR3IRQ_MASK; + ZLL->IRQSTS = irqsts; + /* enable TMR3 compare */ + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TMR3CMP_EN_MASK; + /* enable autosequence stop by TC3 match */ + ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TC3TMOUT_MASK; + + platform_exit_critical(); + +} + +/** + * Call this only One time + */ +static void rf_mac_hw_init(void) +{ + xcvrStatus_t xcvrStatus; + uint32_t phyReg; + + /* The data rate argument only matters when GFSK/MSK protocol is selected */ + xcvrStatus = XCVR_Init(ZIGBEE_MODE, DR_500KBPS); + if (xcvrStatus != gXcvrSuccess_c) { + return; + } + + mPhySeqState = gIdle_c; + + /* Enable 16 bit mode for TC2 - TC2 prime EN, disable all timers, + enable AUTOACK, mask all interrupts */ + ZLL->PHY_CTRL = (gCcaCCA_MODE1_c << ZLL_PHY_CTRL_CCATYPE_SHIFT) | + ZLL_PHY_CTRL_TC2PRIME_EN_MASK | + ZLL_PHY_CTRL_TSM_MSK_MASK | + ZLL_PHY_CTRL_WAKE_MSK_MASK | + ZLL_PHY_CTRL_CRC_MSK_MASK | + ZLL_PHY_CTRL_PLL_UNLOCK_MSK_MASK | + ZLL_PHY_CTRL_FILTERFAIL_MSK_MASK | + ZLL_PHY_CTRL_RX_WMRK_MSK_MASK | + ZLL_PHY_CTRL_CCAMSK_MASK | + ZLL_PHY_CTRL_RXMSK_MASK | + ZLL_PHY_CTRL_TXMSK_MASK | + ZLL_PHY_CTRL_SEQMSK_MASK | + ZLL_PHY_CTRL_AUTOACK_MASK | + ZLL_PHY_CTRL_TRCV_MSK_MASK; + + /* Clear all PP IRQ bits to avoid unexpected interrupts immediately after init + disable all timer interrupts */ + ZLL->IRQSTS = ZLL->IRQSTS; + + /* Clear HW indirect queue */ + ZLL->SAM_TABLE |= ZLL_SAM_TABLE_INVALIDATE_ALL_MASK; + + /* Frame Filtering + FRM_VER[7:6] = b11. Accept FrameVersion 0 and 1 packets, reject all others */ + ZLL->RX_FRAME_FILTER &= ~ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK; + ZLL->RX_FRAME_FILTER = ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3) | + ZLL_RX_FRAME_FILTER_CMD_FT_MASK | + ZLL_RX_FRAME_FILTER_DATA_FT_MASK | + ZLL_RX_FRAME_FILTER_BEACON_FT_MASK; + + /* Set prescaller to obtain 1 symbol (16us) timebase */ + ZLL->TMR_PRESCALE = 0x05; + + /* Set CCA threshold to -75 dBm */ + ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK; + ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_CCA1_THRESH(RF_CCA_THRESHOLD); + + /* Set the default power level */ + PhyPlmeSetPwrLevelRequest(gPhyDefaultTxPowerLevel_d); + + /* Adjust ACK delay to fulfill the 802.15.4 turnaround requirements */ + ZLL->ACKDELAY &= ~ZLL_ACKDELAY_ACKDELAY_MASK; + ZLL->ACKDELAY |= ZLL_ACKDELAY_ACKDELAY(-8); + + /* Adjust LQI compensation */ + ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP_MASK; + ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP(96); + + /* Enable the RxWatermark IRQ */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RX_WMRK_MSK_MASK; + /* Set Rx watermark level */ + ZLL->RX_WTR_MARK = 0; + + /* Set default channels */ + PhyPlmeSetCurrentChannelRequest(0x0B, 0); /* 2405 MHz */ + PhyPlmeSetCurrentChannelRequest(0x0B, 1); /* 2405 MHz */ + + /* DSM settings */ + phyReg = (RSIM->RF_OSC_CTRL & RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_MASK) >> + RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_SHIFT; + phyReg = (1024U << phyReg) / (SystemCoreClock / 32768) + 1; + RSIM->DSM_OSC_OFFSET = phyReg; + + osStatus status = rf_thread.start(mbed::callback(rf_thread_loop)); + MBED_ASSERT(status == osOK); + + /** Clear and enable MAC IRQ at task level, when scheduler is on. */ + InstallIRQHandler((IRQn_Type)Radio_1_IRQn, (uint32_t)PHY_InterruptHandler); + + /* Unmask Transceiver Global Interrupts */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TRCV_MSK_MASK; + + NVIC_ClearPendingIRQ(Radio_1_IRQn); + NVIC_EnableIRQ(Radio_1_IRQn); +} + +/* + * \brief Function reads a time-stamp value from XCVR [symbols] + * + * \param pEndTime pointer to location where time-stamp will be stored + * + * \return none + */ +static void rf_get_timestamp(uint32_t *pRetClk) +{ + if (NULL == pRetClk) { + return; + } + + platform_enter_critical(); + + *pRetClk = 0; + *pRetClk = ZLL->EVENT_TMR >> ZLL_EVENT_TMR_EVENT_TMR_SHIFT; + + platform_exit_critical(); +} + +/* + * \brief Function starts the ACK wait time-out. + * + * \param slots The ACK wait time-out in [symbols] + * + * \return none + */ +static void rf_ack_wait_timer_start(uint16_t time) +{ + uint32_t timestamp, t; + + rf_get_timestamp(×tamp); + + t = (rf_get_timeout() - timestamp) & gPhyTimeMask_c; + + if (t > 1) { + timestamp += time; + rf_set_timeout(timestamp); + } +} + +/* + * \brief Function sets the RF in RX state. + * + * \param none + * + * \return none + */ +static void rf_receive(void) +{ + uint32_t irqSts; + + /* RX can start only from Idle state */ + if (mPhySeqState != gIdle_c) { + rf_abort(); + } + + mPhySeqState = gRX_c; + + /* Ensure that no spurious interrupts are raised, but do not change TMR1 and TMR4 IRQ status */ + irqSts = ZLL->IRQSTS; + irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); + irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; + ZLL->IRQSTS = irqSts; + + /* unmask SEQ interrupt */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; + + /* Start the RX sequence */ + ZLL->PHY_CTRL |= gRX_c; +} + +static void rf_mac_ed_state_enable(void) +{ + uint32_t ccaMode, irqSts; + + mPhySeqState = gCCA_c; + + /* Switch to ED mode */ + ccaMode = (ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCATYPE_MASK) >> ZLL_PHY_CTRL_CCATYPE_SHIFT; + if (ccaMode != gCcaED_c) { + ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_CCATYPE_MASK); + } + + /* Ensure that no spurious interrupts are raised(do not change TMR1 and TMR4 IRQ status) */ + irqSts = ZLL->IRQSTS; + irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); + irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; + ZLL->IRQSTS = irqSts; + + /* Unmask SEQ interrupt */ + ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; + + /* start ED sequence */ + ZLL->PHY_CTRL |= gCCA_c; +} + +/* + * \brief Function is a call back for TX end interrupt. + * + * \param none + * + * \return none + */ +static void rf_handle_tx_end(bool framePending) +{ + /*Start receiver*/ + rf_receive(); + + if (!device_driver.phy_tx_done_cb) { + return; + } + + /*Call PHY TX Done API*/ + if (need_ack) { + if (framePending) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE_PENDING, 1, 1); + } else { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE, 1, 1); + } + } else { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1); + } +} + +/* + * \brief Function converts LQI into RSSI. + * + * \param LQI + * + * \return RSSI + */ +static int8_t rf_convert_LQI_to_RSSI(uint8_t lqi) +{ + int32_t rssi = (36 * lqi - 9836) / 109; + + return (int8_t)rssi; +} + +/* + * \brief Function scale the LQI value reported by RF into a 0-255 value. + * + * \param hwLqi - the LQI value reported by RF + * + * \return scaled LQI + */ +static uint8_t rf_convert_LQI(uint8_t hwLqi) +{ + if (hwLqi >= 220) { + return 255; + } else { + return (51 * hwLqi) / 44; + } +} + +/* + * \brief Function is a call back for RX end interrupt. + * + * \param none + * + * \return none + */ +static void rf_handle_rx_end(void) +{ + uint8_t rf_lqi = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_LQI_VALUE_MASK) >> + ZLL_LQI_AND_RSSI_LQI_VALUE_SHIFT; + int8_t rf_rssi = 0; + uint8_t len; + uint8_t i; + volatile uint8_t *pPB; + + len = (ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; /* Including FCS (2 bytes) */ + + /* Excluding FCS (2 bytes) */ + len -= 2; + + /*Check the length is valid*/ + if (len > 1 && len < MAC_PACKET_SIZE) { + rf_lqi = rf_convert_LQI(rf_lqi); + rf_rssi = rf_convert_LQI_to_RSSI(rf_lqi); + + /* Load data from Packet Buffer */ + pPB = (uint8_t *)ZLL->PKT_BUFFER_RX; + + for (i = 0; i < len; i++) { + PHYPAYLOAD[i] = *pPB++; + } + + /* Start receiver */ + rf_receive(); + + if (device_driver.phy_rx_cb) { + device_driver.phy_rx_cb(PHYPAYLOAD, len, rf_lqi, rf_rssi, rf_radio_driver_id); + } + } else { + /* Start receiver */ + rf_receive(); + } +} + +static void handle_IRQ_events(void) +{ + uint8_t xcvseqCopy; + uint32_t irqStatus; + + /* Read current XCVRSEQ and interrupt status */ + xcvseqCopy = ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK; + irqStatus = ZLL->IRQSTS; + /* Clear all xcvr interrupts */ + ZLL->IRQSTS = irqStatus; + + /* Filter Fail IRQ */ + if (irqStatus & ZLL_IRQSTS_FILTERFAIL_IRQ_MASK) { + + } else { + /* Rx Watermark IRQ */ + if ((!(ZLL->PHY_CTRL & ZLL_PHY_CTRL_RX_WMRK_MSK_MASK)) && + (irqStatus & ZLL_IRQSTS_RXWTRMRKIRQ_MASK)) { + uint32_t rx_len = (irqStatus & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; + + /* Convert to symbols and add IFS and ACK duration */ + rx_len = rx_len * 2 + 12 + 22 + 2; + rf_ack_wait_timer_start(rx_len); + } + } + + /* Sequencer interrupt, the autosequence has completed */ + if (irqStatus & ZLL_IRQSTS_SEQIRQ_MASK) { + + /* XCVR will be set to Idle */ + mPhySeqState = gIdle_c; + + /* PLL unlock, the autosequence has been aborted due to PLL unlock */ + if (irqStatus & ZLL_IRQSTS_PLL_UNLOCK_IRQ_MASK) { + PhyIsrSeqCleanup(); + /* Start receiver */ + rf_receive(); + } + /* TMR3 timeout, the autosequence has been aborted due to TMR3 timeout */ + else if ((irqStatus & ZLL_IRQSTS_TMR3IRQ_MASK) && + (!(irqStatus & ZLL_IRQSTS_RXIRQ_MASK)) && + (xcvseqCopy != gTX_c)) { + PhyIsrTimeoutCleanup(); + /* Start receiver */ + rf_receive(); + } else { + PhyIsrSeqCleanup(); + switch (xcvseqCopy) { + case gTX_c: + if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && + (irqStatus & ZLL_IRQSTS_CCA_MASK)) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, + PHY_LINK_CCA_FAIL, 1, 1); + } else { + rf_handle_tx_end(false); + } + break; + + case gTR_c: + if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && + (irqStatus & ZLL_IRQSTS_CCA_MASK)) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, + PHY_LINK_CCA_FAIL, 1, 1); + } else { + rf_handle_tx_end((irqStatus & ZLL_IRQSTS_RX_FRM_PEND_MASK) > 0); + } + break; + + case gRX_c: + rf_handle_rx_end(); + break; + + case gCCA_c: + rf_ed_value = rf_convert_energy_level((ZLL->LQI_AND_RSSI & + ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >> + ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT); + break; + + default: + break; + } + } + } +} + +NanostackRfPhyKw41z::NanostackRfPhyKw41z() +{ + memcpy(MAC64_addr, MAC64_addr_default, sizeof(MAC64_addr)); +} + +NanostackRfPhyKw41z::~NanostackRfPhyKw41z() +{ + // Do nothing +} + +int8_t NanostackRfPhyKw41z::rf_register() +{ + platform_enter_critical(); + + if (rf != NULL) { + platform_exit_critical(); + error("Multiple registrations of NanostackRfPhyKw41z not supported"); + return -1; + } + + rf = this; + int8_t radio_id = rf_device_register(); + if (radio_id < 0) { + rf = NULL; + } + + platform_exit_critical(); + return radio_id; +} + +void NanostackRfPhyKw41z::rf_unregister() +{ + platform_enter_critical(); + + if (rf != this) { + platform_exit_critical(); + return; + } + + rf_device_unregister(); + rf = NULL; + + platform_exit_critical(); +} + +void NanostackRfPhyKw41z::get_mac_address(uint8_t *mac) +{ + platform_enter_critical(); + + memcpy((void *)mac, (void *)MAC64_addr, sizeof(MAC64_addr)); + + platform_exit_critical(); +} + +void NanostackRfPhyKw41z::set_mac_address(uint8_t *mac) +{ + platform_enter_critical(); + + if (NULL != rf) { + error("NanostackRfPhyKw41z cannot change mac address when running"); + platform_exit_critical(); + return; + } + memcpy((void *)MAC64_addr, (void *)mac, sizeof(MAC64_addr)); + + platform_exit_critical(); +} + +NanostackRfPhy &NanostackRfPhy::get_default_instance() +{ + static NanostackRfPhyKw41z rf_phy; + return rf_phy; +} diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h new file mode 100644 index 0000000..c85f6bd --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANOSTACK_PHY_KW41Z_H_ +#define NANOSTACK_PHY_KW41Z_H_ + +#if MBED_CONF_RTOS_PRESENT +#include "rtos/rtos.h" +#endif +#include "NanostackRfPhy.h" +#include "fsl_common.h" + +class NanostackRfPhyKw41z : public NanostackRfPhy { +public: + NanostackRfPhyKw41z(); + ~NanostackRfPhyKw41z(); + int8_t rf_register(); + void rf_unregister(); + void get_mac_address(uint8_t *mac); + void set_mac_address(uint8_t *mac); +}; + +#endif /* NANOSTACK_PHY_KW41Z_H_ */ diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/mbed_lib.json b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/mbed_lib.json new file mode 100644 index 0000000..ce3d225 --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Freescale/TARGET_KW41Z/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "ns-kw41z-rf" +} diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp new file mode 100644 index 0000000..1369287 --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp @@ -0,0 +1,1281 @@ +/* + * Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "NanostackRfPhyEfr32.h" + +#include + +#include "mbed.h" +#include "mbed_power_mgmt.h" +#include "ns_types.h" +#include "platform/arm_hal_interrupt.h" +#include "nanostack/platform/arm_hal_phy.h" +#include "mbed_toolchain.h" + +#include "mbed-trace/mbed_trace.h" +#define TRACE_GROUP "SLRF" + +/* Enable debug printing with SL_RADIO_DEBUG, override debug printer with SL_DEBUG_PRINT. */ +#ifdef SL_RADIO_DEBUG +#ifndef SL_DEBUG_PRINT +#define SL_DEBUG_PRINT(...) tr_debug(__VA_ARGS__) +#endif +#else +#define SL_DEBUG_PRINT(...) +#endif + +/* RF_THREAD_STACK_SIZE defines tack size for the RF adaptor thread */ +#ifndef RF_THREAD_STACK_SIZE +#define RF_THREAD_STACK_SIZE 1024 +#endif + +/* RF_QUEUE_SIZE defines queue size for incoming messages */ +#ifndef RF_QUEUE_SIZE +#define RF_QUEUE_SIZE 8 +#endif + +/* 802.15.4 maximum size of a single packet including PHY byte is 128 bytes */ +#define MAC_PACKET_MAX_LENGTH 128 +/* Offsets of prepended data in packet buffer */ +#define MAC_PACKET_OFFSET_RSSI 0 +#define MAC_PACKET_OFFSET_LQI 1 +/* This driver prepends RSSI and LQI */ +#define MAC_PACKET_INFO_LENGTH 2 + +/* RFThreadSignal used to signal from interrupts to the adaptor thread */ +enum RFThreadSignal { + SL_RX_DONE = (1 << 1), + SL_TX_DONE = (1 << 2), + SL_TX_ERR = (1 << 3), + SL_TX_TIMEOUT = (1 << 4), + SL_ACK_RECV = (1 << 5), + SL_ACK_TIMEOUT = (1 << 6), + SL_TXFIFO_ERR = (1 << 7), + SL_RXFIFO_ERR = (1 << 8), + SL_CAL_REQ = (1 << 9), + SL_RSSI_DONE = (1 << 10), + SL_QUEUE_FULL = (1 << 11), + + // ACK pend flag can be signalled in addition to RX_DONE + SL_ACK_PEND = (1 << 30), +}; + +/* Adaptor thread definitions */ +static void rf_thread_loop(const void *arg); +static osThreadDef(rf_thread_loop, osPriorityRealtime, RF_THREAD_STACK_SIZE); +static osThreadId rf_thread_id = 0; + +/* Queue for passing messages from interrupt to adaptor thread */ +static volatile uint8_t rx_queue[RF_QUEUE_SIZE][MAC_PACKET_MAX_LENGTH + MAC_PACKET_INFO_LENGTH]; +static volatile size_t rx_queue_head = 0; +static volatile size_t rx_queue_tail = 0; + +/* Silicon Labs headers */ +extern "C" { +#include "rail/rail.h" +#include "rail/pa.h" +#include "rail/pti.h" +#include "rail/ieee802154/rail_ieee802154.h" +} + +/* RF driver data */ +static phy_device_driver_s device_driver; +static int8_t rf_radio_driver_id = -1; +static uint8_t MAC_address[8]; +static uint16_t PAN_address; +static uint16_t short_address; + +/* Driver instance handle */ +static NanostackRfPhyEfr32 *rf = NULL; + +/* Channel configurations */ +static const phy_rf_channel_configuration_s phy_24ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK}; +static const phy_rf_channel_configuration_s phy_subghz = {868300000U, 2000000U, 250000U, 11U, M_OQPSK}; + +static const phy_device_channel_page_s phy_channel_pages[] = { + { CHANNEL_PAGE_0, &phy_24ghz}, + { CHANNEL_PAGE_2, &phy_subghz}, + { CHANNEL_PAGE_0, NULL} +}; + +/* Driver structures */ +typedef enum { + RADIO_UNINIT, + RADIO_INITING, + RADIO_IDLE, + RADIO_TX, + RADIO_RX, + RADIO_CALIBRATION +} siliconlabs_modem_state_t; + +static const RAIL_CsmaConfig_t csma_config = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA; + +#if defined(TARGET_EFR32_1) +#include "ieee802154_subg_efr32xg1_configurator_out.h" +#elif defined(TARGET_EFR32_12) +/* TODO: Add SubG support for EFR32_12 */ +#else +#error "Not a valid target." +#endif + +#if MBED_CONF_SL_RAIL_HAS_SUBGIG +static RAIL_ChannelConfigEntryAttr_t entry_868; +static RAIL_ChannelConfigEntryAttr_t entry_915; +static const RAIL_ChannelConfigEntry_t entry[] = { + { + .phyConfigDeltaAdd = NULL, // Add this to default config for this entry + .baseFrequency = 868300000U, + .channelSpacing = 600000U, + .physicalChannelOffset = 0, + .channelNumberStart = 0, + .channelNumberEnd = 0, + .maxPower = RAIL_TX_POWER_MAX, + .attr = &entry_868 + }, + { + .phyConfigDeltaAdd = NULL, // Add this to default config for this entry + .baseFrequency = 906000000U, + .channelSpacing = 2000000U, + .physicalChannelOffset = 1, + .channelNumberStart = 1, + .channelNumberEnd = 10, + .maxPower = RAIL_TX_POWER_MAX, + .attr = &entry_915 + } +}; +#endif + +#if MBED_CONF_SL_RAIL_BAND == 868 +#if !MBED_CONF_SL_RAIL_HAS_SUBGIG +#error "Sub-Gigahertz band is not supported on this target." +#endif +static const RAIL_ChannelConfig_t channels = { + ieee802154_config_863, + ieee802154_config_863_min, + &entry[0], + 1 +}; +#elif MBED_CONF_SL_RAIL_BAND == 915 +#if !MBED_CONF_SL_RAIL_HAS_SUBGIG +#error "Sub-Gigahertz band is not supported on this target." +#endif +static const RAIL_ChannelConfig_t channels = { + ieee802154_config_915, + ieee802154_config_915_min, + &entry[1], + 1 +}; +#elif MBED_CONF_SL_RAIL_BAND == 2400 +#ifndef MBED_CONF_SL_RAIL_HAS_2P4 +#error "2.4GHz band is not supported on this target." +#endif +#else +#error "sl-rail.band is not correctly defined" +#endif + +#if defined (MBED_CONF_SL_RAIL_HAS_2P4) +// Set up the PA for 2.4 GHz operation +static const RAIL_TxPowerConfig_t paInit2p4 = { + .mode = RAIL_TX_POWER_MODE_2P4_HP, + .voltage = 1800, + .rampTime = 10, +}; +#endif + +#if MBED_CONF_SL_RAIL_HAS_SUBGIG +// Set up the PA for sub-GHz operation +static const RAIL_TxPowerConfig_t paInitSubGhz = { + .mode = RAIL_TX_POWER_MODE_SUBGIG, + .voltage = 1800, + .rampTime = 10, +}; +#endif + +static const RAIL_StateTiming_t timings = { + .idleToRx = 100, + // Make txToRx slightly lower than desired to make sure we get to + // RX in time + .txToRx = 192 - 10, + .idleToTx = 100, + .rxToTx = 192, + .rxSearchTimeout = 0, + .txToRxSearchTimeout = 0 +}; + +static const RAIL_IEEE802154_Config_t config = { + .addresses = NULL, + .ackConfig = { + .enable = true, + .ackTimeout = 1200, + .rxTransitions = { + .success = RAIL_RF_STATE_RX, + .error = RAIL_RF_STATE_RX // ignored + }, + .txTransitions = { + .success = RAIL_RF_STATE_RX, + .error = RAIL_RF_STATE_RX // ignored + } + }, + .timings = timings, + .framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES, + .promiscuousMode = false, + .isPanCoordinator = false +}; + +static volatile siliconlabs_modem_state_t radio_state = RADIO_UNINIT; +static volatile bool sleep_blocked = false; +static volatile int8_t channel = -1; +static volatile uint8_t current_tx_handle = 0; +static volatile uint8_t current_tx_sequence = 0; +static volatile bool waiting_for_ack = false; +static volatile bool data_pending = false, last_ack_pending_bit = false; +static volatile uint32_t last_tx = 0; +static volatile RAIL_Handle_t gRailHandle = NULL; +static uint8_t txFifo[256]; + +/* ARM_NWK_HAL prototypes */ +static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr); +static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel); +static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr); +static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol); + +/* Local function prototypes */ +static bool rail_checkAndSwitchChannel(uint8_t channel); +static void RAILCb_RfReady(RAIL_Handle_t railHandle); +static void radioEventHandler(RAIL_Handle_t railHandle, RAIL_Events_t events); + +static RAIL_Config_t railCfg = { // Must never be const + .eventsCallback = &radioEventHandler, + .protocol = NULL, // For BLE, pointer to a RAIL_BLE_State_t. For IEEE802.15.4 this must be NULL. + .scheduler = NULL, // For MultiProtocol, pointer to a RAIL_SchedConfig_t +}; + +static void rf_thread_loop(const void *arg) +{ + SL_DEBUG_PRINT("rf_thread_loop: starting (id: %d)\n", (int)rf_thread_id); + for (;;) { + osEvent event = osSignalWait(0, osWaitForever); + + if (event.status != osEventSignal) { + continue; + } + + platform_enter_critical(); + + if (event.value.signals & SL_RX_DONE) { + while (rx_queue_tail != rx_queue_head) { + uint8_t *packet = (uint8_t *) rx_queue[rx_queue_tail]; + SL_DEBUG_PRINT("rPKT %d\n", packet[MAC_PACKET_INFO_LENGTH] - 2); + device_driver.phy_rx_cb( + &packet[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ + packet[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ + packet[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ + packet[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ + rf_radio_driver_id); + rx_queue_tail = (rx_queue_tail + 1) % RF_QUEUE_SIZE; + } + + } else if (event.value.signals & SL_TX_DONE) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_TX_SUCCESS, + 1, + 1); + } else if (event.value.signals & SL_ACK_RECV) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + (event.value.signals & SL_ACK_PEND) ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, + 1, + 1); + } else if (event.value.signals & SL_ACK_TIMEOUT) { + waiting_for_ack = false; + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_TX_FAIL, + 1, + 1); + } else if (event.value.signals & SL_TX_ERR) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); + } else if (event.value.signals & SL_TX_TIMEOUT) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); + } else if (event.value.signals & SL_CAL_REQ) { + SL_DEBUG_PRINT("rf_thread_loop: SL_CAL_REQ signal received (unhandled)\n"); + } else if (event.value.signals & SL_RXFIFO_ERR) { + SL_DEBUG_PRINT("rf_thread_loop: SL_RXFIFO_ERR signal received (unhandled)\n"); + } else if (event.value.signals & SL_TXFIFO_ERR) { + SL_DEBUG_PRINT("rf_thread_loop: SL_TXFIFO_ERR signal received (unhandled)\n"); + } else if (event.value.signals & SL_QUEUE_FULL) { + SL_DEBUG_PRINT("rf_thread_loop: SL_QUEUE_FULL signal received (packet dropped)\n"); + } else { + SL_DEBUG_PRINT("rf_thread_loop unhandled event status: %d value: %d\n", event.status, (int)event.value.signals); + } + + platform_exit_critical(); + } +} + +/*============ CODE =========*/ + +/* + * \brief Function initialises and registers the RF driver. + * + * \param none + * + * \return rf_radio_driver_id Driver ID given by NET library + */ +static int8_t rf_device_register(void) +{ + // If we already exist, bail. + if (radio_state != RADIO_UNINIT) { + return -1; + } + + SL_DEBUG_PRINT("rf_device_register: entry\n"); + + // Set up RAIL + // Initialize the RAIL library and any internal state it requires + gRailHandle = RAIL_Init(&railCfg, &RAILCb_RfReady); + + // Configure calibration settings + RAIL_ConfigCal(gRailHandle, RAIL_CAL_ALL); + + // Set up library for IEEE802.15.4 PHY operation +#if (MBED_CONF_SL_RAIL_BAND == 2400) + RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle); + channel = 11; +#elif (MBED_CONF_SL_RAIL_BAND == 915) + RAIL_ConfigChannels(gRailHandle, &channels, NULL); + channel = 1; +#elif (MBED_CONF_SL_RAIL_BAND == 868) + RAIL_ConfigChannels(gRailHandle, &channels, NULL); + channel = 0; +#endif + + // Enable 802.15.4 acceleration features + RAIL_IEEE802154_Init(gRailHandle, &config); + + // Fire all events by default + RAIL_ConfigEvents(gRailHandle, + RAIL_EVENTS_ALL, + RAIL_EVENTS_ALL); + + // Setup the transmit buffer + RAIL_SetTxFifo(gRailHandle, txFifo, 0, sizeof(txFifo)); + +#if MBED_CONF_SL_RAIL_BAND == 2400 + if (RAIL_ConfigTxPower(gRailHandle, &paInit2p4) != RAIL_STATUS_NO_ERROR) { + // Error: The PA could not be initialized due to an improper configuration. + // Please ensure your configuration is valid for the selected part. + while (1) ; + } +#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) + if (RAIL_ConfigTxPower(gRailHandle, &paInitSubGhz) != RAIL_STATUS_NO_ERROR) { + // Error: The PA could not be initialized due to an improper configuration. + // Please ensure your configuration is valid for the selected part. + while (1) ; + } +#endif + // Set the output power to the maximum supported by this chip + RAIL_SetTxPower(gRailHandle, 255); + + // Set up PTI since it makes life so much easier +#if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1) + RAIL_PtiConfig_t ptiConfig = { + MBED_CONF_SL_RAIL_PTI_MODE, + MBED_CONF_SL_RAIL_PTI_BAUDRATE, + MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION, + MBED_CONF_SL_RAIL_PTI_DOUT_PORT, + MBED_CONF_SL_RAIL_PTI_DOUT_PIN, + MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION, + MBED_CONF_SL_RAIL_PTI_DCLK_PORT, + MBED_CONF_SL_RAIL_PTI_DCLK_PIN, + MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION, + MBED_CONF_SL_RAIL_PTI_DFRAME_PORT, + MBED_CONF_SL_RAIL_PTI_DFRAME_PIN + }; + + // Initialize the Packet Trace Interface (PTI) for the EFR32 + RAIL_ConfigPti(RAIL_EFR32_HANDLE, &ptiConfig); + // Enable Packet Trace (PTI) + RAIL_EnablePti(RAIL_EFR32_HANDLE, true); +#endif + + /* Get real MAC address */ + /* MAC is stored MSB first */ + memcpy(MAC_address, (const void *)&DEVINFO->UNIQUEH, 4); + memcpy(&MAC_address[4], (const void *)&DEVINFO->UNIQUEL, 4); + + /*Set pointer to MAC address*/ + device_driver.PHY_MAC = MAC_address; + device_driver.driver_description = (char *)"EFR32_154"; + + /*Type of RF PHY*/ +#if MBED_CONF_SL_RAIL_BAND == 2400 + device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE; +#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) + device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE; +#endif + + device_driver.phy_channel_pages = phy_channel_pages; + /*Maximum size of payload is 127*/ + device_driver.phy_MTU = 127; + /*1 byte header in PHY layer (length)*/ + device_driver.phy_header_length = 1; + /*No tail in PHY layer*/ + device_driver.phy_tail_length = 0; + /*Set address write function*/ + device_driver.address_write = &rf_address_write; + /*Set RF extension function*/ + device_driver.extension = &rf_extension; + /*Set RF state control function*/ + device_driver.state_control = &rf_interface_state_control; + /*Set transmit function*/ + device_driver.tx = &rf_start_cca; + /*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/ + device_driver.phy_rx_cb = NULL; + device_driver.phy_tx_done_cb = NULL; + /*Virtual upper data callback init to NULL*/ + device_driver.arm_net_virtual_rx_cb = NULL; + device_driver.arm_net_virtual_tx_cb = NULL; + + /*Register device driver*/ + rf_radio_driver_id = arm_net_phy_register(&device_driver); + + // If the radio hasn't called the ready callback by now, place it in the initing state + if (radio_state == RADIO_UNINIT) { + radio_state = RADIO_INITING; + } + +#ifdef MBED_CONF_RTOS_PRESENT + rx_queue_head = 0; + rx_queue_tail = 0; + rf_thread_id = osThreadCreate(osThread(rf_thread_loop), NULL); +#endif + + return rf_radio_driver_id; +} + +/* + * \brief Function unregisters the RF driver. + * + * \param none + * + * \return none + */ +static void rf_device_unregister(void) +{ + arm_net_phy_unregister(rf_radio_driver_id); + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } +} + +/* + * \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO. + * + * \param data_ptr Pointer to TX data + * \param data_length Length of the TX data + * \param tx_handle Handle to transmission + * \return 0 Success + * \return -1 Busy + */ +static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol) +{ + switch (radio_state) { + case RADIO_UNINIT: + SL_DEBUG_PRINT("rf_start_cca: Radio uninit\n"); + return -1; + case RADIO_INITING: + SL_DEBUG_PRINT("rf_start_cca: Radio initing\n"); + return -1; + case RADIO_CALIBRATION: + SL_DEBUG_PRINT("rf_start_cca: Radio calibrating\n"); + return -1; + case RADIO_TX: + SL_DEBUG_PRINT("rf_start_cca: Radio in TX mode\n"); + return -1; + case RADIO_IDLE: + case RADIO_RX: + // If we're still waiting for an ACK, don't mess up the internal state + if (waiting_for_ack || RAIL_GetRadioState(gRailHandle) == RAIL_RF_STATE_TX) { + if ((RAIL_GetTime() - last_tx) < 30000) { + SL_DEBUG_PRINT("rf_start_cca: Still waiting on previous ACK\n"); + return -1; + } else { + SL_DEBUG_PRINT("rf_start_cca: TXerr\n"); + } + } + + platform_enter_critical(); + + /* Since we set up Nanostack to give us a 1-byte PHY header, we get the one extra byte at the start of data_ptr + * and need to populate it with the MAC-frame length byte (including the 2-byte hardware-inserted CRC) */ + data_ptr[0] = data_length + 2; + + RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); + RAIL_WriteTxFifo(gRailHandle, data_ptr, data_length + 1, true); + radio_state = RADIO_TX; + + RAIL_TxOptions_t txOpt = RAIL_TX_OPTIONS_DEFAULT; + //Check to see whether we'll be waiting for an ACK + if (data_ptr[1] & (1 << 5)) { + txOpt |= RAIL_TX_OPTION_WAIT_FOR_ACK; + waiting_for_ack = true; + } else { + waiting_for_ack = false; + } + + SL_DEBUG_PRINT("rf_start_cca: Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0); + + if (RAIL_StartCcaCsmaTx(gRailHandle, channel, txOpt, &csma_config, NULL) == 0) { + //Save packet number and sequence + current_tx_handle = tx_handle; + current_tx_sequence = data_ptr[3]; + platform_exit_critical(); + return 0; + } else { + RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); + RAIL_StartRx(gRailHandle, channel, NULL); + radio_state = RADIO_RX; + platform_exit_critical(); + return -1; + } + } + //Should never get here... + return -1; +} + +/* + * \brief Function gives the control of RF states to MAC. + * + * \param new_state RF state + * \param rf_channel RF channel + * + * \return 0 Success + */ +static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) +{ + int8_t ret_val = 0; + switch (new_state) { + /* Reset PHY driver and set to idle */ + case PHY_INTERFACE_RESET: + RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); + radio_state = RADIO_IDLE; + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } + break; + /* Disable PHY Interface driver */ + case PHY_INTERFACE_DOWN: + RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); + radio_state = RADIO_IDLE; + if (sleep_blocked) { + sleep_manager_unlock_deep_sleep(); + sleep_blocked = false; + } + break; + /* Enable RX */ + case PHY_INTERFACE_UP: + if (rail_checkAndSwitchChannel(rf_channel)) { + RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); + RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, false); + RAIL_StartRx(gRailHandle, channel, NULL); + radio_state = RADIO_RX; + if (!sleep_blocked) { + /* RX can only happen in EM0/1*/ + sleep_manager_lock_deep_sleep(); + sleep_blocked = true; + } + } else { + ret_val = -1; + } + break; + /* Enable wireless interface ED scan mode */ + case PHY_INTERFACE_RX_ENERGY_STATE: + SL_DEBUG_PRINT("rf_interface_state_control: Energy det req\n"); + // TODO: implement energy detection + break; + /* Enable RX in promiscuous mode (aka no address filtering) */ + case PHY_INTERFACE_SNIFFER_STATE: + if (rail_checkAndSwitchChannel(rf_channel)) { + RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); + RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, true); + RAIL_StartRx(gRailHandle, channel, NULL); + radio_state = RADIO_RX; + if (!sleep_blocked) { + /* RX can only happen in EM0/1*/ + sleep_manager_lock_deep_sleep(); + sleep_blocked = true; + } + } else { + ret_val = -1; + } + break; + } + return ret_val; +} + +/* + * \brief Function controls the ACK pending, channel setting and energy detection. + * + * \param extension_type Type of control + * \param data_ptr Data from NET library + * + * \return 0 Success + */ +static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) +{ + switch (extension_type) { + /* Control MAC pending bit for Indirect data transmission */ + case PHY_EXTENSION_CTRL_PENDING_BIT: + if (*data_ptr) { + data_pending = true; + } else { + data_pending = false; + } + break; + /* Return frame pending bit from last received ACK */ + case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: + if (last_ack_pending_bit) { + *data_ptr = 0xFF; + } else { + *data_ptr = 0; + } + break; + /* Set channel */ + case PHY_EXTENSION_SET_CHANNEL: + channel = *data_ptr; + break; + /* Read energy on the channel */ + case PHY_EXTENSION_READ_CHANNEL_ENERGY: + // TODO: implement energy detection + *data_ptr = 0; + break; + /* Read status of the link */ + case PHY_EXTENSION_READ_LINK_STATUS: + // TODO: return accurate value here + SL_DEBUG_PRINT("rf_extension: Trying to read link status\n"); + break; + /* Convert between LQI and RSSI */ + case PHY_EXTENSION_CONVERT_SIGNAL_INFO: + // TODO: return accurate value here + SL_DEBUG_PRINT("rf_extension: Trying to read signal info\n"); + break; + case PHY_EXTENSION_ACCEPT_ANY_BEACON: + SL_DEBUG_PRINT("rf_extension: Trying to accept any beacon\n"); + break; + } + return 0; +} + +/* + * \brief Function sets the addresses to RF address filters. + * + * \param address_type Type of address + * \param address_ptr Pointer to given address + * + * \return 0 Success + */ +static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) +{ + int8_t ret_val = 0; + switch (address_type) { + /*Set 48-bit address*/ + case PHY_MAC_48BIT: + // 15.4 does not support 48-bit addressing + ret_val = -1; + break; + /*Set 64-bit MAC address*/ + case PHY_MAC_64BIT: + /* Store MAC in MSB order */ + memcpy(MAC_address, address_ptr, 8); + SL_DEBUG_PRINT("rf_address_write: MACw "); + for (unsigned int i = 0; i < sizeof(MAC_address); i ++) { + SL_DEBUG_PRINT("%02x:", MAC_address[i]); + } + SL_DEBUG_PRINT("\n"); + /* Pass MAC to the RF driver in LSB order */ + uint8_t MAC_reversed[8]; + for (unsigned int i = 0; i < sizeof(MAC_address); i ++) { + MAC_reversed[i] = MAC_address[sizeof(MAC_address) - 1 - i]; + } + RAIL_IEEE802154_SetLongAddress(gRailHandle, MAC_reversed, 0); + break; + /*Set 16-bit address*/ + case PHY_MAC_16BIT: + short_address = address_ptr[0] << 8 | address_ptr[1]; + RAIL_IEEE802154_SetShortAddress(gRailHandle, short_address, 0); + break; + /*Set PAN Id*/ + case PHY_MAC_PANID: + PAN_address = address_ptr[0] << 8 | address_ptr[1]; + RAIL_IEEE802154_SetPanId(gRailHandle, PAN_address, 0); + break; + } + return ret_val; +} + +/*****************************************************************************/ +/*****************************************************************************/ + +static void rf_if_lock(void) +{ + platform_enter_critical(); +} + +static void rf_if_unlock(void) +{ + platform_exit_critical(); +} + +NanostackRfPhyEfr32::NanostackRfPhyEfr32() : NanostackRfPhy() +{ + // Do nothing +} + +NanostackRfPhyEfr32::~NanostackRfPhyEfr32() +{ + rf_unregister(); +} + +int8_t NanostackRfPhyEfr32::rf_register() +{ + + rf_if_lock(); + + if (rf != NULL) { + rf_if_unlock(); + error("Multiple registrations of NanostackRfPhyEfr32 not supported"); + return -1; + } + + int8_t radio_id = rf_device_register(); + if (radio_id < 0) { + rf = NULL; + } else { + rf = this; + } + + rf_if_unlock(); + return radio_id; +} + +void NanostackRfPhyEfr32::rf_unregister() +{ + rf_if_lock(); + + if (rf != this) { + rf_if_unlock(); + return; + } + + rf_device_unregister(); + rf = NULL; + + rf_if_unlock(); +} + +void NanostackRfPhyEfr32::get_mac_address(uint8_t *mac) +{ + rf_if_lock(); + + memcpy(mac, MAC_address, sizeof(MAC_address)); + + rf_if_unlock(); +} + +void NanostackRfPhyEfr32::set_mac_address(uint8_t *mac) +{ + rf_if_lock(); + + if (NULL != rf) { + error("NanostackRfPhyEfr32 cannot change mac address when running"); + rf_if_unlock(); + return; + } + + memcpy(MAC_address, mac, sizeof(MAC_address)); + + rf_if_unlock(); +} + +uint32_t NanostackRfPhyEfr32::get_driver_version() +{ + RAIL_Version_t railversion; + RAIL_GetVersion(&railversion, false); + + return (railversion.major << 24) | + (railversion.minor << 16) | + (railversion.rev << 8) | + (railversion.build); +} + + +//====================== RAIL-defined callbacks ========================= +/** + * Callback that lets the app know when the radio has finished init + * and is ready. + */ +static void RAILCb_RfReady(RAIL_Handle_t handle) +{ + (void) handle; + radio_state = RADIO_IDLE; +} + +/** + * Function to check the requested channel against the current channel, + * and change the radio configuration if necessary. + * + * @param channel The new channel number requested + * @return bool True if able to switch to the requested channel + * + */ +static bool rail_checkAndSwitchChannel(uint8_t newChannel) +{ + if (channel == newChannel) { + return true; + } + + if (newChannel > 0 && newChannel < 11) { + if (MBED_CONF_SL_RAIL_BAND == 915) { + channel = newChannel; + return true; + } else { + return false; + } + } else if (newChannel >= 11 && newChannel <= 26) { + if (MBED_CONF_SL_RAIL_BAND == 2400) { + channel = newChannel; + return true; + } else { + return false; + } + } else { + return false; + } +} + +/** + * Event handler for RAIL-fired events. Usually gets called from IRQ context. + * Due to IRQ latency and tailchaining, multiple event flags might be set simultaneously, + * so we have to check all of them */ +static void radioEventHandler(RAIL_Handle_t railHandle, + RAIL_Events_t events) +{ + /* RAIL_Events_t is a 64-bit event mask, but a thread only supports 32 + * signal bits. This means we have to convert from a RAIL event mask to + * our own custom event mask. */ + if (railHandle != gRailHandle) { + return; + } + +#ifdef MBED_CONF_RTOS_PRESENT + if (rf_thread_id == 0) { + return; + } +#endif + + size_t index = 0; + do { + if (events & 1ull) { + switch (index) { + /* + * Occurs when the AGC averaged RSSI is done. + * It occurs in response to RAIL_StartAverageRssi() to indicate that the + * hardware has completed averaging. Call \ref RAIL_GetAverageRssi to get the + * result. + */ + case RAIL_EVENT_RSSI_AVERAGE_DONE_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_RSSI_DONE); +#else + SL_DEBUG_PRINT("RSSI done (%d)\n", RAIL_GetAverageRssi(gRailHandle)); +#endif + break; + /* + * Notifies the application when searching for an ACK has timed + * out. This event occurs whenever the timeout for searching for an + * ACK is exceeded. + */ + case RAIL_EVENT_RX_ACK_TIMEOUT_SHIFT: + if (waiting_for_ack) { + waiting_for_ack = false; +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_ACK_TIMEOUT); +#else + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_TX_FAIL, + 1, + 1); +#endif + } + break; + /* + * Occurs when the receive FIFO exceeds the configured threshold + * value. Call \ref RAIL_GetRxFifoBytesAvailable to get the number of bytes + * available. + */ + case RAIL_EVENT_RX_FIFO_ALMOST_FULL_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_RXFIFO_ERR); +#else + SL_DEBUG_PRINT("RX near full (%d)\n", RAIL_GetRxFifoBytesAvailable(gRailHandle)); +#endif + break; + /* + * Occurs whenever a packet is received. + * Call RAIL_GetRxPacketInfo() to get basic packet information along + * with a handle to this packet for subsequent use with + * RAIL_PeekRxPacket(), RAIL_GetRxPacketDetails(), + * RAIL_HoldRxPacket(), and RAIL_ReleaseRxPacket() as needed. + * + * If \ref RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, this event also occurs + * for packets with CRC errors. + */ + case RAIL_EVENT_RX_PACKET_RECEIVED_SHIFT: { + /* Get RX packet that got signaled */ + RAIL_RxPacketInfo_t rxPacketInfo; + RAIL_RxPacketHandle_t rxHandle = RAIL_GetRxPacketInfo(gRailHandle, + RAIL_RX_PACKET_HANDLE_NEWEST, + &rxPacketInfo + ); + + /* Only process the packet if it had a correct CRC */ + if (rxPacketInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS) { + uint8_t header[4]; + RAIL_PeekRxPacket(gRailHandle, rxHandle, header, 4, 0); + + /* If this is an ACK, deal with it early */ + if ((header[0] == 5) && + (header[3] == current_tx_sequence) && + waiting_for_ack) { + /* Tell the radio to not ACK an ACK */ + RAIL_CancelAutoAck(gRailHandle); + waiting_for_ack = false; + /* Save the pending bit */ + last_ack_pending_bit = (header[1] & (1 << 4)) != 0; + /* Release packet */ + RAIL_ReleaseRxPacket(gRailHandle, rxHandle); + /* Tell the stack we got an ACK */ +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_ACK_RECV | (last_ack_pending_bit ? SL_ACK_PEND : 0)); +#else + SL_DEBUG_PRINT("rACK\n"); + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + last_ack_pending_bit ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, + 1, + 1); +#endif + } else { + /* Get RSSI and LQI information about this packet */ + RAIL_RxPacketDetails_t rxPacketDetails; + rxPacketDetails.timeReceived.timePosition = RAIL_PACKET_TIME_DEFAULT; + rxPacketDetails.timeReceived.totalPacketBytes = 0; + RAIL_GetRxPacketDetails(gRailHandle, rxHandle, &rxPacketDetails); + +#ifdef MBED_CONF_RTOS_PRESENT + /* Drop this packet if we're out of space */ + if (((rx_queue_head + 1) % RF_QUEUE_SIZE) == rx_queue_tail) { + osSignalSet(rf_thread_id, SL_QUEUE_FULL); + RAIL_ReleaseRxPacket(gRailHandle, rxHandle); + break; + } + + /* Copy into queue */ + uint8_t *packetBuffer = (uint8_t *)rx_queue[rx_queue_head]; +#else + /* Packet going temporarily onto stack for bare-metal apps */ + uint8_t packetBuffer[MAC_PACKET_MAX_LENGTH + MAC_PACKET_INFO_LENGTH]; +#endif + /* First two bytes are RSSI and LQI, respecitvely */ + packetBuffer[MAC_PACKET_OFFSET_RSSI] = (uint8_t)rxPacketDetails.rssi; + packetBuffer[MAC_PACKET_OFFSET_LQI] = (uint8_t)rxPacketDetails.lqi; + + /* Copy packet payload from circular FIFO into contiguous memory */ + RAIL_CopyRxPacket(&packetBuffer[MAC_PACKET_INFO_LENGTH], &rxPacketInfo); + + /* Release RAIL resources early */ + RAIL_ReleaseRxPacket(gRailHandle, rxHandle); + + /* Figure out whether we want to not ACK this packet */ + + /* + * packetBuffer[0] = length + * dataLength = length w/o length byte + * packetBuffer[1:2] = 0x61C9 -> 0b01100001 0b1100 1001 (version 1, dest 3, src 2, ACKreq, type = 1) + * [1] => b[0:2] frame type, b[3] = security enabled, b[4] = frame pending, b[5] = ACKreq, b[6] = intrapan + * [2] => b[2:3] destmode, b[4:5] version, b[6:7] srcmode + */ + if ((packetBuffer[MAC_PACKET_INFO_LENGTH + 1] & (1 << 5)) == 0) { + /* Cancel the ACK if the sender did not request one */ + RAIL_CancelAutoAck(gRailHandle); + } +#ifdef MBED_CONF_RTOS_PRESENT + rx_queue_head = (rx_queue_head + 1) % RF_QUEUE_SIZE; + osSignalSet(rf_thread_id, SL_RX_DONE); +#else + SL_DEBUG_PRINT("rPKT %d\n", packetBuffer[MAC_PACKET_INFO_LENGTH] - 2); + device_driver.phy_rx_cb(&packetBuffer[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ + packetBuffer[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ + packetBuffer[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ + packetBuffer[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ + rf_radio_driver_id); +#endif + } + } + } + break; + /* Event for preamble detection */ + case RAIL_EVENT_RX_PREAMBLE_DETECT_SHIFT: + break; + /* Event for detection of the first sync word */ + case RAIL_EVENT_RX_SYNC1_DETECT_SHIFT: + break; + /** Event for detection of the second sync word */ + case RAIL_EVENT_RX_SYNC2_DETECT_SHIFT: + break; + /* Event for detection of frame errors + * + * For efr32xg1x parts, frame errors include violations of variable length + * minimum/maximum limits, frame coding errors, and CRC errors. If \ref + * RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, \ref RAIL_EVENT_RX_FRAME_ERROR + * will not occur for CRC errors. + */ + case RAIL_EVENT_RX_FRAME_ERROR_SHIFT: + break; + /* Occurs when RX buffer is full */ + case RAIL_EVENT_RX_FIFO_OVERFLOW_SHIFT: + break; + /* Occurs when a packet is address filtered */ + case RAIL_EVENT_RX_ADDRESS_FILTERED_SHIFT: + break; + /* Occurs when an RX event times out */ + case RAIL_EVENT_RX_TIMEOUT_SHIFT: + break; + /* Occurs when the scheduled RX window ends */ + case RAIL_EVENT_RX_SCHEDULED_RX_END_SHIFT: + break; + /* An event for an aborted packet. It is triggered when a more specific + * reason isn't known for why the packet was aborted (e.g. + * \ref RAIL_EVENT_RX_ADDRESS_FILTERED). */ + case RAIL_EVENT_RX_PACKET_ABORTED_SHIFT: + break; + /* + * Occurs when the packet has passed any configured address and frame + * filtering options. + */ + case RAIL_EVENT_RX_FILTER_PASSED_SHIFT: + break; + /* Occurs when modem timing is lost */ + case RAIL_EVENT_RX_TIMING_LOST_SHIFT: + break; + /* Occurs when modem timing is detected */ + case RAIL_EVENT_RX_TIMING_DETECT_SHIFT: + break; + /* + * Indicates a Data Request is being received when using IEEE 802.15.4 + * functionality. This occurs when the command byte of an incoming frame is + * for a data request, which requests an ACK. This callback is called before + * the packet is fully received to allow the node to have more time to decide + * whether to set the frame pending in the outgoing ACK. This event only ever + * occurs if the RAIL IEEE 802.15.4 functionality is enabled. + * + * Call \ref RAIL_IEEE802154_GetAddress to get the source address of the + * packet. + */ + case RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND_SHIFT: + if (data_pending) { + RAIL_IEEE802154_SetFramePending(gRailHandle); + } + break; + + // TX Event Bitmasks + + /* + * Occurs when the transmit FIFO falls under the configured + * threshold value. It only occurs if a rising edge occurs across this + * threshold. This event does not occur on initailization or after resetting + * the transmit FIFO with RAIL_ResetFifo(). + * Call \ref RAIL_GetTxFifoSpaceAvailable to get the number of bytes + * available in the transmit FIFO at the time of the callback dispatch. + */ + case RAIL_EVENT_TX_FIFO_ALMOST_EMPTY_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TXFIFO_ERR); +#else + SL_DEBUG_PRINT("TX near empty (%d)\n", spaceAvailable); +#endif + break; + /* + * Interrupt level event to signify when the packet was sent. + * Call RAIL_GetTxPacketDetails() to get information about the packet + * that was transmitted. + * @note that this structure is only valid during the timeframe of the + * \ref RAIL_Config_t::eventsCallback. + */ + case RAIL_EVENT_TX_PACKET_SENT_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TX_DONE); +#else + if (device_driver.phy_tx_done_cb != NULL) { + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + // Normally we'd switch on ACK requested here, but Nanostack does that for us. + PHY_LINK_TX_SUCCESS, + // Succeeded, so how many times we tried is really not relevant. + 1, + 1); + } +#endif + last_tx = RAIL_GetTime(); + radio_state = RADIO_RX; + break; + /* + * An interrupt level event to signify when the packet was sent. + * Call RAIL_GetTxPacketDetails() to get information about the packet + * that was transmitted. + * @note This structure is only valid during the timeframe of the + * \ref RAIL_Config_t::eventsCallback. + */ + case RAIL_EVENT_TXACK_PACKET_SENT_SHIFT: + break; + /* Occurs when a TX is aborted by the user */ + case RAIL_EVENT_TX_ABORTED_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TX_TIMEOUT); +#else + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); +#endif + waiting_for_ack = false; + radio_state = RADIO_RX; + break; + /* Occurs when a TX ACK is aborted by the user */ + case RAIL_EVENT_TXACK_ABORTED_SHIFT: + break; + /* Occurs when a TX is blocked by something like PTA or RHO */ + case RAIL_EVENT_TX_BLOCKED_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TX_TIMEOUT); +#else + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); +#endif + waiting_for_ack = false; + radio_state = RADIO_RX; + break; + /* Occurs when a TX ACK is blocked by something like PTA or RHO */ + case RAIL_EVENT_TXACK_BLOCKED_SHIFT: + break; + /* Occurs when the TX buffer underflows */ + case RAIL_EVENT_TX_UNDERFLOW_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TX_TIMEOUT); +#else + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); +#endif + waiting_for_ack = false; + radio_state = RADIO_RX; + break; + /* Occurs when the buffer used for TX acking underflows */ + case RAIL_EVENT_TXACK_UNDERFLOW_SHIFT: + break; + /* Occurs when CCA/CSMA/LBT succeeds */ + case RAIL_EVENT_TX_CHANNEL_CLEAR_SHIFT: + break; + /* Occurs when CCA/CSMA/LBT fails */ + case RAIL_EVENT_TX_CHANNEL_BUSY_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_TX_TIMEOUT); +#else + device_driver.phy_tx_done_cb(rf_radio_driver_id, + current_tx_handle, + PHY_LINK_CCA_FAIL, + 8, + 1); +#endif + waiting_for_ack = false; + radio_state = RADIO_RX; + break; + /* Occurs when a CCA check is being retried */ + case RAIL_EVENT_TX_CCA_RETRY_SHIFT: + break; + /** Occurs when a clear channel assessment (CCA) is begun */ + case RAIL_EVENT_TX_START_CCA_SHIFT: + break; + + // Scheduler Event Bitmasks: Not used + + /* Event for when the scheduler switches away from this configuration */ + case RAIL_EVENT_CONFIG_UNSCHEDULED_SHIFT: + break; + /* Event for when the scheduler switches to this configuration */ + case RAIL_EVENT_CONFIG_SCHEDULED_SHIFT: + break; + /* Event for when the status of the scheduler changes */ + case RAIL_EVENT_SCHEDULER_STATUS_SHIFT: + break; + + // Other Event Bitmasks + + /* + * Notifies the application that a calibration is needed. + * It occurs whenever the RAIL library detects that a + * calibration is needed. An application determines a valid + * window to call \ref RAIL_Calibrate(). + */ + case RAIL_EVENT_CAL_NEEDED_SHIFT: +#ifdef MBED_CONF_RTOS_PRESENT + osSignalSet(rf_thread_id, SL_CAL_REQ); +#else + SL_DEBUG_PRINT("!!!! Calling for calibration\n"); +#endif + break; + default: + break; + } + } + events = events >> 1; + index += 1; + } while (events != 0); +} + +NanostackRfPhy &NanostackRfPhy::get_default_instance() +{ + static NanostackRfPhyEfr32 rf_phy; + return rf_phy; +} diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h new file mode 100644 index 0000000..e06d761 --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef NANOSTACK_PHY_EFR32_H_ +#define NANOSTACK_PHY_EFR32_H_ + +#include "mbed.h" +#include "NanostackRfPhy.h" + +class NanostackRfPhyEfr32 : public NanostackRfPhy { +public: + NanostackRfPhyEfr32(); + ~NanostackRfPhyEfr32(); + int8_t rf_register(); + void rf_unregister(); + void get_mac_address(uint8_t *mac); + void set_mac_address(uint8_t *mac); + uint32_t get_driver_version(); +}; + +#endif /* NANOSTACK_PHY_EFR32_H_ */ diff --git a/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/mbed_lib.json b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/mbed_lib.json new file mode 100644 index 0000000..4547bda --- /dev/null +++ b/connectivity/drivers/802.15.4_RF/TARGET_Silicon_Labs/TARGET_SL_RAIL/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "ns-efr32-rf" +} diff --git a/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp index 95ec2bf..c5a6378 100644 --- a/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp +++ b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/fvp_emac.cpp @@ -24,7 +24,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" using namespace std::chrono; diff --git a/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/mbed_lib.json b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/mbed_lib.json new file mode 100644 index 0000000..f9757b4 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_ARM_FM/COMPONENT_LAN91C111/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "lan91c111-emac" +} diff --git a/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp b/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp index ca97bc6..126fb4f 100644 --- a/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp +++ b/connectivity/drivers/emac/TARGET_ARM_SSG/COMPONENT_SMSC9220/smsc9220_emac.cpp @@ -23,7 +23,7 @@ #include "mbed_wait_api.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "smsc9220_emac.h" #include "smsc9220_eth_drv.h" diff --git a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp index 24c3408..9515016 100644 --- a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_SCL/interface/scl_emac.cpp @@ -24,7 +24,7 @@ #include "scl_emac.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "scl_buffer_api.h" #include "cy_result.h" #include "cy_pdl.h" diff --git a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp index 138bd8b..63b67bd 100644 --- a/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Cypress/COMPONENT_WHD/interface/whd_emac.cpp @@ -24,7 +24,7 @@ #include "whd_emac.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "whd_wlioctl.h" #include "whd_buffer_api.h" #include "cybsp_wifi.h" diff --git a/connectivity/drivers/emac/TARGET_Cypress/mbed_lib.json b/connectivity/drivers/emac/TARGET_Cypress/mbed_lib.json new file mode 100644 index 0000000..e8680a3 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_Cypress/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "cypress-emac" +} diff --git a/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp b/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp index 7485284..d7868f1 100644 --- a/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp +++ b/connectivity/drivers/emac/TARGET_Freescale_EMAC/kinetis_emac.cpp @@ -40,7 +40,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp b/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp index f08c041..5816bcd 100644 --- a/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_GD_EMAC/gd32xx_emac.cpp @@ -22,7 +22,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "gd32xx_emac.h" diff --git a/connectivity/drivers/emac/TARGET_GD_EMAC/mbed_lib.json b/connectivity/drivers/emac/TARGET_GD_EMAC/mbed_lib.json new file mode 100644 index 0000000..0f890c3 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_GD_EMAC/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "gd-emac" +} diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/mbed_lib.json b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/mbed_lib.json new file mode 100644 index 0000000..bad3b68 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "nuvoton-emac" +} diff --git a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp index 945583e..48a1dfe 100644 --- a/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NUVOTON_EMAC/numaker_emac.cpp @@ -28,7 +28,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" //#define NU_TRACE diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp index f69d524..8fba334 100644 --- a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_IMX/imx_emac.cpp @@ -40,7 +40,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_LPCTarget/mbed_lib.json b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_LPCTarget/mbed_lib.json new file mode 100644 index 0000000..39fa8c2 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_LPCTarget/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "lpc17-emac" +} diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp index 9b0b9df..b8c1414 100644 --- a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/lpc546xx_emac.cpp @@ -24,7 +24,7 @@ #include "mbed_interface.h" #include "mbed_assert.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "fsl_phy.h" diff --git a/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/mbed_lib.json b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/mbed_lib.json new file mode 100644 index 0000000..233284c --- /dev/null +++ b/connectivity/drivers/emac/TARGET_NXP_EMAC/TARGET_MCU_LPC546XX/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "lpc546xx-emac" +} diff --git a/connectivity/drivers/emac/TARGET_RDA_EMAC/mbed_lib.json b/connectivity/drivers/emac/TARGET_RDA_EMAC/mbed_lib.json new file mode 100644 index 0000000..a0fbd3b --- /dev/null +++ b/connectivity/drivers/emac/TARGET_RDA_EMAC/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "rda5981x-emac" +} diff --git a/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp b/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp index 9ab3557..4b32206 100644 --- a/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp +++ b/connectivity/drivers/emac/TARGET_RDA_EMAC/rda5981x_emac.cpp @@ -18,7 +18,7 @@ #include "cmsis_os.h" #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "lwip/arch.h" #include "lwip/pbuf.h" diff --git a/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/mbed_lib.json b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/mbed_lib.json new file mode 100644 index 0000000..6521e89 --- /dev/null +++ b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/mbed_lib.json @@ -0,0 +1,3 @@ +{ + "name": "rza1-emac" +} diff --git a/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp index 6484f53..9355041 100644 --- a/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp +++ b/connectivity/drivers/emac/TARGET_RZ_A1_EMAC/rza1_emac.cpp @@ -16,7 +16,7 @@ #include "cmsis_os.h" #include "rtos/ThisThread.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "rza1_eth.h" #include "rza1_eth_ext.h" #include "rza1_emac.h" diff --git a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp index 7ae8276..9068ac6 100644 --- a/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp +++ b/connectivity/drivers/emac/TARGET_STM/stm32xx_emac.cpp @@ -24,7 +24,7 @@ #include "mbed.h" #include "mbed_interface.h" #include "mbed_assert.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "netsocket/nsapi_types.h" #include "stm32xx_emac_config.h" diff --git a/connectivity/libraries/ppp/source/ppp_service.cpp b/connectivity/libraries/ppp/source/ppp_service.cpp index 58c2470..374bf52 100644 --- a/connectivity/libraries/ppp/source/ppp_service.cpp +++ b/connectivity/libraries/ppp/source/ppp_service.cpp @@ -22,10 +22,10 @@ #include "netsocket/nsapi_types.h" #if PPP_MBED_EVENT_QUEUE -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #else #include "Thread.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #endif #include "mbed_poll.h" diff --git a/connectivity/libraries/ppp/source/ppp_service_if.cpp b/connectivity/libraries/ppp/source/ppp_service_if.cpp index 7b983e2..72b6d79 100644 --- a/connectivity/libraries/ppp/source/ppp_service_if.cpp +++ b/connectivity/libraries/ppp/source/ppp_service_if.cpp @@ -20,7 +20,7 @@ #include "mbed_interface.h" #include "mbed_trace.h" #include "netsocket/nsapi_types.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "mbed_poll.h" #include "ip4string.h" #include "ip6string.h" diff --git a/connectivity/netsocket/source/NetworkStack.cpp b/connectivity/netsocket/source/NetworkStack.cpp index f9d0c20..be582a9 100644 --- a/connectivity/netsocket/source/NetworkStack.cpp +++ b/connectivity/netsocket/source/NetworkStack.cpp @@ -19,7 +19,7 @@ #include "stddef.h" #include #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "platform/mbed_error.h" // Default NetworkStack operations diff --git a/connectivity/netsocket/source/nsapi_dns.cpp b/connectivity/netsocket/source/nsapi_dns.cpp index 4b639bc..dacce85 100644 --- a/connectivity/netsocket/source/nsapi_dns.cpp +++ b/connectivity/netsocket/source/nsapi_dns.cpp @@ -24,7 +24,7 @@ #include #include #include -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "events/EventQueue.h" #include "netsocket/OnboardNetworkStack.h" #include "rtos/Kernel.h" diff --git a/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp b/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp index e0c3680..db0b981 100644 --- a/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp +++ b/connectivity/netsocket/tests/UNITTESTS/netsocket/IfaceDnsSocket/moduletest.cpp @@ -22,7 +22,7 @@ #include "netsocket/SocketAddress.h" #include "EMAC_mock.h" #include "OnboardNetworkStack_mock.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include #include #include diff --git a/drivers/AnalogIn.h b/drivers/AnalogIn.h index 38c1fc1..dd5d067 100644 --- a/drivers/AnalogIn.h +++ b/drivers/AnalogIn.h @@ -151,7 +151,9 @@ virtual ~AnalogIn() { - // Do nothing + lock(); + analogin_free(&_adc); + unlock(); } protected: diff --git a/drivers/QSPI.h b/drivers/QSPI.h index 1ef8df2..f98fb45 100644 --- a/drivers/QSPI.h +++ b/drivers/QSPI.h @@ -117,6 +117,9 @@ virtual ~QSPI() { + lock(); + qspi_free(&_qspi); + unlock(); } /** Configure the data transmission format diff --git a/drivers/USBCDC_ECM.h b/drivers/USBCDC_ECM.h index b29e051..70ddb9a 100644 --- a/drivers/USBCDC_ECM.h +++ b/drivers/USBCDC_ECM.h @@ -24,7 +24,7 @@ #include "ByteBuffer.h" #include "Mutex.h" #include "EventFlags.h" -#include "EventQueue.h" +#include "events/EventQueue.h" #include "Thread.h" #include "Callback.h" diff --git a/drivers/source/SerialBase.cpp b/drivers/source/SerialBase.cpp index b335f69..40bf7d0 100644 --- a/drivers/source/SerialBase.cpp +++ b/drivers/source/SerialBase.cpp @@ -287,6 +287,10 @@ for (int irq = 0; irq < IrqCnt; irq++) { attach(nullptr, (IrqType)irq); } + + if (_rx_enabled || _tx_enabled) { + serial_free(&_serial); + } } #if DEVICE_SERIAL_FC diff --git a/events/Event.h b/events/Event.h deleted file mode 100644 index 8e13c48..0000000 --- a/events/Event.h +++ /dev/null @@ -1,646 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EVENT_H -#define EVENT_H - -#include -#include "events/EventQueue.h" -#include "platform/mbed_assert.h" - -namespace events { -/** \defgroup events-public-api Events - * \ingroup mbed-os-public - * @{ - */ - -/** Event - * - * Representation of an event for fine-grain dispatch control - */ -template -class Event; - -/** - * \defgroup events_Event Event class - * @{ - */ - -/** Event - * - * Representation of an event for fine-grain dispatch control - */ -template -class Event { -public: - using duration = std::chrono::duration; - - /** Create an event - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param q Event queue to dispatch on - * @param f Function to execute when the event is dispatched - */ - template - Event(EventQueue *q, F f) - { - _event = static_cast( - equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); - - if (_event) { - _event->equeue = &q->_equeue; - _event->id = 0; - _event->delay = duration(0); - _event->period = duration(-1); - - _event->post = &Event::event_post; - _event->dtor = &Event::event_dtor; - - new (_event + 1) F(std::move(f)); - - _event->ref = 1; - } - } - - /** Copy constructor for events - */ - Event(const Event &e) - { - _event = 0; - if (e._event) { - _event = e._event; - _event->ref += 1; - } - } - - /** Assignment operator for events - */ - Event &operator=(const Event &that) - { - if (this != &that) { - this->~Event(); - new (this) Event(that); - } - - return *this; - } - - /** Destructor for events - */ - ~Event() - { - if (_event) { - _event->ref -= 1; - if (_event->ref == 0) { - _event->dtor(_event); - equeue_dealloc(_event->equeue, _event); - } - } - } - - /** Configure the delay of an event - * - * @param d Millisecond delay before dispatching the event - */ - void delay(duration d) - { - if (_event) { - _event->delay = d; - } - } - - /** Configure the delay of an event - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - * - * @param d Millisecond delay before dispatching the event - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - void delay(int d) - { - delay(duration(d)); - } - - /** Configure the period of an event - * - * @param p Millisecond period for repeatedly dispatching an event - */ - void period(duration p) - { - if (_event) { - _event->period = p; - } - } - - /** Configure the period of an event - * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. - * - * @param p Millisecond period for repeatedly dispatching an event - */ - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - void period(int p) - { - period(duration(p)); - } - - /** Posts an event onto the underlying event queue - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param args Arguments to pass to the event - * @return A unique id that represents the posted event and can - * be passed to EventQueue::cancel, or an id of 0 if - * there is not enough memory to allocate the event. - */ - int post(ArgTs... args) const - { - if (!_event) { - return 0; - } - - _event->id = _event->post(_event, args...); - return _event->id; - } - - /** Posts an event onto the underlying event queue, returning void - * - * @param args Arguments to pass to the event - */ - void call(ArgTs... args) const - { - MBED_UNUSED int id = post(args...); - MBED_ASSERT(id); - } - - /** Posts an event onto the underlying event queue, returning void - * - * @param args Arguments to pass to the event - */ - void operator()(ArgTs... args) const - { - return call(args...); - } - - /** Static thunk for passing as C-style function - * - * @param func Event to call passed as a void pointer - * @param args Arguments to pass to the event - */ - static void thunk(void *func, ArgTs... args) - { - return static_cast(func)->call(args...); - } - - /** Cancels the most recently posted event - * - * Attempts to cancel the most recently posted event. It is safe to call - * cancel after an event has already been dispatched. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active, the cancel - * function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. - */ - void cancel() const - { - if (_event) { - equeue_cancel(_event->equeue, _event->id); - } - } - -private: - struct event { - unsigned ref; - equeue_t *equeue; - int id; - - duration delay; - duration period; - - int (*post)(struct event *, ArgTs... args); - void (*dtor)(struct event *); - - // F follows - } *_event; - - // Event attributes - template - static int event_post(struct event *e, ArgTs... args) - { - typedef EventQueue::context C; - void *p = equeue_alloc(e->equeue, sizeof(C)); - if (!p) { - return 0; - } - - new (p) C(*(F *)(e + 1), args...); - equeue_event_delay(p, e->delay.count()); - equeue_event_period(p, e->period.count()); - equeue_event_dtor(p, &EventQueue::function_dtor); - return equeue_post(e->equeue, &EventQueue::function_call, p); - } - - template - static void event_dtor(struct event *e) - { - ((F *)(e + 1))->~F(); - } - -public: - /** Create an event - * @param q Event queue to dispatch on - * @param f Function to execute when the event is dispatched - * @param context_args Arguments to bind to the callback, these arguments are - * allocated on an IRQ-safe allocator from the event queue's - * memory pool. Must be type-compatible with bound_args, the - * arguments to the underlying callback. - */ - template - Event(EventQueue *q, F f, ContextArgTs... context_args) : - Event(q, EventQueue::context(f, context_args...)) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, ArgTs...), B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, ArgTs...) const, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, B0 b0) : - Event(q, mbed::callback(obj, method), b0) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, ArgTs...), B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, ArgTs...) const, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, B0 b0, B1 b1) : - Event(q, mbed::callback(obj, method), b0, b1) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, ArgTs...), B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2) : - Event(q, mbed::callback(obj, method), b0, b1, b2) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } - - /** Create an event - * @see Event::Event - */ - template - Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : - Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } -}; - -/** @}*/ - - -// Convenience functions declared here to avoid cyclic -// dependency between Event and EventQueue -template -Event EventQueue::event(R(*func)(ArgTs...)) -{ - return Event(this, func); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(ArgTs...)) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(ArgTs...) const) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(ArgTs...) volatile) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile) -{ - return Event(this, mbed::callback(obj, method)); -} - -template -Event EventQueue::event(mbed::Callback cb) -{ - return Event(this, cb); -} - -template -Event EventQueue::event(R(*func)(B0, ArgTs...), C0 c0) -{ - return Event(this, func, c0); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0) -{ - return Event(this, mbed::callback(obj, method), c0); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0) -{ - return Event(this, cb, c0); -} - -template -Event EventQueue::event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1) -{ - return Event(this, func, c0, c1); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1) -{ - return Event(this, mbed::callback(obj, method), c0, c1); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1) -{ - return Event(this, cb, c0, c1); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) -{ - return Event(this, func, c0, c1, c2); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2) -{ - return Event(this, cb, c0, c1, c2); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, func, c0, c1, c2, c3); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3) -{ - return Event(this, cb, c0, c1, c2, c3); -} - -template -Event EventQueue::event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, func, c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); -} - -template -Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) -{ - return Event(this, cb, c0, c1, c2, c3, c4); -} - -/** @}*/ -} -#endif diff --git a/events/EventQueue.h b/events/EventQueue.h deleted file mode 100644 index 1bcc018..0000000 --- a/events/EventQueue.h +++ /dev/null @@ -1,1512 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef EVENT_QUEUE_H -#define EVENT_QUEUE_H - -#include "events/equeue.h" -#include "platform/Callback.h" -#include "platform/NonCopyable.h" -#include -#include -#include -#include - -namespace events { -/** - * \addtogroup events-public-api - * @{ - */ - -/** EVENTS_EVENT_SIZE - * Minimum size of an event - * This size fits a Callback at minimum - */ -#define EVENTS_EVENT_SIZE \ - (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) - -/** EVENTS_QUEUE_SIZE - * Default size of buffer for events - */ -#define EVENTS_QUEUE_SIZE (32*EVENTS_EVENT_SIZE) - -// Predeclared classes -template -class Event; -template -class UserAllocatedEvent; - -/** - * \defgroup events_EventQueue EventQueue class - * @{ - */ - -/** EventQueue - * - * Flexible event queue for dispatching events - */ -class EventQueue : private mbed::NonCopyable { -public: - using duration = std::chrono::duration; - - /** Create an EventQueue - * - * Create an event queue. The event queue either allocates a buffer of - * the specified size with malloc or uses the user provided buffer or - * uses 1B dummy buffer if 0 size passed. - * - * 0 size queue is a special purpose queue to dispatch static events - * only (see UserAllocatedEvent). Such a queue gives the guarantee - * that no dynamic memory allocation will take place while queue - * creation and events posting & dispatching. - * - * @param size Size of buffer to use for events in bytes - * (default to EVENTS_QUEUE_SIZE) - * If 0 provided then 1B dummy buffer is used - * @param buffer Pointer to buffer to use for events - * (default to NULL) - */ - EventQueue(unsigned size = EVENTS_QUEUE_SIZE, unsigned char *buffer = NULL); - - /** Destroy an EventQueue - */ - ~EventQueue(); - - /** Dispatch events - * - * Executes events until the specified milliseconds have passed. - * If ms is negative, the dispatch function will dispatch events - * indefinitely or until break_dispatch is called on this queue. - * - * When called with a finite timeout, the dispatch function is guaranteed - * to terminate. When called with a timeout of 0, the dispatch function - * does not wait and is IRQ safe. - * - * @param ms Time to wait for events in milliseconds, a negative - * value will dispatch events indefinitely - * (default to -1) - */ - void dispatch(int ms = -1); - - /** Dispatch events without a timeout - * - * This is equivalent to EventQueue::dispatch with no arguments, but - * avoids overload ambiguities when passed as a callback. - * - * @see EventQueue::dispatch - */ - void dispatch_forever() - { - dispatch(); - } - - /** Break out of a running event loop - * - * Forces the specified event queue's dispatch loop to terminate. Pending - * events may finish executing, but no new events will be executed. - */ - void break_dispatch(); - - /** Millisecond counter - * - * Returns the underlying tick of the event queue represented as the - * number of milliseconds that have passed since an arbitrary point in - * time. Intentionally overflows to 0 after 2^32-1. - * - * @return The underlying tick of the event queue in milliseconds - */ - unsigned tick(); - - /** Cancel an in-flight event - * - * Attempts to cancel an event referenced by the unique id returned from - * one of the call functions. It is safe to call cancel after an event - * has already been dispatched. - * - * id must be valid i.e. event must have not finished executing. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active in another thread, - * the cancel function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. A call made from - * the same thread as the dispatch loop will always succeed with a valid id. - * - * @param id Unique id of the event - * @return true if event was successfully cancelled - * false if event was not cancelled (invalid id or executing already begun) - */ - bool cancel(int id); - - /** Cancel an in-flight user allocated event - * - * Attempts to cancel an UserAllocatedEvent referenced by its address - * It is safe to call cancel after an event has already been dispatched. - * - * Event must be valid i.e. event must have not finished executing - * and must have been bound to this queue. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active in another thread, - * the cancel function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. A call made from - * the same thread as the dispatch loop will always succeed with a valid id. - * - * @param event Address of the event - * @return true if event was successfully cancelled - * false if event was not cancelled (invalid queue or executing already begun) - */ - template - bool cancel(UserAllocatedEvent *event) - { - if (event->_equeue != &_equeue) { - return false; - } - return equeue_cancel_user_allocated(&_equeue, event); - } - - /** Query how much time is left for delayed event - * - * If the event is delayed, this function can be used to query how much time - * is left until the event is due to be dispatched. - * - * id must be valid i.e. event must have not finished executing. - * - * This function is IRQ safe. - * - * @param id Unique id of the event - * - * @return Remaining time in milliseconds or - * 0 if event is already due to be dispatched or - * is currently executing. - * Undefined if id is invalid. - * - */ - int time_left(int id); - - /** Query how much time is left for delayed UserAllocatedEvent - * - * If the event is delayed, this function can be used to query how much time - * is left until the event is due to be dispatched. - * - * Event must be valid i.e. event must have not finished executing - * and must have been bound to this queue. - * - * This function is IRQ safe. - * - * @param event Address of the event - * - * @return Remaining time in milliseconds or - * 0 if event is already due to be dispatched or - * is currently executing. - * Undefined if id is invalid. - * - */ - template - int time_left(UserAllocatedEvent *event) - { - if (event && event->_equeue != &_equeue) { - return -1; - } - return equeue_timeleft_user_allocated(&_equeue, &event->_e); - } - - /** Background an event queue onto a single-shot timer-interrupt - * - * When updated, the event queue will call the provided update function - * with a timeout indicating when the queue should be dispatched. A - * negative timeout will be passed to the update function when the - * timer-interrupt is no longer needed. - * - * Passing a null function disables the existing update function. - * - * The background function allows an event queue to take advantage of - * hardware timers or other event loops, allowing an event queue to be - * ran in the background without consuming the foreground thread. - * - * @param update Function called to indicate when the queue should be - * dispatched - */ - void background(mbed::Callback update); - - /** Chain an event queue onto another event queue - * - * After chaining a queue to a target, calling dispatch on the target - * queue will also dispatch events from this queue. The queues use - * their own buffers and events must be handled independently. - * - * A null queue as the target will unchain the existing queue. - * - * The chain function allows multiple event queues to be composed, - * sharing the context of a dispatch loop while still being managed - * independently - * - * @param target Queue that will dispatch this queue's events as a - * part of its dispatch loop - * - * @return Zero on success and negative error code value if chaining fails - * - */ - int chain(EventQueue *target); - - - -#if defined(DOXYGEN_ONLY) - /** Calls an event on the queue - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - * Returned id will remain valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks - * queue.call(printf, "called immediately\n"); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call(F f, Args ...args); - - /** Calls an event on the queue - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * Returned ID remains valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(1); - * - * // events are simple callbacks, call object method - * // with provided parameter - * queue.call(&handler_cb, &EventHandler::handler, 2); - * - * // the dispath method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call(T *obj, R (T::*method)(Args ...args), Args ...args); - // *INDENT-ON* - - /** Calls an event on the queue after a specified delay - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks - * queue.call_in(2s, printf, "called in 2 seconds\n"); - * - * // the dispatch methods executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call_in(duration ms, F f, ArgTs ...args); - - /** Calls an event on the queue after a specified delay - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(3); - * - * // events are simple callbacks, call object method in 2 seconds - * // with provided parameter - * queue.call_in(2s, &handler_cb, &EventHandler::handler, 4); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call_in(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); - // *INDENT-ON* - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param ms Period of the event in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // events are simple callbacks, call every 2 seconds - * queue.call_every(2s, printf, "Calling every 2 seconds\n"); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - template - int call_every(duration ms, F f, ArgTs ...args); - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param ms Period of the event in milliseconds - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * - * @code - * #include "mbed.h" - * using namespace std::chrono_literals; - * - * class EventHandler { - * int _id; - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() { - * // creates a queue with the default size - * EventQueue queue; - * - * // Create EventHandler object with state - * EventHandler handler_cb(5); - * - * // events are simple callbacks, call object method every 2 seconds - * // with provided parameter - * queue.call_every(2s, &handler_cb, &EventHandler::handler, 6); - * - * // the dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - int call_every(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param func Function to execute when the event is dispatched - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * void handler(int c) { - * printf("Param: %d\r\n", c); - * } - * - * int main() - * { - * EventQueue queue; - * - * // Create event with parameter - * Event e = queue.event(handler, 1); - * e(); - * - * // Create event and post parameter later - * Event e2 = queue.event(handler); - * - * // Post the event with paramter 8 - * e.post(8); - * - * // The dispatch method executes events - * queue.dispatch(); - * - * e2.post(2); - * - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - Event event(R (*func)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param obj Object to call with the member function - * @param method Member function to execute in the context of the dispatch loop - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * class EventHandler { - * int _id; - * - * public: - * EventHandler(int id) : _id(id) { } - * - * void handler(int c) { - * printf("ID: %d Param: %d\r\n", _id, c); - * } - * }; - * - * int main() - * { - * EventQueue queue; - * - * EventHandler handler_cb(10); - * - * // Create event on the eventqueue with a method callback - * Event e = queue.event(&handler_cb, &EventHandler::handler); - * - * // Post the event with paramter 8 - * e.post(11); - * - * // The dispatch method executes events - * queue.dispatch(); - * } - * @endcode - */ - // AStyle ignore, not handling correctly below - // *INDENT-OFF* - template - Event event(T *obj, R (T::*method)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); - // *INDENT-ON* - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param cb Callback object - * @param context_args Arguments to pass to the callback - * @return Event that dispatches on the specific queue - * - * @code - * #include "mbed.h" - * - * void handler(int c) { - * printf("Param: %d\r\n", c); - * } - * - * int main() - * { - * EventQueue queue; - * // Create callback object acting as a function - * // pointer to handler - * Callback cb(handler); - * - * // Pass the callback object to the eventqueue - * Event e = queue.event(cb); - * - * // Post the event with parameter 8 - * e.post(9); - * - * // The dispatch method executes events - * q.dispatch(); - * } - * @endcode - */ - template - Event event(mbed::Callback cb, ContextArgTs ...context_args); - - /** Creates an user allocated event bound to the event queue - * - * Constructs an user allocated event bound to the specified event queue. - * The specified callback acts as the target for the event and is executed - * in the context of the event queue's dispatch loop once posted. - * - * @code - * #include "mbed.h" - * - * void handler(int data) { ... } - * - * class Device { - * public: - * void handler(int data) { ... } - * }; - * - * Device dev; - * - * // queue with not internal storage for dynamic events - * // accepts only user allocated events - * static EventQueue queue(0); - * // Create events - * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); - * static auto e2 = queue.make_user_allocated_event(handler, 3); - * - * int main() - * { - * e1.call_on(&queue); - * e2.call(); - * - * queue.dispatch(1); - * } - * @endcode - * - * @param f Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); - - -#else - - /** Calls an event on the queue - * - * The specified callback is executed in the context of the event - * queue's dispatch loop. - * - * The call function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @return A unique ID that represents the posted event and can - * be passed to cancel, or an ID of 0 if there is not - * enough memory to allocate the event. - * Returned ID remains valid until event has finished - * executing. - * - * @code - * #include "mbed.h" - * - * int main() - * { - * EventQueue queue; - * - * Callback cb(handler); - * - * // Create event on the eventqueue with a separate callback object - * Event e = queue.event(cb); - * e.post(1); - * queue.dispatch(); - * } - * @endcode - */ - template - int call(F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - - /** Calls an event on the queue - * @see EventQueue::call - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - int call(F f, ArgTs... args) - { - return call(context(std::move(f), args...)); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue - * @see EventQueue::call - */ - template - int call(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call(mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - int call_in(duration ms, F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_delay(e, ms.count()); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - int call_in(duration ms, F f, ArgTs... args) - { - return call_in(ms, context(std::move(f), args...)); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - int call_in(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_in(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue after a specified delay - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_in function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, F f) - { - return call_in(duration(ms), std::move(f)); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - * @param ms Time to delay in milliseconds - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, F f, ArgTs... args) - { - return call_in(duration(ms), std::move(f), args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue after a specified delay - * @see EventQueue::call_in - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_in(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_in(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param ms Period of the event in milliseconds - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - int call_every(duration ms, F f) - { - void *p = equeue_alloc(&_equeue, sizeof(F)); - if (!p) { - return 0; - } - - F *e = new (p) F(std::move(f)); - equeue_event_delay(e, ms.count()); - equeue_event_period(e, ms.count()); - equeue_event_dtor(e, &EventQueue::function_dtor); - return equeue_post(&_equeue, &EventQueue::function_call, e); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @param ms Period of the event in milliseconds - */ - template - int call_every(duration ms, F f, ArgTs... args) - { - return call_every(ms, context(std::move(f), args...)); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - int call_every(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_every(ms, mbed::callback(obj, method), args...); - } - - /** Calls an event on the queue periodically - * - * @note The first call_every event occurs after the specified delay. - * To create a periodic event that fires immediately, @see Event. - * - * The specified callback will be executed in the context of the event - * queue's dispatch loop. - * - * The call_every function is IRQ safe and can act as a mechanism for - * moving events out of IRQ contexts. - * - * @param f Function to execute in the context of the dispatch loop - * @param ms Period of the event in milliseconds - * @return A unique id that represents the posted event and can - * be passed to cancel, or an id of 0 if there is not - * enough memory to allocate the event. - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, F f) - { - return call_every(duration(ms), std::move(f)); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - * @param f Function to execute in the context of the dispatch loop - * @param args Arguments to pass to the callback - * @param ms Period of the event in milliseconds - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, F f, ArgTs... args) - { - return call_every(duration(ms), std::move(f), args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Calls an event on the queue periodically - * @see EventQueue::call_every - */ - template - MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") - int call_every(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) - { - return call_every(duration(ms), obj, method, args...); - } - - /** Creates an event bound to the event queue - * - * Constructs an event bound to the specified event queue. The specified - * callback acts as the target for the event and is executed in the - * context of the event queue's dispatch loop once posted. - * - * @param func Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - Event event(R(*func)(ArgTs...)); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(ArgTs...)); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(ArgTs...) const); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(ArgTs...) volatile); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, ArgTs...), C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an event bound to the event queue - * @see EventQueue::event - */ - template - Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); - - /** Creates an user allocated event bound to the event queue - * - * Constructs an user allocated event bound to the specified event queue. - * The specified callback acts as the target for the event and is executed - * in the context of the event queue's dispatch loop once posted. - * - * @param f Function to execute when the event is dispatched - * @return Event that will dispatch on the specific queue - */ - template - UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args); - - /** Creates an user allocated event bound to the event queue - * @see EventQueue::make_user_allocated_event - */ - template - UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args); -#endif - -protected: -#if !defined(DOXYGEN_ONLY) - template - friend class Event; - template - friend class UserAllocatedEvent; - struct equeue _equeue; - mbed::Callback _update; - - // Function attributes - template - static void function_call(void *p) - { - (*(F *)p)(); - } - - template - static void function_dtor(void *p) - { - ((F *)p)->~F(); - } - - // Context structures - template - struct context; - - template - struct context { - F f; - - constexpr context(F f) - : f(f) {} - - template - void operator()(ArgTs... args) - { - f(args...); - } - }; - - template - struct context { - F f; - C0 c0; - - constexpr context(F f, C0 c0) - : f(f), c0(c0) {} - - template - void operator()(ArgTs... args) - { - f(c0, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - - constexpr context(F f, C0 c0, C1 c1) - : f(f), c0(c0), c1(c1) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - - constexpr context(F f, C0 c0, C1 c1, C2 c2) - : f(f), c0(c0), c1(c1), c2(c2) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - C3 c3; - - constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3) - : f(f), c0(c0), c1(c1), c2(c2), c3(c3) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, c3, args...); - } - }; - - template - struct context { - F f; - C0 c0; - C1 c1; - C2 c2; - C3 c3; - C4 c4; - - constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) - : f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {} - - template - void operator()(ArgTs... args) - { - f(c0, c1, c2, c3, c4, args...); - } - }; -#endif //!defined(DOXYGEN_ONLY) -}; - -/** @}*/ -/** @}*/ - -} - -#endif diff --git a/events/UserAllocatedEvent.h b/events/UserAllocatedEvent.h deleted file mode 100644 index 94dd360..0000000 --- a/events/UserAllocatedEvent.h +++ /dev/null @@ -1,468 +0,0 @@ -/* - * Copyright (c) 2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef USER_ALLOCATED_EVENT_H -#define USER_ALLOCATED_EVENT_H - -#include "events/EventQueue.h" -#include "platform/mbed_assert.h" -#include "platform/mbed_atomic.h" - -namespace events { -/** - * \addtogroup events-public-api - * @{ - */ -template -class UserAllocatedEvent; - -/** - * \defgroup events_Event UserAllocatedEvent class - * @{ - */ - -/** UserAllocatedEvent - * - * Representation of an static event for fine-grain dispatch control. - * - * UserAllocatedEvent provides mechanism for event posting and dispatching - * without utilization of queue internal memory. It embeds all underlying - * event data and doesn't require any memory allocation while posting and dispatching. - * All of these makes it cannot fail due to memory exhaustion while posting - * - * Usage: - * @code - * #include "mbed.h" - * - * void handler(int data) { ... } - * - * class Device { - * public: - * void handler(int data) { ... } - * }; - * - * Device dev; - * - * // queue with not internal storage for dynamic events - * // accepts only user allocated events - * static EventQueue queue(0); - * // Create events - * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); - * static auto e2 = queue.make_user_allocated_event(handler, 3); - * - * int main() - * { - * e1.call_on(&queue); - * e2.call(); - * - * queue.dispatch(1); - * } - * @endcode - */ -template -class UserAllocatedEvent { -public: - typedef EventQueue::context C; - - /** Create an event - * - * Constructs an event. The specified callback acts as the target - * for the event and is executed in the context of the - * event queue's dispatch loop once posted. - * - * @param f Function to execute when the event is dispatched - * @param args Arguments to bind to the callback - */ - constexpr UserAllocatedEvent(F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(), _post_ref() - { - } - - /** Create an event - * - * Constructs an event. The specified callback acts as the target - * for the event and is executed in the context of the - * event queue's dispatch loop once posted. - * - * @param queue Event queue to dispatch on - * @param f Function to execute when the event is dispatched - * @param args Arguments to bind to the callback - */ - constexpr UserAllocatedEvent(EventQueue *queue, F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(&queue->_equeue), _post_ref() - { - } - - /** Destructor for events - */ -#if !defined(NDEBUG) - // Remove the user provided destructor in release to allow constexpr optimization - // constexpr requires destructor to be trivial and only default one is treated as trivial - ~UserAllocatedEvent() - { - MBED_ASSERT(!_post_ref); - } -#endif - - /** Posts an event onto the underlying event queue, returning void - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - */ - void call() - { - MBED_ASSERT(!_post_ref); - MBED_ASSERT(_equeue); - MBED_UNUSED bool status = post(); - MBED_ASSERT(status); - } - - /** Posts an event onto the event queue passed as argument, returning void - * - * The event is posted to the event queue passed as argument - * and is executed in the context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. - * - */ - void call_on(EventQueue *queue) - { - MBED_ASSERT(!_post_ref); - MBED_UNUSED bool status = post_on(queue); - MBED_ASSERT(status); - } - - /** Posts an event onto the underlying event queue - * - * The event is posted to the event queue passed as argument - * and is executed in the context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * @return False if the event was already posted - * true otherwise - * - */ - bool try_call() - { - return post(); - } - - /** Posts an event onto the event queue passed as argument, - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. - * @return False if the event was already posted - * true otherwise - * - */ - bool try_call_on(EventQueue *queue) - { - return post_on(queue); - } - - /** Posts an event onto the underlying event queue, returning void - * - * The event is posted to the underlying queue and is executed in the - * context of the event queue's dispatch loop. - * - * This call cannot fail due queue memory exhaustion - * because it doesn't allocate any memory - * - * The post function is IRQ safe and can act as a mechanism for moving - * events out of IRQ contexts. - * - */ - void operator()() - { - return call(); - } - - /** Configure the delay of an event - * - * @param delay Millisecond delay before dispatching the event - */ - void delay(int delay) - { - MBED_ASSERT(!_post_ref); - _delay = delay; - } - - /** Configure the period of an event - * - * @param period Millisecond period for repeatedly dispatching an event - */ - void period(int period) - { - MBED_ASSERT(!_post_ref); - _period = period; - } - - /** Cancels posted event - * - * Attempts to cancel posted event. It is safe to call - * cancel after an event has already been dispatched. - * - * The cancel function is IRQ safe. - * - * If called while the event queue's dispatch loop is active, the cancel - * function does not guarantee that the event will not execute after it - * returns, as the event may have already begun executing. - * - * @return true if event was successfully cancelled - */ - bool cancel() - { - return _post_ref > 0 ? equeue_cancel_user_allocated(_equeue, &_e) : false; - } - - -private: - friend class EventQueue; - struct equeue_event _e; - C _c; - int _delay; - int _period; - struct equeue *_equeue; - uint8_t _post_ref; - - bool post() - { - if (_post_ref) { - return false; - } - core_util_atomic_incr_u8(&_post_ref, 1); - equeue_event_delay(&_e + 1, _delay); - equeue_event_period(&_e + 1, _period); - equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); - return true; - } - - bool post_on(EventQueue *queue) - { - MBED_ASSERT(queue); - if (_post_ref) { - return false; - } - _equeue = &(queue->_equeue); - core_util_atomic_incr_u8(&_post_ref, 1); - equeue_event_delay(&_e + 1, _delay); - equeue_event_period(&_e + 1, _period); - equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); - return true; - } - - static void event_dtor(void *p) - { - UserAllocatedEvent *instance = (UserAllocatedEvent *)(((equeue_event *)p) - 1); - core_util_atomic_decr_u8(&instance->_post_ref, 1); - MBED_ASSERT(!instance->_post_ref); - } - - constexpr static equeue_event get_default_equeue_event() - { - return equeue_event{ 0, 0, 0, NULL, NULL, NULL, 0, -1, &UserAllocatedEvent::event_dtor, NULL }; - } - -public: - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, T *obj, R(T::*method)(ArgTs...), ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : - UserAllocatedEvent(mbed::callback(obj, method), args...) { } - - /** Create an event - * @see UserAllocatedEvent::UserAllocatedEvent - */ - template - constexpr UserAllocatedEvent(EventQueue *q, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : - UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } -}; - -// Convenience functions declared here to avoid cyclic -// dependency between Event and EventQueue -template -UserAllocatedEvent EventQueue::make_user_allocated_event(F f, ArgTs... args) -{ - return UserAllocatedEvent(this, f, args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - -template -UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); -} - - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args) -{ - return UserAllocatedEvent(f, args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments - * - * UserAllocatedEvent doesn't utilize EventQueue internal memory, - * therefore it can be posted on the queue without being afraid - * of post fail due to queue memory exhaustion - * - * @return UserAllocatedEvent object instance - * - */ -template -constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) -{ - return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); -} - -/** @}*/ - -/** @}*/ -} -#endif diff --git a/events/equeue.h b/events/equeue.h deleted file mode 100644 index 5d452fc..0000000 --- a/events/equeue.h +++ /dev/null @@ -1,275 +0,0 @@ - -/* - * Flexible event queue for dispatching events - * - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EQUEUE_H -#define EQUEUE_H - -#ifdef __cplusplus -extern "C" { -#endif - -// Platform specific files -#include "events/internal/equeue_platform.h" - -#include -#include - -/** - * \addtogroup events-public-api - * @{ - */ - -// The minimum size of an event -// This size is guaranteed to fit events created by event_call -#define EQUEUE_EVENT_SIZE (sizeof(struct equeue_event) + 2*sizeof(void*)) - -// Internal event structure -struct equeue_event { - unsigned size; - uint8_t id; - uint8_t generation; - - struct equeue_event *next; - struct equeue_event *sibling; - struct equeue_event **ref; - - unsigned target; - int period; - void (*dtor)(void *); - - void (*cb)(void *); - // data follows -}; - -// Event queue structure -typedef struct equeue { - struct equeue_event *queue; - unsigned tick; - bool break_requested; - uint8_t generation; - - unsigned char *buffer; - unsigned npw2; - void *allocated; - - struct equeue_event *chunks; - struct equeue_slab { - size_t size; - unsigned char *data; - } slab; - - struct equeue_background { - bool active; - void (*update)(void *timer, int ms); - void *timer; - } background; - - equeue_sema_t eventsema; - equeue_mutex_t queuelock; - equeue_mutex_t memlock; -} equeue_t; - - -// Queue lifetime operations -// -// Creates and destroys an event queue. The event queue either allocates a -// buffer of the specified size with malloc or uses a user provided buffer -// if constructed with equeue_create_inplace. -// -// If the event queue creation fails, equeue_create returns a negative, -// platform-specific error code. -// -// If queues are chained, it is needed to unchain them first, before calling destroy, -// or call the destroy function on queues in order that chained queues are destroyed first. -int equeue_create(equeue_t *queue, size_t size); -int equeue_create_inplace(equeue_t *queue, size_t size, void *buffer); -void equeue_destroy(equeue_t *queue); - -// Dispatch events -// -// Executes events until the specified milliseconds have passed. If ms is -// negative, equeue_dispatch will dispatch events indefinitely or until -// equeue_break is called on this queue. -// -// When called with a finite timeout, the equeue_dispatch function is -// guaranteed to terminate. When called with a timeout of 0, the -// equeue_dispatch does not wait and is irq safe. -void equeue_dispatch(equeue_t *queue, int ms); - -// Break out of a running event loop -// -// Forces the specified event queue's dispatch loop to terminate. Pending -// events may finish executing, but no new events will be executed. -void equeue_break(equeue_t *queue); - -// Simple event calls -// -// The specified callback will be executed in the context of the event queue's -// dispatch loop. When the callback is executed depends on the call function. -// -// equeue_call - Immediately post an event to the queue -// equeue_call_in - Post an event after a specified time in milliseconds -// equeue_call_every - Post an event periodically every milliseconds -// -// All equeue_call functions are irq safe and can act as a mechanism for -// moving events out of irq contexts. -// -// The return value is a unique id that represents the posted event and can -// be passed to equeue_cancel. If there is not enough memory to allocate the -// event, equeue_call returns an id of 0. -int equeue_call(equeue_t *queue, void (*cb)(void *), void *data); -int equeue_call_in(equeue_t *queue, int ms, void (*cb)(void *), void *data); -int equeue_call_every(equeue_t *queue, int ms, void (*cb)(void *), void *data); - -// Allocate memory for events -// -// The equeue_alloc function allocates an event that can be manually dispatched -// with equeue_post. The equeue_dealloc function may be used to free an event -// that has not been posted. Once posted, an event's memory is managed by the -// event queue and should not be deallocated. -// -// Both equeue_alloc and equeue_dealloc are irq safe. -// -// The equeue allocator is designed to minimize jitter in interrupt contexts as -// well as avoid memory fragmentation on small devices. The allocator achieves -// both constant-runtime and zero-fragmentation for fixed-size events, however -// grows linearly as the quantity of different sized allocations increases. -// -// The equeue_alloc function returns a pointer to the event's allocated memory -// and acts as a handle to the underlying event. If there is not enough memory -// to allocate the event, equeue_alloc returns null. -void *equeue_alloc(equeue_t *queue, size_t size); -void equeue_dealloc(equeue_t *queue, void *event); - -// Configure an allocated event -// -// equeue_event_delay - Millisecond delay before dispatching an event -// equeue_event_period - Millisecond period for repeating dispatching an event -// equeue_event_dtor - Destructor to run when the event is deallocated -void equeue_event_delay(void *event, int ms); -void equeue_event_period(void *event, int ms); -void equeue_event_dtor(void *event, void (*dtor)(void *)); - -// Post an event onto the event queue -// -// The equeue_post function takes a callback and a pointer to an event -// allocated by equeue_alloc. The specified callback will be executed in the -// context of the event queue's dispatch loop with the allocated event -// as its argument. -// -// The equeue_post function is irq safe and can act as a mechanism for -// moving events out of irq contexts. -// -// The return value is a unique id that represents the posted event and can -// be passed to equeue_cancel. -int equeue_post(equeue_t *queue, void (*cb)(void *), void *event); - -// Post an user allocated event onto the event queue -// -// The equeue_post_user_allocated function takes a callback and a pointer -// to an event allocated by user. The specified callback will be executed -// in the context of the event queue's dispatch loop with the allocated -// event as its argument. -// -// The equeue_post_user_allocated function is irq safe and can act as -// a mechanism for moving events out of irq contexts. -void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event); - -// Cancel an in-flight event -// -// Attempts to cancel an event referenced by the unique id returned from -// equeue_call or equeue_post. It is safe to call equeue_cancel after an event -// has already been dispatched. -// -// The equeue_cancel function is irq safe. -// -// If called while the event queue's dispatch loop is active in another thread, -// equeue_cancel does not guarantee that the event will not execute after it returns as -// the event may have already begun executing. -// Returning true guarantees that cancel succeeded and event will not execute. -// Returning false if invalid id or already started executing. -bool equeue_cancel(equeue_t *queue, int id); - -// Cancel an in-flight user allocated event -// -// Attempts to cancel an event referenced by its address. -// It is safe to call equeue_cancel_user_allocated after an event -// has already been dispatched. -// -// The equeue_cancel_user_allocated function is irq safe. -// -// If called while the event queue's dispatch loop is active, -// equeue_cancel_user_allocated does not guarantee that the event -// will not not execute after it returns as the event may have -// already begun executing. -bool equeue_cancel_user_allocated(equeue_t *queue, void *event); - -// Query how much time is left for delayed event -// -// If event is delayed, this function can be used to query how much time -// is left until the event is due to be dispatched. -// -// This function is irq safe. -// -int equeue_timeleft(equeue_t *q, int id); - -// Query how much time is left for delayed user allocated event -// -// If event is delayed, this function can be used to query how much time -// is left until the event is due to be dispatched. -// -// This function is irq safe. -// -int equeue_timeleft_user_allocated(equeue_t *q, void *event); - -// Background an event queue onto a single-shot timer -// -// The provided update function will be called to indicate when the queue -// should be dispatched. A negative timeout will be passed to the update -// function when the timer is no longer needed. -// -// Passing a null update function disables the existing timer. -// -// The equeue_background function allows an event queue to take advantage -// of hardware timers or even other event loops, allowing an event queue to -// be effectively backgrounded. -void equeue_background(equeue_t *queue, - void (*update)(void *timer, int ms), void *timer); - -// Chain an event queue onto another event queue -// -// After chaining a queue to a target, calling equeue_dispatch on the -// target queue will also dispatch events from this queue. The queues -// use their own buffers and events must be managed independently. -// -// Passing a null queue as the target will unchain the existing queue. -// -// The equeue_chain function allows multiple equeues to be composed, sharing -// the context of a dispatch loop while still being managed independently. -// -// If the event queue chaining fails, equeue_chain returns a negative, -// platform-specific error code. -int equeue_chain(equeue_t *queue, equeue_t *target); - -/** @}*/ - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/events/include/events/Event.h b/events/include/events/Event.h new file mode 100644 index 0000000..8e13c48 --- /dev/null +++ b/events/include/events/Event.h @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EVENT_H +#define EVENT_H + +#include +#include "events/EventQueue.h" +#include "platform/mbed_assert.h" + +namespace events { +/** \defgroup events-public-api Events + * \ingroup mbed-os-public + * @{ + */ + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event; + +/** + * \defgroup events_Event Event class + * @{ + */ + +/** Event + * + * Representation of an event for fine-grain dispatch control + */ +template +class Event { +public: + using duration = std::chrono::duration; + + /** Create an event + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param q Event queue to dispatch on + * @param f Function to execute when the event is dispatched + */ + template + Event(EventQueue *q, F f) + { + _event = static_cast( + equeue_alloc(&q->_equeue, sizeof(struct event) + sizeof(F))); + + if (_event) { + _event->equeue = &q->_equeue; + _event->id = 0; + _event->delay = duration(0); + _event->period = duration(-1); + + _event->post = &Event::event_post; + _event->dtor = &Event::event_dtor; + + new (_event + 1) F(std::move(f)); + + _event->ref = 1; + } + } + + /** Copy constructor for events + */ + Event(const Event &e) + { + _event = 0; + if (e._event) { + _event = e._event; + _event->ref += 1; + } + } + + /** Assignment operator for events + */ + Event &operator=(const Event &that) + { + if (this != &that) { + this->~Event(); + new (this) Event(that); + } + + return *this; + } + + /** Destructor for events + */ + ~Event() + { + if (_event) { + _event->ref -= 1; + if (_event->ref == 0) { + _event->dtor(_event); + equeue_dealloc(_event->equeue, _event); + } + } + } + + /** Configure the delay of an event + * + * @param d Millisecond delay before dispatching the event + */ + void delay(duration d) + { + if (_event) { + _event->delay = d; + } + } + + /** Configure the delay of an event + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + * + * @param d Millisecond delay before dispatching the event + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + void delay(int d) + { + delay(duration(d)); + } + + /** Configure the period of an event + * + * @param p Millisecond period for repeatedly dispatching an event + */ + void period(duration p) + { + if (_event) { + _event->period = p; + } + } + + /** Configure the period of an event + * @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`. + * + * @param p Millisecond period for repeatedly dispatching an event + */ + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + void period(int p) + { + period(duration(p)); + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param args Arguments to pass to the event + * @return A unique id that represents the posted event and can + * be passed to EventQueue::cancel, or an id of 0 if + * there is not enough memory to allocate the event. + */ + int post(ArgTs... args) const + { + if (!_event) { + return 0; + } + + _event->id = _event->post(_event, args...); + return _event->id; + } + + /** Posts an event onto the underlying event queue, returning void + * + * @param args Arguments to pass to the event + */ + void call(ArgTs... args) const + { + MBED_UNUSED int id = post(args...); + MBED_ASSERT(id); + } + + /** Posts an event onto the underlying event queue, returning void + * + * @param args Arguments to pass to the event + */ + void operator()(ArgTs... args) const + { + return call(args...); + } + + /** Static thunk for passing as C-style function + * + * @param func Event to call passed as a void pointer + * @param args Arguments to pass to the event + */ + static void thunk(void *func, ArgTs... args) + { + return static_cast(func)->call(args...); + } + + /** Cancels the most recently posted event + * + * Attempts to cancel the most recently posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. + */ + void cancel() const + { + if (_event) { + equeue_cancel(_event->equeue, _event->id); + } + } + +private: + struct event { + unsigned ref; + equeue_t *equeue; + int id; + + duration delay; + duration period; + + int (*post)(struct event *, ArgTs... args); + void (*dtor)(struct event *); + + // F follows + } *_event; + + // Event attributes + template + static int event_post(struct event *e, ArgTs... args) + { + typedef EventQueue::context C; + void *p = equeue_alloc(e->equeue, sizeof(C)); + if (!p) { + return 0; + } + + new (p) C(*(F *)(e + 1), args...); + equeue_event_delay(p, e->delay.count()); + equeue_event_period(p, e->period.count()); + equeue_event_dtor(p, &EventQueue::function_dtor); + return equeue_post(e->equeue, &EventQueue::function_call, p); + } + + template + static void event_dtor(struct event *e) + { + ((F *)(e + 1))->~F(); + } + +public: + /** Create an event + * @param q Event queue to dispatch on + * @param f Function to execute when the event is dispatched + * @param context_args Arguments to bind to the callback, these arguments are + * allocated on an IRQ-safe allocator from the event queue's + * memory pool. Must be type-compatible with bound_args, the + * arguments to the underlying callback. + */ + template + Event(EventQueue *q, F f, ContextArgTs... context_args) : + Event(q, EventQueue::context(f, context_args...)) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, ArgTs...), B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, ArgTs...) const, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, B0 b0) : + Event(q, mbed::callback(obj, method), b0) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, ArgTs...), B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, ArgTs...) const, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, B0 b0, B1 b1) : + Event(q, mbed::callback(obj, method), b0, b1) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, ArgTs...), B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2) : + Event(q, mbed::callback(obj, method), b0, b1, b2) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } + + /** Create an event + * @see Event::Event + */ + template + Event(EventQueue *q, const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, B0 b0, B1 b1, B2 b2, B3 b3, B4 b4) : + Event(q, mbed::callback(obj, method), b0, b1, b2, b3, b4) { } +}; + +/** @}*/ + + +// Convenience functions declared here to avoid cyclic +// dependency between Event and EventQueue +template +Event EventQueue::event(R(*func)(ArgTs...)) +{ + return Event(this, func); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(ArgTs...)) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(ArgTs...) const) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(ArgTs...) volatile) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile) +{ + return Event(this, mbed::callback(obj, method)); +} + +template +Event EventQueue::event(mbed::Callback cb) +{ + return Event(this, cb); +} + +template +Event EventQueue::event(R(*func)(B0, ArgTs...), C0 c0) +{ + return Event(this, func, c0); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0) +{ + return Event(this, mbed::callback(obj, method), c0); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0) +{ + return Event(this, cb, c0); +} + +template +Event EventQueue::event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1) +{ + return Event(this, func, c0, c1); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1) +{ + return Event(this, mbed::callback(obj, method), c0, c1); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1) +{ + return Event(this, cb, c0, c1); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) +{ + return Event(this, func, c0, c1, c2); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2) +{ + return Event(this, cb, c0, c1, c2); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, func, c0, c1, c2, c3); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3) +{ + return Event(this, cb, c0, c1, c2, c3); +} + +template +Event EventQueue::event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, func, c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, mbed::callback(obj, method), c0, c1, c2, c3, c4); +} + +template +Event EventQueue::event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) +{ + return Event(this, cb, c0, c1, c2, c3, c4); +} + +/** @}*/ +} +#endif diff --git a/events/include/events/EventQueue.h b/events/include/events/EventQueue.h new file mode 100644 index 0000000..1bcc018 --- /dev/null +++ b/events/include/events/EventQueue.h @@ -0,0 +1,1512 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include "events/equeue.h" +#include "platform/Callback.h" +#include "platform/NonCopyable.h" +#include +#include +#include +#include + +namespace events { +/** + * \addtogroup events-public-api + * @{ + */ + +/** EVENTS_EVENT_SIZE + * Minimum size of an event + * This size fits a Callback at minimum + */ +#define EVENTS_EVENT_SIZE \ + (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) + +/** EVENTS_QUEUE_SIZE + * Default size of buffer for events + */ +#define EVENTS_QUEUE_SIZE (32*EVENTS_EVENT_SIZE) + +// Predeclared classes +template +class Event; +template +class UserAllocatedEvent; + +/** + * \defgroup events_EventQueue EventQueue class + * @{ + */ + +/** EventQueue + * + * Flexible event queue for dispatching events + */ +class EventQueue : private mbed::NonCopyable { +public: + using duration = std::chrono::duration; + + /** Create an EventQueue + * + * Create an event queue. The event queue either allocates a buffer of + * the specified size with malloc or uses the user provided buffer or + * uses 1B dummy buffer if 0 size passed. + * + * 0 size queue is a special purpose queue to dispatch static events + * only (see UserAllocatedEvent). Such a queue gives the guarantee + * that no dynamic memory allocation will take place while queue + * creation and events posting & dispatching. + * + * @param size Size of buffer to use for events in bytes + * (default to EVENTS_QUEUE_SIZE) + * If 0 provided then 1B dummy buffer is used + * @param buffer Pointer to buffer to use for events + * (default to NULL) + */ + EventQueue(unsigned size = EVENTS_QUEUE_SIZE, unsigned char *buffer = NULL); + + /** Destroy an EventQueue + */ + ~EventQueue(); + + /** Dispatch events + * + * Executes events until the specified milliseconds have passed. + * If ms is negative, the dispatch function will dispatch events + * indefinitely or until break_dispatch is called on this queue. + * + * When called with a finite timeout, the dispatch function is guaranteed + * to terminate. When called with a timeout of 0, the dispatch function + * does not wait and is IRQ safe. + * + * @param ms Time to wait for events in milliseconds, a negative + * value will dispatch events indefinitely + * (default to -1) + */ + void dispatch(int ms = -1); + + /** Dispatch events without a timeout + * + * This is equivalent to EventQueue::dispatch with no arguments, but + * avoids overload ambiguities when passed as a callback. + * + * @see EventQueue::dispatch + */ + void dispatch_forever() + { + dispatch(); + } + + /** Break out of a running event loop + * + * Forces the specified event queue's dispatch loop to terminate. Pending + * events may finish executing, but no new events will be executed. + */ + void break_dispatch(); + + /** Millisecond counter + * + * Returns the underlying tick of the event queue represented as the + * number of milliseconds that have passed since an arbitrary point in + * time. Intentionally overflows to 0 after 2^32-1. + * + * @return The underlying tick of the event queue in milliseconds + */ + unsigned tick(); + + /** Cancel an in-flight event + * + * Attempts to cancel an event referenced by the unique id returned from + * one of the call functions. It is safe to call cancel after an event + * has already been dispatched. + * + * id must be valid i.e. event must have not finished executing. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active in another thread, + * the cancel function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. A call made from + * the same thread as the dispatch loop will always succeed with a valid id. + * + * @param id Unique id of the event + * @return true if event was successfully cancelled + * false if event was not cancelled (invalid id or executing already begun) + */ + bool cancel(int id); + + /** Cancel an in-flight user allocated event + * + * Attempts to cancel an UserAllocatedEvent referenced by its address + * It is safe to call cancel after an event has already been dispatched. + * + * Event must be valid i.e. event must have not finished executing + * and must have been bound to this queue. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active in another thread, + * the cancel function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. A call made from + * the same thread as the dispatch loop will always succeed with a valid id. + * + * @param event Address of the event + * @return true if event was successfully cancelled + * false if event was not cancelled (invalid queue or executing already begun) + */ + template + bool cancel(UserAllocatedEvent *event) + { + if (event->_equeue != &_equeue) { + return false; + } + return equeue_cancel_user_allocated(&_equeue, event); + } + + /** Query how much time is left for delayed event + * + * If the event is delayed, this function can be used to query how much time + * is left until the event is due to be dispatched. + * + * id must be valid i.e. event must have not finished executing. + * + * This function is IRQ safe. + * + * @param id Unique id of the event + * + * @return Remaining time in milliseconds or + * 0 if event is already due to be dispatched or + * is currently executing. + * Undefined if id is invalid. + * + */ + int time_left(int id); + + /** Query how much time is left for delayed UserAllocatedEvent + * + * If the event is delayed, this function can be used to query how much time + * is left until the event is due to be dispatched. + * + * Event must be valid i.e. event must have not finished executing + * and must have been bound to this queue. + * + * This function is IRQ safe. + * + * @param event Address of the event + * + * @return Remaining time in milliseconds or + * 0 if event is already due to be dispatched or + * is currently executing. + * Undefined if id is invalid. + * + */ + template + int time_left(UserAllocatedEvent *event) + { + if (event && event->_equeue != &_equeue) { + return -1; + } + return equeue_timeleft_user_allocated(&_equeue, &event->_e); + } + + /** Background an event queue onto a single-shot timer-interrupt + * + * When updated, the event queue will call the provided update function + * with a timeout indicating when the queue should be dispatched. A + * negative timeout will be passed to the update function when the + * timer-interrupt is no longer needed. + * + * Passing a null function disables the existing update function. + * + * The background function allows an event queue to take advantage of + * hardware timers or other event loops, allowing an event queue to be + * ran in the background without consuming the foreground thread. + * + * @param update Function called to indicate when the queue should be + * dispatched + */ + void background(mbed::Callback update); + + /** Chain an event queue onto another event queue + * + * After chaining a queue to a target, calling dispatch on the target + * queue will also dispatch events from this queue. The queues use + * their own buffers and events must be handled independently. + * + * A null queue as the target will unchain the existing queue. + * + * The chain function allows multiple event queues to be composed, + * sharing the context of a dispatch loop while still being managed + * independently + * + * @param target Queue that will dispatch this queue's events as a + * part of its dispatch loop + * + * @return Zero on success and negative error code value if chaining fails + * + */ + int chain(EventQueue *target); + + + +#if defined(DOXYGEN_ONLY) + /** Calls an event on the queue + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + * Returned id will remain valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks + * queue.call(printf, "called immediately\n"); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call(F f, Args ...args); + + /** Calls an event on the queue + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * Returned ID remains valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(1); + * + * // events are simple callbacks, call object method + * // with provided parameter + * queue.call(&handler_cb, &EventHandler::handler, 2); + * + * // the dispath method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call(T *obj, R (T::*method)(Args ...args), Args ...args); + // *INDENT-ON* + + /** Calls an event on the queue after a specified delay + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks + * queue.call_in(2s, printf, "called in 2 seconds\n"); + * + * // the dispatch methods executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call_in(duration ms, F f, ArgTs ...args); + + /** Calls an event on the queue after a specified delay + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(3); + * + * // events are simple callbacks, call object method in 2 seconds + * // with provided parameter + * queue.call_in(2s, &handler_cb, &EventHandler::handler, 4); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call_in(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); + // *INDENT-ON* + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param ms Period of the event in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // events are simple callbacks, call every 2 seconds + * queue.call_every(2s, printf, "Calling every 2 seconds\n"); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + template + int call_every(duration ms, F f, ArgTs ...args); + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param ms Period of the event in milliseconds + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * + * @code + * #include "mbed.h" + * using namespace std::chrono_literals; + * + * class EventHandler { + * int _id; + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() { + * // creates a queue with the default size + * EventQueue queue; + * + * // Create EventHandler object with state + * EventHandler handler_cb(5); + * + * // events are simple callbacks, call object method every 2 seconds + * // with provided parameter + * queue.call_every(2s, &handler_cb, &EventHandler::handler, 6); + * + * // the dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + int call_every(duration ms, T *obj, R (T::*method)(ArgTs ...args), ArgTs ...args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param func Function to execute when the event is dispatched + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * void handler(int c) { + * printf("Param: %d\r\n", c); + * } + * + * int main() + * { + * EventQueue queue; + * + * // Create event with parameter + * Event e = queue.event(handler, 1); + * e(); + * + * // Create event and post parameter later + * Event e2 = queue.event(handler); + * + * // Post the event with paramter 8 + * e.post(8); + * + * // The dispatch method executes events + * queue.dispatch(); + * + * e2.post(2); + * + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + Event event(R (*func)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param obj Object to call with the member function + * @param method Member function to execute in the context of the dispatch loop + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * class EventHandler { + * int _id; + * + * public: + * EventHandler(int id) : _id(id) { } + * + * void handler(int c) { + * printf("ID: %d Param: %d\r\n", _id, c); + * } + * }; + * + * int main() + * { + * EventQueue queue; + * + * EventHandler handler_cb(10); + * + * // Create event on the eventqueue with a method callback + * Event e = queue.event(&handler_cb, &EventHandler::handler); + * + * // Post the event with paramter 8 + * e.post(11); + * + * // The dispatch method executes events + * queue.dispatch(); + * } + * @endcode + */ + // AStyle ignore, not handling correctly below + // *INDENT-OFF* + template + Event event(T *obj, R (T::*method)(BoundArgTs..., ArgTs...), ContextArgTs ...context_args); + // *INDENT-ON* + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param cb Callback object + * @param context_args Arguments to pass to the callback + * @return Event that dispatches on the specific queue + * + * @code + * #include "mbed.h" + * + * void handler(int c) { + * printf("Param: %d\r\n", c); + * } + * + * int main() + * { + * EventQueue queue; + * // Create callback object acting as a function + * // pointer to handler + * Callback cb(handler); + * + * // Pass the callback object to the eventqueue + * Event e = queue.event(cb); + * + * // Post the event with parameter 8 + * e.post(9); + * + * // The dispatch method executes events + * q.dispatch(); + * } + * @endcode + */ + template + Event event(mbed::Callback cb, ContextArgTs ...context_args); + + /** Creates an user allocated event bound to the event queue + * + * Constructs an user allocated event bound to the specified event queue. + * The specified callback acts as the target for the event and is executed + * in the context of the event queue's dispatch loop once posted. + * + * @code + * #include "mbed.h" + * + * void handler(int data) { ... } + * + * class Device { + * public: + * void handler(int data) { ... } + * }; + * + * Device dev; + * + * // queue with not internal storage for dynamic events + * // accepts only user allocated events + * static EventQueue queue(0); + * // Create events + * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); + * static auto e2 = queue.make_user_allocated_event(handler, 3); + * + * int main() + * { + * e1.call_on(&queue); + * e2.call(); + * + * queue.dispatch(1); + * } + * @endcode + * + * @param f Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); + + +#else + + /** Calls an event on the queue + * + * The specified callback is executed in the context of the event + * queue's dispatch loop. + * + * The call function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @return A unique ID that represents the posted event and can + * be passed to cancel, or an ID of 0 if there is not + * enough memory to allocate the event. + * Returned ID remains valid until event has finished + * executing. + * + * @code + * #include "mbed.h" + * + * int main() + * { + * EventQueue queue; + * + * Callback cb(handler); + * + * // Create event on the eventqueue with a separate callback object + * Event e = queue.event(cb); + * e.post(1); + * queue.dispatch(); + * } + * @endcode + */ + template + int call(F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + + /** Calls an event on the queue + * @see EventQueue::call + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + int call(F f, ArgTs... args) + { + return call(context(std::move(f), args...)); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue + * @see EventQueue::call + */ + template + int call(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call(mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + int call_in(duration ms, F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_delay(e, ms.count()); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + int call_in(duration ms, F f, ArgTs... args) + { + return call_in(ms, context(std::move(f), args...)); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + int call_in(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_in(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue after a specified delay + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_in function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, F f) + { + return call_in(duration(ms), std::move(f)); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + * @param ms Time to delay in milliseconds + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, F f, ArgTs... args) + { + return call_in(duration(ms), std::move(f), args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue after a specified delay + * @see EventQueue::call_in + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_in(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_in(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param ms Period of the event in milliseconds + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + int call_every(duration ms, F f) + { + void *p = equeue_alloc(&_equeue, sizeof(F)); + if (!p) { + return 0; + } + + F *e = new (p) F(std::move(f)); + equeue_event_delay(e, ms.count()); + equeue_event_period(e, ms.count()); + equeue_event_dtor(e, &EventQueue::function_dtor); + return equeue_post(&_equeue, &EventQueue::function_call, e); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @param ms Period of the event in milliseconds + */ + template + int call_every(duration ms, F f, ArgTs... args) + { + return call_every(ms, context(std::move(f), args...)); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + int call_every(duration ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_every(ms, mbed::callback(obj, method), args...); + } + + /** Calls an event on the queue periodically + * + * @note The first call_every event occurs after the specified delay. + * To create a periodic event that fires immediately, @see Event. + * + * The specified callback will be executed in the context of the event + * queue's dispatch loop. + * + * The call_every function is IRQ safe and can act as a mechanism for + * moving events out of IRQ contexts. + * + * @param f Function to execute in the context of the dispatch loop + * @param ms Period of the event in milliseconds + * @return A unique id that represents the posted event and can + * be passed to cancel, or an id of 0 if there is not + * enough memory to allocate the event. + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, F f) + { + return call_every(duration(ms), std::move(f)); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + * @param f Function to execute in the context of the dispatch loop + * @param args Arguments to pass to the callback + * @param ms Period of the event in milliseconds + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, F f, ArgTs... args) + { + return call_every(duration(ms), std::move(f), args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, T *obj, R(T::*method)(ArgTs...), ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Calls an event on the queue periodically + * @see EventQueue::call_every + */ + template + MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.") + int call_every(int ms, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) + { + return call_every(duration(ms), obj, method, args...); + } + + /** Creates an event bound to the event queue + * + * Constructs an event bound to the specified event queue. The specified + * callback acts as the target for the event and is executed in the + * context of the event queue's dispatch loop once posted. + * + * @param func Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + Event event(R(*func)(ArgTs...)); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(ArgTs...)); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(ArgTs...) const); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(ArgTs...) volatile); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(ArgTs...) const volatile); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, ArgTs...), C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, ArgTs...), C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, ArgTs...) const, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, ArgTs...) volatile, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, ArgTs...) const volatile, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, ArgTs...), C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, ArgTs...), C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, ArgTs...) const, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, ArgTs...) volatile, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, ArgTs...) const volatile, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, ArgTs...), C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) volatile, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(R(*func)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...), C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(const volatile T *obj, R(T::*method)(B0, B1, B2, B3, B4, ArgTs...) const volatile, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an event bound to the event queue + * @see EventQueue::event + */ + template + Event event(mbed::Callback cb, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4); + + /** Creates an user allocated event bound to the event queue + * + * Constructs an user allocated event bound to the specified event queue. + * The specified callback acts as the target for the event and is executed + * in the context of the event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @return Event that will dispatch on the specific queue + */ + template + UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args); + + /** Creates an user allocated event bound to the event queue + * @see EventQueue::make_user_allocated_event + */ + template + UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args); +#endif + +protected: +#if !defined(DOXYGEN_ONLY) + template + friend class Event; + template + friend class UserAllocatedEvent; + struct equeue _equeue; + mbed::Callback _update; + + // Function attributes + template + static void function_call(void *p) + { + (*(F *)p)(); + } + + template + static void function_dtor(void *p) + { + ((F *)p)->~F(); + } + + // Context structures + template + struct context; + + template + struct context { + F f; + + constexpr context(F f) + : f(f) {} + + template + void operator()(ArgTs... args) + { + f(args...); + } + }; + + template + struct context { + F f; + C0 c0; + + constexpr context(F f, C0 c0) + : f(f), c0(c0) {} + + template + void operator()(ArgTs... args) + { + f(c0, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + + constexpr context(F f, C0 c0, C1 c1) + : f(f), c0(c0), c1(c1) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + + constexpr context(F f, C0 c0, C1 c1, C2 c2) + : f(f), c0(c0), c1(c1), c2(c2) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + C3 c3; + + constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3) + : f(f), c0(c0), c1(c1), c2(c2), c3(c3) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, c3, args...); + } + }; + + template + struct context { + F f; + C0 c0; + C1 c1; + C2 c2; + C3 c3; + C4 c4; + + constexpr context(F f, C0 c0, C1 c1, C2 c2, C3 c3, C4 c4) + : f(f), c0(c0), c1(c1), c2(c2), c3(c3), c4(c4) {} + + template + void operator()(ArgTs... args) + { + f(c0, c1, c2, c3, c4, args...); + } + }; +#endif //!defined(DOXYGEN_ONLY) +}; + +/** @}*/ +/** @}*/ + +} + +#endif diff --git a/events/include/events/UserAllocatedEvent.h b/events/include/events/UserAllocatedEvent.h new file mode 100644 index 0000000..94dd360 --- /dev/null +++ b/events/include/events/UserAllocatedEvent.h @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef USER_ALLOCATED_EVENT_H +#define USER_ALLOCATED_EVENT_H + +#include "events/EventQueue.h" +#include "platform/mbed_assert.h" +#include "platform/mbed_atomic.h" + +namespace events { +/** + * \addtogroup events-public-api + * @{ + */ +template +class UserAllocatedEvent; + +/** + * \defgroup events_Event UserAllocatedEvent class + * @{ + */ + +/** UserAllocatedEvent + * + * Representation of an static event for fine-grain dispatch control. + * + * UserAllocatedEvent provides mechanism for event posting and dispatching + * without utilization of queue internal memory. It embeds all underlying + * event data and doesn't require any memory allocation while posting and dispatching. + * All of these makes it cannot fail due to memory exhaustion while posting + * + * Usage: + * @code + * #include "mbed.h" + * + * void handler(int data) { ... } + * + * class Device { + * public: + * void handler(int data) { ... } + * }; + * + * Device dev; + * + * // queue with not internal storage for dynamic events + * // accepts only user allocated events + * static EventQueue queue(0); + * // Create events + * static auto e1 = make_user_allocated_event(&dev, Device::handler, 2); + * static auto e2 = queue.make_user_allocated_event(handler, 3); + * + * int main() + * { + * e1.call_on(&queue); + * e2.call(); + * + * queue.dispatch(1); + * } + * @endcode + */ +template +class UserAllocatedEvent { +public: + typedef EventQueue::context C; + + /** Create an event + * + * Constructs an event. The specified callback acts as the target + * for the event and is executed in the context of the + * event queue's dispatch loop once posted. + * + * @param f Function to execute when the event is dispatched + * @param args Arguments to bind to the callback + */ + constexpr UserAllocatedEvent(F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(), _post_ref() + { + } + + /** Create an event + * + * Constructs an event. The specified callback acts as the target + * for the event and is executed in the context of the + * event queue's dispatch loop once posted. + * + * @param queue Event queue to dispatch on + * @param f Function to execute when the event is dispatched + * @param args Arguments to bind to the callback + */ + constexpr UserAllocatedEvent(EventQueue *queue, F f, ArgTs... args) : _e(get_default_equeue_event()), _c(f, args...), _delay(), _period(-1), _equeue(&queue->_equeue), _post_ref() + { + } + + /** Destructor for events + */ +#if !defined(NDEBUG) + // Remove the user provided destructor in release to allow constexpr optimization + // constexpr requires destructor to be trivial and only default one is treated as trivial + ~UserAllocatedEvent() + { + MBED_ASSERT(!_post_ref); + } +#endif + + /** Posts an event onto the underlying event queue, returning void + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + */ + void call() + { + MBED_ASSERT(!_post_ref); + MBED_ASSERT(_equeue); + MBED_UNUSED bool status = post(); + MBED_ASSERT(status); + } + + /** Posts an event onto the event queue passed as argument, returning void + * + * The event is posted to the event queue passed as argument + * and is executed in the context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. + * + */ + void call_on(EventQueue *queue) + { + MBED_ASSERT(!_post_ref); + MBED_UNUSED bool status = post_on(queue); + MBED_ASSERT(status); + } + + /** Posts an event onto the underlying event queue + * + * The event is posted to the event queue passed as argument + * and is executed in the context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * @return False if the event was already posted + * true otherwise + * + */ + bool try_call() + { + return post(); + } + + /** Posts an event onto the event queue passed as argument, + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * @param queue Event queue to dispatch on. Will replace earlier bound EventQueue. + * @return False if the event was already posted + * true otherwise + * + */ + bool try_call_on(EventQueue *queue) + { + return post_on(queue); + } + + /** Posts an event onto the underlying event queue, returning void + * + * The event is posted to the underlying queue and is executed in the + * context of the event queue's dispatch loop. + * + * This call cannot fail due queue memory exhaustion + * because it doesn't allocate any memory + * + * The post function is IRQ safe and can act as a mechanism for moving + * events out of IRQ contexts. + * + */ + void operator()() + { + return call(); + } + + /** Configure the delay of an event + * + * @param delay Millisecond delay before dispatching the event + */ + void delay(int delay) + { + MBED_ASSERT(!_post_ref); + _delay = delay; + } + + /** Configure the period of an event + * + * @param period Millisecond period for repeatedly dispatching an event + */ + void period(int period) + { + MBED_ASSERT(!_post_ref); + _period = period; + } + + /** Cancels posted event + * + * Attempts to cancel posted event. It is safe to call + * cancel after an event has already been dispatched. + * + * The cancel function is IRQ safe. + * + * If called while the event queue's dispatch loop is active, the cancel + * function does not guarantee that the event will not execute after it + * returns, as the event may have already begun executing. + * + * @return true if event was successfully cancelled + */ + bool cancel() + { + return _post_ref > 0 ? equeue_cancel_user_allocated(_equeue, &_e) : false; + } + + +private: + friend class EventQueue; + struct equeue_event _e; + C _c; + int _delay; + int _period; + struct equeue *_equeue; + uint8_t _post_ref; + + bool post() + { + if (_post_ref) { + return false; + } + core_util_atomic_incr_u8(&_post_ref, 1); + equeue_event_delay(&_e + 1, _delay); + equeue_event_period(&_e + 1, _period); + equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); + return true; + } + + bool post_on(EventQueue *queue) + { + MBED_ASSERT(queue); + if (_post_ref) { + return false; + } + _equeue = &(queue->_equeue); + core_util_atomic_incr_u8(&_post_ref, 1); + equeue_event_delay(&_e + 1, _delay); + equeue_event_period(&_e + 1, _period); + equeue_post_user_allocated(_equeue, &EventQueue::function_call, &_e); + return true; + } + + static void event_dtor(void *p) + { + UserAllocatedEvent *instance = (UserAllocatedEvent *)(((equeue_event *)p) - 1); + core_util_atomic_decr_u8(&instance->_post_ref, 1); + MBED_ASSERT(!instance->_post_ref); + } + + constexpr static equeue_event get_default_equeue_event() + { + return equeue_event{ 0, 0, 0, NULL, NULL, NULL, 0, -1, &UserAllocatedEvent::event_dtor, NULL }; + } + +public: + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(T *obj, R(T::*method)(ArgTs...), ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, T *obj, R(T::*method)(ArgTs...), ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, const T *obj, R(T::*method)(ArgTs...) const, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, volatile T *obj, R(T::*method)(ArgTs...) volatile, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : + UserAllocatedEvent(mbed::callback(obj, method), args...) { } + + /** Create an event + * @see UserAllocatedEvent::UserAllocatedEvent + */ + template + constexpr UserAllocatedEvent(EventQueue *q, const volatile T *obj, R(T::*method)(ArgTs...) const volatile, ArgTs... args) : + UserAllocatedEvent(q, mbed::callback(obj, method), args...) { } +}; + +// Convenience functions declared here to avoid cyclic +// dependency between Event and EventQueue +template +UserAllocatedEvent EventQueue::make_user_allocated_event(F f, ArgTs... args) +{ + return UserAllocatedEvent(this, f, args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + +template +UserAllocatedEvent, void(ArgTs...)> EventQueue::make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(this, mbed::callback(obj, method), args...); +} + + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent make_user_allocated_event(F f, ArgTs... args) +{ + return UserAllocatedEvent(f, args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(T *obj, R(T::*method)(ArgTs... args), ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const T *obj, R(T::*method)(ArgTs... args) const, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(volatile T *obj, R(T::*method)(ArgTs... args) volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** Creates a UserAllocatedEvent object, deducing the target type from the types of arguments + * + * UserAllocatedEvent doesn't utilize EventQueue internal memory, + * therefore it can be posted on the queue without being afraid + * of post fail due to queue memory exhaustion + * + * @return UserAllocatedEvent object instance + * + */ +template +constexpr UserAllocatedEvent, void(ArgTs...)> make_user_allocated_event(const volatile T *obj, R(T::*method)(ArgTs... args) const volatile, ArgTs... args) +{ + return UserAllocatedEvent, void(ArgTs...)>(mbed::callback(obj, method), args...); +} + +/** @}*/ + +/** @}*/ +} +#endif diff --git a/events/include/events/equeue.h b/events/include/events/equeue.h new file mode 100644 index 0000000..5d452fc --- /dev/null +++ b/events/include/events/equeue.h @@ -0,0 +1,275 @@ + +/* + * Flexible event queue for dispatching events + * + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EQUEUE_H +#define EQUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +// Platform specific files +#include "events/internal/equeue_platform.h" + +#include +#include + +/** + * \addtogroup events-public-api + * @{ + */ + +// The minimum size of an event +// This size is guaranteed to fit events created by event_call +#define EQUEUE_EVENT_SIZE (sizeof(struct equeue_event) + 2*sizeof(void*)) + +// Internal event structure +struct equeue_event { + unsigned size; + uint8_t id; + uint8_t generation; + + struct equeue_event *next; + struct equeue_event *sibling; + struct equeue_event **ref; + + unsigned target; + int period; + void (*dtor)(void *); + + void (*cb)(void *); + // data follows +}; + +// Event queue structure +typedef struct equeue { + struct equeue_event *queue; + unsigned tick; + bool break_requested; + uint8_t generation; + + unsigned char *buffer; + unsigned npw2; + void *allocated; + + struct equeue_event *chunks; + struct equeue_slab { + size_t size; + unsigned char *data; + } slab; + + struct equeue_background { + bool active; + void (*update)(void *timer, int ms); + void *timer; + } background; + + equeue_sema_t eventsema; + equeue_mutex_t queuelock; + equeue_mutex_t memlock; +} equeue_t; + + +// Queue lifetime operations +// +// Creates and destroys an event queue. The event queue either allocates a +// buffer of the specified size with malloc or uses a user provided buffer +// if constructed with equeue_create_inplace. +// +// If the event queue creation fails, equeue_create returns a negative, +// platform-specific error code. +// +// If queues are chained, it is needed to unchain them first, before calling destroy, +// or call the destroy function on queues in order that chained queues are destroyed first. +int equeue_create(equeue_t *queue, size_t size); +int equeue_create_inplace(equeue_t *queue, size_t size, void *buffer); +void equeue_destroy(equeue_t *queue); + +// Dispatch events +// +// Executes events until the specified milliseconds have passed. If ms is +// negative, equeue_dispatch will dispatch events indefinitely or until +// equeue_break is called on this queue. +// +// When called with a finite timeout, the equeue_dispatch function is +// guaranteed to terminate. When called with a timeout of 0, the +// equeue_dispatch does not wait and is irq safe. +void equeue_dispatch(equeue_t *queue, int ms); + +// Break out of a running event loop +// +// Forces the specified event queue's dispatch loop to terminate. Pending +// events may finish executing, but no new events will be executed. +void equeue_break(equeue_t *queue); + +// Simple event calls +// +// The specified callback will be executed in the context of the event queue's +// dispatch loop. When the callback is executed depends on the call function. +// +// equeue_call - Immediately post an event to the queue +// equeue_call_in - Post an event after a specified time in milliseconds +// equeue_call_every - Post an event periodically every milliseconds +// +// All equeue_call functions are irq safe and can act as a mechanism for +// moving events out of irq contexts. +// +// The return value is a unique id that represents the posted event and can +// be passed to equeue_cancel. If there is not enough memory to allocate the +// event, equeue_call returns an id of 0. +int equeue_call(equeue_t *queue, void (*cb)(void *), void *data); +int equeue_call_in(equeue_t *queue, int ms, void (*cb)(void *), void *data); +int equeue_call_every(equeue_t *queue, int ms, void (*cb)(void *), void *data); + +// Allocate memory for events +// +// The equeue_alloc function allocates an event that can be manually dispatched +// with equeue_post. The equeue_dealloc function may be used to free an event +// that has not been posted. Once posted, an event's memory is managed by the +// event queue and should not be deallocated. +// +// Both equeue_alloc and equeue_dealloc are irq safe. +// +// The equeue allocator is designed to minimize jitter in interrupt contexts as +// well as avoid memory fragmentation on small devices. The allocator achieves +// both constant-runtime and zero-fragmentation for fixed-size events, however +// grows linearly as the quantity of different sized allocations increases. +// +// The equeue_alloc function returns a pointer to the event's allocated memory +// and acts as a handle to the underlying event. If there is not enough memory +// to allocate the event, equeue_alloc returns null. +void *equeue_alloc(equeue_t *queue, size_t size); +void equeue_dealloc(equeue_t *queue, void *event); + +// Configure an allocated event +// +// equeue_event_delay - Millisecond delay before dispatching an event +// equeue_event_period - Millisecond period for repeating dispatching an event +// equeue_event_dtor - Destructor to run when the event is deallocated +void equeue_event_delay(void *event, int ms); +void equeue_event_period(void *event, int ms); +void equeue_event_dtor(void *event, void (*dtor)(void *)); + +// Post an event onto the event queue +// +// The equeue_post function takes a callback and a pointer to an event +// allocated by equeue_alloc. The specified callback will be executed in the +// context of the event queue's dispatch loop with the allocated event +// as its argument. +// +// The equeue_post function is irq safe and can act as a mechanism for +// moving events out of irq contexts. +// +// The return value is a unique id that represents the posted event and can +// be passed to equeue_cancel. +int equeue_post(equeue_t *queue, void (*cb)(void *), void *event); + +// Post an user allocated event onto the event queue +// +// The equeue_post_user_allocated function takes a callback and a pointer +// to an event allocated by user. The specified callback will be executed +// in the context of the event queue's dispatch loop with the allocated +// event as its argument. +// +// The equeue_post_user_allocated function is irq safe and can act as +// a mechanism for moving events out of irq contexts. +void equeue_post_user_allocated(equeue_t *queue, void (*cb)(void *), void *event); + +// Cancel an in-flight event +// +// Attempts to cancel an event referenced by the unique id returned from +// equeue_call or equeue_post. It is safe to call equeue_cancel after an event +// has already been dispatched. +// +// The equeue_cancel function is irq safe. +// +// If called while the event queue's dispatch loop is active in another thread, +// equeue_cancel does not guarantee that the event will not execute after it returns as +// the event may have already begun executing. +// Returning true guarantees that cancel succeeded and event will not execute. +// Returning false if invalid id or already started executing. +bool equeue_cancel(equeue_t *queue, int id); + +// Cancel an in-flight user allocated event +// +// Attempts to cancel an event referenced by its address. +// It is safe to call equeue_cancel_user_allocated after an event +// has already been dispatched. +// +// The equeue_cancel_user_allocated function is irq safe. +// +// If called while the event queue's dispatch loop is active, +// equeue_cancel_user_allocated does not guarantee that the event +// will not not execute after it returns as the event may have +// already begun executing. +bool equeue_cancel_user_allocated(equeue_t *queue, void *event); + +// Query how much time is left for delayed event +// +// If event is delayed, this function can be used to query how much time +// is left until the event is due to be dispatched. +// +// This function is irq safe. +// +int equeue_timeleft(equeue_t *q, int id); + +// Query how much time is left for delayed user allocated event +// +// If event is delayed, this function can be used to query how much time +// is left until the event is due to be dispatched. +// +// This function is irq safe. +// +int equeue_timeleft_user_allocated(equeue_t *q, void *event); + +// Background an event queue onto a single-shot timer +// +// The provided update function will be called to indicate when the queue +// should be dispatched. A negative timeout will be passed to the update +// function when the timer is no longer needed. +// +// Passing a null update function disables the existing timer. +// +// The equeue_background function allows an event queue to take advantage +// of hardware timers or even other event loops, allowing an event queue to +// be effectively backgrounded. +void equeue_background(equeue_t *queue, + void (*update)(void *timer, int ms), void *timer); + +// Chain an event queue onto another event queue +// +// After chaining a queue to a target, calling equeue_dispatch on the +// target queue will also dispatch events from this queue. The queues +// use their own buffers and events must be managed independently. +// +// Passing a null queue as the target will unchain the existing queue. +// +// The equeue_chain function allows multiple equeues to be composed, sharing +// the context of a dispatch loop while still being managed independently. +// +// If the event queue chaining fails, equeue_chain returns a negative, +// platform-specific error code. +int equeue_chain(equeue_t *queue, equeue_t *target); + +/** @}*/ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/events/include/events/internal/equeue_platform.h b/events/include/events/internal/equeue_platform.h new file mode 100644 index 0000000..5d47c14 --- /dev/null +++ b/events/include/events/internal/equeue_platform.h @@ -0,0 +1,154 @@ +/* + * System specific implementation + * + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef EQUEUE_PLATFORM_H +#define EQUEUE_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +// Currently supported platforms +// +// Uncomment to select a supported platform or reimplement this file +// for a specific target. +//#define EQUEUE_PLATFORM_POSIX +//#define EQUEUE_PLATFORM_MBED + +// Try to infer a platform if none was manually selected +#if !defined(EQUEUE_PLATFORM_POSIX) \ + && !defined(EQUEUE_PLATFORM_MBED) +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__) || defined(__MINGW64__) +#define EQUEUE_PLATFORM_POSIX +#elif defined(__MBED__) +#define EQUEUE_PLATFORM_MBED +#else +#warning "Unknown platform! Please update equeue_platform.h" +#endif +#endif + +// Platform includes +#if defined(EQUEUE_PLATFORM_POSIX) +#include +#elif defined(EQUEUE_PLATFORM_MBED) && defined(MBED_CONF_RTOS_PRESENT) +#include "cmsis_os2.h" +#include "mbed_rtos_storage.h" +#endif + + +// Platform millisecond counter +// +// Return a tick that represents the number of milliseconds that have passed +// since an arbitrary point in time. The granularity does not need to be at +// the millisecond level, however the accuracy of the equeue library is +// limited by the accuracy of this tick. +// +// Must intentionally overflow to 0 after 2^32-1 +void equeue_tick_init(void); +unsigned equeue_tick(void); + + +// Platform mutex type +// +// The equeue library requires at minimum a non-recursive mutex that is +// safe in interrupt contexts. The mutex section is help for a bounded +// amount of time, so simply disabling interrupts is acceptable +// +// If irq safety is not required, a regular blocking mutex can be used. +#if defined(EQUEUE_PLATFORM_POSIX) +typedef pthread_mutex_t equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_WINDOWS) +typedef CRITICAL_SECTION equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_MBED) +typedef unsigned equeue_mutex_t; +#elif defined(EQUEUE_PLATFORM_FREERTOS) +typedef UBaseType_t equeue_mutex_t; +#endif + +// Platform mutex operations +// +// The equeue_mutex_create and equeue_mutex_destroy manage the lifetime +// of the mutex. On error, equeue_mutex_create should return a negative +// error code. +// +// The equeue_mutex_lock and equeue_mutex_unlock lock and unlock the +// underlying mutex. +int equeue_mutex_create(equeue_mutex_t *mutex); +void equeue_mutex_destroy(equeue_mutex_t *mutex); +void equeue_mutex_lock(equeue_mutex_t *mutex); +void equeue_mutex_unlock(equeue_mutex_t *mutex); + + +// Platform semaphore type +// +// The equeue library requires a binary semaphore type that can be safely +// signaled from interrupt contexts and from inside a equeue_mutex section. +// +// The equeue_signal_wait is relied upon by the equeue library to sleep the +// processor between events. Spurious wakeups have no negative-effects. +// +// A counting semaphore will also work, however may cause the event queue +// dispatch loop to run unnecessarily. For that matter, equeue_signal_wait +// may even be implemented as a single return statement. +#if defined(EQUEUE_PLATFORM_POSIX) +typedef struct equeue_sema { + pthread_mutex_t mutex; + pthread_cond_t cond; + bool signal; +} equeue_sema_t; +#elif defined(EQUEUE_PLATFORM_MBED) && MBED_CONF_RTOS_API_PRESENT +typedef struct equeue_sema { + // We will actually store a C++ rtos:EventQueue in here; + // attempt to match layout for storage, and assert size in equeue_mbed.cpp +#if MBED_CONF_RTOS_PRESENT + osEventFlagsId_t _id; + mbed_rtos_storage_event_flags_t _obj_mem; +#else + uint32_t _flags; +#endif +} equeue_sema_t; +#elif defined(EQUEUE_PLATFORM_MBED) +typedef int equeue_sema_t; +#endif + +// Platform semaphore operations +// +// The equeue_sema_create and equeue_sema_destroy manage the lifetime +// of the semaphore. On error, equeue_sema_create should return a negative +// error code. +// +// The equeue_sema_signal marks a semaphore as signalled such that the next +// equeue_sema_wait will return true. +// +// The equeue_sema_wait waits for a semaphore to be signalled or returns +// immediately if equeue_sema_signal had been called since the last +// equeue_sema_wait. The equeue_sema_wait returns true if it detected that +// equeue_sema_signal had been called. If ms is negative, equeue_sema_wait +// will wait for a signal indefinitely. +int equeue_sema_create(equeue_sema_t *sema); +void equeue_sema_destroy(equeue_sema_t *sema); +void equeue_sema_signal(equeue_sema_t *sema); +bool equeue_sema_wait(equeue_sema_t *sema, int ms); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/events/include/events/mbed_events.h b/events/include/events/mbed_events.h new file mode 100644 index 0000000..991211c --- /dev/null +++ b/events/include/events/mbed_events.h @@ -0,0 +1,36 @@ +/* Copyright (c) 2016-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_EVENTS_H +#define MBED_EVENTS_H + +#ifdef __cplusplus + +#include "events/EventQueue.h" +#include "events/Event.h" +#include "events/UserAllocatedEvent.h" + +#include "events/mbed_shared_queues.h" + +/** \addtogroup events-public-api */ +/** @{*/ +#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE +using namespace events; +#endif +/** @}*/ + +#endif + +#endif diff --git a/events/include/events/mbed_shared_queues.h b/events/include/events/mbed_shared_queues.h new file mode 100644 index 0000000..9be5ff1 --- /dev/null +++ b/events/include/events/mbed_shared_queues.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016-2019 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SHARED_QUEUES_H +#define MBED_SHARED_QUEUES_H + +#include "events/EventQueue.h" + +namespace mbed { +/** \addtogroup events-public-api */ +/** @{*/ + +/** + * Return a pointer to an EventQueue, on which normal tasks can be queued. + * + * All calls to this return the same EventQueue - it and its dispatch thread + * are created on the first call to this function. The dispatch thread + * runs at default priority (currently osPriorityNormal). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the normal event queue should + * normally take less than 10ms to execute, to avoid starving other users. As + * such, users can expect that event latency will typically be 10ms or less, + * but could occasionally be significantly higher if many events are queued. + * + * If an RTOS is not present or the configuration option + * `events.shared-dispatch-from-application` is set to true, then this + * does not create a dedicated dispatch thread - instead the application is + * expected to run the EventQueue's dispatch, for example from main. This is + * necessary for the event loop to work without an RTOS, or an RTOS system can + * save memory by reusing the main stack. + * + * @note + * mbed_event_queue is not itself IRQ safe. To use the mbed_event_queue in + * interrupt context, you must first call `mbed_event_queue()` in threaded + * context and store the pointer for later use. + * + * @return pointer to event queue + */ +events::EventQueue *mbed_event_queue(); + +#ifdef MBED_CONF_RTOS_PRESENT +/** + * Return a pointer to an EventQueue, on which small high-priority tasks can + * be queues, such as simple deferrals from interrupt. + * + * All calls to this return the same EventQueue - it and its thread are + * created on the first call to this function. The dispatch thread + * runs at a high priority (currently osPriorityHigh). + * + * The EventQueue returned may be used to call() Events, or to chain() other + * EventQueues so that they are run in the same context. + * + * Events (or chained EventQueues) executing on the high-priority event queue + * should normally take less than 100us to execute, to avoid starving other + * users. As such, users can expect that event latency will typically be 100us + * or less, but could occasionally be significantly higher if many events are + * queued. + * + * @note + * mbed_highprio_event_queue is not itself IRQ safe. To use the + * mbed_highprio_event_queue in interrupt context, you must first call + * `mbed_highprio_event_queue()` in threaded context and store the pointer for + * later use. + * + * @return pointer to high-priority event queue + */ + +events::EventQueue *mbed_highprio_event_queue(); + +#endif // MBED_CONF_RTOS_PRESENT + +/** @}*/ + +} + +#endif diff --git a/events/internal/equeue_platform.h b/events/internal/equeue_platform.h deleted file mode 100644 index 5d47c14..0000000 --- a/events/internal/equeue_platform.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * System specific implementation - * - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef EQUEUE_PLATFORM_H -#define EQUEUE_PLATFORM_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -// Currently supported platforms -// -// Uncomment to select a supported platform or reimplement this file -// for a specific target. -//#define EQUEUE_PLATFORM_POSIX -//#define EQUEUE_PLATFORM_MBED - -// Try to infer a platform if none was manually selected -#if !defined(EQUEUE_PLATFORM_POSIX) \ - && !defined(EQUEUE_PLATFORM_MBED) -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__) || defined(__MINGW64__) -#define EQUEUE_PLATFORM_POSIX -#elif defined(__MBED__) -#define EQUEUE_PLATFORM_MBED -#else -#warning "Unknown platform! Please update equeue_platform.h" -#endif -#endif - -// Platform includes -#if defined(EQUEUE_PLATFORM_POSIX) -#include -#elif defined(EQUEUE_PLATFORM_MBED) && defined(MBED_CONF_RTOS_PRESENT) -#include "cmsis_os2.h" -#include "mbed_rtos_storage.h" -#endif - - -// Platform millisecond counter -// -// Return a tick that represents the number of milliseconds that have passed -// since an arbitrary point in time. The granularity does not need to be at -// the millisecond level, however the accuracy of the equeue library is -// limited by the accuracy of this tick. -// -// Must intentionally overflow to 0 after 2^32-1 -void equeue_tick_init(void); -unsigned equeue_tick(void); - - -// Platform mutex type -// -// The equeue library requires at minimum a non-recursive mutex that is -// safe in interrupt contexts. The mutex section is help for a bounded -// amount of time, so simply disabling interrupts is acceptable -// -// If irq safety is not required, a regular blocking mutex can be used. -#if defined(EQUEUE_PLATFORM_POSIX) -typedef pthread_mutex_t equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_WINDOWS) -typedef CRITICAL_SECTION equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_MBED) -typedef unsigned equeue_mutex_t; -#elif defined(EQUEUE_PLATFORM_FREERTOS) -typedef UBaseType_t equeue_mutex_t; -#endif - -// Platform mutex operations -// -// The equeue_mutex_create and equeue_mutex_destroy manage the lifetime -// of the mutex. On error, equeue_mutex_create should return a negative -// error code. -// -// The equeue_mutex_lock and equeue_mutex_unlock lock and unlock the -// underlying mutex. -int equeue_mutex_create(equeue_mutex_t *mutex); -void equeue_mutex_destroy(equeue_mutex_t *mutex); -void equeue_mutex_lock(equeue_mutex_t *mutex); -void equeue_mutex_unlock(equeue_mutex_t *mutex); - - -// Platform semaphore type -// -// The equeue library requires a binary semaphore type that can be safely -// signaled from interrupt contexts and from inside a equeue_mutex section. -// -// The equeue_signal_wait is relied upon by the equeue library to sleep the -// processor between events. Spurious wakeups have no negative-effects. -// -// A counting semaphore will also work, however may cause the event queue -// dispatch loop to run unnecessarily. For that matter, equeue_signal_wait -// may even be implemented as a single return statement. -#if defined(EQUEUE_PLATFORM_POSIX) -typedef struct equeue_sema { - pthread_mutex_t mutex; - pthread_cond_t cond; - bool signal; -} equeue_sema_t; -#elif defined(EQUEUE_PLATFORM_MBED) && MBED_CONF_RTOS_API_PRESENT -typedef struct equeue_sema { - // We will actually store a C++ rtos:EventQueue in here; - // attempt to match layout for storage, and assert size in equeue_mbed.cpp -#if MBED_CONF_RTOS_PRESENT - osEventFlagsId_t _id; - mbed_rtos_storage_event_flags_t _obj_mem; -#else - uint32_t _flags; -#endif -} equeue_sema_t; -#elif defined(EQUEUE_PLATFORM_MBED) -typedef int equeue_sema_t; -#endif - -// Platform semaphore operations -// -// The equeue_sema_create and equeue_sema_destroy manage the lifetime -// of the semaphore. On error, equeue_sema_create should return a negative -// error code. -// -// The equeue_sema_signal marks a semaphore as signalled such that the next -// equeue_sema_wait will return true. -// -// The equeue_sema_wait waits for a semaphore to be signalled or returns -// immediately if equeue_sema_signal had been called since the last -// equeue_sema_wait. The equeue_sema_wait returns true if it detected that -// equeue_sema_signal had been called. If ms is negative, equeue_sema_wait -// will wait for a signal indefinitely. -int equeue_sema_create(equeue_sema_t *sema); -void equeue_sema_destroy(equeue_sema_t *sema); -void equeue_sema_signal(equeue_sema_t *sema); -bool equeue_sema_wait(equeue_sema_t *sema, int ms); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/events/mbed_events.h b/events/mbed_events.h deleted file mode 100644 index 991211c..0000000 --- a/events/mbed_events.h +++ /dev/null @@ -1,36 +0,0 @@ -/* Copyright (c) 2016-2019 ARM Limited - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_EVENTS_H -#define MBED_EVENTS_H - -#ifdef __cplusplus - -#include "events/EventQueue.h" -#include "events/Event.h" -#include "events/UserAllocatedEvent.h" - -#include "events/mbed_shared_queues.h" - -/** \addtogroup events-public-api */ -/** @{*/ -#ifndef MBED_NO_GLOBAL_USING_DIRECTIVE -using namespace events; -#endif -/** @}*/ - -#endif - -#endif diff --git a/events/mbed_shared_queues.h b/events/mbed_shared_queues.h deleted file mode 100644 index 9be5ff1..0000000 --- a/events/mbed_shared_queues.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2016-2019 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#ifndef MBED_SHARED_QUEUES_H -#define MBED_SHARED_QUEUES_H - -#include "events/EventQueue.h" - -namespace mbed { -/** \addtogroup events-public-api */ -/** @{*/ - -/** - * Return a pointer to an EventQueue, on which normal tasks can be queued. - * - * All calls to this return the same EventQueue - it and its dispatch thread - * are created on the first call to this function. The dispatch thread - * runs at default priority (currently osPriorityNormal). - * - * The EventQueue returned may be used to call() Events, or to chain() other - * EventQueues so that they are run in the same context. - * - * Events (or chained EventQueues) executing on the normal event queue should - * normally take less than 10ms to execute, to avoid starving other users. As - * such, users can expect that event latency will typically be 10ms or less, - * but could occasionally be significantly higher if many events are queued. - * - * If an RTOS is not present or the configuration option - * `events.shared-dispatch-from-application` is set to true, then this - * does not create a dedicated dispatch thread - instead the application is - * expected to run the EventQueue's dispatch, for example from main. This is - * necessary for the event loop to work without an RTOS, or an RTOS system can - * save memory by reusing the main stack. - * - * @note - * mbed_event_queue is not itself IRQ safe. To use the mbed_event_queue in - * interrupt context, you must first call `mbed_event_queue()` in threaded - * context and store the pointer for later use. - * - * @return pointer to event queue - */ -events::EventQueue *mbed_event_queue(); - -#ifdef MBED_CONF_RTOS_PRESENT -/** - * Return a pointer to an EventQueue, on which small high-priority tasks can - * be queues, such as simple deferrals from interrupt. - * - * All calls to this return the same EventQueue - it and its thread are - * created on the first call to this function. The dispatch thread - * runs at a high priority (currently osPriorityHigh). - * - * The EventQueue returned may be used to call() Events, or to chain() other - * EventQueues so that they are run in the same context. - * - * Events (or chained EventQueues) executing on the high-priority event queue - * should normally take less than 100us to execute, to avoid starving other - * users. As such, users can expect that event latency will typically be 100us - * or less, but could occasionally be significantly higher if many events are - * queued. - * - * @note - * mbed_highprio_event_queue is not itself IRQ safe. To use the - * mbed_highprio_event_queue in interrupt context, you must first call - * `mbed_highprio_event_queue()` in threaded context and store the pointer for - * later use. - * - * @return pointer to high-priority event queue - */ - -events::EventQueue *mbed_highprio_event_queue(); - -#endif // MBED_CONF_RTOS_PRESENT - -/** @}*/ - -} - -#endif diff --git a/events/source/.mbedignore b/events/source/.mbedignore deleted file mode 100644 index e7e1fb0..0000000 --- a/events/source/.mbedignore +++ /dev/null @@ -1 +0,0 @@ -tests/* diff --git a/events/source/Makefile b/events/source/Makefile deleted file mode 100644 index 6e636bc..0000000 --- a/events/source/Makefile +++ /dev/null @@ -1,60 +0,0 @@ -TARGET = libequeue.a - -CC = gcc -AR = ar -SIZE = size - -SRC += $(wildcard *.c) -OBJ := $(SRC:.c=.o) -DEP := $(SRC:.c=.d) -ASM := $(SRC:.c=.s) - -ifdef DEBUG -CFLAGS += -O0 -g3 -else -CFLAGS += -O2 -endif -ifdef WORD -CFLAGS += -m$(WORD) -endif -CFLAGS += -I. -I.. -I../.. -CFLAGS += -std=c99 -CFLAGS += -Wall -CFLAGS += -D_XOPEN_SOURCE=600 - -LFLAGS += -pthread - - -all: $(TARGET) - -test: tests/tests.o $(OBJ) - $(CC) $(CFLAGS) $^ $(LFLAGS) -o tests/tests - tests/tests - -prof: tests/prof.o $(OBJ) - $(CC) $(CFLAGS) $^ $(LFLAGS) -o tests/prof - tests/prof - -asm: $(ASM) - -size: $(OBJ) - $(SIZE) -t $^ - --include $(DEP) - -%.a: $(OBJ) - $(AR) rcs $@ $^ - -%.o: %.c - $(CC) -c -MMD $(CFLAGS) $< -o $@ - -%.s: %.c - $(CC) -S $(CFLAGS) $< -o $@ - -clean: - rm -f $(TARGET) - rm -f tests/tests tests/tests.o tests/tests.d - rm -f tests/prof tests/prof.o tests/prof.d - rm -f $(OBJ) - rm -f $(DEP) - rm -f $(ASM) diff --git a/events/source/tests/prof.c b/events/source/tests/prof.c deleted file mode 100644 index f7b8f90..0000000 --- a/events/source/tests/prof.c +++ /dev/null @@ -1,424 +0,0 @@ -/* - * Profiling framework for the events library - * - * Copyright (c) 2016 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "events/equeue.h" -#include -#include -#include -#include -#include -#include -#include - - -// Performance measurement utils -#define PROF_RUNS 5 -#define PROF_INTERVAL 100000000 - -#define prof_volatile(t) __attribute__((unused)) volatile t - -typedef uint64_t prof_cycle_t; - -static volatile prof_cycle_t prof_start_cycle; -static volatile prof_cycle_t prof_stop_cycle; -static prof_cycle_t prof_accum_cycle; -static prof_cycle_t prof_baseline_cycle; -static prof_cycle_t prof_iterations; -static const char *prof_units; - -#define prof_cycle() ({ \ - uint32_t a, b; \ - __asm__ volatile ("rdtsc" : "=a" (a), "=d" (b)); \ - ((uint64_t)b << 32) | (uint64_t)a; \ -}) - -#define prof_loop() \ - for (prof_iterations = 0; \ - prof_accum_cycle < PROF_INTERVAL; \ - prof_iterations++) - -#define prof_start() ({ \ - prof_start_cycle = prof_cycle(); \ -}) - -#define prof_stop() ({ \ - prof_stop_cycle = prof_cycle(); \ - prof_accum_cycle += prof_stop_cycle - prof_start_cycle; \ -}) - -#define prof_result(value, units) ({ \ - prof_accum_cycle = value+prof_baseline_cycle; \ - prof_iterations = 1; \ - prof_units = units; \ -}) - -#define prof_measure(func, ...) ({ \ - printf("%s: ...", #func); \ - fflush(stdout); \ - \ - prof_units = "cycles"; \ - prof_cycle_t runs[PROF_RUNS]; \ - for (int i = 0; i < PROF_RUNS; i++) { \ - prof_accum_cycle = 0; \ - prof_iterations = 0; \ - func(__VA_ARGS__); \ - runs[i] = prof_accum_cycle / prof_iterations; \ - } \ - \ - prof_cycle_t res = runs[0]; \ - for (int i = 0; i < PROF_RUNS; i++) { \ - if (runs[i] < res) { \ - res = runs[i]; \ - } \ - } \ - res -= prof_baseline_cycle; \ - printf("\r%s: %"PRIu64" %s", #func, res, prof_units); \ - \ - if (!isatty(0)) { \ - prof_cycle_t prev; \ - while (scanf("%*[^0-9]%"PRIu64, &prev) == 0); \ - int64_t perc = 100*((int64_t)prev - (int64_t)res) / (int64_t)prev; \ - \ - if (perc > 10) { \ - printf(" (\e[32m%+"PRId64"%%\e[0m)", perc); \ - } else if (perc < -10) { \ - printf(" (\e[31m%+"PRId64"%%\e[0m)", perc); \ - } else { \ - printf(" (%+"PRId64"%%)", perc); \ - } \ - } \ - \ - printf("\n"); \ - res; \ -}) - -#define prof_baseline(func, ...) ({ \ - prof_baseline_cycle = 0; \ - prof_baseline_cycle = prof_measure(func, __VA_ARGS__); \ -}) - - -// Various test functions -void no_func(void *eh) -{ -} - - -// Actual performance tests -void baseline_prof(void) -{ - prof_loop() { - prof_start(); - __asm__ volatile(""); - prof_stop(); - } -} - -void equeue_tick_prof(void) -{ - prof_volatile(unsigned) res; - prof_loop() { - prof_start(); - res = equeue_tick(); - prof_stop(); - } -} - -void equeue_alloc_prof(void) -{ - struct equeue q; - equeue_create(&q, 32 * EQUEUE_EVENT_SIZE); - - prof_loop() { - prof_start(); - void *e = equeue_alloc(&q, 8 * sizeof(int)); - prof_stop(); - - equeue_dealloc(&q, e); - } - - equeue_destroy(&q); -} - -void equeue_alloc_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - void *es[count]; - - for (int i = 0; i < count; i++) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = 0; i < count; i++) { - equeue_dealloc(&q, es[i]); - } - - prof_loop() { - prof_start(); - void *e = equeue_alloc(&q, 8 * sizeof(int)); - prof_stop(); - - equeue_dealloc(&q, e); - } - - equeue_destroy(&q); -} - -void equeue_post_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - void *e = equeue_alloc(&q, 0); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - void *e = equeue_alloc(&q, 0); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_future_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - void *e = equeue_alloc(&q, 0); - equeue_event_delay(e, 1000); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_post_future_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - void *e = equeue_alloc(&q, 0); - equeue_event_delay(e, 1000); - - prof_start(); - int id = equeue_post(&q, no_func, e); - prof_stop(); - - equeue_cancel(&q, id); - } - - equeue_destroy(&q); -} - -void equeue_dispatch_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - equeue_call(&q, no_func, 0); - - prof_start(); - equeue_dispatch(&q, 0); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_dispatch_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - prof_loop() { - for (int i = 0; i < count; i++) { - equeue_call(&q, no_func, 0); - } - - prof_start(); - equeue_dispatch(&q, 0); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_cancel_prof(void) -{ - struct equeue q; - equeue_create(&q, EQUEUE_EVENT_SIZE); - - prof_loop() { - int id = equeue_call(&q, no_func, 0); - - prof_start(); - equeue_cancel(&q, id); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_cancel_many_prof(int count) -{ - struct equeue q; - equeue_create(&q, count * EQUEUE_EVENT_SIZE); - - for (int i = 0; i < count - 1; i++) { - equeue_call(&q, no_func, 0); - } - - prof_loop() { - int id = equeue_call(&q, no_func, 0); - - prof_start(); - equeue_cancel(&q, id); - prof_stop(); - } - - equeue_destroy(&q); -} - -void equeue_alloc_size_prof(void) -{ - size_t size = 32 * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - equeue_alloc(&q, 0); - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - -void equeue_alloc_many_size_prof(int count) -{ - size_t size = count * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - - for (int i = 0; i < count; i++) { - equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - -void equeue_alloc_fragmented_size_prof(int count) -{ - size_t size = count * EQUEUE_EVENT_SIZE; - - struct equeue q; - equeue_create(&q, size); - - void *es[count]; - - for (int i = 0; i < count; i++) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = 0; i < count; i++) { - equeue_dealloc(&q, es[i]); - } - - for (int i = count - 1; i >= 0; i--) { - es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - for (int i = count - 1; i >= 0; i--) { - equeue_dealloc(&q, es[i]); - } - - for (int i = 0; i < count; i++) { - equeue_alloc(&q, (i % 4) * sizeof(int)); - } - - prof_result(size - q.slab.size, "bytes"); - - equeue_destroy(&q); -} - - -// Entry point -int main() -{ - printf("beginning profiling...\n"); - - prof_baseline(baseline_prof); - - prof_measure(equeue_tick_prof); - prof_measure(equeue_alloc_prof); - prof_measure(equeue_post_prof); - prof_measure(equeue_post_future_prof); - prof_measure(equeue_dispatch_prof); - prof_measure(equeue_cancel_prof); - - prof_measure(equeue_alloc_many_prof, 1000); - prof_measure(equeue_post_many_prof, 1000); - prof_measure(equeue_post_future_many_prof, 1000); - prof_measure(equeue_dispatch_many_prof, 100); - prof_measure(equeue_cancel_many_prof, 100); - - prof_measure(equeue_alloc_size_prof); - prof_measure(equeue_alloc_many_size_prof, 1000); - prof_measure(equeue_alloc_fragmented_size_prof, 1000); - - printf("done!\n"); -} diff --git a/events/source/tests/tests.c b/events/source/tests/tests.c deleted file mode 100644 index efbf7f4..0000000 --- a/events/source/tests/tests.c +++ /dev/null @@ -1,902 +0,0 @@ -/* - * Testing framework for the events library - * - * Copyright (c) 2016 ARM Limited - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "events/equeue.h" -#include -#include -#include -#include -#include -#include - - -// Testing setup -static jmp_buf test_buf; -static int test_line; -static int test_failure; - -#define test_assert(test) ({ \ - if (!(test)) { \ - test_line = __LINE__; \ - longjmp(test_buf, 1); \ - } \ -}) - -#define test_run(func, ...) ({ \ - printf("%s: ...", #func); \ - fflush(stdout); \ - \ - if (!setjmp(test_buf)) { \ - func(__VA_ARGS__); \ - printf("\r%s: \e[32mpassed\e[0m\n", #func); \ - } else { \ - printf("\r%s: \e[31mfailed\e[0m at line %d\n", #func, test_line); \ - test_failure = true; \ - } \ -}) - - -// Test functions -void pass_func(void *eh) -{ -} - -void simple_func(void *p) -{ - (*(int *)p)++; -} - -void sloth_func(void *p) -{ - usleep(10000); - (*(int *)p)++; -} - -struct indirect { - int *touched; - uint8_t buffer[7]; -}; - -void indirect_func(void *p) -{ - struct indirect *i = (struct indirect *)p; - (*i->touched)++; -} - -struct timing { - unsigned tick; - unsigned delay; -}; - -void timing_func(void *p) -{ - struct timing *timing = (struct timing *)p; - unsigned tick = equeue_tick(); - - unsigned t1 = timing->delay; - unsigned t2 = tick - timing->tick; - test_assert(t1 > t2 - 10 && t1 < t2 + 10); - - timing->tick = tick; -} - -struct fragment { - equeue_t *q; - size_t size; - struct timing timing; -}; - -void fragment_func(void *p) -{ - struct fragment *fragment = (struct fragment *)p; - timing_func(&fragment->timing); - - struct fragment *nfragment = equeue_alloc(fragment->q, fragment->size); - test_assert(nfragment); - - *nfragment = *fragment; - equeue_event_delay(nfragment, fragment->timing.delay); - - int id = equeue_post(nfragment->q, fragment_func, nfragment); - test_assert(id); -} - -struct cancel { - equeue_t *q; - int id; -}; - -void cancel_func(void *p) -{ - struct cancel *cancel = (struct cancel *)p; - equeue_cancel(cancel->q, cancel->id); -} - -struct nest { - equeue_t *q; - void (*cb)(void *); - void *data; -}; - -void nest_func(void *p) -{ - struct nest *nest = (struct nest *)p; - equeue_call(nest->q, nest->cb, nest->data); - - usleep(10000); -} - - -// Simple call tests -void simple_call_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call(&q, simple_func, &touched); - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_call_in_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int id = equeue_call_in(&q, 10, simple_func, &touched); - test_assert(id); - - equeue_dispatch(&q, 15); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_call_every_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int id = equeue_call_every(&q, 10, simple_func, &touched); - test_assert(id); - - equeue_dispatch(&q, 15); - test_assert(touched); - - equeue_destroy(&q); -} - -void simple_post_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched = false; - struct indirect *i = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(i); - - i->touched = &touched; - int id = equeue_post(&q, indirect_func, i); - test_assert(id); - - equeue_dispatch(&q, 0); - test_assert(*i->touched); - - equeue_destroy(&q); -} - -// Misc tests -void destructor_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched; - struct indirect *e; - int ids[3]; - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - test_assert(id); - } - - equeue_dispatch(&q, 0); - test_assert(touched == 3); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - ids[i] = equeue_post(&q, pass_func, e); - test_assert(ids[i]); - } - - for (int i = 0; i < 3; i++) { - equeue_cancel(&q, ids[i]); - } - - equeue_dispatch(&q, 0); - test_assert(touched == 3); - - touched = 0; - for (int i = 0; i < 3; i++) { - e = equeue_alloc(&q, sizeof(struct indirect)); - test_assert(e); - - e->touched = &touched; - equeue_event_dtor(e, indirect_func); - int id = equeue_post(&q, pass_func, e); - test_assert(id); - } - - equeue_destroy(&q); - test_assert(touched == 3); -} - -void allocation_failure_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - void *p = equeue_alloc(&q, 4096); - test_assert(!p); - - for (int i = 0; i < 100; i++) { - p = equeue_alloc(&q, 0); - } - test_assert(!p); - - equeue_destroy(&q); -} - -void cancel_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - int *ids = malloc(N * sizeof(int)); - - for (int i = 0; i < N; i++) { - ids[i] = equeue_call(&q, simple_func, &touched); - } - - for (int i = N - 1; i >= 0; i--) { - test_assert(equeue_cancel(&q, ids[i])); - } - - free(ids); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - equeue_destroy(&q); -} - -void cancel_inflight_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - - int id = equeue_call(&q, simple_func, &touched); - test_assert(equeue_cancel(&q, id)); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - id = equeue_call(&q, simple_func, &touched); - test_assert(equeue_cancel(&q, id)); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - struct cancel *cancel = equeue_alloc(&q, sizeof(struct cancel)); - test_assert(cancel); - cancel->q = &q; - cancel->id = 0; - - id = equeue_post(&q, cancel_func, cancel); - test_assert(id); - - cancel->id = equeue_call(&q, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(!touched); - - equeue_destroy(&q); -} - -void cancel_unnecessarily_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int id = equeue_call(&q, pass_func, 0); - for (int i = 0; i < 5; i++) { - test_assert(equeue_cancel(&q, id) == (i == 0)); - } - - id = equeue_call(&q, pass_func, 0); - equeue_dispatch(&q, 0); - for (int i = 0; i < 5; i++) { - test_assert(!equeue_cancel(&q, id)); - } - - bool touched = false; - equeue_call(&q, simple_func, &touched); - for (int i = 0; i < 5; i++) { - test_assert(!equeue_cancel(&q, id)); - } - - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void loop_protect_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(touched); - - touched = false; - equeue_call_every(&q, 1, simple_func, &touched); - - equeue_dispatch(&q, 0); - test_assert(touched); - - equeue_destroy(&q); -} - -void break_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - bool touched = false; - equeue_call_every(&q, 0, simple_func, &touched); - - equeue_break(&q); - equeue_dispatch(&q, -1); - test_assert(touched); - - equeue_destroy(&q); -} - -void break_no_windup_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int count = 0; - equeue_call_every(&q, 0, simple_func, &count); - - equeue_break(&q); - equeue_break(&q); - equeue_dispatch(&q, -1); - test_assert(count == 1); - - count = 0; - equeue_dispatch(&q, 55); - test_assert(count > 1); - - equeue_destroy(&q); -} - -void period_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int count = 0; - equeue_call_every(&q, 10, simple_func, &count); - - equeue_dispatch(&q, 55); - test_assert(count == 5); - - equeue_destroy(&q); -} - -void nested_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched = 0; - struct nest *nest = equeue_alloc(&q, sizeof(struct nest)); - test_assert(nest); - nest->q = &q; - nest->cb = simple_func; - nest->data = &touched; - - int id = equeue_post(&q, nest_func, nest); - test_assert(id); - - equeue_dispatch(&q, 5); - test_assert(touched == 0); - - equeue_dispatch(&q, 5); - test_assert(touched == 1); - - touched = 0; - nest = equeue_alloc(&q, sizeof(struct nest)); - test_assert(nest); - nest->q = &q; - nest->cb = simple_func; - nest->data = &touched; - - id = equeue_post(&q, nest_func, nest); - test_assert(id); - - equeue_dispatch(&q, 20); - test_assert(touched == 1); - - equeue_destroy(&q); -} - -void sloth_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched = 0; - int id = equeue_call(&q, sloth_func, &touched); - test_assert(id); - - id = equeue_call_in(&q, 5, simple_func, &touched); - test_assert(id); - - id = equeue_call_in(&q, 15, simple_func, &touched); - test_assert(id); - - equeue_dispatch(&q, 20); - test_assert(touched == 3); - - equeue_destroy(&q); -} - -void *multithread_thread(void *p) -{ - equeue_t *q = (equeue_t *)p; - equeue_dispatch(q, -1); - return 0; -} - -void multithread_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int touched = 0; - equeue_call_every(&q, 1, simple_func, &touched); - - pthread_t thread; - err = pthread_create(&thread, 0, multithread_thread, &q); - test_assert(!err); - - usleep(10000); - equeue_break(&q); - err = pthread_join(thread, 0); - test_assert(!err); - - test_assert(touched); - - equeue_destroy(&q); -} - -void background_func(void *p, int ms) -{ - *(unsigned *)p = ms; -} - -void background_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - int id = equeue_call_in(&q, 20, pass_func, 0); - test_assert(id); - - unsigned ms; - equeue_background(&q, background_func, &ms); - test_assert(ms == 20); - - id = equeue_call_in(&q, 10, pass_func, 0); - test_assert(id); - test_assert(ms == 10); - - id = equeue_call(&q, pass_func, 0); - test_assert(id); - test_assert(ms == 0); - - equeue_dispatch(&q, 0); - test_assert(ms == 10); - - equeue_destroy(&q); - test_assert(ms == -1); -} - -void chain_test(void) -{ - equeue_t q1; - int err = equeue_create(&q1, 2048); - test_assert(!err); - - equeue_t q2; - err = equeue_create(&q2, 2048); - test_assert(!err); - - equeue_chain(&q2, &q1); - - int touched = 0; - - int id1 = equeue_call_in(&q1, 20, simple_func, &touched); - int id2 = equeue_call_in(&q2, 20, simple_func, &touched); - test_assert(id1 && id2); - - id1 = equeue_call(&q1, simple_func, &touched); - id2 = equeue_call(&q2, simple_func, &touched); - test_assert(id1 && id2); - - id1 = equeue_call_in(&q1, 5, simple_func, &touched); - id2 = equeue_call_in(&q2, 5, simple_func, &touched); - test_assert(id1 && id2); - - test_assert(equeue_cancel(&q1, id1)); - test_assert(equeue_cancel(&q2, id2)); - - id1 = equeue_call_in(&q1, 10, simple_func, &touched); - id2 = equeue_call_in(&q2, 10, simple_func, &touched); - test_assert(id1 && id2); - - equeue_dispatch(&q1, 30); - - test_assert(touched == 6); - - equeue_destroy(&q2); - equeue_destroy(&q1); -} - -void unchain_test(void) -{ - equeue_t q1; - int err = equeue_create(&q1, 2048); - test_assert(!err); - - equeue_t q2; - err = equeue_create(&q2, 2048); - test_assert(!err); - - equeue_chain(&q2, &q1); - - int touched = 0; - int id1 = equeue_call(&q1, simple_func, &touched); - int id2 = equeue_call(&q2, simple_func, &touched); - test_assert(id1 && id2); - - equeue_dispatch(&q1, 0); - test_assert(touched == 2); - - equeue_chain(&q2, 0); - equeue_chain(&q1, &q2); - - id1 = equeue_call(&q1, simple_func, &touched); - id2 = equeue_call(&q2, simple_func, &touched); - test_assert(id1 && id2); - - equeue_dispatch(&q2, 0); - test_assert(touched == 4); - - equeue_destroy(&q1); - equeue_destroy(&q2); -} - -// Barrage tests -void simple_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - test_assert(!err); - - for (int i = 0; i < N; i++) { - struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); - test_assert(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - test_assert(id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -void fragmenting_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, - 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); - test_assert(!err); - - for (int i = 0; i < N; i++) { - size_t size = sizeof(struct fragment) + i * sizeof(int); - struct fragment *fragment = equeue_alloc(&q, size); - test_assert(fragment); - - fragment->q = &q; - fragment->size = size; - fragment->timing.tick = equeue_tick(); - fragment->timing.delay = (i + 1) * 100; - equeue_event_delay(fragment, fragment->timing.delay); - - int id = equeue_post(&q, fragment_func, fragment); - test_assert(id); - } - - equeue_dispatch(&q, N * 100); - - equeue_destroy(&q); -} - -struct ethread { - pthread_t thread; - equeue_t *q; - int ms; -}; - -static void *ethread_dispatch(void *p) -{ - struct ethread *t = (struct ethread *)p; - equeue_dispatch(t->q, t->ms); - return 0; -} - -void multithreaded_barrage_test(int N) -{ - equeue_t q; - int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); - test_assert(!err); - - struct ethread t; - t.q = &q; - t.ms = N * 100; - err = pthread_create(&t.thread, 0, ethread_dispatch, &t); - test_assert(!err); - - for (int i = 0; i < N; i++) { - struct timing *timing = equeue_alloc(&q, sizeof(struct timing)); - test_assert(timing); - - timing->tick = equeue_tick(); - timing->delay = (i + 1) * 100; - equeue_event_delay(timing, timing->delay); - equeue_event_period(timing, timing->delay); - - int id = equeue_post(&q, timing_func, timing); - test_assert(id); - } - - err = pthread_join(t.thread, 0); - test_assert(!err); - - equeue_destroy(&q); -} - -struct count_and_queue { - int p; - equeue_t *q; -}; - -void simple_breaker(void *p) -{ - struct count_and_queue *caq = (struct count_and_queue *)p; - equeue_break(caq->q); - usleep(10000); - caq->p++; -} - -void break_request_cleared_on_timeout(void) -{ - equeue_t q; - int err = equeue_create(&q, 2048); - test_assert(!err); - - struct count_and_queue pq; - pq.p = 0; - pq.q = &q; - - int id = equeue_call_every(&q, 10, simple_breaker, &pq); - - equeue_dispatch(&q, 10); - test_assert(pq.p == 1); - - test_assert(equeue_cancel(&q, id)); - - int count = 0; - equeue_call_every(&q, 10, simple_func, &count); - - equeue_dispatch(&q, 55); - test_assert(count > 1); - - equeue_destroy(&q); -} - -void sibling_test(void) -{ - equeue_t q; - int err = equeue_create(&q, 1024); - test_assert(!err); - - int id0 = equeue_call_in(&q, 1, pass_func, 0); - int id1 = equeue_call_in(&q, 1, pass_func, 0); - int id2 = equeue_call_in(&q, 1, pass_func, 0); - - struct equeue_event *e = q.queue; - - for (; e; e = e->next) { - for (struct equeue_event *s = e->sibling; s; s = s->sibling) { - test_assert(!s->next); - } - } - test_assert(equeue_cancel(&q, id0)); - test_assert(equeue_cancel(&q, id1)); - test_assert(equeue_cancel(&q, id2)); - equeue_destroy(&q); -} - -struct user_allocated_event { - struct equeue_event e; - bool touched; -}; - -void user_allocated_event_test() -{ - equeue_t q; - int err = equeue_create(&q, EQUEUE_EVENT_SIZE); - test_assert(!err); - - bool touched = false; - struct user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - struct user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 1, -1, NULL, NULL }, 0 }; - struct user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; - - test_assert(0 != equeue_call(&q, simple_func, &touched)); - test_assert(0 == equeue_call(&q, simple_func, &touched)); - test_assert(0 == equeue_call(&q, simple_func, &touched)); - - equeue_post_user_allocated(&q, simple_func, &e1.e); - equeue_post_user_allocated(&q, simple_func, &e2.e); - equeue_post_user_allocated(&q, simple_func, &e3.e); - equeue_post_user_allocated(&q, simple_func, &e4.e); - equeue_post_user_allocated(&q, simple_func, &e5.e); - test_assert(equeue_cancel_user_allocated(&q, &e3.e)); - - equeue_dispatch(&q, 1); - - test_assert(true == touched); - test_assert(true == e1.touched); - test_assert(true == e2.touched); - test_assert(false == e3.touched); - test_assert(true == e4.touched); - test_assert(true == e5.touched); - - equeue_dispatch(&q, 10); - - test_assert(true == touched); - test_assert(true == e1.touched); - test_assert(true == e2.touched); - test_assert(false == e3.touched); - test_assert(true == e4.touched); - test_assert(true == e5.touched); - - equeue_destroy(&q); -} - -void id_cycle() -{ - equeue_t q; - int err = equeue_create(&q, 10000000); - test_assert(!err); - - for (int i = 0; i < 300; i++) { - int id = equeue_call(&q, pass_func, 0); - test_assert(id != 0); - test_assert(equeue_cancel(&q, id)); - } - - equeue_destroy(&q); -} - -int main() -{ - printf("beginning tests...\n"); - - test_run(simple_call_test); - test_run(simple_call_in_test); - test_run(simple_call_every_test); - test_run(simple_post_test); - test_run(destructor_test); - test_run(allocation_failure_test); - test_run(cancel_test, 20); - test_run(cancel_inflight_test); - test_run(cancel_unnecessarily_test); - test_run(loop_protect_test); - test_run(break_test); - test_run(break_no_windup_test); - test_run(period_test); - test_run(nested_test); - test_run(sloth_test); - test_run(background_test); - test_run(chain_test); - test_run(unchain_test); - test_run(multithread_test); - test_run(simple_barrage_test, 20); - test_run(fragmenting_barrage_test, 20); - test_run(multithreaded_barrage_test, 20); - test_run(break_request_cleared_on_timeout); - test_run(sibling_test); - test_run(user_allocated_event_test); - test_run(id_cycle); - printf("done!\n"); - return test_failure; -} diff --git a/events/tests/.mbedignore b/events/tests/.mbedignore new file mode 100644 index 0000000..3700faa --- /dev/null +++ b/events/tests/.mbedignore @@ -0,0 +1,2 @@ +UNITTESTS/* +unit/ \ No newline at end of file diff --git a/events/tests/TESTS/events/equeue/main.cpp b/events/tests/TESTS/events/equeue/main.cpp new file mode 100644 index 0000000..96c18d6 --- /dev/null +++ b/events/tests/TESTS/events/equeue/main.cpp @@ -0,0 +1,1093 @@ +/* mbed Microcontroller Library + * Copyright (c) 2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_EXTENDED_TESTS +#error [NOT_SUPPORTED] When running on CI this test is disabled due to limiting testing time. +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" + +#include "events/equeue.h" +#include "mbed.h" + +using namespace utest::v1; + +#define TEST_EQUEUE_SIZE (4*EVENTS_EVENT_SIZE) +#define TEST_THREAD_STACK_SIZE 512 +#define DISPATCH_INFINITE -1 + +// Test functions +static void pass_func(void *eh) +{ +} + +static void simple_func(void *p) +{ + uint8_t *d = reinterpret_cast(p); + if (*d < 255) { + (*d)++; + } +} + +static void sloth_func(void *p) +{ + ThisThread::sleep_for(10); + (*(reinterpret_cast(p)))++; +} + +struct indirect { + uint8_t *touched; + uint8_t buffer[7]; +}; + +static void indirect_func(void *p) +{ + struct indirect *i = reinterpret_cast(p); + (*i->touched)++; +} + +struct timing { + unsigned tick; + unsigned delay; +}; + +static void timing_func(void *p) +{ + struct timing *timing = reinterpret_cast(p); + unsigned tick = equeue_tick(); + + unsigned t1 = timing->delay; + unsigned t2 = tick - timing->tick; + TEST_ASSERT_UINT_WITHIN(10, t2, t1); + + timing->tick = tick; +} + +struct fragment { + equeue_t *q; + size_t size; + struct timing timing; +}; + +static void fragment_func(void *p) +{ + struct fragment *fragment = reinterpret_cast(p); + timing_func(&fragment->timing); + + struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); + TEST_ASSERT_NOT_NULL(nfragment); + + *nfragment = *fragment; + equeue_event_delay(nfragment, fragment->timing.delay); + + int id = equeue_post(nfragment->q, fragment_func, nfragment); + TEST_ASSERT_NOT_EQUAL(0, id); +} + +struct cancel { + equeue_t *q; + int id; +}; + +static void cancel_func(void *p) +{ + struct cancel *ccel = reinterpret_cast(p); + equeue_cancel(ccel->q, ccel->id); +} + +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +static void nest_func(void *p) +{ + struct nest *nst = reinterpret_cast(p); + equeue_call(nst->q, nst->cb, nst->data); + + ThisThread::sleep_for(10); +} + +static void multithread_thread(equeue_t *p) +{ + equeue_dispatch(p, DISPATCH_INFINITE); +} + +static void background_func(void *p, int ms) +{ + *(reinterpret_cast(p)) = ms; +} + +struct ethread { + equeue_t *q; + int ms; +}; + +static void ethread_dispatch(void *p) +{ + struct ethread *t = reinterpret_cast(p); + equeue_dispatch(t->q, t->ms); +} + +struct count_and_queue { + int p; + equeue_t *q; +}; + +static void simple_breaker(void *p) +{ + struct count_and_queue *caq = reinterpret_cast(p); + equeue_break(caq->q); + ThisThread::sleep_for(10); + caq->p++; +} + +// Simple call tests + +/** Test that equeue executes function passed by equeue_call. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call is executed properly. + */ +static void test_equeue_simple_call() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_dispatch(&q, 10); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_in. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_in is executed properly. + */ +static void test_equeue_simple_call_in() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int id = equeue_call_in(&q, 10, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 15); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_dispatch(&q, 10); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_every. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_every is executed properly. + */ +static void test_equeue_simple_call_every() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int id = equeue_call_every(&q, 10, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 15); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_post. + * + * Given queue is initialized. + * When the event is posted and after that equeue_dispatch is called. + * Then function passed by equeue_post is executed properly. + */ +static void test_equeue_simple_post() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(i); + + i->touched = &touched; + int id = equeue_post(&q, indirect_func, i); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, *i->touched); + + equeue_destroy(&q); +} + + +// Misc tests + +/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. + * + * Given queue is initialized. + * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. + * Then functions attached to equeue events destructors are executed properly. + */ +static void test_equeue_destructor() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct indirect *e; + int ids[3]; + + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(3, touched); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + ids[i] = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, ids[i]); + } + + for (int i = 0; i < 3; i++) { + equeue_cancel(&q, ids[i]); + } + TEST_ASSERT_EQUAL_UINT8(3, touched); + + equeue_dispatch(&q, 0); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + TEST_ASSERT_NOT_NULL(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_destroy(&q); + TEST_ASSERT_EQUAL_UINT8(3, touched); +} + +/** Test that equeue_alloc returns 0 when equeue can not be allocated. + * + * Given queue is initialized. + * When equeue_alloc is called and equeue can not be allocated + * Then function equeue_alloc returns NULL. + */ +static void test_equeue_allocation_failure() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); + TEST_ASSERT_NULL(p); + + for (int i = 0; i < 100; i++) { + p = equeue_alloc(&q, 0); + } + TEST_ASSERT_NULL(p); + + equeue_destroy(&q); +} + +/** Test that equeue does not execute evenets that has been canceled. + * + * Given queue is initialized. + * When events are canceled by equeue_cancel. + * Then they are not executed by calling equeue_dispatch. + */ +template +static void test_equeue_cancel() +{ + equeue_t q; + int err = equeue_create(&q, (N * EVENTS_EVENT_SIZE)); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + int ids[N]; + + for (int i = 0; i < N; i++) { + ids[i] = equeue_call(&q, simple_func, &touched); + TEST_ASSERT_NOT_EQUAL(0, ids[i]); + } + + for (int i = N - 1; i >= 0; i--) { + equeue_cancel(&q, ids[i]); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT(0, touched); + + equeue_destroy(&q); +} + +/** Test that events can be cancelled by function executed by equeue_dispatch. + * + * Given queue is initialized. + * When event is cancelled by another event while dispatching. + * Then event that was cancelled is not being executed. + */ +static void test_equeue_cancel_inflight() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + + int id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); + TEST_ASSERT_NOT_NULL(ccel); + ccel->q = &q; + ccel->id = 0; + + id = equeue_post(&q, cancel_func, ccel); + TEST_ASSERT_NOT_EQUAL(0, id); + + ccel->id = equeue_call(&q, simple_func, &touched); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + equeue_destroy(&q); +} + +/** Test that unnecessary canceling events would not affect executing other events. + * + * Given queue is initialized. + * When event is unnecessary canceled by equeue_cancel. + * Then other events are properly executed after calling equeue_dispatch. + */ +static void test_equeue_cancel_unnecessarily() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + int id = equeue_call(&q, pass_func, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + id = equeue_call(&q, pass_func, 0); + equeue_dispatch(&q, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. + * + * Given queue is initialized. + * When events have 0 ms period time. + * Then dispatching would not end up in infinite loop. + */ +static void test_equeue_loop_protect() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + + touched1 = 0; + uint8_t touched2 = 0; + equeue_call_every(&q, 1, simple_func, &touched2); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break breaks event queue out of dispatching. + * + * Given queue is initialized. + * When equeue_break is called. + * Then event queue will stop dispatching after finisching current dispatching cycle. + */ +static void test_equeue_break() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + uint8_t touched2 = 0; + equeue_call_every(&q, 5, simple_func, &touched2); + + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break function breaks equeue dispatching only once. + * + * Given queue is initialized. + * When equeue_break is called several times. + * Then equeue is stopped only once. + */ +static void test_equeue_break_no_windup() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_break(&q); + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + touched = 0; + equeue_dispatch(&q, 55); + TEST_ASSERT(touched > 1); + + equeue_destroy(&q); +} + +/** Test that function passed by equeue_call_every is being executed periodically. + * + * Given queue is initialized. + * When function is passed by equeue_call_every with specified period. + * Then event is executed (dispatch time/period) times. + */ +static void test_equeue_period() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + TEST_ASSERT_EQUAL_UINT8(5, touched); + + equeue_destroy(&q); +} + +/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. + * + * Given queue is initialized. + * When nested function is added to enqueue. + * Then it is executed in the next dispatch, or after execution of "mother" function. + */ +static void test_equeue_nested() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + TEST_ASSERT_NOT_NULL(nst); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + int id = equeue_post(&q, nest_func, nst); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 5); + TEST_ASSERT_EQUAL_UINT8(0, touched); + + equeue_dispatch(&q, 1); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + touched = 0; + nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + TEST_ASSERT_NOT_NULL(nst); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + id = equeue_post(&q, nest_func, nst); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 20); + TEST_ASSERT_EQUAL_UINT8(1, touched); + + equeue_destroy(&q); +} + +/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. + * + * Given queue is initialized. + * When sloth function is being called before other functions. + * Then if it is possible all functions start according to predefined schedule correctly. + */ +static void test_equeue_sloth() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + uint8_t touched3 = 0; + int id = equeue_call(&q, sloth_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id); + + id = equeue_call_in(&q, 5, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id); + + id = equeue_call_in(&q, 15, simple_func, &touched3); + TEST_ASSERT_NOT_EQUAL(0, id); + + equeue_dispatch(&q, 20); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + TEST_ASSERT_EQUAL_UINT8(1, touched3); + + equeue_destroy(&q); +} + +/** Test that equeue can be broken of dispatching from a different thread. + * + * Given queue is initialized. + * When equeue starts dispatching in one thread. + * Then it can be stopped from another thread via equeue_break. + */ +static void test_equeue_multithread() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 1, simple_func, &touched); + + Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); + t1.start(callback(multithread_thread, &q)); + ThisThread::sleep_for(10); + equeue_break(&q); + err = t1.join(); + TEST_ASSERT_EQUAL_INT(0, err); + + TEST_ASSERT(touched > 1); + + equeue_destroy(&q); +} + +/** Test that variable referred via equeue_background shows value in ms to the next event. + * + * Given queue is initialized. + * When variable is referred via equeue_background. + * Then it depicts the time in ms to the next event. + */ +static void test_equeue_background() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + int id = equeue_call_in(&q, 20, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + + int ms; + equeue_background(&q, background_func, &ms); + TEST_ASSERT_EQUAL_INT(20, ms); + + id = equeue_call_in(&q, 10, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + TEST_ASSERT_EQUAL_INT(10, ms); + + id = equeue_call(&q, pass_func, 0); + TEST_ASSERT_NOT_EQUAL(0, id); + TEST_ASSERT_EQUAL_INT(0, ms); + + equeue_dispatch(&q, 0); + TEST_ASSERT_EQUAL_INT(10, ms); + + equeue_destroy(&q); + TEST_ASSERT_EQUAL_INT(-1, ms); +} + +/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. + * + * Given queue is initialized. + * When target chained equeue is dispatched. + * Then events from both chained equeues are executed. + */ +static void test_equeue_chain() +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call_in(&q2, 5, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call_in(&q2, 10, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 30); + + TEST_ASSERT_EQUAL_UINT8(3, touched1); + TEST_ASSERT_EQUAL_UINT8(3, touched2); + + equeue_destroy(&q2); + equeue_destroy(&q1); +} + +/** Test that unchaining equeues makes them work on their own. + * + * Given queue is initialized. + * When equeue is unchained. + * Then it can be only dispatched by calling with reference to it. + */ +static void test_equeue_unchain() +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + int id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + int id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_chain(&q2, 0); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q1, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(0, touched2); + + equeue_dispatch(&q2, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_chain(&q1, &q2); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + TEST_ASSERT_NOT_EQUAL(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + TEST_ASSERT_NOT_EQUAL(0, id2); + + equeue_dispatch(&q2, 0); + TEST_ASSERT_EQUAL_UINT8(1, touched1); + TEST_ASSERT_EQUAL_UINT8(1, touched2); + + equeue_destroy(&q1); + equeue_destroy(&q2); +} + +// Barrage tests + +/** Test that equeue keeps good time at starting events. + * + * Given queue is initialized. + * When equeue is being dispatched. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_simple_barrage() +{ + equeue_t q; + int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + TEST_ASSERT_EQUAL_INT(0, err); + + for (int i = 0; i < N; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + TEST_ASSERT_NOT_NULL(timing); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, N * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. + * + * Given queue is initialized. + * When equeue is being dispatched and new events are added via already placed in equeue. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_fragmenting_barrage() +{ + equeue_t q; + int err = equeue_create(&q, + 2 * N * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + N * sizeof(int))); + TEST_ASSERT_EQUAL_INT(0, err); + + for (int i = 0; i < N; i++) { + size_t size = sizeof(struct fragment) + i * sizeof(int); + struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); + TEST_ASSERT_NOT_NULL(fragment); + + fragment->q = &q; + fragment->size = size; + fragment->timing.tick = equeue_tick(); + fragment->timing.delay = (i + 1) * 100; + equeue_event_delay(fragment, fragment->timing.delay); + + int id = equeue_post(&q, fragment_func, fragment); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + equeue_dispatch(&q, N * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events even if it is working on different thread. + * + * Given queue is initialized. + * When equeue is being dispatched on different thread. + * Then events happen according to the schedule with an error within a specified range. + */ +template +static void test_equeue_multithreaded_barrage() +{ + equeue_t q; + int err = equeue_create(&q, N * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + TEST_ASSERT_EQUAL_INT(0, err); + + struct ethread t; + t.q = &q; + t.ms = N * 100; + + Thread t1(osPriorityNormal, TEST_THREAD_STACK_SIZE); + + t1.start(callback(ethread_dispatch, &t)); + + for (int i = 0; i < N; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + TEST_ASSERT_NOT_NULL(timing); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + TEST_ASSERT_NOT_EQUAL(0, id); + } + + err = t1.join(); + TEST_ASSERT_EQUAL_INT(0, err); + + equeue_destroy(&q); +} + +/** Test that break request flag is cleared when equeue stops dispatching timeouts. + * + * Given queue is initialized. + * When equeue break request flag is called but equeue stops dispatching because of timeout. + * Then next equeue dispatch is not stopped. + */ +static void test_equeue_break_request_cleared_on_timeout() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL(0, err); + + struct count_and_queue pq; + pq.p = 0; + pq.q = &q; + + int id = equeue_call_in(&q, 1, simple_breaker, &pq); + + equeue_dispatch(&q, 10); + + TEST_ASSERT_EQUAL_INT(1, pq.p); + + equeue_cancel(&q, id); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + TEST_ASSERT_EQUAL_UINT8(5, touched); + + equeue_destroy(&q); +} + +/** Test that siblings events don't have next pointers. + * + * Given queue is initialized. + * When events are scheduled on the same time. + * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. + */ +static void test_equeue_sibling() +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + TEST_ASSERT_EQUAL(0, err); + + int id0 = equeue_call_in(&q, 1, pass_func, 0); + int id1 = equeue_call_in(&q, 1, pass_func, 0); + int id2 = equeue_call_in(&q, 1, pass_func, 0); + + struct equeue_event *e = q.queue; + + for (; e; e = e->next) { + for (struct equeue_event *s = e->sibling; s; s = s->sibling) { + TEST_ASSERT_NULL(s->next); + } + } + equeue_cancel(&q, id0); + equeue_cancel(&q, id1); + equeue_cancel(&q, id2); + equeue_destroy(&q); +} + +struct user_allocated_event { + struct equeue_event e; + uint8_t touched; +}; + +/** Test that equeue executes user allocated events passed by equeue_post. + * + * Given queue is initialized and its size is set to store one event at max in its internal memory. + * When post events allocated in queues internal memory (what is done by calling equeue_call). + * Then only one event can be posted due to queue memory size. + * When post user allocated events. + * Then number of posted events is not limited by queue memory size. + * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. + * Then both types of events are executed properly. + */ +static void test_equeue_user_allocated_event_post() +{ + equeue_t q; + int err = equeue_create(&q, EQUEUE_EVENT_SIZE); + TEST_ASSERT_EQUAL_INT(0, err); + + uint8_t touched = 0; + user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + + TEST_ASSERT_NOT_EQUAL(0, equeue_call_every(&q, 10, simple_func, &touched)); + TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); + TEST_ASSERT_EQUAL_INT(0, equeue_call_every(&q, 10, simple_func, &touched)); + + equeue_post_user_allocated(&q, simple_func, &e1.e); + equeue_post_user_allocated(&q, simple_func, &e2.e); + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_post_user_allocated(&q, simple_func, &e4.e); + equeue_post_user_allocated(&q, simple_func, &e5.e); + equeue_cancel_user_allocated(&q, &e3.e); + equeue_cancel_user_allocated(&q, &e3.e); + + equeue_dispatch(&q, 11); + + TEST_ASSERT_EQUAL_UINT8(1, touched); + TEST_ASSERT_EQUAL_UINT8(1, e1.touched); + TEST_ASSERT_EQUAL_UINT8(1, e2.touched); + TEST_ASSERT_EQUAL_UINT8(0, e3.touched); + TEST_ASSERT_EQUAL_UINT8(1, e4.touched); + TEST_ASSERT_EQUAL_UINT8(1, e5.touched); + + e3.e.target = 10; // set target as it's modified by equeue_call + e3.e.period = 10; // set period as it's reset by equeue_cancel + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_dispatch(&q, 101); + + TEST_ASSERT_EQUAL_UINT8(11, touched); + TEST_ASSERT_EQUAL_UINT8(1, e1.touched); + TEST_ASSERT_EQUAL_UINT8(11, e2.touched); + TEST_ASSERT_EQUAL_UINT8(10, e3.touched); + TEST_ASSERT_EQUAL_UINT8(11, e4.touched); + TEST_ASSERT_EQUAL_UINT8(1, e5.touched); + + equeue_destroy(&q); +} + +Case cases[] = { + Case("simple call test", test_equeue_simple_call), + Case("simple call in test", test_equeue_simple_call_in), + Case("simple call every test", test_equeue_simple_call_every), + Case("simple post test", test_equeue_simple_post), + + Case("destructor test", test_equeue_destructor), + Case("allocation failure test", test_equeue_allocation_failure), + Case("cancel test", test_equeue_cancel<20>), + Case("cancel inflight test", test_equeue_cancel_inflight), + Case("cancel unnecessarily test", test_equeue_cancel_unnecessarily), + Case("loop protect test", test_equeue_loop_protect), + Case("break test", test_equeue_break), + Case("break no windup test", test_equeue_break_no_windup), + Case("period test", test_equeue_period), + Case("nested test", test_equeue_nested), + Case("sloth test", test_equeue_sloth), + + Case("multithread test", test_equeue_multithread), + + Case("background test", test_equeue_background), + Case("chain test", test_equeue_chain), + Case("unchain test", test_equeue_unchain), + + Case("simple barrage test", test_equeue_simple_barrage<20>), + Case("fragmenting barrage test", test_equeue_fragmenting_barrage<10>), + Case("multithreaded barrage test", test_equeue_multithreaded_barrage<10>), + Case("break request cleared on timeout test", test_equeue_break_request_cleared_on_timeout), + Case("sibling test", test_equeue_sibling), + Case("user allocated event test", test_equeue_user_allocated_event_post) + +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(40, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // MBED_EXTENDED_TESTS diff --git a/events/tests/TESTS/events/queue/main.cpp b/events/tests/TESTS/events/queue/main.cpp new file mode 100644 index 0000000..1b03793 --- /dev/null +++ b/events/tests/TESTS/events/queue/main.cpp @@ -0,0 +1,549 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017-2019 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_events.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported +#else + +using namespace utest::v1; + +// Assume that tolerance is 5% of measured time. +#define DELTA(ms) (ms / 20) + +// TEST_EQUEUE_SIZE was reduced below 1024B to fit this test to devices with small RAM (RAM <= 16kB) +// additionally TEST_EQUEUE_SIZE was expressed in EVENTS_EVENT_SIZE to increase readability +// (for more details about EVENTS_EVENT_SIZE see EventQueue constructor) +#define TEST_EQUEUE_SIZE (18*EVENTS_EVENT_SIZE) + +// By empirical, we take 80MHz CPU/2ms delay as base tolerance for time left test. +// For higher CPU frequency, tolerance is fixed to 2ms. +// For lower CPU frequency, tolerance is inversely proportional to CPU frequency. +// E.g.: +// 100MHz: 2ms +// 80MHz: 2ms +// 64MHz: 3ms +// 48MHz: 4ms +#define ALLOWED_TIME_LEFT_TOLERANCE_MS ((SystemCoreClock >= 80000000) ? 2 : ((80000000 * 2 + SystemCoreClock - 1) / SystemCoreClock)) + +// flag for called +volatile bool touched = false; + +// static functions +void func5(int a0, int a1, int a2, int a3, int a4) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3 | a4, 0x1f); +} + +void func4(int a0, int a1, int a2, int a3) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2 | a3, 0xf); +} + +void func3(int a0, int a1, int a2) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1 | a2, 0x7); +} + +void func2(int a0, int a1) +{ + touched = true; + TEST_ASSERT_EQUAL(a0 | a1, 0x3); +} + +void func1(int a0) +{ + touched = true; + TEST_ASSERT_EQUAL(a0, 0x1); +} + +void func0() +{ + touched = true; +} + +#define SIMPLE_POSTS_TEST(i, ...) \ +void simple_posts_test##i() { \ + EventQueue queue(TEST_EQUEUE_SIZE); \ + \ + touched = false; \ + queue.call(func##i,##__VA_ARGS__); \ + queue.dispatch(0); \ + TEST_ASSERT(touched); \ + \ + touched = false; \ + queue.call_in(1, func##i,##__VA_ARGS__); \ + queue.dispatch(2); \ + TEST_ASSERT(touched); \ + \ + touched = false; \ + queue.call_every(1, func##i,##__VA_ARGS__); \ + queue.dispatch(2); \ + TEST_ASSERT(touched); \ +} + +SIMPLE_POSTS_TEST(5, 0x01, 0x02, 0x04, 0x08, 0x010) +SIMPLE_POSTS_TEST(4, 0x01, 0x02, 0x04, 0x08) +SIMPLE_POSTS_TEST(3, 0x01, 0x02, 0x04) +SIMPLE_POSTS_TEST(2, 0x01, 0x02) +SIMPLE_POSTS_TEST(1, 0x01) +SIMPLE_POSTS_TEST(0) + + +void time_func(Timer *t, int ms) +{ + TEST_ASSERT_INT_WITHIN(DELTA(ms), ms, t->read_ms()); + t->reset(); +} + +template +void call_in_test() +{ + Timer tickers[N]; + + EventQueue queue(TEST_EQUEUE_SIZE); + + for (int i = 0; i < N; i++) { + tickers[i].start(); + queue.call_in((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); + } + + queue.dispatch(N * 100); +} + +template +void call_every_test() +{ + Timer tickers[N]; + + EventQueue queue(TEST_EQUEUE_SIZE); + + for (int i = 0; i < N; i++) { + tickers[i].start(); + queue.call_every((i + 1) * 100, time_func, &tickers[i], (i + 1) * 100); + } + + queue.dispatch(N * 100); +} + +void allocate_failure_test() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + int id; + + for (int i = 0; i < 100; i++) { + id = queue.call((void (*)())0); + } + + TEST_ASSERT(!id); +} + +void no() +{ + TEST_ASSERT(false); +} + +template +void cancel_test1() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + + int ids[N]; + + for (int i = 0; i < N; i++) { + ids[i] = queue.call_in(1000, no); + } + + for (int i = N - 1; i >= 0; i--) { + queue.cancel(ids[i]); + } + + queue.dispatch(0); +} + + +// Testing the dynamic arguments to the event class +unsigned counter = 0; + +void count5(unsigned a0, unsigned a1, unsigned a2, unsigned a3, unsigned a5) +{ + counter += a0 + a1 + a2 + a3 + a5; +} + +void count4(unsigned a0, unsigned a1, unsigned a2, unsigned a3) +{ + counter += a0 + a1 + a2 + a3; +} + +void count3(unsigned a0, unsigned a1, unsigned a2) +{ + counter += a0 + a1 + a2; +} + +void count2(unsigned a0, unsigned a1) +{ + counter += a0 + a1; +} + +void count1(unsigned a0) +{ + counter += a0; +} + +void count0() +{ + counter += 0; +} + +void event_class_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + Event e5(&queue, count5); + Event e4(&queue, count5, 1); + Event e3(&queue, count5, 1, 1); + Event e2(&queue, count5, 1, 1, 1); + Event e1(&queue, count5, 1, 1, 1, 1); + Event e0(&queue, count5, 1, 1, 1, 1, 1); + + e5.post(1, 1, 1, 1, 1); + e4.post(1, 1, 1, 1); + e3.post(1, 1, 1); + e2.post(1, 1); + e1.post(1); + e0.post(); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 30); +} + +void event_class_helper_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + Event e5 = queue.event(count5, 1, 1, 1, 1, 1); + Event e4 = queue.event(count4, 1, 1, 1, 1); + Event e3 = queue.event(count3, 1, 1, 1); + Event e2 = queue.event(count2, 1, 1); + Event e1 = queue.event(count1, 1); + Event e0 = queue.event(count0); + + e5.post(); + e4.post(); + e3.post(); + e2.post(); + e1.post(); + e0.post(); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 15); +} + +void event_inference_test() +{ + counter = 0; + EventQueue queue(TEST_EQUEUE_SIZE); + + queue.event(count5, 1, 1, 1, 1, 1).post(); + queue.event(count5, 1, 1, 1, 1).post(1); + queue.event(count5, 1, 1, 1).post(1, 1); + queue.event(count5, 1, 1).post(1, 1, 1); + queue.event(count5, 1).post(1, 1, 1, 1); + queue.event(count5).post(1, 1, 1, 1, 1); + + queue.event(callback(count5), 1, 1, 1, 1, 1).post(); + queue.event(callback(count5), 1, 1, 1, 1).post(1); + queue.event(callback(count5), 1, 1, 1).post(1, 1); + queue.event(callback(count5), 1, 1).post(1, 1, 1); + queue.event(callback(count5), 1).post(1, 1, 1, 1); + queue.event(callback(count5)).post(1, 1, 1, 1, 1); + + queue.dispatch(0); + + TEST_ASSERT_EQUAL(counter, 60); +} + +int timeleft_events[2]; + +void check_time_left(EventQueue *queue, int index, int expected) +{ + const int event_id = timeleft_events[index]; + TEST_ASSERT_INT_WITHIN(ALLOWED_TIME_LEFT_TOLERANCE_MS, expected, queue->time_left(event_id)); + touched = true; +} + +void time_left(EventQueue *queue, int index) +{ + const int event_id = timeleft_events[index]; + TEST_ASSERT_EQUAL(0, queue->time_left(event_id)); +} + +void time_left_test() +{ + EventQueue queue(TEST_EQUEUE_SIZE); + + // Enque check events + TEST_ASSERT(queue.call_in(50, check_time_left, &queue, 0, 100 - 50)); + TEST_ASSERT(queue.call_in(200, check_time_left, &queue, 1, 200 - 200)); + + // Enque events to be checked + timeleft_events[0] = queue.call_in(100, time_left, &queue, 0); + timeleft_events[1] = queue.call_in(200, time_left, &queue, 1); + TEST_ASSERT(timeleft_events[0]); + TEST_ASSERT(timeleft_events[1]); + + queue.dispatch(300); + + // Ensure check was called + TEST_ASSERT(touched); + touched = false; + + int id = queue.call(func0); + TEST_ASSERT(id); + TEST_ASSERT_EQUAL(0, queue.time_left(id)); + queue.dispatch(10); + + // Test invalid event id + TEST_ASSERT_EQUAL(-1, queue.time_left(0)); +} + +void f5(int a1, int a2, int a3, int a4, int a5) +{ + touched = true; +} + +class EventTest { +public: + EventTest() : counter() {} + void f0() + { + counter++; + } + void f1(int a) + { + counter += a; + } + void f5(int a, int b, int c, int d, int e) + { + counter += a + b + c + d + e; + } + uint32_t counter; +}; + +/** Test that queue executes both dynamic and user allocated events. + * + * Given queue is initialized and its size is set to store three Event at max in its internal memory. + * When post queue allocated event. + * Then only three event can be posted due to queue memory size. + * When post user allocated evens. + * Then number of posted events is not limited by queue memory size. + * When both Event and UserAllocatedEvent are posted and queue dispatch is called. + * Then both types of events are executed properly. + * + */ +void mixed_dynamic_static_events_queue_test() +{ + { + EventQueue queue(9 * EVENTS_EVENT_SIZE); + + EventTest e1_test; + Event e1 = queue.event(&e1_test, &EventTest::f0); + e1.delay(10); + e1.period(10); + int id1 = e1.post(); + TEST_ASSERT_NOT_EQUAL(0, id1); + EventTest e2_test; + Event e2 = queue.event(&e2_test, &EventTest::f1, 3); + e2.period(10); + int id2 = e2.post(); + TEST_ASSERT_NOT_EQUAL(0, id2); + EventTest e3_test; + Event e3 = queue.event(&e3_test, &EventTest::f5, 1, 2, 3, 4, 5); + e3.period(10); + int id3 = e3.post(); + TEST_ASSERT_NOT_EQUAL(0, id3); + + + auto ue0 = make_user_allocated_event(func0); + EventTest ue1_test; + auto ue1 = make_user_allocated_event(&ue1_test, &EventTest::f0); + EventTest ue2_test; + auto ue2 = make_user_allocated_event(&ue2_test, &EventTest::f1, 3); + EventTest ue3_test; + auto ue3 = make_user_allocated_event(&ue3_test, &EventTest::f5, 1, 2, 3, 4, 5); + EventTest ue4_test; + auto ue4 = make_user_allocated_event(&ue4_test, &EventTest::f5, 1, 2, 3, 4, 5); + + touched = false; + + ue0.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue0.try_call()); + ue1.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue1.try_call()); + ue2.period(10); + ue2.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue2.try_call()); + ue3.period(10); + ue3.delay(50); + ue3.call_on(&queue); + TEST_ASSERT_EQUAL(false, ue3.try_call()); + ue4.call_on(&queue); + ue4.cancel(); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + ue4.cancel(); + e2.cancel(); + + queue.dispatch(101); + + TEST_ASSERT_EQUAL(true, touched); + TEST_ASSERT_EQUAL(1, ue1_test.counter); + TEST_ASSERT_EQUAL(33, ue2_test.counter); + TEST_ASSERT_EQUAL(90, ue3_test.counter); + TEST_ASSERT_EQUAL(0, ue4_test.counter); + TEST_ASSERT_EQUAL(10, e1_test.counter); + TEST_ASSERT_EQUAL(0, e2_test.counter); + TEST_ASSERT_EQUAL(165, e3_test.counter); + + // user allocated event have to be canceled(removed from the queue) before destruction + // cancel all periodic user events + ue2.cancel(); + ue3.cancel(); + } +} + + +static EventQueue g_queue(0); +static auto ue0 = g_queue.make_user_allocated_event(func0); +static EventTest test1; +static auto ue1 = make_user_allocated_event(&test1, &EventTest::f0); +static EventTest test2; +static auto ue2 = g_queue.make_user_allocated_event(&test2, &EventTest::f1, 3); +static EventTest test3; +static auto ue3 = make_user_allocated_event(&test3, &EventTest::f5, 1, 2, 3, 4, 5); +static EventTest test4; +static auto ue4 = g_queue.make_user_allocated_event(&test4, &EventTest::f5, 1, 2, 3, 4, 5); + + +/** Test that static queue executes user allocated events. + * + * Given static queue is initialized + * When post user allocated evens. + * Then UserAllocatedEvent are posted and dispatched without any error. + */ +void static_events_queue_test() +{ + // check that no dynamic event can be posted + Event e0 = g_queue.event(func0); + TEST_ASSERT_EQUAL(0, e0.post()); + + ue0.delay(100); + ue0.period(200); + + ue1.delay(100); + ue1.period(200); + + ue2.delay(100); + ue2.period(200); + + ue3.delay(100); + ue3.period(200); + + ue4.delay(100); + ue4.period(200); + + ue0.call(); + TEST_ASSERT_EQUAL(false, ue0.try_call()); + ue1.call_on(&g_queue); + TEST_ASSERT_EQUAL(false, ue1.try_call()); + ue2(); + TEST_ASSERT_EQUAL(false, ue2.try_call()); + ue3.call_on(&g_queue); + TEST_ASSERT_EQUAL(false, ue3.try_call()); + ue4.call(); + ue4.cancel(); + ue4.cancel(); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + g_queue.cancel(&ue4); + g_queue.cancel(&ue4); + + g_queue.dispatch(400); + + TEST_ASSERT_EQUAL(2, test1.counter); + TEST_ASSERT_EQUAL(6, test2.counter); + TEST_ASSERT_EQUAL(30, test3.counter); + TEST_ASSERT_EQUAL(0, test4.counter); + + ue4.delay(1); + TEST_ASSERT_EQUAL(true, ue4.try_call()); + g_queue.dispatch(1); + + TEST_ASSERT_EQUAL(2, test1.counter); + TEST_ASSERT_EQUAL(6, test2.counter); + TEST_ASSERT_EQUAL(30, test3.counter); + TEST_ASSERT_EQUAL(15, test4.counter); +} + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(20, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +const Case cases[] = { + Case("Testing calls with 5 args", simple_posts_test5), + Case("Testing calls with 4 args", simple_posts_test4), + Case("Testing calls with 3 args", simple_posts_test3), + Case("Testing calls with 2 args", simple_posts_test2), + Case("Testing calls with 1 args", simple_posts_test1), + Case("Testing calls with 0 args", simple_posts_test0), + + Case("Testing call_in", call_in_test<20>), + Case("Testing call_every", call_every_test<20>), + + Case("Testing allocate failure", allocate_failure_test), + + Case("Testing event cancel 1", cancel_test1<20>), + Case("Testing the event class", event_class_test), + Case("Testing the event class helpers", event_class_helper_test), + Case("Testing the event inference", event_inference_test), + + Case("Testing time_left", time_left_test), + Case("Testing mixed dynamic & static events queue", mixed_dynamic_static_events_queue_test), + Case("Testing static events queue", static_events_queue_test) + +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/events/tests/TESTS/events/timing/main.cpp b/events/tests/TESTS/events/timing/main.cpp new file mode 100644 index 0000000..fb0b759 --- /dev/null +++ b/events/tests/TESTS/events/timing/main.cpp @@ -0,0 +1,151 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "mbed_events.h" +#include "mbed.h" +#include "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include +#include + +using namespace utest::v1; + +#if !DEVICE_USTICKER +#error [NOT_SUPPORTED] test not supported +#else + +// Test delay +#ifndef TEST_EVENTS_TIMING_TIME +#define TEST_EVENTS_TIMING_TIME 20000 +#endif + +#ifndef TEST_EVENTS_TIMING_MEAN +#define TEST_EVENTS_TIMING_MEAN 25 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327950288 +#endif + +// Random number generation to skew timing values +float gauss(float mu, float sigma) +{ + float x = (float)rand() / ((float)RAND_MAX + 1); + float y = (float)rand() / ((float)RAND_MAX + 1); + float x2pi = x * 2.0 * M_PI; + float g2rad = sqrt(-2.0 * log(1.0 - y)); + float z = cos(x2pi) * g2rad; + return mu + z * sigma; +} + +float chisq(float sigma) +{ + return pow(gauss(0, sqrt(sigma)), 2); +} + + +Timer timer; +DigitalOut led(LED1); + +equeue_sema_t sema; + +// Timer timing test +void timer_timing_test() +{ + timer.reset(); + timer.start(); + int prev = timer.read_us(); + + while (prev < TEST_EVENTS_TIMING_TIME * 1000) { + int next = timer.read_us(); + if (next < prev) { + printf("backwards drift %d -> %d (%08x -> %08x)\r\n", + prev, next, prev, next); + } + TEST_ASSERT(next >= prev); + prev = next; + } +} + +// equeue tick timing test +void tick_timing_test() +{ + unsigned start = equeue_tick(); + int prev = 0; + + while (prev < TEST_EVENTS_TIMING_TIME) { + int next = equeue_tick() - start; + if (next < prev) { + printf("backwards drift %d -> %d (%08x -> %08x)\r\n", + prev, next, prev, next); + } + TEST_ASSERT(next >= prev); + prev = next; + } +} + +// equeue semaphore timing test +void semaphore_timing_test() +{ + srand(0); + timer.reset(); + timer.start(); + + int err = equeue_sema_create(&sema); + TEST_ASSERT_EQUAL(0, err); + + while (timer.read_ms() < TEST_EVENTS_TIMING_TIME) { + int delay = chisq(TEST_EVENTS_TIMING_MEAN); + + int start = timer.read_us(); + equeue_sema_wait(&sema, delay); + int taken = timer.read_us() - start; + + if (taken < (delay * 1000 - 5000) || taken > (delay * 1000 + 5000)) { + printf("delay %dms => error %dus\r\n", delay, abs(1000 * delay - taken)); + } + + TEST_ASSERT_INT_WITHIN(5000, taken, delay * 1000); + + led = !led; + } + + equeue_sema_destroy(&sema); +} + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP((number_of_cases + 1)*TEST_EVENTS_TIMING_TIME / 1000, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +const Case cases[] = { + Case("Testing accuracy of timer", timer_timing_test), + Case("Testing accuracy of equeue tick", tick_timing_test), + Case("Testing accuracy of equeue semaphore", semaphore_timing_test), +}; + +Specification specification(test_setup, cases); + +int main() +{ + return !Harness::run(specification); +} + +#endif // !DEVICE_USTICKER diff --git a/events/tests/UNITTESTS/equeue/test_equeue.cpp b/events/tests/UNITTESTS/equeue/test_equeue.cpp new file mode 100644 index 0000000..ae017cb --- /dev/null +++ b/events/tests/UNITTESTS/equeue/test_equeue.cpp @@ -0,0 +1,1114 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "gtest/gtest.h" +#include "events/equeue.h" +#include "mbed.h" +#include +#include + +#define EVENTS_EVENT_SIZE (EQUEUE_EVENT_SIZE - 2*sizeof(void*) + sizeof(mbed::Callback)) +#define TEST_EQUEUE_SIZE 2048 +#define TEST_THREAD_STACK_SIZE 512 +#define DISPATCH_INFINITE -1 +#define ITERATION_TIMES 10 + +extern unsigned int equeue_global_time; + +class TestEqueue : public testing::Test { + virtual void SetUp() + { + } + + virtual void TearDown() + { + } +}; + +// Test functions +static void pass_func(void *eh) +{ +} + +static void simple_func(void *p) +{ + uint8_t *d = reinterpret_cast(p); + if (*d < 255) { + (*d)++; + } +} + +static void sloth_func(void *p) +{ + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; + (*(reinterpret_cast(p)))++; +} + +struct indirect { + uint8_t *touched; + uint8_t buffer[7]; +}; + +static void indirect_func(void *p) +{ + struct indirect *i = reinterpret_cast(p); + (*i->touched)++; +} + +struct timing { + unsigned tick; + unsigned delay; +}; + +static void timing_func(void *p) +{ + struct timing *timing = reinterpret_cast(p); + unsigned tick = equeue_tick(); + + unsigned t1 = timing->delay; + unsigned t2 = tick - timing->tick; + EXPECT_TRUE(t2 - 10 < t1 < t2 + 10); + + timing->tick = tick; +} + +struct fragment { + equeue_t *q; + size_t size; + struct timing timing; +}; + +static void fragment_func(void *p) +{ + struct fragment *fragment = reinterpret_cast(p); + timing_func(&fragment->timing); + + struct fragment *nfragment = reinterpret_cast(equeue_alloc(fragment->q, fragment->size)); + ASSERT_TRUE(nfragment != NULL); + + *nfragment = *fragment; + equeue_event_delay(nfragment, fragment->timing.delay); + + int id = equeue_post(nfragment->q, fragment_func, nfragment); + ASSERT_NE(0, id); +} + +struct cancel { + equeue_t *q; + int id; +}; + +static void cancel_func(void *p) +{ + struct cancel *ccel = reinterpret_cast(p); + equeue_cancel(ccel->q, ccel->id); +} + +struct nest { + equeue_t *q; + void (*cb)(void *); + void *data; +}; + +static void nest_func(void *p) +{ + struct nest *nst = reinterpret_cast(p); + equeue_call(nst->q, nst->cb, nst->data); + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; +} + +static void *multithread_thread(void *p) +{ + equeue_t *q = reinterpret_cast(p); + equeue_dispatch(q, DISPATCH_INFINITE); + return 0; +} + +class ecount { + mutable pthread_mutex_t mutex; + pthread_cond_t cond; + uint8_t count; +public: + ecount() : count(0) + { + int err = pthread_mutex_init(&mutex, NULL); + EXPECT_EQ(0, err); + err = pthread_cond_init(&cond, NULL); + EXPECT_EQ(0, err); + } + + ~ecount() + { + int err = pthread_mutex_destroy(&mutex); + EXPECT_EQ(0, err); + err = pthread_cond_destroy(&cond); + EXPECT_EQ(0, err); + } + + void lock() const + { + int err = pthread_mutex_lock(&mutex); + EXPECT_EQ(0, err); + } + + void unlock() const + { + int err = pthread_mutex_unlock(&mutex); + EXPECT_EQ(0, err); + } + + void touch() + { + lock(); + if (count < 200) { + count++; + } + unlock(); + int err = pthread_cond_broadcast(&cond); + EXPECT_EQ(0, err); + } + + void wait_for_touches(uint8_t n) + { + lock(); + while (count < n) { + int err = pthread_cond_wait(&cond, &mutex); + EXPECT_EQ(0, err); + } + unlock(); + } +}; + +static void multithread_func(void *p) +{ + static_cast(p)->touch(); +} + +static void background_func(void *p, int ms) +{ + *(reinterpret_cast(p)) = ms; +} + +struct ethread { + pthread_t thread; + equeue_t *q; + int ms; +}; + +static void *ethread_dispatch(void *p) +{ + struct ethread *t = reinterpret_cast(p); + equeue_dispatch(t->q, t->ms); + return 0; +} + +struct count_and_queue { + int p; + equeue_t *q; +}; + +static void simple_breaker(void *p) +{ + struct count_and_queue *caq = reinterpret_cast(p); + equeue_break(caq->q); + // adding to equeue_global_time becouse this simulates that this function takes some time + equeue_global_time += 10; + caq->p++; +} + +// Simple call tests + +/** Test that equeue executes function passed by equeue_call. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_call) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 10); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_in. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_in is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_call_in) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int id = equeue_call_in(&q, 10, simple_func, &touched); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 15); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 10); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_call_every. + * + * Given queue is initialized. + * When the event is scheduled and after that equeue_dispatch is called. + * Then function passed by equeue_call_every is executed properly. + */ + +TEST_F(TestEqueue, test_equeue_simple_call_every) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int id = equeue_call_every(&q, 10, simple_func, &touched); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 15); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that equeue executes function passed by equeue_post. + * + * Given queue is initialized. + * When the event is posted and after that equeue_dispatch is called. + * Then function passed by equeue_post is executed properly. + */ +TEST_F(TestEqueue, test_equeue_simple_post) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct indirect *i = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(i != NULL); + + i->touched = &touched; + int id = equeue_post(&q, indirect_func, i); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, *i->touched); + + equeue_destroy(&q); +} + +// Misc tests + +/** Test that equeue executes events attached to its events destructors by equeue_event_dtor. + * + * Given queue is initialized. + * When equeue events are being destroyed by equeue_dispatch, equeue_cancel, or equeue_destroy. + * Then functions attached to equeue events destructors are executed properly. + */ +TEST_F(TestEqueue, test_equeue_destructor) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct indirect *e; + int ids[3]; + + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e != NULL); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(3, touched); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e != NULL); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + ids[i] = equeue_post(&q, pass_func, e); + ASSERT_NE(0, ids[i]); + } + + for (int i = 0; i < 3; i++) { + equeue_cancel(&q, ids[i]); + } + EXPECT_EQ(3, touched); + + equeue_dispatch(&q, 0); + + touched = 0; + for (int i = 0; i < 3; i++) { + e = reinterpret_cast(equeue_alloc(&q, sizeof(struct indirect))); + ASSERT_TRUE(e); + + e->touched = &touched; + equeue_event_dtor(e, indirect_func); + int id = equeue_post(&q, pass_func, e); + ASSERT_NE(0, id); + } + + equeue_destroy(&q); + EXPECT_EQ(3, touched); +} + +/** Test that equeue_alloc returns 0 when equeue can not be allocated. + * + * Given queue is initialized. + * When equeue_alloc is called and equeue can not be allocated + * Then function equeue_alloc returns NULL. + */ +TEST_F(TestEqueue, test_equeue_allocation_failure) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + void *p = equeue_alloc(&q, 2 * TEST_EQUEUE_SIZE); + EXPECT_TRUE(p == NULL); + + for (int i = 0; i < 100; i++) { + p = equeue_alloc(&q, 0); + } + EXPECT_TRUE(p == NULL); + + equeue_destroy(&q); +} + +/** Test that equeue does not execute evenets that has been canceled. + * + * Given queue is initialized. + * When events are canceled by equeue_cancel. + * Then they are not executed by calling equeue_dispatch. + */ +TEST_F(TestEqueue, test_equeue_cancel) +{ + equeue_t q; + int err = equeue_create(&q, (2 * ITERATION_TIMES * EVENTS_EVENT_SIZE)); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + int ids[2 * ITERATION_TIMES]; + + for (int i = 0; i < 2 * ITERATION_TIMES; i++) { + ids[i] = equeue_call(&q, simple_func, &touched); + ASSERT_NE(0, ids[i]); + } + + for (int i = 2 * ITERATION_TIMES - 1; i >= 0; i--) { + equeue_cancel(&q, ids[i]); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that events can be cancelled by function executed by equeue_dispatch. + * + * Given queue is initialized. + * When event is cancelled by another event while dispatching. + * Then event that was cancelled is not being executed. + */ +TEST_F(TestEqueue, test_equeue_cancel_inflight) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + + int id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + id = equeue_call(&q, simple_func, &touched); + equeue_cancel(&q, id); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + struct cancel *ccel = reinterpret_cast(equeue_alloc(&q, sizeof(struct cancel))); + ASSERT_TRUE(ccel != NULL); + ccel->q = &q; + ccel->id = 0; + + id = equeue_post(&q, cancel_func, ccel); + ASSERT_NE(0, id); + + ccel->id = equeue_call(&q, simple_func, &touched); + + equeue_dispatch(&q, 0); + EXPECT_EQ(0, touched); + + equeue_destroy(&q); +} + +/** Test that unnecessary canceling events would not affect executing other events. + * + * Given queue is initialized. + * When event is unnecessary canceled by equeue_cancel. + * Then other events are properly executed after calling equeue_dispatch. + */ +TEST_F(TestEqueue, test_equeue_cancel_unnecessarily) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id = equeue_call(&q, pass_func, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + id = equeue_call(&q, pass_func, 0); + equeue_dispatch(&q, 0); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + uint8_t touched = 0; + equeue_call(&q, simple_func, &touched); + for (int i = 0; i < 5; i++) { + equeue_cancel(&q, id); + } + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that dispatching events that have 0 ms period time would not end up in infinite loop. + * + * Given queue is initialized. + * When events have 0 ms period time. + * Then dispatching would not end up in infinite loop. + */ +TEST_F(TestEqueue, test_equeue_loop_protect) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched1); + + touched1 = 0; + uint8_t touched2 = 0; + equeue_call_every(&q, 1, simple_func, &touched2); + + equeue_dispatch(&q, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break breaks event queue out of dispatching. + * + * Given queue is initialized. + * When equeue_break is called. + * Then event queue will stop dispatching after finisching current dispatching cycle. + */ +TEST_F(TestEqueue, test_equeue_break) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + equeue_call_every(&q, 0, simple_func, &touched1); + + uint8_t touched2 = 0; + equeue_call_every(&q, 5, simple_func, &touched2); + + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_destroy(&q); +} + +/** Test that equeue_break function breaks equeue dispatching only once. + * + * Given queue is initialized. + * When equeue_break is called several times. + * Then equeue is stopped only once. + */ +TEST_F(TestEqueue, test_equeue_break_no_windup) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 0, simple_func, &touched); + + equeue_break(&q); + equeue_break(&q); + equeue_dispatch(&q, DISPATCH_INFINITE); + EXPECT_EQ(1, touched); + + touched = 0; + equeue_dispatch(&q, 55); + EXPECT_TRUE(touched > 1); + + equeue_destroy(&q); +} + +/** Test that function passed by equeue_call_every is being executed periodically. + * + * Given queue is initialized. + * When function is passed by equeue_call_every with specified period. + * Then event is executed (dispatch time/period) times. + */ + +TEST_F(TestEqueue, test_equeue_period) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + EXPECT_EQ(5, touched); + + equeue_destroy(&q); +} + +/** Test that function added to the equeue by other function which already is in equeue executes in the next dispatch, or after the end of execution of the "mother" event. + * + * Given queue is initialized. + * When nested function is added to enqueue. + * Then it is executed in the next dispatch, or after execution of "mother" function. + */ +TEST_F(TestEqueue, test_equeue_nested) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + struct nest *nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + ASSERT_TRUE(nst != NULL); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + int id = equeue_post(&q, nest_func, nst); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 5); + EXPECT_EQ(0, touched); + + equeue_dispatch(&q, 1); + EXPECT_EQ(1, touched); + + touched = 0; + nst = reinterpret_cast(equeue_alloc(&q, sizeof(struct nest))); + ASSERT_TRUE(nst != NULL); + nst->q = &q; + nst->cb = simple_func; + nst->data = &touched; + + id = equeue_post(&q, nest_func, nst); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 20); + EXPECT_EQ(1, touched); + + equeue_destroy(&q); +} + +/** Test that functions scheduled after slow function would execute according to the schedule if it is possible, if not they would execute right after sloth function. + * + * Given queue is initialized. + * When sloth function is being called before other functions. + * Then if it is possible all functions start according to predefined schedule correctly. + */ +TEST_F(TestEqueue, test_equeue_sloth) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + uint8_t touched3 = 0; + int id = equeue_call(&q, sloth_func, &touched1); + ASSERT_NE(0, id); + + id = equeue_call_in(&q, 5, simple_func, &touched2); + ASSERT_NE(0, id); + + id = equeue_call_in(&q, 15, simple_func, &touched3); + ASSERT_NE(0, id); + + equeue_dispatch(&q, 20); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + EXPECT_EQ(1, touched3); + + equeue_destroy(&q); +} + +/** Test that equeue can be broken of dispatching from a different thread. + * + * Given queue is initialized. + * When equeue starts dispatching in one thread. + * Then it can be stopped from another thread via equeue_break. + */ +TEST_F(TestEqueue, test_equeue_multithread) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + ecount t; + equeue_call_every(&q, 1, multithread_func, &t); + + pthread_t thread; + err = pthread_create(&thread, 0, multithread_thread, &q); + ASSERT_EQ(0, err); + + t.wait_for_touches(1); + equeue_break(&q); + err = pthread_join(thread, 0); + ASSERT_EQ(0, err); + + equeue_destroy(&q); +} + +/** Test that variable referred via equeue_background shows value in ms to the next event. + * + * Given queue is initialized. + * When variable is referred via equeue_background. + * Then it depicts the time in ms to the next event. + */ +TEST_F(TestEqueue, test_equeue_background) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id = equeue_call_in(&q, 20, pass_func, 0); + ASSERT_NE(0, id); + + int ms; + equeue_background(&q, background_func, &ms); + EXPECT_EQ(20, ms); + + id = equeue_call_in(&q, 10, pass_func, 0); + ASSERT_NE(0, id); + EXPECT_EQ(10, ms); + + id = equeue_call(&q, pass_func, 0); + ASSERT_NE(0, id); + EXPECT_EQ(0, ms); + + equeue_dispatch(&q, 0); + EXPECT_EQ(10, ms); + + equeue_destroy(&q); + EXPECT_EQ(-1, ms); +} + +/** Test that when chaining two equeues, events from both equeues execute by calling dispatch only on target. + * + * Given queue is initialized. + * When target chained equeue is dispatched. + * Then events from both chained equeues are executed. + */ +TEST_F(TestEqueue, test_equeue_chain) +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + + int id1 = equeue_call_in(&q1, 20, simple_func, &touched1); + ASSERT_NE(0, id1); + int id2 = equeue_call_in(&q2, 20, simple_func, &touched2); + ASSERT_NE(0, id2); + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + id1 = equeue_call_in(&q1, 5, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call_in(&q2, 5, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_cancel(&q1, id1); + equeue_cancel(&q2, id2); + + id1 = equeue_call_in(&q1, 10, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call_in(&q2, 10, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 30); + + EXPECT_EQ(3, touched1); + EXPECT_EQ(3, touched2); + + equeue_destroy(&q2); + equeue_destroy(&q1); +} + +/** Test that unchaining equeues makes them work on their own. + * + * Given queue is initialized. + * When equeue is unchained. + * Then it can be only dispatched by calling with reference to it. + */ +TEST_F(TestEqueue, test_equeue_unchain) +{ + equeue_t q1; + int err = equeue_create(&q1, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_t q2; + err = equeue_create(&q2, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + equeue_chain(&q2, &q1); + + uint8_t touched1 = 0; + uint8_t touched2 = 0; + int id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + int id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_chain(&q2, 0); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q1, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(0, touched2); + + equeue_dispatch(&q2, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_chain(&q1, &q2); + + touched1 = 0; + touched2 = 0; + + id1 = equeue_call(&q1, simple_func, &touched1); + ASSERT_NE(0, id1); + id2 = equeue_call(&q2, simple_func, &touched2); + ASSERT_NE(0, id2); + + equeue_dispatch(&q2, 0); + EXPECT_EQ(1, touched1); + EXPECT_EQ(1, touched2); + + equeue_destroy(&q1); + equeue_destroy(&q2); +} + +// Barrage tests + +/** Test that equeue keeps good time at starting events. + * + * Given queue is initialized. + * When equeue is being dispatched. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_simple_barrage) +{ + equeue_t q; + int err = equeue_create(&q, 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + ASSERT_EQ(0, err); + + for (int i = 0; i < 2 * ITERATION_TIMES; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + ASSERT_TRUE(timing != NULL); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, 2 * ITERATION_TIMES * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events when events are added via functions already placed in equeue. + * + * Given queue is initialized. + * When equeue is being dispatched and new events are added via already placed in equeue. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_fragmenting_barrage) +{ + equeue_t q; + int err = equeue_create(&q, + 2 * ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct fragment) + ITERATION_TIMES * sizeof(int))); + ASSERT_EQ(0, err); + + for (int i = 0; i < ITERATION_TIMES; i++) { + size_t size = sizeof(struct fragment) + i * sizeof(int); + struct fragment *fragment = reinterpret_cast(equeue_alloc(&q, size)); + ASSERT_TRUE(fragment != NULL); + + fragment->q = &q; + fragment->size = size; + fragment->timing.tick = equeue_tick(); + fragment->timing.delay = (i + 1) * 100; + equeue_event_delay(fragment, fragment->timing.delay); + + int id = equeue_post(&q, fragment_func, fragment); + ASSERT_NE(0, id); + } + + equeue_dispatch(&q, ITERATION_TIMES * 100); + + equeue_destroy(&q); +} + +/** Test that equeue keeps good time at starting events even if it is working on different thread. + * + * Given queue is initialized. + * When equeue is being dispatched on different thread. + * Then events happen according to the schedule with an error within a specified range. + */ +TEST_F(TestEqueue, test_equeue_multithreaded_barrage) +{ + equeue_t q; + int err = equeue_create(&q, ITERATION_TIMES * (EQUEUE_EVENT_SIZE + sizeof(struct timing))); + ASSERT_EQ(0, err); + + struct ethread t; + t.q = &q; + t.ms = ITERATION_TIMES * 100; + + err = pthread_create(&t.thread, 0, ethread_dispatch, &t); + ASSERT_EQ(0, err); + + for (int i = 0; i < ITERATION_TIMES; i++) { + struct timing *timing = reinterpret_cast(equeue_alloc(&q, sizeof(struct timing))); + ASSERT_TRUE(timing != NULL); + + timing->tick = equeue_tick(); + timing->delay = (i + 1) * 100; + equeue_event_delay(timing, timing->delay); + equeue_event_period(timing, timing->delay); + + int id = equeue_post(&q, timing_func, timing); + ASSERT_NE(0, id); + } + + err = pthread_join(t.thread, 0); + ASSERT_EQ(0, err); + + equeue_destroy(&q); +} + +/** Test that break request flag is cleared when equeue stops dispatching timeouts. + * + * Given queue is initialized. + * When equeue break request flag is called but equeue stops dispatching because of timeout. + * Then next equeue dispatch is not stopped. + */ +TEST_F(TestEqueue, test_equeue_break_request_cleared_on_timeout) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + struct count_and_queue pq; + pq.p = 0; + pq.q = &q; + + int id = equeue_call_in(&q, 1, simple_breaker, &pq); + + equeue_dispatch(&q, 10); + + EXPECT_EQ(1, pq.p); + + equeue_cancel(&q, id); + + uint8_t touched = 0; + equeue_call_every(&q, 10, simple_func, &touched); + + equeue_dispatch(&q, 55); + EXPECT_EQ(5, touched); + + equeue_destroy(&q); +} + +/** Test that siblings events don't have next pointers. + * + * Given queue is initialized. + * When events are scheduled on the same time. + * Then they are connected via sibling pointers and siblings have their next pointer pointing to NULL. + */ +TEST_F(TestEqueue, test_equeue_sibling) +{ + equeue_t q; + int err = equeue_create(&q, TEST_EQUEUE_SIZE); + ASSERT_EQ(0, err); + + int id0 = equeue_call_in(&q, 1, pass_func, 0); + int id1 = equeue_call_in(&q, 1, pass_func, 0); + int id2 = equeue_call_in(&q, 1, pass_func, 0); + + struct equeue_event *e = q.queue; + + for (; e; e = e->next) { + for (struct equeue_event *s = e->sibling; s; s = s->sibling) { + EXPECT_TRUE(s->next == NULL); + } + } + equeue_cancel(&q, id0); + equeue_cancel(&q, id1); + equeue_cancel(&q, id2); + equeue_destroy(&q); +} + +/** Test that equeue executes user allocated events passed by equeue_post. + * + * Given queue is initialized and its size is set to store one event at max in its internal memory. + * When post events allocated in queues internal memory (what is done by calling equeue_call). + * Then only one event can be posted due to queue memory size. + * When post user allocated events. + * Then number of posted events is not limited by queue memory size. + * When both queue allocaded and user allocated events are posted and equeue_dispatch is called. + * Then both types of events are executed properly. + */ +TEST_F(TestEqueue, test_equeue_user_allocated_event_post) +{ + struct user_allocated_event { + struct equeue_event e; + uint8_t touched; + }; + equeue_t q; + int err = equeue_create(&q, EQUEUE_EVENT_SIZE); + ASSERT_EQ(0, err); + + uint8_t touched = 0; + user_allocated_event e1 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + user_allocated_event e2 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e3 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e4 = { { 0, 0, 0, NULL, NULL, NULL, 10, 10, NULL, NULL }, 0 }; + user_allocated_event e5 = { { 0, 0, 0, NULL, NULL, NULL, 0, -1, NULL, NULL }, 0 }; + + EXPECT_NE(0, equeue_call_every(&q, 10, simple_func, &touched)); + EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); + EXPECT_EQ(0, equeue_call_every(&q, 10, simple_func, &touched)); + + equeue_post_user_allocated(&q, simple_func, &e1.e); + equeue_post_user_allocated(&q, simple_func, &e2.e); + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_post_user_allocated(&q, simple_func, &e4.e); + equeue_post_user_allocated(&q, simple_func, &e5.e); + equeue_cancel_user_allocated(&q, &e3.e); + equeue_cancel_user_allocated(&q, &e3.e); + + equeue_dispatch(&q, 11); + + EXPECT_EQ(1, touched); + EXPECT_EQ(1, e1.touched); + EXPECT_EQ(1, e2.touched); + EXPECT_EQ(0, e3.touched); + EXPECT_EQ(1, e4.touched); + EXPECT_EQ(1, e5.touched); + + e3.e.target = 10; // set target as it's modified by equeue_call + e3.e.period = 10; // set period as it's reset by equeue_cancel + equeue_post_user_allocated(&q, simple_func, &e3.e); + equeue_dispatch(&q, 101); + + EXPECT_EQ(11, touched); + EXPECT_EQ(1 , e1.touched); + EXPECT_EQ(11, e2.touched); + EXPECT_EQ(10 , e3.touched); + EXPECT_EQ(11, e4.touched); + EXPECT_EQ(1 , e5.touched); + + equeue_destroy(&q); +} diff --git a/events/tests/UNITTESTS/equeue/unittest.cmake b/events/tests/UNITTESTS/equeue/unittest.cmake new file mode 100644 index 0000000..fe0f05a --- /dev/null +++ b/events/tests/UNITTESTS/equeue/unittest.cmake @@ -0,0 +1,27 @@ + +#################### +# UNIT TESTS +#################### + +list(REMOVE_ITEM unittest-includes ${PROJECT_SOURCE_DIR}/../events/tests/UNITTESTS/target_h ${PROJECT_SOURCE_DIR}/../events/test/UNITTESTS/target_h/equeue) + +set(unittest-includes ${unittest-includes} + ../events/source + ../events/include/events + ../events/include/events/internal +) + +set(unittest-sources + ../events/source/equeue.c +) + +set(unittest-test-sources + ../events/tests/UNITTESTS/equeue/test_equeue.cpp + ../events/tests/UNITTESTS/stubs/EqueuePosix_stub.c +) + +set(unittest-test-flags + -pthread + -DEQUEUE_PLATFORM_POSIX +) + diff --git a/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c b/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c new file mode 100644 index 0000000..e3d975e --- /dev/null +++ b/events/tests/UNITTESTS/stubs/EqueuePosix_stub.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2019, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "internal/equeue_platform.h" + +#if defined(EQUEUE_PLATFORM_POSIX) + +#include + +/* + * Using global variable as a simulation of passing time. Use of sleep functions may cause problems with thread preempting, which can lead to bad timings. + * This problem does not occur on targets. + * This variable is only increased in equeue_sema_wait function and in functions that simulate pass of time. + */ +unsigned int equeue_global_time = 0; + +// Tick operations +void equeue_tick_init(void) {} + +unsigned equeue_tick(void) +{ + return equeue_global_time; +} + + +// Mutex operations +int equeue_mutex_create(equeue_mutex_t *m) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + return pthread_mutex_init(m, &attr); +} + +void equeue_mutex_destroy(equeue_mutex_t *m) +{ + pthread_mutex_destroy(m); +} + +void equeue_mutex_lock(equeue_mutex_t *m) +{ + pthread_mutex_lock(m); +} + +void equeue_mutex_unlock(equeue_mutex_t *m) +{ + pthread_mutex_unlock(m); +} + + +// Semaphore operations +int equeue_sema_create(equeue_sema_t *s) +{ + int err = pthread_mutex_init(&s->mutex, 0); + if (err) { + return err; + } + + err = pthread_cond_init(&s->cond, 0); + if (err) { + return err; + } + + s->signal = false; + return 0; +} + +void equeue_sema_destroy(equeue_sema_t *s) +{ + pthread_cond_destroy(&s->cond); + pthread_mutex_destroy(&s->mutex); +} + +void equeue_sema_signal(equeue_sema_t *s) +{ + pthread_mutex_lock(&s->mutex); + s->signal = true; + pthread_cond_signal(&s->cond); + pthread_mutex_unlock(&s->mutex); +} + +bool equeue_sema_wait(equeue_sema_t *s, int ms) +{ + pthread_mutex_lock(&s->mutex); + if (!s->signal) { + if (ms < 0) { + pthread_cond_wait(&s->cond, &s->mutex); + } else { + for (int i = 0; i < ms; i++) { + equeue_global_time++; + } + // If ms == 0 increase time so functions don't get stuck in infinite loops. + if (ms == 0) { + equeue_global_time++; + } + } + } + + bool signal = s->signal; + s->signal = false; + pthread_mutex_unlock(&s->mutex); + + return signal; +} + +#endif diff --git a/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h b/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h new file mode 100644 index 0000000..7d9f968 --- /dev/null +++ b/events/tests/UNITTESTS/target_h/equeue/equeue_platform.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef EQUEUE_PLATFORM_H +#define EQUEUE_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef unsigned equeue_mutex_t; + +typedef struct equeue_sema { +} equeue_sema_t; + +#ifdef __cplusplus +} +#endif + +#endif //EQUEUE_PLATFORM_H diff --git a/events/tests/UNITTESTS/target_h/mbed_events.h b/events/tests/UNITTESTS/target_h/mbed_events.h new file mode 100644 index 0000000..c104e73 --- /dev/null +++ b/events/tests/UNITTESTS/target_h/mbed_events.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017, Arm Limited and affiliates. + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBED_EVENTS_H +#define MBED_EVENTS_H + +#include "events/EventQueue.h" + +#include "events/mbed_shared_queues.h" + +#endif diff --git a/events/tests/unit/Makefile b/events/tests/unit/Makefile new file mode 100644 index 0000000..777d315 --- /dev/null +++ b/events/tests/unit/Makefile @@ -0,0 +1,55 @@ +TARGET = libequeue.a + +CC = gcc +AR = ar +SIZE = size + +SRC += $(wildcard ../../source/*.c) +OBJ := $(SRC:.c=.o) +DEP := $(SRC:.c=.d) +ASM := $(SRC:.c=.s) + +ifdef DEBUG +CFLAGS += -O0 -g3 +else +CFLAGS += -O2 +endif +ifdef WORD +CFLAGS += -m$(WORD) +endif +CFLAGS += -I. -I../../include +CFLAGS += -std=c99 +CFLAGS += -Wall +CFLAGS += -D_XOPEN_SOURCE=600 + +LFLAGS += -pthread + + +all: $(TARGET) + +prof: prof.o $(OBJ) + $(CC) $(CFLAGS) $^ $(LFLAGS) -o prof + ./prof + +asm: $(ASM) + +size: $(OBJ) + $(SIZE) -t $^ + +-include $(DEP) + +%.a: $(OBJ) + $(AR) rcs $@ $^ + +%.o: %.c + $(CC) -c -MMD $(CFLAGS) $< -o $@ + +%.s: %.c + $(CC) -S $(CFLAGS) $< -o $@ + +clean: + rm -f $(TARGET) + rm -f prof prof.o prof.d + rm -f $(OBJ) + rm -f $(DEP) + rm -f $(ASM) diff --git a/events/tests/unit/prof.c b/events/tests/unit/prof.c new file mode 100644 index 0000000..f7b8f90 --- /dev/null +++ b/events/tests/unit/prof.c @@ -0,0 +1,424 @@ +/* + * Profiling framework for the events library + * + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "events/equeue.h" +#include +#include +#include +#include +#include +#include +#include + + +// Performance measurement utils +#define PROF_RUNS 5 +#define PROF_INTERVAL 100000000 + +#define prof_volatile(t) __attribute__((unused)) volatile t + +typedef uint64_t prof_cycle_t; + +static volatile prof_cycle_t prof_start_cycle; +static volatile prof_cycle_t prof_stop_cycle; +static prof_cycle_t prof_accum_cycle; +static prof_cycle_t prof_baseline_cycle; +static prof_cycle_t prof_iterations; +static const char *prof_units; + +#define prof_cycle() ({ \ + uint32_t a, b; \ + __asm__ volatile ("rdtsc" : "=a" (a), "=d" (b)); \ + ((uint64_t)b << 32) | (uint64_t)a; \ +}) + +#define prof_loop() \ + for (prof_iterations = 0; \ + prof_accum_cycle < PROF_INTERVAL; \ + prof_iterations++) + +#define prof_start() ({ \ + prof_start_cycle = prof_cycle(); \ +}) + +#define prof_stop() ({ \ + prof_stop_cycle = prof_cycle(); \ + prof_accum_cycle += prof_stop_cycle - prof_start_cycle; \ +}) + +#define prof_result(value, units) ({ \ + prof_accum_cycle = value+prof_baseline_cycle; \ + prof_iterations = 1; \ + prof_units = units; \ +}) + +#define prof_measure(func, ...) ({ \ + printf("%s: ...", #func); \ + fflush(stdout); \ + \ + prof_units = "cycles"; \ + prof_cycle_t runs[PROF_RUNS]; \ + for (int i = 0; i < PROF_RUNS; i++) { \ + prof_accum_cycle = 0; \ + prof_iterations = 0; \ + func(__VA_ARGS__); \ + runs[i] = prof_accum_cycle / prof_iterations; \ + } \ + \ + prof_cycle_t res = runs[0]; \ + for (int i = 0; i < PROF_RUNS; i++) { \ + if (runs[i] < res) { \ + res = runs[i]; \ + } \ + } \ + res -= prof_baseline_cycle; \ + printf("\r%s: %"PRIu64" %s", #func, res, prof_units); \ + \ + if (!isatty(0)) { \ + prof_cycle_t prev; \ + while (scanf("%*[^0-9]%"PRIu64, &prev) == 0); \ + int64_t perc = 100*((int64_t)prev - (int64_t)res) / (int64_t)prev; \ + \ + if (perc > 10) { \ + printf(" (\e[32m%+"PRId64"%%\e[0m)", perc); \ + } else if (perc < -10) { \ + printf(" (\e[31m%+"PRId64"%%\e[0m)", perc); \ + } else { \ + printf(" (%+"PRId64"%%)", perc); \ + } \ + } \ + \ + printf("\n"); \ + res; \ +}) + +#define prof_baseline(func, ...) ({ \ + prof_baseline_cycle = 0; \ + prof_baseline_cycle = prof_measure(func, __VA_ARGS__); \ +}) + + +// Various test functions +void no_func(void *eh) +{ +} + + +// Actual performance tests +void baseline_prof(void) +{ + prof_loop() { + prof_start(); + __asm__ volatile(""); + prof_stop(); + } +} + +void equeue_tick_prof(void) +{ + prof_volatile(unsigned) res; + prof_loop() { + prof_start(); + res = equeue_tick(); + prof_stop(); + } +} + +void equeue_alloc_prof(void) +{ + struct equeue q; + equeue_create(&q, 32 * EQUEUE_EVENT_SIZE); + + prof_loop() { + prof_start(); + void *e = equeue_alloc(&q, 8 * sizeof(int)); + prof_stop(); + + equeue_dealloc(&q, e); + } + + equeue_destroy(&q); +} + +void equeue_alloc_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + void *es[count]; + + for (int i = 0; i < count; i++) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = 0; i < count; i++) { + equeue_dealloc(&q, es[i]); + } + + prof_loop() { + prof_start(); + void *e = equeue_alloc(&q, 8 * sizeof(int)); + prof_stop(); + + equeue_dealloc(&q, e); + } + + equeue_destroy(&q); +} + +void equeue_post_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + void *e = equeue_alloc(&q, 0); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + void *e = equeue_alloc(&q, 0); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_future_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + void *e = equeue_alloc(&q, 0); + equeue_event_delay(e, 1000); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_post_future_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + void *e = equeue_alloc(&q, 0); + equeue_event_delay(e, 1000); + + prof_start(); + int id = equeue_post(&q, no_func, e); + prof_stop(); + + equeue_cancel(&q, id); + } + + equeue_destroy(&q); +} + +void equeue_dispatch_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + equeue_call(&q, no_func, 0); + + prof_start(); + equeue_dispatch(&q, 0); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_dispatch_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + prof_loop() { + for (int i = 0; i < count; i++) { + equeue_call(&q, no_func, 0); + } + + prof_start(); + equeue_dispatch(&q, 0); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_cancel_prof(void) +{ + struct equeue q; + equeue_create(&q, EQUEUE_EVENT_SIZE); + + prof_loop() { + int id = equeue_call(&q, no_func, 0); + + prof_start(); + equeue_cancel(&q, id); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_cancel_many_prof(int count) +{ + struct equeue q; + equeue_create(&q, count * EQUEUE_EVENT_SIZE); + + for (int i = 0; i < count - 1; i++) { + equeue_call(&q, no_func, 0); + } + + prof_loop() { + int id = equeue_call(&q, no_func, 0); + + prof_start(); + equeue_cancel(&q, id); + prof_stop(); + } + + equeue_destroy(&q); +} + +void equeue_alloc_size_prof(void) +{ + size_t size = 32 * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + equeue_alloc(&q, 0); + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + +void equeue_alloc_many_size_prof(int count) +{ + size_t size = count * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + + for (int i = 0; i < count; i++) { + equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + +void equeue_alloc_fragmented_size_prof(int count) +{ + size_t size = count * EQUEUE_EVENT_SIZE; + + struct equeue q; + equeue_create(&q, size); + + void *es[count]; + + for (int i = 0; i < count; i++) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = 0; i < count; i++) { + equeue_dealloc(&q, es[i]); + } + + for (int i = count - 1; i >= 0; i--) { + es[i] = equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + for (int i = count - 1; i >= 0; i--) { + equeue_dealloc(&q, es[i]); + } + + for (int i = 0; i < count; i++) { + equeue_alloc(&q, (i % 4) * sizeof(int)); + } + + prof_result(size - q.slab.size, "bytes"); + + equeue_destroy(&q); +} + + +// Entry point +int main() +{ + printf("beginning profiling...\n"); + + prof_baseline(baseline_prof); + + prof_measure(equeue_tick_prof); + prof_measure(equeue_alloc_prof); + prof_measure(equeue_post_prof); + prof_measure(equeue_post_future_prof); + prof_measure(equeue_dispatch_prof); + prof_measure(equeue_cancel_prof); + + prof_measure(equeue_alloc_many_prof, 1000); + prof_measure(equeue_post_many_prof, 1000); + prof_measure(equeue_post_future_many_prof, 1000); + prof_measure(equeue_dispatch_many_prof, 100); + prof_measure(equeue_cancel_many_prof, 100); + + prof_measure(equeue_alloc_size_prof); + prof_measure(equeue_alloc_many_size_prof, 1000); + prof_measure(equeue_alloc_fragmented_size_prof, 1000); + + printf("done!\n"); +} diff --git a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp index 783405d..1a86b7d 100644 --- a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp +++ b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/arm_hal_fhss_timer.cpp @@ -24,9 +24,9 @@ #include "platform/SingletonPtr.h" #include "platform/arm_hal_interrupt.h" #include "platform/mbed_power_mgmt.h" -#include "equeue.h" +#include "events/equeue.h" #include "events/EventQueue.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "Timeout.h" #define TRACE_GROUP "fhdr" diff --git a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp index 7921e5b..dc274db 100644 --- a/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp +++ b/features/nanostack/nanostack-hal-mbed-cmsis-rtos/ns_event_loop_mbed.cpp @@ -27,7 +27,7 @@ #include "eventOS_scheduler.h" #include "mbed_error.h" -#include "mbed_shared_queues.h" +#include "events/mbed_shared_queues.h" #include "events/Event.h" #include "ns_event_loop_mutex.h" #include "ns_event_loop.h" diff --git a/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp b/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp deleted file mode 100644 index 9ddc9cd..0000000 --- a/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.cpp +++ /dev/null @@ -1,1124 +0,0 @@ -/* -* Copyright (c) 2016-2018 ARM Limited. All rights reserved. -* SPDX-License-Identifier: Apache-2.0 -* Licensed under the Apache License, Version 2.0 (the License); you may -* not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an AS IS BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -#include "mbed_power_mgmt.h" -#include "common_functions.h" -#include "platform/arm_hal_interrupt.h" -#include "platform/arm_hal_phy.h" -#include "NanostackRfPhyKw41z.h" - -#include "fsl_xcvr.h" - -#define RF_THREAD_STACK_SIZE 1024 - -static void rf_thread_loop(); -Thread rf_thread(osPriorityRealtime, RF_THREAD_STACK_SIZE); - -#define PHY_MTU_SIZE 127 -#define CRC_LENGTH 0 -#define PHY_HEADER_LENGTH 0 - -#define BM_ZLL_IRQSTS_TMRxMSK (ZLL_IRQSTS_TMR1MSK_MASK | \ - ZLL_IRQSTS_TMR2MSK_MASK | \ - ZLL_IRQSTS_TMR3MSK_MASK | \ - ZLL_IRQSTS_TMR4MSK_MASK) - -#define RF_CCA_THRESHOLD 75 /* -75 dBm */ - -#define gPhyDefaultTxPowerLevel_d (22) -#define gPhyMaxTxPowerLevel_d (32) -#define gCcaED_c (0) -#define gCcaCCA_MODE1_c (1) - -#define gPhyTimeMask_c (0x00FFFFFF) - -#define KW41Z_SHR_PHY_TIME 12 -#define KW41Z_PER_BYTE_TIME 2 -#define KW41Z_ACK_WAIT_TIME 54 - -static int8_t rf_radio_driver_id = -1; -static uint8_t need_ack = 0; - -/* PHY states */ -typedef enum xcvrState_tag { - gIdle_c, - gRX_c, - gTX_c, - gCCA_c, - gTR_c, - gCCCA_c, -} xcvrState_t; - -static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel); -static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol); -static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr); -static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr); -static void rf_mac_hw_init(void); -static void rf_mac_ed_state_enable(void); -static void rf_mac_set_pending(uint8_t status); -static void rf_mac_set_shortAddress(uint8_t *valueAddress); -static void rf_mac_set_panId(uint8_t *valueAddress); -static void rf_mac_set_mac64(const uint8_t *valueAddress); -static uint8_t rf_convert_energy_level(uint8_t energyLevel); -static void rf_abort(void); -static void rf_ack_wait_timer_start(uint16_t time); -static void rf_get_timestamp(uint32_t *pRetClk); -static uint32_t rf_get_timeout(void); -static void rf_set_timeout(uint32_t timeout); -static void rf_promiscuous(uint8_t state); -static void handle_IRQ_events(void); -static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan); -static void rf_receive(void); -static void rf_handle_rx_end(void); - -static uint8_t MAC64_addr_default[8] = {1, 2, 3, 4, 5, 6, 7, 8}; -static uint8_t MAC64_addr[8]; - -static xcvrState_t mPhySeqState; -static uint8_t rf_mac_handle; -static volatile uint8_t rf_ed_value = 0; -static volatile bool rf_ack_pending_state = false; -static volatile bool sleep_blocked = false; - -static NanostackRfPhyKw41z *rf = NULL; - -#define MAC_PACKET_SIZE 127 //MAX MAC payload is 127 bytes -static uint8_t PHYPAYLOAD[MAC_PACKET_SIZE]; - -const phy_rf_channel_configuration_s phy_2_4ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK}; - -const phy_device_channel_page_s phy_channel_pages[] = { - {CHANNEL_PAGE_0, &phy_2_4ghz}, - {CHANNEL_PAGE_0, NULL} -}; - -static phy_device_driver_s device_driver = { - PHY_LINK_15_4_2_4GHZ_TYPE, - PHY_LAYER_PAYLOAD_DATA_FLOW, - MAC64_addr, - PHY_MTU_SIZE, - (char *)"NXP kw41z", - CRC_LENGTH, - PHY_HEADER_LENGTH, - &rf_interface_state_control, - &rf_start_cca, - &rf_address_write, - &rf_extension, - phy_channel_pages, - NULL, - NULL, - NULL, - NULL -}; - -static void rf_thread_loop() -{ - for (;;) { - ThisThread::flags_wait_all(1); - - platform_enter_critical(); - - handle_IRQ_events(); - - platform_exit_critical(); - NVIC_ClearPendingIRQ(Radio_1_IRQn); - NVIC_EnableIRQ(Radio_1_IRQn); - } -} - -static int8_t rf_device_register(void) -{ - if (rf_radio_driver_id < 0) { - rf_mac_hw_init(); - /** - * Read factory stored Mac address to RAM - */ - common_write_32_bit(ZLL->MACLONGADDRS0_MSB, MAC64_addr); - common_write_32_bit(ZLL->MACLONGADDRS0_LSB, MAC64_addr + 4); - - rf_radio_driver_id = arm_net_phy_register(&device_driver); - } - - return rf_radio_driver_id; -} - -static void rf_device_unregister(void) -{ - arm_net_phy_unregister(rf_radio_driver_id); - - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } -} - -/* - * \brief Function enables/disables Rx promiscuous mode. - * - * \param state of XCVR promiscuous mode - * - * \return none - */ -static void rf_promiscuous(uint8_t state) -{ - if (state) { - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_PROMISCUOUS_MASK; - /* FRM_VER[11:8] = b1111. Any FrameVersion accepted */ - ZLL->RX_FRAME_FILTER |= (ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | - ZLL_RX_FRAME_FILTER_ACK_FT_MASK | - ZLL_RX_FRAME_FILTER_NS_FT_MASK); - } else { - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_PROMISCUOUS_MASK; - /* FRM_VER[11:8] = b0011. Accept FrameVersion 0 and 1 packets, reject all others */ - /* Beacon, Data and MAC command frame types accepted */ - ZLL->RX_FRAME_FILTER &= ~(ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK | - ZLL_RX_FRAME_FILTER_ACK_FT_MASK | - ZLL_RX_FRAME_FILTER_NS_FT_MASK | - ZLL_RX_FRAME_FILTER_ACTIVE_PROMISCUOUS_MASK); - ZLL->RX_FRAME_FILTER |= ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3); - } -} - -static void rf_mac_set_pending(uint8_t status) -{ - uint32_t reg = ZLL->SAM_TABLE; - - /* Disable the Source Address Matching feature and set FP manually */ - reg |= ZLL_SAM_TABLE_ACK_FRM_PND_CTRL_MASK; - if (status) { - reg |= ZLL_SAM_TABLE_ACK_FRM_PND_MASK; - rf_ack_pending_state = true; - } else { - reg &= ~ZLL_SAM_TABLE_ACK_FRM_PND_MASK; - rf_ack_pending_state = false; - } - ZLL->SAM_TABLE = reg; -} - -static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) -{ - platform_enter_critical(); - - switch (new_state) { - /*Reset PHY driver and set to idle*/ - case PHY_INTERFACE_RESET: - rf_abort(); - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } - break; - /*Disable PHY Interface driver*/ - case PHY_INTERFACE_DOWN: - rf_abort(); - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } - break; - /*Enable PHY Interface driver*/ - case PHY_INTERFACE_UP: - if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { - return 1; - } - if (!sleep_blocked) { - /* Disable enter to deep sleep when transfer active */ - sleep_manager_lock_deep_sleep(); - sleep_blocked = true; - } - rf_receive(); - break; - /*Enable wireless interface ED scan mode*/ - case PHY_INTERFACE_RX_ENERGY_STATE: - if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { - return 1; - } - if (!sleep_blocked) { - /* Disable enter to deep sleep when transfer active */ - sleep_manager_lock_deep_sleep(); - sleep_blocked = true; - } - rf_abort(); - rf_mac_ed_state_enable(); - break; - case PHY_INTERFACE_SNIFFER_STATE: /**< Enable Sniffer state */ - rf_promiscuous(1); - if (PhyPlmeSetCurrentChannelRequest(rf_channel, 0)) { - return 1; - } - if (!sleep_blocked) { - /* Disable enter to deep sleep when transfer active */ - sleep_manager_lock_deep_sleep(); - sleep_blocked = true; - } - rf_receive(); - break; - } - - platform_exit_critical(); - - return 0; -} - -/* - * \brief Function forces the XCVR to Idle state. - * - * \param none - * - * \return none - */ -static void rf_abort(void) -{ - /* Mask XCVR irq */ - NVIC_DisableIRQ(Radio_1_IRQn); - - mPhySeqState = gIdle_c; - - /* Mask SEQ interrupt */ - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_SEQMSK_MASK; - - /* Disable timer trigger (for scheduled XCVSEQ) */ - if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_TMRTRIGEN_MASK) { - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMRTRIGEN_MASK; - /* give the FSM enough time to start if it was triggered */ - while ((XCVR_MISC->XCVR_CTRL & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) == 0) {} - } - - /* If XCVR is not idle, abort current SEQ */ - if (ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK) { - /* Abort current SEQ */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; - - /* Wait for Sequence Idle (if not already) */ - while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} - } - - /* Stop timers */ - ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR1CMP_EN_MASK | - ZLL_PHY_CTRL_TMR2CMP_EN_MASK | - ZLL_PHY_CTRL_TMR3CMP_EN_MASK | - ZLL_PHY_CTRL_TC3TMOUT_MASK); - - /* clear all IRQ bits to avoid unexpected interrupts */ - ZLL->IRQSTS = ZLL->IRQSTS; - - /* Unmask XCVR irq */ - NVIC_EnableIRQ(Radio_1_IRQn); -} - -static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol) -{ - uint32_t reg, tx_len; - uint32_t irqSts; - volatile uint8_t *pPB; - uint8_t i; - uint32_t tx_warmup_time; - - platform_enter_critical(); - - if (mPhySeqState == gRX_c) { - rf_abort(); - } - - /* Check if transmitter is busy*/ - if (mPhySeqState != gIdle_c) { - platform_exit_critical(); - /*Return busy*/ - return -1; - } - - /*Store TX handle*/ - rf_mac_handle = tx_handle; - - /* Check if transmitted data needs to be acked */ - need_ack = (*data_ptr & 0x20) == 0x20; - - /* Load data into Packet Buffer */ - pPB = (uint8_t *)ZLL->PKT_BUFFER_TX; - - tx_len = data_length + 2; - *pPB++ = tx_len; /* including 2 bytes of FCS */ - - for (i = 0; i < data_length; i++) { - *pPB++ = *data_ptr++; - } - - reg = ZLL->PHY_CTRL; - - /* Perform CCA before TX */ - reg |= ZLL_PHY_CTRL_CCABFRTX_MASK; - - /* Set CCA mode 1 */ - reg &= ~(ZLL_PHY_CTRL_CCATYPE_MASK); - reg |= ZLL_PHY_CTRL_CCATYPE(gCcaCCA_MODE1_c); - ZLL->PHY_CTRL = reg; - - /* Perform TxRxAck sequence if required by phyTxMode */ - if (need_ack) { - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_RXACKRQD_MASK; - mPhySeqState = gTR_c; - } else { - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RXACKRQD_MASK; - mPhySeqState = gTX_c; - } - - /* Ensure that no spurious interrupts are raised */ - irqSts = ZLL->IRQSTS; - irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); - irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; - ZLL->IRQSTS = irqSts; - - tx_warmup_time = (XCVR_TSM->END_OF_SEQ & XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_MASK) >> - XCVR_TSM_END_OF_SEQ_END_OF_TX_WU_SHIFT; - - /* Compute warmup times (scaled to 16us) */ - if (tx_warmup_time & 0x0F) { - tx_warmup_time = 1 + (tx_warmup_time >> 4); - } else { - tx_warmup_time = tx_warmup_time >> 4; - } - - if (need_ack) { - rf_ack_wait_timer_start(tx_warmup_time + KW41Z_SHR_PHY_TIME + - tx_len * KW41Z_PER_BYTE_TIME + 10 + KW41Z_ACK_WAIT_TIME); - } - - /* Unmask SEQ interrupt */ - ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_SEQMSK_MASK); - - /* Start the TX / TRX */ - reg = ZLL->PHY_CTRL; - reg &= ~(ZLL_PHY_CTRL_XCVSEQ_MASK); - reg |= mPhySeqState; - ZLL->PHY_CTRL = reg; - - platform_exit_critical(); - - /*Return success*/ - return 0; -} - -static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) -{ - int8_t ret_val = 0; - - platform_enter_critical(); - - switch (address_type) { - case PHY_MAC_64BIT: - rf_mac_set_mac64(address_ptr); - break; - /*Set 16-bit address*/ - case PHY_MAC_16BIT: - rf_mac_set_shortAddress(address_ptr); - break; - /*Set PAN Id*/ - case PHY_MAC_PANID: - rf_mac_set_panId(address_ptr); - break; - default: - ret_val = -1; - } - - platform_exit_critical(); - - return ret_val; -} - -static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) -{ - int ret_value = 0; - - platform_enter_critical(); - - switch (extension_type) { - case PHY_EXTENSION_CTRL_PENDING_BIT: /**< Control MAC pending bit for indirect data. */ - rf_mac_set_pending(*data_ptr); - break; - /* Return frame Auto Ack frame pending status */ - case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: { - *data_ptr = rf_ack_pending_state; - break; - } - case PHY_EXTENSION_SET_CHANNEL: /**< Net library channel set. */ - break; - case PHY_EXTENSION_READ_CHANNEL_ENERGY: /**< RF interface ED scan energy read. */ - *data_ptr = rf_ed_value; - break; - case PHY_EXTENSION_READ_LINK_STATUS: /**< Net library could read link status. */ - case PHY_EXTENSION_CONVERT_SIGNAL_INFO: /**< Convert signal info. */ - default: - ret_value = -1; - } - - platform_exit_critical(); - - return ret_value; -} - -/* - * \brief Function converts the energy level from dBm to a 0-255 value. - * - * \param energyLevel in dBm - * - * \return energy level (0-255) - */ -static uint8_t rf_convert_energy_level(uint8_t energyLevel) -{ - int32_t temp = (int8_t)energyLevel; - - if (temp <= -82) { - temp = 0x00; - } else if (temp >= -3) { - temp = 0xFF; - } else { - /* Convert energy level from dbm into a 0x00-0xFF value */ - temp = (255 * temp + 20910) / 79; - } - - return (uint8_t)temp; -} - -/** - * SET MAC 16 address to Register - */ -static void rf_mac_set_shortAddress(uint8_t *valueAddress) -{ - ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACSHORTADDRS0_MASK; - ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACSHORTADDRS0(common_read_16_bit(valueAddress)); -} - -/** - * SET PAN-ID to Register - */ -static void rf_mac_set_panId(uint8_t *valueAddress) -{ - ZLL->MACSHORTADDRS0 &= ~ZLL_MACSHORTADDRS0_MACPANID0_MASK; - ZLL->MACSHORTADDRS0 |= ZLL_MACSHORTADDRS0_MACPANID0(common_read_16_bit(valueAddress)); -} - -/** - * SET MAC64 address to register - */ -static void rf_mac_set_mac64(const uint8_t *valueAddress) -{ - ZLL->MACLONGADDRS0_MSB = common_read_32_bit(valueAddress); - valueAddress += 4; - ZLL->MACLONGADDRS0_LSB = common_read_32_bit(valueAddress); -} - -static void PhyPlmeSetPwrLevelRequest(uint8_t pwrStep) -{ - /* Do not exceed the Tx power limit for the current channel */ - if (pwrStep > gPhyMaxTxPowerLevel_d) { - pwrStep = gPhyMaxTxPowerLevel_d; - } - - if (pwrStep > 2) { - pwrStep = (pwrStep << 1) - 2; - } - - ZLL->PA_PWR = pwrStep; -} - -static uint8_t PhyPlmeGetPwrLevelRequest(void) -{ - uint8_t pwrStep = (uint8_t)ZLL->PA_PWR; - - if (pwrStep > 2) { - pwrStep = (pwrStep + 2) >> 1; - } - - return pwrStep; -} - -static uint8_t PhyPlmeSetCurrentChannelRequest(uint8_t channel, uint8_t pan) -{ - if ((channel < 11) || (channel > 26)) { - return 1; - } - - if (!pan) { - ZLL->CHANNEL_NUM0 = channel; - } else { - ZLL->CHANNEL_NUM1 = channel; - } - - /* Make sure the current Tx power doesn't exceed the Tx power limit for the new channel */ - if (PhyPlmeGetPwrLevelRequest() > gPhyMaxTxPowerLevel_d) { - PhyPlmeSetPwrLevelRequest(gPhyMaxTxPowerLevel_d); - } - - return 0; -} - -/* - * Function is a RF interrupt vector. - */ -static void PHY_InterruptHandler(void) -{ - /* Disable and clear transceiver(IRQ_B) interrupt */ - NVIC_DisableIRQ(Radio_1_IRQn); - rf_thread.flags_set(1); -} - -static void PhyIsrSeqCleanup(void) -{ - uint32_t irqStatus; - - /* Set the PHY sequencer back to IDLE */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_XCVSEQ_MASK; - /* Mask SEQ, RX, TX and CCA interrupts */ - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK | - ZLL_PHY_CTRL_RXMSK_MASK | - ZLL_PHY_CTRL_TXMSK_MASK | - ZLL_PHY_CTRL_SEQMSK_MASK; - - while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} - - irqStatus = ZLL->IRQSTS; - /* Mask TMR3 interrupt */ - irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK; - /* Clear transceiver interrupts except TMRxIRQ */ - irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | - ZLL_IRQSTS_TMR2IRQ_MASK | - ZLL_IRQSTS_TMR3IRQ_MASK | - ZLL_IRQSTS_TMR4IRQ_MASK); - ZLL->IRQSTS = irqStatus; -} - -static void PhyIsrTimeoutCleanup(void) -{ - uint32_t irqStatus; - - /* Set the PHY sequencer back to IDLE and disable TMR3 comparator and timeout */ - ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_TMR3CMP_EN_MASK | - ZLL_PHY_CTRL_TC3TMOUT_MASK | - ZLL_PHY_CTRL_XCVSEQ_MASK); - /* Mask SEQ, RX, TX and CCA interrupts */ - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_CCAMSK_MASK | - ZLL_PHY_CTRL_RXMSK_MASK | - ZLL_PHY_CTRL_TXMSK_MASK | - ZLL_PHY_CTRL_SEQMSK_MASK; - - while (ZLL->SEQ_STATE & ZLL_SEQ_STATE_SEQ_STATE_MASK) {} - - irqStatus = ZLL->IRQSTS; - /* Mask TMR3 interrupt */ - irqStatus |= ZLL_IRQSTS_TMR3MSK_MASK; - /* Clear transceiver interrupts except TMR1IRQ and TMR4IRQ. */ - irqStatus &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | - ZLL_IRQSTS_TMR4IRQ_MASK); - ZLL->IRQSTS = irqStatus; - - /* The packet was transmitted successfully, but no ACK was received */ - if (device_driver.phy_tx_done_cb) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1); - } -} - -/* - * \brief Function get the time-out for the current sequence. - * - * \return sequence time-out value - */ -static uint32_t rf_get_timeout(void) -{ - return ZLL->T3CMP; -} - -/* - * \brief Function set a time-out to an XCVR sequence. - * - * \param pEndTime sequence time-out value [symbols] - * - * \return none - */ -static void rf_set_timeout(uint32_t pEndTime) -{ - uint32_t irqsts; - - platform_enter_critical(); - - /* disable TMR3 compare */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TMR3CMP_EN_MASK; - ZLL->T3CMP = pEndTime & ZLL_T3CMP_T3CMP_MASK; - - /* acknowledge TMR3 IRQ */ - irqsts = ZLL->IRQSTS & BM_ZLL_IRQSTS_TMRxMSK; - irqsts |= ZLL_IRQSTS_TMR3IRQ_MASK; - ZLL->IRQSTS = irqsts; - /* enable TMR3 compare */ - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TMR3CMP_EN_MASK; - /* enable autosequence stop by TC3 match */ - ZLL->PHY_CTRL |= ZLL_PHY_CTRL_TC3TMOUT_MASK; - - platform_exit_critical(); - -} - -/** - * Call this only One time - */ -static void rf_mac_hw_init(void) -{ - xcvrStatus_t xcvrStatus; - uint32_t phyReg; - - /* The data rate argument only matters when GFSK/MSK protocol is selected */ - xcvrStatus = XCVR_Init(ZIGBEE_MODE, DR_500KBPS); - if (xcvrStatus != gXcvrSuccess_c) { - return; - } - - mPhySeqState = gIdle_c; - - /* Enable 16 bit mode for TC2 - TC2 prime EN, disable all timers, - enable AUTOACK, mask all interrupts */ - ZLL->PHY_CTRL = (gCcaCCA_MODE1_c << ZLL_PHY_CTRL_CCATYPE_SHIFT) | - ZLL_PHY_CTRL_TC2PRIME_EN_MASK | - ZLL_PHY_CTRL_TSM_MSK_MASK | - ZLL_PHY_CTRL_WAKE_MSK_MASK | - ZLL_PHY_CTRL_CRC_MSK_MASK | - ZLL_PHY_CTRL_PLL_UNLOCK_MSK_MASK | - ZLL_PHY_CTRL_FILTERFAIL_MSK_MASK | - ZLL_PHY_CTRL_RX_WMRK_MSK_MASK | - ZLL_PHY_CTRL_CCAMSK_MASK | - ZLL_PHY_CTRL_RXMSK_MASK | - ZLL_PHY_CTRL_TXMSK_MASK | - ZLL_PHY_CTRL_SEQMSK_MASK | - ZLL_PHY_CTRL_AUTOACK_MASK | - ZLL_PHY_CTRL_TRCV_MSK_MASK; - - /* Clear all PP IRQ bits to avoid unexpected interrupts immediately after init - disable all timer interrupts */ - ZLL->IRQSTS = ZLL->IRQSTS; - - /* Clear HW indirect queue */ - ZLL->SAM_TABLE |= ZLL_SAM_TABLE_INVALIDATE_ALL_MASK; - - /* Frame Filtering - FRM_VER[7:6] = b11. Accept FrameVersion 0 and 1 packets, reject all others */ - ZLL->RX_FRAME_FILTER &= ~ZLL_RX_FRAME_FILTER_FRM_VER_FILTER_MASK; - ZLL->RX_FRAME_FILTER = ZLL_RX_FRAME_FILTER_FRM_VER_FILTER(3) | - ZLL_RX_FRAME_FILTER_CMD_FT_MASK | - ZLL_RX_FRAME_FILTER_DATA_FT_MASK | - ZLL_RX_FRAME_FILTER_BEACON_FT_MASK; - - /* Set prescaller to obtain 1 symbol (16us) timebase */ - ZLL->TMR_PRESCALE = 0x05; - - /* Set CCA threshold to -75 dBm */ - ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_CCA1_THRESH_MASK; - ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_CCA1_THRESH(RF_CCA_THRESHOLD); - - /* Set the default power level */ - PhyPlmeSetPwrLevelRequest(gPhyDefaultTxPowerLevel_d); - - /* Adjust ACK delay to fulfill the 802.15.4 turnaround requirements */ - ZLL->ACKDELAY &= ~ZLL_ACKDELAY_ACKDELAY_MASK; - ZLL->ACKDELAY |= ZLL_ACKDELAY_ACKDELAY(-8); - - /* Adjust LQI compensation */ - ZLL->CCA_LQI_CTRL &= ~ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP_MASK; - ZLL->CCA_LQI_CTRL |= ZLL_CCA_LQI_CTRL_LQI_OFFSET_COMP(96); - - /* Enable the RxWatermark IRQ */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_RX_WMRK_MSK_MASK; - /* Set Rx watermark level */ - ZLL->RX_WTR_MARK = 0; - - /* Set default channels */ - PhyPlmeSetCurrentChannelRequest(0x0B, 0); /* 2405 MHz */ - PhyPlmeSetCurrentChannelRequest(0x0B, 1); /* 2405 MHz */ - - /* DSM settings */ - phyReg = (RSIM->RF_OSC_CTRL & RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_MASK) >> - RSIM_RF_OSC_CTRL_BB_XTAL_READY_COUNT_SEL_SHIFT; - phyReg = (1024U << phyReg) / (SystemCoreClock / 32768) + 1; - RSIM->DSM_OSC_OFFSET = phyReg; - - osStatus status = rf_thread.start(mbed::callback(rf_thread_loop)); - MBED_ASSERT(status == osOK); - - /** Clear and enable MAC IRQ at task level, when scheduler is on. */ - InstallIRQHandler((IRQn_Type)Radio_1_IRQn, (uint32_t)PHY_InterruptHandler); - - /* Unmask Transceiver Global Interrupts */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_TRCV_MSK_MASK; - - NVIC_ClearPendingIRQ(Radio_1_IRQn); - NVIC_EnableIRQ(Radio_1_IRQn); -} - -/* - * \brief Function reads a time-stamp value from XCVR [symbols] - * - * \param pEndTime pointer to location where time-stamp will be stored - * - * \return none - */ -static void rf_get_timestamp(uint32_t *pRetClk) -{ - if (NULL == pRetClk) { - return; - } - - platform_enter_critical(); - - *pRetClk = 0; - *pRetClk = ZLL->EVENT_TMR >> ZLL_EVENT_TMR_EVENT_TMR_SHIFT; - - platform_exit_critical(); -} - -/* - * \brief Function starts the ACK wait time-out. - * - * \param slots The ACK wait time-out in [symbols] - * - * \return none - */ -static void rf_ack_wait_timer_start(uint16_t time) -{ - uint32_t timestamp, t; - - rf_get_timestamp(×tamp); - - t = (rf_get_timeout() - timestamp) & gPhyTimeMask_c; - - if (t > 1) { - timestamp += time; - rf_set_timeout(timestamp); - } -} - -/* - * \brief Function sets the RF in RX state. - * - * \param none - * - * \return none - */ -static void rf_receive(void) -{ - uint32_t irqSts; - - /* RX can start only from Idle state */ - if (mPhySeqState != gIdle_c) { - rf_abort(); - } - - mPhySeqState = gRX_c; - - /* Ensure that no spurious interrupts are raised, but do not change TMR1 and TMR4 IRQ status */ - irqSts = ZLL->IRQSTS; - irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); - irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; - ZLL->IRQSTS = irqSts; - - /* unmask SEQ interrupt */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; - - /* Start the RX sequence */ - ZLL->PHY_CTRL |= gRX_c; -} - -static void rf_mac_ed_state_enable(void) -{ - uint32_t ccaMode, irqSts; - - mPhySeqState = gCCA_c; - - /* Switch to ED mode */ - ccaMode = (ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCATYPE_MASK) >> ZLL_PHY_CTRL_CCATYPE_SHIFT; - if (ccaMode != gCcaED_c) { - ZLL->PHY_CTRL &= ~(ZLL_PHY_CTRL_CCATYPE_MASK); - } - - /* Ensure that no spurious interrupts are raised(do not change TMR1 and TMR4 IRQ status) */ - irqSts = ZLL->IRQSTS; - irqSts &= ~(ZLL_IRQSTS_TMR1IRQ_MASK | ZLL_IRQSTS_TMR4IRQ_MASK); - irqSts |= ZLL_IRQSTS_TMR3MSK_MASK; - ZLL->IRQSTS = irqSts; - - /* Unmask SEQ interrupt */ - ZLL->PHY_CTRL &= ~ZLL_PHY_CTRL_SEQMSK_MASK; - - /* start ED sequence */ - ZLL->PHY_CTRL |= gCCA_c; -} - -/* - * \brief Function is a call back for TX end interrupt. - * - * \param none - * - * \return none - */ -static void rf_handle_tx_end(bool framePending) -{ - /*Start receiver*/ - rf_receive(); - - if (!device_driver.phy_tx_done_cb) { - return; - } - - /*Call PHY TX Done API*/ - if (need_ack) { - if (framePending) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE_PENDING, 1, 1); - } else { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_DONE, 1, 1); - } - } else { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, PHY_LINK_TX_SUCCESS, 1, 1); - } -} - -/* - * \brief Function converts LQI into RSSI. - * - * \param LQI - * - * \return RSSI - */ -static int8_t rf_convert_LQI_to_RSSI(uint8_t lqi) -{ - int32_t rssi = (36 * lqi - 9836) / 109; - - return (int8_t)rssi; -} - -/* - * \brief Function scale the LQI value reported by RF into a 0-255 value. - * - * \param hwLqi - the LQI value reported by RF - * - * \return scaled LQI - */ -static uint8_t rf_convert_LQI(uint8_t hwLqi) -{ - if (hwLqi >= 220) { - return 255; - } else { - return (51 * hwLqi) / 44; - } -} - -/* - * \brief Function is a call back for RX end interrupt. - * - * \param none - * - * \return none - */ -static void rf_handle_rx_end(void) -{ - uint8_t rf_lqi = (ZLL->LQI_AND_RSSI & ZLL_LQI_AND_RSSI_LQI_VALUE_MASK) >> - ZLL_LQI_AND_RSSI_LQI_VALUE_SHIFT; - int8_t rf_rssi = 0; - uint8_t len; - uint8_t i; - volatile uint8_t *pPB; - - len = (ZLL->IRQSTS & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; /* Including FCS (2 bytes) */ - - /* Excluding FCS (2 bytes) */ - len -= 2; - - /*Check the length is valid*/ - if (len > 1 && len < MAC_PACKET_SIZE) { - rf_lqi = rf_convert_LQI(rf_lqi); - rf_rssi = rf_convert_LQI_to_RSSI(rf_lqi); - - /* Load data from Packet Buffer */ - pPB = (uint8_t *)ZLL->PKT_BUFFER_RX; - - for (i = 0; i < len; i++) { - PHYPAYLOAD[i] = *pPB++; - } - - /* Start receiver */ - rf_receive(); - - if (device_driver.phy_rx_cb) { - device_driver.phy_rx_cb(PHYPAYLOAD, len, rf_lqi, rf_rssi, rf_radio_driver_id); - } - } else { - /* Start receiver */ - rf_receive(); - } -} - -static void handle_IRQ_events(void) -{ - uint8_t xcvseqCopy; - uint32_t irqStatus; - - /* Read current XCVRSEQ and interrupt status */ - xcvseqCopy = ZLL->PHY_CTRL & ZLL_PHY_CTRL_XCVSEQ_MASK; - irqStatus = ZLL->IRQSTS; - /* Clear all xcvr interrupts */ - ZLL->IRQSTS = irqStatus; - - /* Filter Fail IRQ */ - if (irqStatus & ZLL_IRQSTS_FILTERFAIL_IRQ_MASK) { - - } else { - /* Rx Watermark IRQ */ - if ((!(ZLL->PHY_CTRL & ZLL_PHY_CTRL_RX_WMRK_MSK_MASK)) && - (irqStatus & ZLL_IRQSTS_RXWTRMRKIRQ_MASK)) { - uint32_t rx_len = (irqStatus & ZLL_IRQSTS_RX_FRAME_LENGTH_MASK) >> ZLL_IRQSTS_RX_FRAME_LENGTH_SHIFT; - - /* Convert to symbols and add IFS and ACK duration */ - rx_len = rx_len * 2 + 12 + 22 + 2; - rf_ack_wait_timer_start(rx_len); - } - } - - /* Sequencer interrupt, the autosequence has completed */ - if (irqStatus & ZLL_IRQSTS_SEQIRQ_MASK) { - - /* XCVR will be set to Idle */ - mPhySeqState = gIdle_c; - - /* PLL unlock, the autosequence has been aborted due to PLL unlock */ - if (irqStatus & ZLL_IRQSTS_PLL_UNLOCK_IRQ_MASK) { - PhyIsrSeqCleanup(); - /* Start receiver */ - rf_receive(); - } - /* TMR3 timeout, the autosequence has been aborted due to TMR3 timeout */ - else if ((irqStatus & ZLL_IRQSTS_TMR3IRQ_MASK) && - (!(irqStatus & ZLL_IRQSTS_RXIRQ_MASK)) && - (xcvseqCopy != gTX_c)) { - PhyIsrTimeoutCleanup(); - /* Start receiver */ - rf_receive(); - } else { - PhyIsrSeqCleanup(); - switch (xcvseqCopy) { - case gTX_c: - if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && - (irqStatus & ZLL_IRQSTS_CCA_MASK)) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, - PHY_LINK_CCA_FAIL, 1, 1); - } else { - rf_handle_tx_end(false); - } - break; - - case gTR_c: - if ((ZLL->PHY_CTRL & ZLL_PHY_CTRL_CCABFRTX_MASK) && - (irqStatus & ZLL_IRQSTS_CCA_MASK)) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, rf_mac_handle, - PHY_LINK_CCA_FAIL, 1, 1); - } else { - rf_handle_tx_end((irqStatus & ZLL_IRQSTS_RX_FRM_PEND_MASK) > 0); - } - break; - - case gRX_c: - rf_handle_rx_end(); - break; - - case gCCA_c: - rf_ed_value = rf_convert_energy_level((ZLL->LQI_AND_RSSI & - ZLL_LQI_AND_RSSI_CCA1_ED_FNL_MASK) >> - ZLL_LQI_AND_RSSI_CCA1_ED_FNL_SHIFT); - break; - - default: - break; - } - } - } -} - -NanostackRfPhyKw41z::NanostackRfPhyKw41z() -{ - memcpy(MAC64_addr, MAC64_addr_default, sizeof(MAC64_addr)); -} - -NanostackRfPhyKw41z::~NanostackRfPhyKw41z() -{ - // Do nothing -} - -int8_t NanostackRfPhyKw41z::rf_register() -{ - platform_enter_critical(); - - if (rf != NULL) { - platform_exit_critical(); - error("Multiple registrations of NanostackRfPhyKw41z not supported"); - return -1; - } - - rf = this; - int8_t radio_id = rf_device_register(); - if (radio_id < 0) { - rf = NULL; - } - - platform_exit_critical(); - return radio_id; -} - -void NanostackRfPhyKw41z::rf_unregister() -{ - platform_enter_critical(); - - if (rf != this) { - platform_exit_critical(); - return; - } - - rf_device_unregister(); - rf = NULL; - - platform_exit_critical(); -} - -void NanostackRfPhyKw41z::get_mac_address(uint8_t *mac) -{ - platform_enter_critical(); - - memcpy((void *)mac, (void *)MAC64_addr, sizeof(MAC64_addr)); - - platform_exit_critical(); -} - -void NanostackRfPhyKw41z::set_mac_address(uint8_t *mac) -{ - platform_enter_critical(); - - if (NULL != rf) { - error("NanostackRfPhyKw41z cannot change mac address when running"); - platform_exit_critical(); - return; - } - memcpy((void *)MAC64_addr, (void *)mac, sizeof(MAC64_addr)); - - platform_exit_critical(); -} - -NanostackRfPhy &NanostackRfPhy::get_default_instance() -{ - static NanostackRfPhyKw41z rf_phy; - return rf_phy; -} diff --git a/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h b/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h deleted file mode 100644 index c85f6bd..0000000 --- a/features/nanostack/nanostack-interface/targets/TARGET_Freescale/TARGET_KW41Z/NanostackRfPhyKw41z.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2018 ARM Limited. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NANOSTACK_PHY_KW41Z_H_ -#define NANOSTACK_PHY_KW41Z_H_ - -#if MBED_CONF_RTOS_PRESENT -#include "rtos/rtos.h" -#endif -#include "NanostackRfPhy.h" -#include "fsl_common.h" - -class NanostackRfPhyKw41z : public NanostackRfPhy { -public: - NanostackRfPhyKw41z(); - ~NanostackRfPhyKw41z(); - int8_t rf_register(); - void rf_unregister(); - void get_mac_address(uint8_t *mac); - void set_mac_address(uint8_t *mac); -}; - -#endif /* NANOSTACK_PHY_KW41Z_H_ */ diff --git a/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp b/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp deleted file mode 100644 index 1369287..0000000 --- a/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.cpp +++ /dev/null @@ -1,1281 +0,0 @@ -/* - * Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#include "NanostackRfPhyEfr32.h" - -#include - -#include "mbed.h" -#include "mbed_power_mgmt.h" -#include "ns_types.h" -#include "platform/arm_hal_interrupt.h" -#include "nanostack/platform/arm_hal_phy.h" -#include "mbed_toolchain.h" - -#include "mbed-trace/mbed_trace.h" -#define TRACE_GROUP "SLRF" - -/* Enable debug printing with SL_RADIO_DEBUG, override debug printer with SL_DEBUG_PRINT. */ -#ifdef SL_RADIO_DEBUG -#ifndef SL_DEBUG_PRINT -#define SL_DEBUG_PRINT(...) tr_debug(__VA_ARGS__) -#endif -#else -#define SL_DEBUG_PRINT(...) -#endif - -/* RF_THREAD_STACK_SIZE defines tack size for the RF adaptor thread */ -#ifndef RF_THREAD_STACK_SIZE -#define RF_THREAD_STACK_SIZE 1024 -#endif - -/* RF_QUEUE_SIZE defines queue size for incoming messages */ -#ifndef RF_QUEUE_SIZE -#define RF_QUEUE_SIZE 8 -#endif - -/* 802.15.4 maximum size of a single packet including PHY byte is 128 bytes */ -#define MAC_PACKET_MAX_LENGTH 128 -/* Offsets of prepended data in packet buffer */ -#define MAC_PACKET_OFFSET_RSSI 0 -#define MAC_PACKET_OFFSET_LQI 1 -/* This driver prepends RSSI and LQI */ -#define MAC_PACKET_INFO_LENGTH 2 - -/* RFThreadSignal used to signal from interrupts to the adaptor thread */ -enum RFThreadSignal { - SL_RX_DONE = (1 << 1), - SL_TX_DONE = (1 << 2), - SL_TX_ERR = (1 << 3), - SL_TX_TIMEOUT = (1 << 4), - SL_ACK_RECV = (1 << 5), - SL_ACK_TIMEOUT = (1 << 6), - SL_TXFIFO_ERR = (1 << 7), - SL_RXFIFO_ERR = (1 << 8), - SL_CAL_REQ = (1 << 9), - SL_RSSI_DONE = (1 << 10), - SL_QUEUE_FULL = (1 << 11), - - // ACK pend flag can be signalled in addition to RX_DONE - SL_ACK_PEND = (1 << 30), -}; - -/* Adaptor thread definitions */ -static void rf_thread_loop(const void *arg); -static osThreadDef(rf_thread_loop, osPriorityRealtime, RF_THREAD_STACK_SIZE); -static osThreadId rf_thread_id = 0; - -/* Queue for passing messages from interrupt to adaptor thread */ -static volatile uint8_t rx_queue[RF_QUEUE_SIZE][MAC_PACKET_MAX_LENGTH + MAC_PACKET_INFO_LENGTH]; -static volatile size_t rx_queue_head = 0; -static volatile size_t rx_queue_tail = 0; - -/* Silicon Labs headers */ -extern "C" { -#include "rail/rail.h" -#include "rail/pa.h" -#include "rail/pti.h" -#include "rail/ieee802154/rail_ieee802154.h" -} - -/* RF driver data */ -static phy_device_driver_s device_driver; -static int8_t rf_radio_driver_id = -1; -static uint8_t MAC_address[8]; -static uint16_t PAN_address; -static uint16_t short_address; - -/* Driver instance handle */ -static NanostackRfPhyEfr32 *rf = NULL; - -/* Channel configurations */ -static const phy_rf_channel_configuration_s phy_24ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK}; -static const phy_rf_channel_configuration_s phy_subghz = {868300000U, 2000000U, 250000U, 11U, M_OQPSK}; - -static const phy_device_channel_page_s phy_channel_pages[] = { - { CHANNEL_PAGE_0, &phy_24ghz}, - { CHANNEL_PAGE_2, &phy_subghz}, - { CHANNEL_PAGE_0, NULL} -}; - -/* Driver structures */ -typedef enum { - RADIO_UNINIT, - RADIO_INITING, - RADIO_IDLE, - RADIO_TX, - RADIO_RX, - RADIO_CALIBRATION -} siliconlabs_modem_state_t; - -static const RAIL_CsmaConfig_t csma_config = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA; - -#if defined(TARGET_EFR32_1) -#include "ieee802154_subg_efr32xg1_configurator_out.h" -#elif defined(TARGET_EFR32_12) -/* TODO: Add SubG support for EFR32_12 */ -#else -#error "Not a valid target." -#endif - -#if MBED_CONF_SL_RAIL_HAS_SUBGIG -static RAIL_ChannelConfigEntryAttr_t entry_868; -static RAIL_ChannelConfigEntryAttr_t entry_915; -static const RAIL_ChannelConfigEntry_t entry[] = { - { - .phyConfigDeltaAdd = NULL, // Add this to default config for this entry - .baseFrequency = 868300000U, - .channelSpacing = 600000U, - .physicalChannelOffset = 0, - .channelNumberStart = 0, - .channelNumberEnd = 0, - .maxPower = RAIL_TX_POWER_MAX, - .attr = &entry_868 - }, - { - .phyConfigDeltaAdd = NULL, // Add this to default config for this entry - .baseFrequency = 906000000U, - .channelSpacing = 2000000U, - .physicalChannelOffset = 1, - .channelNumberStart = 1, - .channelNumberEnd = 10, - .maxPower = RAIL_TX_POWER_MAX, - .attr = &entry_915 - } -}; -#endif - -#if MBED_CONF_SL_RAIL_BAND == 868 -#if !MBED_CONF_SL_RAIL_HAS_SUBGIG -#error "Sub-Gigahertz band is not supported on this target." -#endif -static const RAIL_ChannelConfig_t channels = { - ieee802154_config_863, - ieee802154_config_863_min, - &entry[0], - 1 -}; -#elif MBED_CONF_SL_RAIL_BAND == 915 -#if !MBED_CONF_SL_RAIL_HAS_SUBGIG -#error "Sub-Gigahertz band is not supported on this target." -#endif -static const RAIL_ChannelConfig_t channels = { - ieee802154_config_915, - ieee802154_config_915_min, - &entry[1], - 1 -}; -#elif MBED_CONF_SL_RAIL_BAND == 2400 -#ifndef MBED_CONF_SL_RAIL_HAS_2P4 -#error "2.4GHz band is not supported on this target." -#endif -#else -#error "sl-rail.band is not correctly defined" -#endif - -#if defined (MBED_CONF_SL_RAIL_HAS_2P4) -// Set up the PA for 2.4 GHz operation -static const RAIL_TxPowerConfig_t paInit2p4 = { - .mode = RAIL_TX_POWER_MODE_2P4_HP, - .voltage = 1800, - .rampTime = 10, -}; -#endif - -#if MBED_CONF_SL_RAIL_HAS_SUBGIG -// Set up the PA for sub-GHz operation -static const RAIL_TxPowerConfig_t paInitSubGhz = { - .mode = RAIL_TX_POWER_MODE_SUBGIG, - .voltage = 1800, - .rampTime = 10, -}; -#endif - -static const RAIL_StateTiming_t timings = { - .idleToRx = 100, - // Make txToRx slightly lower than desired to make sure we get to - // RX in time - .txToRx = 192 - 10, - .idleToTx = 100, - .rxToTx = 192, - .rxSearchTimeout = 0, - .txToRxSearchTimeout = 0 -}; - -static const RAIL_IEEE802154_Config_t config = { - .addresses = NULL, - .ackConfig = { - .enable = true, - .ackTimeout = 1200, - .rxTransitions = { - .success = RAIL_RF_STATE_RX, - .error = RAIL_RF_STATE_RX // ignored - }, - .txTransitions = { - .success = RAIL_RF_STATE_RX, - .error = RAIL_RF_STATE_RX // ignored - } - }, - .timings = timings, - .framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES, - .promiscuousMode = false, - .isPanCoordinator = false -}; - -static volatile siliconlabs_modem_state_t radio_state = RADIO_UNINIT; -static volatile bool sleep_blocked = false; -static volatile int8_t channel = -1; -static volatile uint8_t current_tx_handle = 0; -static volatile uint8_t current_tx_sequence = 0; -static volatile bool waiting_for_ack = false; -static volatile bool data_pending = false, last_ack_pending_bit = false; -static volatile uint32_t last_tx = 0; -static volatile RAIL_Handle_t gRailHandle = NULL; -static uint8_t txFifo[256]; - -/* ARM_NWK_HAL prototypes */ -static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr); -static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel); -static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr); -static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol); - -/* Local function prototypes */ -static bool rail_checkAndSwitchChannel(uint8_t channel); -static void RAILCb_RfReady(RAIL_Handle_t railHandle); -static void radioEventHandler(RAIL_Handle_t railHandle, RAIL_Events_t events); - -static RAIL_Config_t railCfg = { // Must never be const - .eventsCallback = &radioEventHandler, - .protocol = NULL, // For BLE, pointer to a RAIL_BLE_State_t. For IEEE802.15.4 this must be NULL. - .scheduler = NULL, // For MultiProtocol, pointer to a RAIL_SchedConfig_t -}; - -static void rf_thread_loop(const void *arg) -{ - SL_DEBUG_PRINT("rf_thread_loop: starting (id: %d)\n", (int)rf_thread_id); - for (;;) { - osEvent event = osSignalWait(0, osWaitForever); - - if (event.status != osEventSignal) { - continue; - } - - platform_enter_critical(); - - if (event.value.signals & SL_RX_DONE) { - while (rx_queue_tail != rx_queue_head) { - uint8_t *packet = (uint8_t *) rx_queue[rx_queue_tail]; - SL_DEBUG_PRINT("rPKT %d\n", packet[MAC_PACKET_INFO_LENGTH] - 2); - device_driver.phy_rx_cb( - &packet[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ - packet[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ - packet[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ - packet[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ - rf_radio_driver_id); - rx_queue_tail = (rx_queue_tail + 1) % RF_QUEUE_SIZE; - } - - } else if (event.value.signals & SL_TX_DONE) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_TX_SUCCESS, - 1, - 1); - } else if (event.value.signals & SL_ACK_RECV) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - (event.value.signals & SL_ACK_PEND) ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, - 1, - 1); - } else if (event.value.signals & SL_ACK_TIMEOUT) { - waiting_for_ack = false; - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_TX_FAIL, - 1, - 1); - } else if (event.value.signals & SL_TX_ERR) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); - } else if (event.value.signals & SL_TX_TIMEOUT) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); - } else if (event.value.signals & SL_CAL_REQ) { - SL_DEBUG_PRINT("rf_thread_loop: SL_CAL_REQ signal received (unhandled)\n"); - } else if (event.value.signals & SL_RXFIFO_ERR) { - SL_DEBUG_PRINT("rf_thread_loop: SL_RXFIFO_ERR signal received (unhandled)\n"); - } else if (event.value.signals & SL_TXFIFO_ERR) { - SL_DEBUG_PRINT("rf_thread_loop: SL_TXFIFO_ERR signal received (unhandled)\n"); - } else if (event.value.signals & SL_QUEUE_FULL) { - SL_DEBUG_PRINT("rf_thread_loop: SL_QUEUE_FULL signal received (packet dropped)\n"); - } else { - SL_DEBUG_PRINT("rf_thread_loop unhandled event status: %d value: %d\n", event.status, (int)event.value.signals); - } - - platform_exit_critical(); - } -} - -/*============ CODE =========*/ - -/* - * \brief Function initialises and registers the RF driver. - * - * \param none - * - * \return rf_radio_driver_id Driver ID given by NET library - */ -static int8_t rf_device_register(void) -{ - // If we already exist, bail. - if (radio_state != RADIO_UNINIT) { - return -1; - } - - SL_DEBUG_PRINT("rf_device_register: entry\n"); - - // Set up RAIL - // Initialize the RAIL library and any internal state it requires - gRailHandle = RAIL_Init(&railCfg, &RAILCb_RfReady); - - // Configure calibration settings - RAIL_ConfigCal(gRailHandle, RAIL_CAL_ALL); - - // Set up library for IEEE802.15.4 PHY operation -#if (MBED_CONF_SL_RAIL_BAND == 2400) - RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle); - channel = 11; -#elif (MBED_CONF_SL_RAIL_BAND == 915) - RAIL_ConfigChannels(gRailHandle, &channels, NULL); - channel = 1; -#elif (MBED_CONF_SL_RAIL_BAND == 868) - RAIL_ConfigChannels(gRailHandle, &channels, NULL); - channel = 0; -#endif - - // Enable 802.15.4 acceleration features - RAIL_IEEE802154_Init(gRailHandle, &config); - - // Fire all events by default - RAIL_ConfigEvents(gRailHandle, - RAIL_EVENTS_ALL, - RAIL_EVENTS_ALL); - - // Setup the transmit buffer - RAIL_SetTxFifo(gRailHandle, txFifo, 0, sizeof(txFifo)); - -#if MBED_CONF_SL_RAIL_BAND == 2400 - if (RAIL_ConfigTxPower(gRailHandle, &paInit2p4) != RAIL_STATUS_NO_ERROR) { - // Error: The PA could not be initialized due to an improper configuration. - // Please ensure your configuration is valid for the selected part. - while (1) ; - } -#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) - if (RAIL_ConfigTxPower(gRailHandle, &paInitSubGhz) != RAIL_STATUS_NO_ERROR) { - // Error: The PA could not be initialized due to an improper configuration. - // Please ensure your configuration is valid for the selected part. - while (1) ; - } -#endif - // Set the output power to the maximum supported by this chip - RAIL_SetTxPower(gRailHandle, 255); - - // Set up PTI since it makes life so much easier -#if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1) - RAIL_PtiConfig_t ptiConfig = { - MBED_CONF_SL_RAIL_PTI_MODE, - MBED_CONF_SL_RAIL_PTI_BAUDRATE, - MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION, - MBED_CONF_SL_RAIL_PTI_DOUT_PORT, - MBED_CONF_SL_RAIL_PTI_DOUT_PIN, - MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION, - MBED_CONF_SL_RAIL_PTI_DCLK_PORT, - MBED_CONF_SL_RAIL_PTI_DCLK_PIN, - MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION, - MBED_CONF_SL_RAIL_PTI_DFRAME_PORT, - MBED_CONF_SL_RAIL_PTI_DFRAME_PIN - }; - - // Initialize the Packet Trace Interface (PTI) for the EFR32 - RAIL_ConfigPti(RAIL_EFR32_HANDLE, &ptiConfig); - // Enable Packet Trace (PTI) - RAIL_EnablePti(RAIL_EFR32_HANDLE, true); -#endif - - /* Get real MAC address */ - /* MAC is stored MSB first */ - memcpy(MAC_address, (const void *)&DEVINFO->UNIQUEH, 4); - memcpy(&MAC_address[4], (const void *)&DEVINFO->UNIQUEL, 4); - - /*Set pointer to MAC address*/ - device_driver.PHY_MAC = MAC_address; - device_driver.driver_description = (char *)"EFR32_154"; - - /*Type of RF PHY*/ -#if MBED_CONF_SL_RAIL_BAND == 2400 - device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE; -#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868) - device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE; -#endif - - device_driver.phy_channel_pages = phy_channel_pages; - /*Maximum size of payload is 127*/ - device_driver.phy_MTU = 127; - /*1 byte header in PHY layer (length)*/ - device_driver.phy_header_length = 1; - /*No tail in PHY layer*/ - device_driver.phy_tail_length = 0; - /*Set address write function*/ - device_driver.address_write = &rf_address_write; - /*Set RF extension function*/ - device_driver.extension = &rf_extension; - /*Set RF state control function*/ - device_driver.state_control = &rf_interface_state_control; - /*Set transmit function*/ - device_driver.tx = &rf_start_cca; - /*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/ - device_driver.phy_rx_cb = NULL; - device_driver.phy_tx_done_cb = NULL; - /*Virtual upper data callback init to NULL*/ - device_driver.arm_net_virtual_rx_cb = NULL; - device_driver.arm_net_virtual_tx_cb = NULL; - - /*Register device driver*/ - rf_radio_driver_id = arm_net_phy_register(&device_driver); - - // If the radio hasn't called the ready callback by now, place it in the initing state - if (radio_state == RADIO_UNINIT) { - radio_state = RADIO_INITING; - } - -#ifdef MBED_CONF_RTOS_PRESENT - rx_queue_head = 0; - rx_queue_tail = 0; - rf_thread_id = osThreadCreate(osThread(rf_thread_loop), NULL); -#endif - - return rf_radio_driver_id; -} - -/* - * \brief Function unregisters the RF driver. - * - * \param none - * - * \return none - */ -static void rf_device_unregister(void) -{ - arm_net_phy_unregister(rf_radio_driver_id); - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } -} - -/* - * \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO. - * - * \param data_ptr Pointer to TX data - * \param data_length Length of the TX data - * \param tx_handle Handle to transmission - * \return 0 Success - * \return -1 Busy - */ -static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol) -{ - switch (radio_state) { - case RADIO_UNINIT: - SL_DEBUG_PRINT("rf_start_cca: Radio uninit\n"); - return -1; - case RADIO_INITING: - SL_DEBUG_PRINT("rf_start_cca: Radio initing\n"); - return -1; - case RADIO_CALIBRATION: - SL_DEBUG_PRINT("rf_start_cca: Radio calibrating\n"); - return -1; - case RADIO_TX: - SL_DEBUG_PRINT("rf_start_cca: Radio in TX mode\n"); - return -1; - case RADIO_IDLE: - case RADIO_RX: - // If we're still waiting for an ACK, don't mess up the internal state - if (waiting_for_ack || RAIL_GetRadioState(gRailHandle) == RAIL_RF_STATE_TX) { - if ((RAIL_GetTime() - last_tx) < 30000) { - SL_DEBUG_PRINT("rf_start_cca: Still waiting on previous ACK\n"); - return -1; - } else { - SL_DEBUG_PRINT("rf_start_cca: TXerr\n"); - } - } - - platform_enter_critical(); - - /* Since we set up Nanostack to give us a 1-byte PHY header, we get the one extra byte at the start of data_ptr - * and need to populate it with the MAC-frame length byte (including the 2-byte hardware-inserted CRC) */ - data_ptr[0] = data_length + 2; - - RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); - RAIL_WriteTxFifo(gRailHandle, data_ptr, data_length + 1, true); - radio_state = RADIO_TX; - - RAIL_TxOptions_t txOpt = RAIL_TX_OPTIONS_DEFAULT; - //Check to see whether we'll be waiting for an ACK - if (data_ptr[1] & (1 << 5)) { - txOpt |= RAIL_TX_OPTION_WAIT_FOR_ACK; - waiting_for_ack = true; - } else { - waiting_for_ack = false; - } - - SL_DEBUG_PRINT("rf_start_cca: Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0); - - if (RAIL_StartCcaCsmaTx(gRailHandle, channel, txOpt, &csma_config, NULL) == 0) { - //Save packet number and sequence - current_tx_handle = tx_handle; - current_tx_sequence = data_ptr[3]; - platform_exit_critical(); - return 0; - } else { - RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); - RAIL_StartRx(gRailHandle, channel, NULL); - radio_state = RADIO_RX; - platform_exit_critical(); - return -1; - } - } - //Should never get here... - return -1; -} - -/* - * \brief Function gives the control of RF states to MAC. - * - * \param new_state RF state - * \param rf_channel RF channel - * - * \return 0 Success - */ -static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel) -{ - int8_t ret_val = 0; - switch (new_state) { - /* Reset PHY driver and set to idle */ - case PHY_INTERFACE_RESET: - RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); - radio_state = RADIO_IDLE; - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } - break; - /* Disable PHY Interface driver */ - case PHY_INTERFACE_DOWN: - RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); - radio_state = RADIO_IDLE; - if (sleep_blocked) { - sleep_manager_unlock_deep_sleep(); - sleep_blocked = false; - } - break; - /* Enable RX */ - case PHY_INTERFACE_UP: - if (rail_checkAndSwitchChannel(rf_channel)) { - RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); - RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, false); - RAIL_StartRx(gRailHandle, channel, NULL); - radio_state = RADIO_RX; - if (!sleep_blocked) { - /* RX can only happen in EM0/1*/ - sleep_manager_lock_deep_sleep(); - sleep_blocked = true; - } - } else { - ret_val = -1; - } - break; - /* Enable wireless interface ED scan mode */ - case PHY_INTERFACE_RX_ENERGY_STATE: - SL_DEBUG_PRINT("rf_interface_state_control: Energy det req\n"); - // TODO: implement energy detection - break; - /* Enable RX in promiscuous mode (aka no address filtering) */ - case PHY_INTERFACE_SNIFFER_STATE: - if (rail_checkAndSwitchChannel(rf_channel)) { - RAIL_Idle(gRailHandle, RAIL_IDLE_FORCE_SHUTDOWN_CLEAR_FLAGS, true); - RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, true); - RAIL_StartRx(gRailHandle, channel, NULL); - radio_state = RADIO_RX; - if (!sleep_blocked) { - /* RX can only happen in EM0/1*/ - sleep_manager_lock_deep_sleep(); - sleep_blocked = true; - } - } else { - ret_val = -1; - } - break; - } - return ret_val; -} - -/* - * \brief Function controls the ACK pending, channel setting and energy detection. - * - * \param extension_type Type of control - * \param data_ptr Data from NET library - * - * \return 0 Success - */ -static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr) -{ - switch (extension_type) { - /* Control MAC pending bit for Indirect data transmission */ - case PHY_EXTENSION_CTRL_PENDING_BIT: - if (*data_ptr) { - data_pending = true; - } else { - data_pending = false; - } - break; - /* Return frame pending bit from last received ACK */ - case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS: - if (last_ack_pending_bit) { - *data_ptr = 0xFF; - } else { - *data_ptr = 0; - } - break; - /* Set channel */ - case PHY_EXTENSION_SET_CHANNEL: - channel = *data_ptr; - break; - /* Read energy on the channel */ - case PHY_EXTENSION_READ_CHANNEL_ENERGY: - // TODO: implement energy detection - *data_ptr = 0; - break; - /* Read status of the link */ - case PHY_EXTENSION_READ_LINK_STATUS: - // TODO: return accurate value here - SL_DEBUG_PRINT("rf_extension: Trying to read link status\n"); - break; - /* Convert between LQI and RSSI */ - case PHY_EXTENSION_CONVERT_SIGNAL_INFO: - // TODO: return accurate value here - SL_DEBUG_PRINT("rf_extension: Trying to read signal info\n"); - break; - case PHY_EXTENSION_ACCEPT_ANY_BEACON: - SL_DEBUG_PRINT("rf_extension: Trying to accept any beacon\n"); - break; - } - return 0; -} - -/* - * \brief Function sets the addresses to RF address filters. - * - * \param address_type Type of address - * \param address_ptr Pointer to given address - * - * \return 0 Success - */ -static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr) -{ - int8_t ret_val = 0; - switch (address_type) { - /*Set 48-bit address*/ - case PHY_MAC_48BIT: - // 15.4 does not support 48-bit addressing - ret_val = -1; - break; - /*Set 64-bit MAC address*/ - case PHY_MAC_64BIT: - /* Store MAC in MSB order */ - memcpy(MAC_address, address_ptr, 8); - SL_DEBUG_PRINT("rf_address_write: MACw "); - for (unsigned int i = 0; i < sizeof(MAC_address); i ++) { - SL_DEBUG_PRINT("%02x:", MAC_address[i]); - } - SL_DEBUG_PRINT("\n"); - /* Pass MAC to the RF driver in LSB order */ - uint8_t MAC_reversed[8]; - for (unsigned int i = 0; i < sizeof(MAC_address); i ++) { - MAC_reversed[i] = MAC_address[sizeof(MAC_address) - 1 - i]; - } - RAIL_IEEE802154_SetLongAddress(gRailHandle, MAC_reversed, 0); - break; - /*Set 16-bit address*/ - case PHY_MAC_16BIT: - short_address = address_ptr[0] << 8 | address_ptr[1]; - RAIL_IEEE802154_SetShortAddress(gRailHandle, short_address, 0); - break; - /*Set PAN Id*/ - case PHY_MAC_PANID: - PAN_address = address_ptr[0] << 8 | address_ptr[1]; - RAIL_IEEE802154_SetPanId(gRailHandle, PAN_address, 0); - break; - } - return ret_val; -} - -/*****************************************************************************/ -/*****************************************************************************/ - -static void rf_if_lock(void) -{ - platform_enter_critical(); -} - -static void rf_if_unlock(void) -{ - platform_exit_critical(); -} - -NanostackRfPhyEfr32::NanostackRfPhyEfr32() : NanostackRfPhy() -{ - // Do nothing -} - -NanostackRfPhyEfr32::~NanostackRfPhyEfr32() -{ - rf_unregister(); -} - -int8_t NanostackRfPhyEfr32::rf_register() -{ - - rf_if_lock(); - - if (rf != NULL) { - rf_if_unlock(); - error("Multiple registrations of NanostackRfPhyEfr32 not supported"); - return -1; - } - - int8_t radio_id = rf_device_register(); - if (radio_id < 0) { - rf = NULL; - } else { - rf = this; - } - - rf_if_unlock(); - return radio_id; -} - -void NanostackRfPhyEfr32::rf_unregister() -{ - rf_if_lock(); - - if (rf != this) { - rf_if_unlock(); - return; - } - - rf_device_unregister(); - rf = NULL; - - rf_if_unlock(); -} - -void NanostackRfPhyEfr32::get_mac_address(uint8_t *mac) -{ - rf_if_lock(); - - memcpy(mac, MAC_address, sizeof(MAC_address)); - - rf_if_unlock(); -} - -void NanostackRfPhyEfr32::set_mac_address(uint8_t *mac) -{ - rf_if_lock(); - - if (NULL != rf) { - error("NanostackRfPhyEfr32 cannot change mac address when running"); - rf_if_unlock(); - return; - } - - memcpy(MAC_address, mac, sizeof(MAC_address)); - - rf_if_unlock(); -} - -uint32_t NanostackRfPhyEfr32::get_driver_version() -{ - RAIL_Version_t railversion; - RAIL_GetVersion(&railversion, false); - - return (railversion.major << 24) | - (railversion.minor << 16) | - (railversion.rev << 8) | - (railversion.build); -} - - -//====================== RAIL-defined callbacks ========================= -/** - * Callback that lets the app know when the radio has finished init - * and is ready. - */ -static void RAILCb_RfReady(RAIL_Handle_t handle) -{ - (void) handle; - radio_state = RADIO_IDLE; -} - -/** - * Function to check the requested channel against the current channel, - * and change the radio configuration if necessary. - * - * @param channel The new channel number requested - * @return bool True if able to switch to the requested channel - * - */ -static bool rail_checkAndSwitchChannel(uint8_t newChannel) -{ - if (channel == newChannel) { - return true; - } - - if (newChannel > 0 && newChannel < 11) { - if (MBED_CONF_SL_RAIL_BAND == 915) { - channel = newChannel; - return true; - } else { - return false; - } - } else if (newChannel >= 11 && newChannel <= 26) { - if (MBED_CONF_SL_RAIL_BAND == 2400) { - channel = newChannel; - return true; - } else { - return false; - } - } else { - return false; - } -} - -/** - * Event handler for RAIL-fired events. Usually gets called from IRQ context. - * Due to IRQ latency and tailchaining, multiple event flags might be set simultaneously, - * so we have to check all of them */ -static void radioEventHandler(RAIL_Handle_t railHandle, - RAIL_Events_t events) -{ - /* RAIL_Events_t is a 64-bit event mask, but a thread only supports 32 - * signal bits. This means we have to convert from a RAIL event mask to - * our own custom event mask. */ - if (railHandle != gRailHandle) { - return; - } - -#ifdef MBED_CONF_RTOS_PRESENT - if (rf_thread_id == 0) { - return; - } -#endif - - size_t index = 0; - do { - if (events & 1ull) { - switch (index) { - /* - * Occurs when the AGC averaged RSSI is done. - * It occurs in response to RAIL_StartAverageRssi() to indicate that the - * hardware has completed averaging. Call \ref RAIL_GetAverageRssi to get the - * result. - */ - case RAIL_EVENT_RSSI_AVERAGE_DONE_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_RSSI_DONE); -#else - SL_DEBUG_PRINT("RSSI done (%d)\n", RAIL_GetAverageRssi(gRailHandle)); -#endif - break; - /* - * Notifies the application when searching for an ACK has timed - * out. This event occurs whenever the timeout for searching for an - * ACK is exceeded. - */ - case RAIL_EVENT_RX_ACK_TIMEOUT_SHIFT: - if (waiting_for_ack) { - waiting_for_ack = false; -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_ACK_TIMEOUT); -#else - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_TX_FAIL, - 1, - 1); -#endif - } - break; - /* - * Occurs when the receive FIFO exceeds the configured threshold - * value. Call \ref RAIL_GetRxFifoBytesAvailable to get the number of bytes - * available. - */ - case RAIL_EVENT_RX_FIFO_ALMOST_FULL_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_RXFIFO_ERR); -#else - SL_DEBUG_PRINT("RX near full (%d)\n", RAIL_GetRxFifoBytesAvailable(gRailHandle)); -#endif - break; - /* - * Occurs whenever a packet is received. - * Call RAIL_GetRxPacketInfo() to get basic packet information along - * with a handle to this packet for subsequent use with - * RAIL_PeekRxPacket(), RAIL_GetRxPacketDetails(), - * RAIL_HoldRxPacket(), and RAIL_ReleaseRxPacket() as needed. - * - * If \ref RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, this event also occurs - * for packets with CRC errors. - */ - case RAIL_EVENT_RX_PACKET_RECEIVED_SHIFT: { - /* Get RX packet that got signaled */ - RAIL_RxPacketInfo_t rxPacketInfo; - RAIL_RxPacketHandle_t rxHandle = RAIL_GetRxPacketInfo(gRailHandle, - RAIL_RX_PACKET_HANDLE_NEWEST, - &rxPacketInfo - ); - - /* Only process the packet if it had a correct CRC */ - if (rxPacketInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS) { - uint8_t header[4]; - RAIL_PeekRxPacket(gRailHandle, rxHandle, header, 4, 0); - - /* If this is an ACK, deal with it early */ - if ((header[0] == 5) && - (header[3] == current_tx_sequence) && - waiting_for_ack) { - /* Tell the radio to not ACK an ACK */ - RAIL_CancelAutoAck(gRailHandle); - waiting_for_ack = false; - /* Save the pending bit */ - last_ack_pending_bit = (header[1] & (1 << 4)) != 0; - /* Release packet */ - RAIL_ReleaseRxPacket(gRailHandle, rxHandle); - /* Tell the stack we got an ACK */ -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_ACK_RECV | (last_ack_pending_bit ? SL_ACK_PEND : 0)); -#else - SL_DEBUG_PRINT("rACK\n"); - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - last_ack_pending_bit ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE, - 1, - 1); -#endif - } else { - /* Get RSSI and LQI information about this packet */ - RAIL_RxPacketDetails_t rxPacketDetails; - rxPacketDetails.timeReceived.timePosition = RAIL_PACKET_TIME_DEFAULT; - rxPacketDetails.timeReceived.totalPacketBytes = 0; - RAIL_GetRxPacketDetails(gRailHandle, rxHandle, &rxPacketDetails); - -#ifdef MBED_CONF_RTOS_PRESENT - /* Drop this packet if we're out of space */ - if (((rx_queue_head + 1) % RF_QUEUE_SIZE) == rx_queue_tail) { - osSignalSet(rf_thread_id, SL_QUEUE_FULL); - RAIL_ReleaseRxPacket(gRailHandle, rxHandle); - break; - } - - /* Copy into queue */ - uint8_t *packetBuffer = (uint8_t *)rx_queue[rx_queue_head]; -#else - /* Packet going temporarily onto stack for bare-metal apps */ - uint8_t packetBuffer[MAC_PACKET_MAX_LENGTH + MAC_PACKET_INFO_LENGTH]; -#endif - /* First two bytes are RSSI and LQI, respecitvely */ - packetBuffer[MAC_PACKET_OFFSET_RSSI] = (uint8_t)rxPacketDetails.rssi; - packetBuffer[MAC_PACKET_OFFSET_LQI] = (uint8_t)rxPacketDetails.lqi; - - /* Copy packet payload from circular FIFO into contiguous memory */ - RAIL_CopyRxPacket(&packetBuffer[MAC_PACKET_INFO_LENGTH], &rxPacketInfo); - - /* Release RAIL resources early */ - RAIL_ReleaseRxPacket(gRailHandle, rxHandle); - - /* Figure out whether we want to not ACK this packet */ - - /* - * packetBuffer[0] = length - * dataLength = length w/o length byte - * packetBuffer[1:2] = 0x61C9 -> 0b01100001 0b1100 1001 (version 1, dest 3, src 2, ACKreq, type = 1) - * [1] => b[0:2] frame type, b[3] = security enabled, b[4] = frame pending, b[5] = ACKreq, b[6] = intrapan - * [2] => b[2:3] destmode, b[4:5] version, b[6:7] srcmode - */ - if ((packetBuffer[MAC_PACKET_INFO_LENGTH + 1] & (1 << 5)) == 0) { - /* Cancel the ACK if the sender did not request one */ - RAIL_CancelAutoAck(gRailHandle); - } -#ifdef MBED_CONF_RTOS_PRESENT - rx_queue_head = (rx_queue_head + 1) % RF_QUEUE_SIZE; - osSignalSet(rf_thread_id, SL_RX_DONE); -#else - SL_DEBUG_PRINT("rPKT %d\n", packetBuffer[MAC_PACKET_INFO_LENGTH] - 2); - device_driver.phy_rx_cb(&packetBuffer[MAC_PACKET_INFO_LENGTH + 1], /* Data payload for Nanostack starts at FCS */ - packetBuffer[MAC_PACKET_INFO_LENGTH] - 2, /* Payload length is part of frame, but need to subtract CRC bytes */ - packetBuffer[MAC_PACKET_OFFSET_LQI], /* LQI in second byte */ - packetBuffer[MAC_PACKET_OFFSET_RSSI], /* RSSI in first byte */ - rf_radio_driver_id); -#endif - } - } - } - break; - /* Event for preamble detection */ - case RAIL_EVENT_RX_PREAMBLE_DETECT_SHIFT: - break; - /* Event for detection of the first sync word */ - case RAIL_EVENT_RX_SYNC1_DETECT_SHIFT: - break; - /** Event for detection of the second sync word */ - case RAIL_EVENT_RX_SYNC2_DETECT_SHIFT: - break; - /* Event for detection of frame errors - * - * For efr32xg1x parts, frame errors include violations of variable length - * minimum/maximum limits, frame coding errors, and CRC errors. If \ref - * RAIL_RX_OPTION_IGNORE_CRC_ERRORS is set, \ref RAIL_EVENT_RX_FRAME_ERROR - * will not occur for CRC errors. - */ - case RAIL_EVENT_RX_FRAME_ERROR_SHIFT: - break; - /* Occurs when RX buffer is full */ - case RAIL_EVENT_RX_FIFO_OVERFLOW_SHIFT: - break; - /* Occurs when a packet is address filtered */ - case RAIL_EVENT_RX_ADDRESS_FILTERED_SHIFT: - break; - /* Occurs when an RX event times out */ - case RAIL_EVENT_RX_TIMEOUT_SHIFT: - break; - /* Occurs when the scheduled RX window ends */ - case RAIL_EVENT_RX_SCHEDULED_RX_END_SHIFT: - break; - /* An event for an aborted packet. It is triggered when a more specific - * reason isn't known for why the packet was aborted (e.g. - * \ref RAIL_EVENT_RX_ADDRESS_FILTERED). */ - case RAIL_EVENT_RX_PACKET_ABORTED_SHIFT: - break; - /* - * Occurs when the packet has passed any configured address and frame - * filtering options. - */ - case RAIL_EVENT_RX_FILTER_PASSED_SHIFT: - break; - /* Occurs when modem timing is lost */ - case RAIL_EVENT_RX_TIMING_LOST_SHIFT: - break; - /* Occurs when modem timing is detected */ - case RAIL_EVENT_RX_TIMING_DETECT_SHIFT: - break; - /* - * Indicates a Data Request is being received when using IEEE 802.15.4 - * functionality. This occurs when the command byte of an incoming frame is - * for a data request, which requests an ACK. This callback is called before - * the packet is fully received to allow the node to have more time to decide - * whether to set the frame pending in the outgoing ACK. This event only ever - * occurs if the RAIL IEEE 802.15.4 functionality is enabled. - * - * Call \ref RAIL_IEEE802154_GetAddress to get the source address of the - * packet. - */ - case RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND_SHIFT: - if (data_pending) { - RAIL_IEEE802154_SetFramePending(gRailHandle); - } - break; - - // TX Event Bitmasks - - /* - * Occurs when the transmit FIFO falls under the configured - * threshold value. It only occurs if a rising edge occurs across this - * threshold. This event does not occur on initailization or after resetting - * the transmit FIFO with RAIL_ResetFifo(). - * Call \ref RAIL_GetTxFifoSpaceAvailable to get the number of bytes - * available in the transmit FIFO at the time of the callback dispatch. - */ - case RAIL_EVENT_TX_FIFO_ALMOST_EMPTY_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TXFIFO_ERR); -#else - SL_DEBUG_PRINT("TX near empty (%d)\n", spaceAvailable); -#endif - break; - /* - * Interrupt level event to signify when the packet was sent. - * Call RAIL_GetTxPacketDetails() to get information about the packet - * that was transmitted. - * @note that this structure is only valid during the timeframe of the - * \ref RAIL_Config_t::eventsCallback. - */ - case RAIL_EVENT_TX_PACKET_SENT_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TX_DONE); -#else - if (device_driver.phy_tx_done_cb != NULL) { - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - // Normally we'd switch on ACK requested here, but Nanostack does that for us. - PHY_LINK_TX_SUCCESS, - // Succeeded, so how many times we tried is really not relevant. - 1, - 1); - } -#endif - last_tx = RAIL_GetTime(); - radio_state = RADIO_RX; - break; - /* - * An interrupt level event to signify when the packet was sent. - * Call RAIL_GetTxPacketDetails() to get information about the packet - * that was transmitted. - * @note This structure is only valid during the timeframe of the - * \ref RAIL_Config_t::eventsCallback. - */ - case RAIL_EVENT_TXACK_PACKET_SENT_SHIFT: - break; - /* Occurs when a TX is aborted by the user */ - case RAIL_EVENT_TX_ABORTED_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TX_TIMEOUT); -#else - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); -#endif - waiting_for_ack = false; - radio_state = RADIO_RX; - break; - /* Occurs when a TX ACK is aborted by the user */ - case RAIL_EVENT_TXACK_ABORTED_SHIFT: - break; - /* Occurs when a TX is blocked by something like PTA or RHO */ - case RAIL_EVENT_TX_BLOCKED_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TX_TIMEOUT); -#else - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); -#endif - waiting_for_ack = false; - radio_state = RADIO_RX; - break; - /* Occurs when a TX ACK is blocked by something like PTA or RHO */ - case RAIL_EVENT_TXACK_BLOCKED_SHIFT: - break; - /* Occurs when the TX buffer underflows */ - case RAIL_EVENT_TX_UNDERFLOW_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TX_TIMEOUT); -#else - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); -#endif - waiting_for_ack = false; - radio_state = RADIO_RX; - break; - /* Occurs when the buffer used for TX acking underflows */ - case RAIL_EVENT_TXACK_UNDERFLOW_SHIFT: - break; - /* Occurs when CCA/CSMA/LBT succeeds */ - case RAIL_EVENT_TX_CHANNEL_CLEAR_SHIFT: - break; - /* Occurs when CCA/CSMA/LBT fails */ - case RAIL_EVENT_TX_CHANNEL_BUSY_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_TX_TIMEOUT); -#else - device_driver.phy_tx_done_cb(rf_radio_driver_id, - current_tx_handle, - PHY_LINK_CCA_FAIL, - 8, - 1); -#endif - waiting_for_ack = false; - radio_state = RADIO_RX; - break; - /* Occurs when a CCA check is being retried */ - case RAIL_EVENT_TX_CCA_RETRY_SHIFT: - break; - /** Occurs when a clear channel assessment (CCA) is begun */ - case RAIL_EVENT_TX_START_CCA_SHIFT: - break; - - // Scheduler Event Bitmasks: Not used - - /* Event for when the scheduler switches away from this configuration */ - case RAIL_EVENT_CONFIG_UNSCHEDULED_SHIFT: - break; - /* Event for when the scheduler switches to this configuration */ - case RAIL_EVENT_CONFIG_SCHEDULED_SHIFT: - break; - /* Event for when the status of the scheduler changes */ - case RAIL_EVENT_SCHEDULER_STATUS_SHIFT: - break; - - // Other Event Bitmasks - - /* - * Notifies the application that a calibration is needed. - * It occurs whenever the RAIL library detects that a - * calibration is needed. An application determines a valid - * window to call \ref RAIL_Calibrate(). - */ - case RAIL_EVENT_CAL_NEEDED_SHIFT: -#ifdef MBED_CONF_RTOS_PRESENT - osSignalSet(rf_thread_id, SL_CAL_REQ); -#else - SL_DEBUG_PRINT("!!!! Calling for calibration\n"); -#endif - break; - default: - break; - } - } - events = events >> 1; - index += 1; - } while (events != 0); -} - -NanostackRfPhy &NanostackRfPhy::get_default_instance() -{ - static NanostackRfPhyEfr32 rf_phy; - return rf_phy; -} diff --git a/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h b/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h deleted file mode 100644 index e06d761..0000000 --- a/features/nanostack/nanostack-interface/targets/TARGET_Silicon_Labs/TARGET_SL_RAIL/NanostackRfPhyEfr32.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com - * SPDX-License-Identifier: Apache-2.0 - * Licensed under the Apache License, Version 2.0 (the License); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an AS IS BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef NANOSTACK_PHY_EFR32_H_ -#define NANOSTACK_PHY_EFR32_H_ - -#include "mbed.h" -#include "NanostackRfPhy.h" - -class NanostackRfPhyEfr32 : public NanostackRfPhy { -public: - NanostackRfPhyEfr32(); - ~NanostackRfPhyEfr32(); - int8_t rf_register(); - void rf_unregister(); - void get_mac_address(uint8_t *mac); - void set_mac_address(uint8_t *mac); - uint32_t get_driver_version(); -}; - -#endif /* NANOSTACK_PHY_EFR32_H_ */ diff --git a/platform/source/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S b/platform/source/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S index 8455ec7..dfa5a2f 100644 --- a/platform/source/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S +++ b/platform/source/TARGET_CORTEX_M/TOOLCHAIN_IAR/except.S @@ -36,9 +36,9 @@ FAULT_TYPE_USAGE_FAULT EQU 0x40 PRESERVE8 - THUMB SECTION .text:CODE:NOROOT(2) + THUMB HardFault_Handler EXPORT HardFault_Handler @@ -111,7 +111,7 @@ #else B . #endif - ALIGN + ALIGNROM 2 #endif END diff --git a/rtos/tests/TESTS/host_tests/timing_drift_auto.py b/rtos/tests/TESTS/host_tests/timing_drift_auto.py new file mode 100644 index 0000000..65ac90a --- /dev/null +++ b/rtos/tests/TESTS/host_tests/timing_drift_auto.py @@ -0,0 +1,136 @@ +""" +mbed SDK +Copyright (c) 2011-2013 ARM Limited +SPDX-License-Identifier: Apache-2.0 + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +from mbed_host_tests import BaseHostTest +import time + + +class TimingDriftSync(BaseHostTest): + """ + This works as master-slave fashion + 1) Device says its booted up and ready to run the test, wait for host to respond + 2) Host sends the message to get the device current time i.e base time + + # + # * + # * | + #<---* DUT<- base_time | - round_trip_base_time ------ + # * | | + # * - | + # - | + # | | + # | | + # | - measurement_stretch | - nominal_time + # | | + # | | + # - | + # * - | + # * | | + #<---* DUT <-final_time | - round_trip_final_time------ + # * | + # * - + # + # + # As we increase the measurement_stretch, the error because of transport delay diminishes. + # The values of measurement_stretch is propotional to round_trip_base_time(transport delays) + # by factor time_measurement_multiplier.This multiplier is used is 80 to tolerate 2 sec of + # transport delay and test time ~ 180 secs + # + # Failure in timing can occur if we are ticking too fast or we are ticking too slow, hence we have + # min_range and max_range. if we cross on either side tests would be marked fail. The range is a function of + # tolerance/acceptable drift currently its 5%. + # + + """ + __result = None + mega = 1000000.0 + max_measurement_time = 180 + + # this value is obtained for measurements when there is 0 transport delay and we want accurancy of 5% + time_measurement_multiplier = 80 + + def _callback_timing_drift_check_start(self, key, value, timestamp): + self.round_trip_base_start = timestamp + self.send_kv("base_time", 0) + + def _callback_base_time(self, key, value, timestamp): + self.round_trip_base_end = timestamp + self.device_time_base = float(value) + self.round_trip_base_time = self.round_trip_base_end - self.round_trip_base_start + + self.log("Device base time {}".format(value)) + measurement_stretch = (self.round_trip_base_time * self.time_measurement_multiplier) + 5 + + if measurement_stretch > self.max_measurement_time: + self.log("Time required {} to determine device timer is too high due to transport delay, skipping".format(measurement_stretch)) + else: + self.log("sleeping for {} to measure drift accurately".format(measurement_stretch)) + time.sleep(measurement_stretch) + self.round_trip_final_start = time.time() + self.send_kv("final_time", 0) + + def _callback_final_time(self, key, value, timestamp): + self.round_trip_final_end = timestamp + self.device_time_final = float(value) + self.round_trip_final_time = self.round_trip_final_end - self.round_trip_final_start + self.log("Device final time {} ".format(value)) + + # compute the test results and send to device + results = "pass" if self.compute_parameter() else "fail" + self.send_kv(results, "0") + + def setup(self): + self.register_callback('timing_drift_check_start', self._callback_timing_drift_check_start) + self.register_callback('base_time', self._callback_base_time) + self.register_callback('final_time', self._callback_final_time) + + def compute_parameter(self, failure_criteria=0.05): + t_max = self.round_trip_final_end - self.round_trip_base_start + t_min = self.round_trip_final_start - self.round_trip_base_end + t_max_hi = t_max * (1 + failure_criteria) + t_max_lo = t_max * (1 - failure_criteria) + t_min_hi = t_min * (1 + failure_criteria) + t_min_lo = t_min * (1 - failure_criteria) + device_time = (self.device_time_final - self.device_time_base) / self.mega + + self.log("Compute host events") + self.log("Transport delay 0: {}".format(self.round_trip_base_time)) + self.log("Transport delay 1: {}".format(self.round_trip_final_time)) + self.log("DUT base time : {}".format(self.device_time_base)) + self.log("DUT end time : {}".format(self.device_time_final)) + + self.log("min_pass : {} , max_pass : {} for {}%%".format(t_max_lo, t_min_hi, failure_criteria * 100)) + self.log("min_inconclusive : {} , max_inconclusive : {}".format(t_min_lo, t_max_hi)) + self.log("Time reported by device: {}".format(device_time)) + + if t_max_lo <= device_time <= t_min_hi: + self.log("Test passed !!!") + self.__result = True + elif t_min_lo <= device_time <= t_max_hi: + self.log("Test inconclusive due to transport delay, retrying") + self.__result = False + else: + self.log("Time outside of passing range. Timing drift seems to be present !!!") + self.__result = False + return self.__result + + def result(self): + return self.__result + + def teardown(self): + pass diff --git a/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp b/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp index a405ca7..48839f0 100644 --- a/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp +++ b/rtos/tests/TESTS/mbedmicro-rtos-mbed/basic/main.cpp @@ -23,10 +23,14 @@ #include "utest/utest.h" #include "unity/unity.h" -#if defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER +#if !DEVICE_USTICKER #error [NOT_SUPPORTED] UsTicker need to be enabled for this test. #else +#if defined(SKIP_TIME_DRIFT_TESTS) +#error [NOT_SUPPORTED] timing accuracy tests skipped +#endif // defined(SKIP_TIME_DRIFT_TESTS + using utest::v1::Case; using std::milli; using std::micro; @@ -122,5 +126,5 @@ utest::v1::Harness::run(specification); } -#endif // defined(SKIP_TIME_DRIFT_TESTS) || !DEVICE_USTICKER +#endif // !DEVICE_USTICKER #endif // defined(MBED_RTOS_SINGLE_THREAD) || !defined(MBED_CONF_RTOS_PRESENT) diff --git a/targets/targets.json b/targets/targets.json index 3dfc49a..4667ead 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -1709,11 +1709,7 @@ "components_add": [ "SPIF" ], - "device_name": "STM32F413RHTx", - "mbed_rom_start": "0x08000000", - "mbed_rom_size": "0x180000", - "mbed_ram_start": "0x200001D8", - "mbed_ram_size": "0x50000" + "device_name": "STM32F413RHTx" }, "DISCO_F413ZH": { "inherits": [ diff --git a/tools/test/examples/examples.json b/tools/test/examples/examples.json index 8ca728a..4fa8c6e 100644 --- a/tools/test/examples/examples.json +++ b/tools/test/examples/examples.json @@ -339,13 +339,13 @@ "sub-repo-example": false, "subs": [], "features" : [], - "targets" : ["ARM_MUSCA_A1", "ARM_MUSCA_B1", "K64F", "CY8CKIT_064S2_4343W"], + "targets" : ["ARM_MUSCA_B1", "K64F"], "toolchains" : ["GCC_ARM", "ARM"], "exporters": [], "compile" : true, "export": true, "test" : true, - "baud_rate": 115200, + "baud_rate": 9600, "compare_log": ["mbed-os-example-psa/tests/psa.log"], "auto-update" : true }, @@ -441,11 +441,7 @@ "sub-repo-example": false, "subs": [], "features" : [], - "targets" : ["CY8CKIT_062_WIFI_BT_PSA", - "K66F", - "NUCLEO_F429ZI", - "LPC55S69_NS" - ], + "targets" : [ "K66F", "NUCLEO_F429ZI", "ARM_MUSCA_B1"], "toolchains" : [], "exporters": [], "compile" : true,