| /* |
| * Copyright 2017, Data61, CSIRO (ABN 41 687 119 230) |
| * |
| * SPDX-License-Identifier: BSD-2-Clause |
| */ |
| |
| #include <autoconf.h> |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <sel4/sel4.h> |
| #include <sel4/arch/constants.h> |
| #include <camkes.h> |
| #include <camkes/irq.h> |
| #include <platsupport/time_manager.h> |
| #include <platsupport/local_time_manager.h> |
| #include <platsupport/irq.h> |
| #include <utils/util.h> |
| #include <sel4utils/sel4_zf_logif.h> |
| #include <simple/simple.h> |
| #include <camkes/io.h> |
| |
| #include "plat.h" |
| |
| /* ltimer for accessing timer devices */ |
| static ltimer_t ltimer; |
| /* time manager for timeout multiplexing */ |
| static time_manager_t time_manager; |
| |
| /* declare the memory needed for the clients |
| * this field tracks which timeouts have triggered |
| * for a specific client */ |
| uint32_t *client_state = NULL; |
| |
| /* Prototype for this function is not generated by the camkes templates yet */ |
| seL4_Word the_timer_get_sender_id(); |
| void the_timer_emit(unsigned int); |
| int the_timer_largest_badge(void); |
| |
| static ps_io_ops_t io_ops; |
| |
| static inline uint64_t current_time_ns() |
| { |
| uint64_t time; |
| int error = ltimer_get_time(<imer, &time); |
| ZF_LOGF_IF(error, "Failed to get time"); |
| return time; |
| } |
| |
| static inline unsigned int get_time_token(int cid, int tid) |
| { |
| return (unsigned int) cid * timers_per_client + tid; |
| } |
| |
| static int signal_client(uintptr_t token) |
| { |
| |
| int cid = ((int) token) / timers_per_client; |
| int tid = ((int) token) % timers_per_client; |
| |
| assert(client_state != NULL); |
| |
| client_state[cid] |= BIT(tid); |
| the_timer_emit(cid + 1); |
| |
| return 0; |
| } |
| |
| void time_server_ltimer_handle(UNUSED void *empty_token, ltimer_event_t ltimer_event) |
| { |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| error = tm_update(&time_manager); |
| ZF_LOGF_IF(error, "Failed to update time manager"); |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| } |
| |
| static int _oneshot_relative(int cid, int tid, uint64_t ns) |
| { |
| if (tid >= timers_per_client || tid < 0) { |
| ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client); |
| return -1; |
| } |
| |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| unsigned int id = get_time_token(cid, tid); |
| error = tm_register_rel_cb(&time_manager, ns, id, signal_client, (uintptr_t) id); |
| ZF_LOGF_IF(error, "Failed to set timeout"); |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| return 0; |
| } |
| |
| static int _oneshot_absolute(int cid, int tid, uint64_t ns) |
| { |
| if (tid >= timers_per_client || tid < 0) { |
| ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client); |
| return -1; |
| } |
| |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| unsigned int token = get_time_token(cid, tid); |
| |
| error = tm_register_abs_cb(&time_manager, ns, token, signal_client, (uintptr_t) token); |
| if (error == ETIME) { |
| signal_client(token); |
| error = 0; |
| } |
| ZF_LOGF_IF(error, "Failed to set timeout"); |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| return 0; |
| } |
| |
| static int _periodic(int cid, int tid, uint64_t ns) |
| { |
| if (tid >= timers_per_client || tid < 0) { |
| ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client); |
| return -1; |
| } |
| |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| unsigned int token = get_time_token(cid, tid); |
| error = tm_register_periodic_cb(&time_manager, ns, 0, token, signal_client, (uintptr_t) token); |
| ZF_LOGF_IF(error, "Failed to set timeout"); |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| return 0; |
| } |
| |
| static int _stop(int cid, int tid) |
| { |
| if (tid >= timers_per_client || tid < 0) { |
| ZF_LOGE("invalid tid, 0 >= %d >= %d\n", tid, timers_per_client); |
| return -1; |
| } |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| error = tm_deregister_cb(&time_manager, get_time_token(cid, tid)); |
| ZF_LOGF_IF(error, "Failed to deregister callback"); |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| return 0; |
| } |
| |
| static unsigned int _completed(int cid) |
| { |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock time server"); |
| |
| assert(client_state != NULL); |
| unsigned int ret = client_state[cid]; |
| client_state[cid] = 0; |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock time server"); |
| |
| return ret; |
| } |
| |
| static uint64_t _time(int cid) |
| { |
| return current_time_ns(); |
| } |
| |
| /* substract 1 from the badge as we started counting badges at 1 |
| * to avoid using the 0 badge */ |
| int the_timer_oneshot_relative(int id, uint64_t ns) |
| { |
| return _oneshot_relative(the_timer_get_sender_id() - 1, id, ns); |
| } |
| |
| int the_timer_oneshot_absolute(int id, uint64_t ns) |
| { |
| return _oneshot_absolute(the_timer_get_sender_id() - 1, id, ns); |
| } |
| |
| int the_timer_periodic(int id, uint64_t ns) |
| { |
| return _periodic(the_timer_get_sender_id() - 1, id, ns); |
| } |
| |
| int the_timer_stop(int id) |
| { |
| return _stop(the_timer_get_sender_id() - 1, id); |
| } |
| |
| unsigned int the_timer_completed() |
| { |
| return _completed(the_timer_get_sender_id() - 1); |
| } |
| |
| uint64_t the_timer_time() |
| { |
| return _time(the_timer_get_sender_id() - 1); |
| } |
| |
| void post_init() |
| { |
| int error = time_server_lock(); |
| ZF_LOGF_IF(error, "Failed to lock timer server"); |
| |
| error = camkes_io_ops(&io_ops); |
| ZF_LOGF_IF(error, "Failed to get camkes_io_ops"); |
| |
| error = ps_calloc(&(io_ops.malloc_ops), the_timer_largest_badge(), sizeof(*client_state), (void **) &client_state); |
| ZF_LOGF_IF(error, "Failed to allocate client state"); |
| |
| if (plat_pre_init) { |
| plat_pre_init(); |
| } |
| |
| error = ltimer_default_init(<imer, io_ops, time_server_ltimer_handle, NULL); |
| ZF_LOGF_IF(error, "Failed to init timer"); |
| |
| if (plat_post_init) { |
| plat_post_init(<imer); |
| } |
| |
| int num_timers = timers_per_client * the_timer_largest_badge(); |
| tm_init(&time_manager, <imer, &io_ops, num_timers); |
| for (unsigned int i = 0; i < num_timers; i++) { |
| error = tm_alloc_id_at(&time_manager, i); |
| ZF_LOGF_IF(error, "Failed to alloc id at %u\n", i); |
| } |
| |
| error = time_server_unlock(); |
| ZF_LOGF_IF(error, "Failed to unlock timer server"); |
| |
| set_putchar(putchar_putchar); |
| } |