/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup sptests * * @brief Test for timer server with blocking calls. */ /* * Copyright (C) 2009, 2015 embedded brains GmbH & Co. KG * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include const char rtems_test_name[] = "SP 68"; /* forward declarations to avoid warnings */ rtems_task Init(rtems_task_argument argument); #define TIMER_COUNT 6 #define OBTAIN 0 #define RELEASE 1 #define INTERRUPT 2 #define DELAYED 3 #define SERVER_TRIGGERED 4 #define INTERRUPT_TRIGGERED 5 #define T0 0 #define T1 1 #define T2 2 #define T3 3 #define T4 4 #define T5 5 #define T6 6 static volatile bool obtain_try; static volatile bool obtain_done; static volatile bool release_happened; static volatile bool interrupt_happened; static volatile bool delayed_happened; static volatile bool server_triggered_happened; static volatile bool interrupt_triggered_happened; static rtems_id timer [TIMER_COUNT]; static rtems_id semaphore; static rtems_id mutex; static rtems_id message_queue; static rtems_id region; static rtems_id barrier; static void *region_item; static rtems_interval start; static rtems_id timer_server_id; static volatile enum resource_type { SEMAPHORE = 0, MUTEX, MESSAGE_QUEUE, REGION, EVENT, BARRIER, TASK_WAKE_AFTER } resource_type; static const char *const resource_type_desc [] = { "SEMAPHORE", "MUTEX", "MESSAGE QUEUE", "REGION", "EVENT", "BARRIER", "TASK WAKE AFTER" }; static void assert_time(rtems_interval expected) { rtems_test_assert((rtems_clock_get_ticks_since_boot() - start) == expected); } static void obtain_callback(rtems_id timer_id, void *arg) { rtems_status_code sc = RTEMS_SUCCESSFUL; char buf [1]; size_t size = sizeof(buf); void *new_region_item = NULL; rtems_event_set events = 0; assert_time(T1); rtems_test_assert( !release_happened && !interrupt_happened && !delayed_happened && !interrupt_triggered_happened && !server_triggered_happened ); obtain_try = true; switch (resource_type) { case SEMAPHORE: sc = rtems_semaphore_obtain(semaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT); break; case MUTEX: sc = rtems_semaphore_obtain(mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); break; case MESSAGE_QUEUE: sc = rtems_message_queue_receive( message_queue, buf, &size, RTEMS_WAIT, RTEMS_NO_TIMEOUT); break; case REGION: sc = rtems_region_get_segment( region, 1, RTEMS_WAIT, RTEMS_NO_TIMEOUT, &new_region_item); break; case EVENT: timer_server_id = rtems_task_self(); sc = rtems_event_receive( RTEMS_EVENT_0, RTEMS_EVENT_ALL | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &events); break; case BARRIER: sc = rtems_barrier_wait(barrier, RTEMS_NO_TIMEOUT); break; case TASK_WAKE_AFTER: sc = rtems_task_wake_after(T4 - T1); break; default: rtems_test_assert(false); break; } directive_failed(sc, "obtain"); obtain_done = true; } static void release_callback(rtems_id timer_id, void *arg) { rtems_status_code sc = RTEMS_SUCCESSFUL; char buf [1] = { 0 }; size_t size = sizeof(buf); uint32_t released = 0; assert_time(T4); rtems_test_assert( obtain_try && interrupt_happened && !delayed_happened && !interrupt_triggered_happened && !server_triggered_happened ); switch (resource_type) { case SEMAPHORE: sc = rtems_semaphore_release(semaphore); break; case MUTEX: sc = rtems_semaphore_release(mutex); break; case MESSAGE_QUEUE: sc = rtems_message_queue_send(message_queue, buf, size); break; case EVENT: sc = rtems_event_send(timer_server_id, RTEMS_EVENT_0); break; case BARRIER: sc = rtems_barrier_release(barrier, &released); break; case TASK_WAKE_AFTER: sc = RTEMS_SUCCESSFUL; break; default: rtems_test_assert(false); break; } directive_failed_with_level(sc, "release", 1); release_happened = true; } static void interrupt_triggered_callback(rtems_id timer_id, void *arg) { /* * This callback is scheduled to fire at T3, but is delayed due to the * blocked obtain callback. */ assert_time(T4); rtems_test_assert( obtain_done && release_happened && interrupt_happened && !server_triggered_happened ); interrupt_triggered_happened = true; } static void interrupt_callback(rtems_id timer_id, void *arg) { rtems_status_code sc = RTEMS_SUCCESSFUL; assert_time(T2); rtems_test_assert( obtain_try && !obtain_done && !release_happened && !delayed_happened && !interrupt_triggered_happened && !server_triggered_happened ); sc = rtems_timer_server_fire_after( timer [INTERRUPT_TRIGGERED], T3 - T2, interrupt_triggered_callback, NULL ); directive_failed_with_level(sc, "rtems_timer_server_fire_after", -1); interrupt_happened = true; } static void server_triggered_callback(rtems_id timer_id, void *arg) { assert_time(T5); rtems_test_assert( obtain_done && release_happened && interrupt_happened && delayed_happened && interrupt_triggered_happened ); server_triggered_happened = true; } static void delayed_callback(rtems_id timer_id, void *arg) { rtems_status_code sc = RTEMS_SUCCESSFUL; assert_time(T4); rtems_test_assert( obtain_done && release_happened && interrupt_happened && !server_triggered_happened ); sc = rtems_timer_server_fire_after( timer [SERVER_TRIGGERED], T5 - T4, server_triggered_callback, NULL ); directive_failed(sc, "rtems_timer_server_fire_after"); delayed_happened = true; } static void test_reset(void) { rtems_status_code sc = RTEMS_SUCCESSFUL; obtain_try = false; obtain_done = false; release_happened = false; interrupt_happened = false; delayed_happened = false; interrupt_triggered_happened = false; server_triggered_happened = false; /* Synchronize with tick */ sc = rtems_task_wake_after(1); directive_failed(sc, "rtems_task_wake_after"); start = rtems_clock_get_ticks_since_boot(); } static void test_case(enum resource_type rt) { rtems_status_code sc = RTEMS_SUCCESSFUL; printf("test case: %s\n", resource_type_desc [rt]); resource_type = rt; test_reset(); sc = rtems_timer_server_fire_after( timer [OBTAIN], T1 - T0, obtain_callback, NULL ); directive_failed(sc, "rtems_timer_server_fire_after"); sc = rtems_timer_fire_after( timer [INTERRUPT], T2 - T0, interrupt_callback, NULL ); directive_failed(sc, "rtems_timer_fire_after"); sc = rtems_timer_server_fire_after( timer [DELAYED], T3 - T0, delayed_callback, NULL ); directive_failed(sc, "rtems_timer_server_fire_after"); if (resource_type != REGION) { sc = rtems_timer_fire_after( timer [RELEASE], T4 - T0, release_callback, NULL ); directive_failed(sc, "rtems_timer_fire_after"); assert_time(T0); sc = rtems_task_wake_after(T6 - T0); directive_failed(sc, "task_wake_after"); } else { sc = rtems_task_wake_after(T4 - T0); directive_failed(sc, "task_wake_after"); assert_time(T4); rtems_test_assert( obtain_try && interrupt_happened && !delayed_happened && !interrupt_triggered_happened && !server_triggered_happened ); sc = rtems_region_return_segment(region, region_item); directive_failed(sc, "rtems_region_return_segment"); release_happened = true; sc = rtems_task_wake_after(T6 - T4); directive_failed(sc, "task_wake_after"); } assert_time(T6); rtems_test_assert( obtain_done && interrupt_happened && release_happened && delayed_happened && interrupt_triggered_happened && server_triggered_happened ); } rtems_task Init(rtems_task_argument argument) { rtems_status_code sc = RTEMS_SUCCESSFUL; char region_area [256]; enum resource_type rt = SEMAPHORE; void *new_region_item = NULL; size_t i = 0; TEST_BEGIN(); for (i = 0; i < TIMER_COUNT; ++i) { sc = rtems_timer_create( rtems_build_name('T', 'I', 'M', '0' + i), &timer [i] ); directive_failed(sc, "rtems_timer_create"); } sc = rtems_timer_initiate_server( RTEMS_MINIMUM_PRIORITY, RTEMS_MINIMUM_STACK_SIZE, RTEMS_DEFAULT_ATTRIBUTES ); directive_failed(sc, "rtems_timer_initiate_server"); sc = rtems_semaphore_create( rtems_build_name('S', 'E', 'M', 'A'), 0, RTEMS_LOCAL | RTEMS_FIFO | RTEMS_COUNTING_SEMAPHORE, 0, &semaphore ); directive_failed(sc, "rtems_semaphore_create"); sc = rtems_semaphore_create( rtems_build_name('M', 'U', 'T', 'X'), 0, RTEMS_LOCAL | RTEMS_FIFO | RTEMS_SIMPLE_BINARY_SEMAPHORE, 0, &mutex ); directive_failed(sc, "rtems_semaphore_create"); sc = rtems_message_queue_create( rtems_build_name('M', 'S', 'G', 'Q'), 1, 1, RTEMS_LOCAL | RTEMS_FIFO, &message_queue ); directive_failed(sc, "rtems_message_queue_create"); sc = rtems_region_create( rtems_build_name('R', 'E', 'G', 'I'), region_area, sizeof(region_area), 1, RTEMS_LOCAL | RTEMS_FIFO, ®ion ); directive_failed(sc, "rtems_region_create"); do { region_item = new_region_item; sc = rtems_region_get_segment( region, 1, RTEMS_NO_WAIT, 0, &new_region_item); } while (sc == RTEMS_SUCCESSFUL); sc = rtems_barrier_create( rtems_build_name('B', 'A', 'R', 'R'), RTEMS_LOCAL | RTEMS_FIFO, 2, &barrier ); directive_failed(sc, "rtems_barrier_create"); while (rt <= TASK_WAKE_AFTER) { test_case(rt); ++rt; } TEST_END(); rtems_test_exit(0); } #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER #define CONFIGURE_MAXIMUM_TASKS 2 #define CONFIGURE_MAXIMUM_TIMERS TIMER_COUNT #define CONFIGURE_MAXIMUM_SEMAPHORES 2 #define CONFIGURE_MAXIMUM_MESSAGE_QUEUES 1 #define CONFIGURE_MESSAGE_BUFFER_MEMORY \ CONFIGURE_MESSAGE_BUFFERS_FOR_QUEUE(1, 1) #define CONFIGURE_MAXIMUM_REGIONS 1 #define CONFIGURE_MAXIMUM_BARRIERS 1 #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_RTEMS_INIT_TASKS_TABLE #define CONFIGURE_INIT #include