#pragma once #include #include #include #ifdef __cplusplus extern "C" { #endif // Syscalls that can be invoked from tasks. enum rt_syscall { RT_SYSCALL_SLEEP, RT_SYSCALL_SLEEP_PERIODIC, RT_SYSCALL_SEM_WAIT, RT_SYSCALL_SEM_TIMEDWAIT, RT_SYSCALL_SEM_POST, RT_SYSCALL_MUTEX_LOCK, RT_SYSCALL_MUTEX_TIMEDLOCK, RT_SYSCALL_MUTEX_UNLOCK, RT_SYSCALL_EVENT_WAIT, RT_SYSCALL_EVENT_TIMEDWAIT, RT_SYSCALL_EVENT_SET, RT_SYSCALL_YIELD, RT_SYSCALL_EXIT, }; // Syscalls that can be invoked from interrupts. enum rt_syscall_pendable { RT_SYSCALL_PENDABLE_SEM_POST, RT_SYSCALL_PENDABLE_EVENT_SET, RT_SYSCALL_PENDABLE_TICK, }; struct rt_sem; struct rt_mutex; struct rt_event; union rt_syscall_args { struct rt_syscall_args_sem_post { struct rt_sem *sem; int n; } sem_post; struct rt_syscall_args_event_set { struct rt_event *event; } event_set; }; struct rt_syscall_record { struct rt_syscall_record *next; union rt_syscall_args args; enum rt_syscall_pendable syscall; rt_atomic_flag pending; }; /* Architecture-dependent synchronous syscall triggers. The syscall should be * executed before this function returns. */ void rt_syscall_0(enum rt_syscall syscall); void rt_syscall_1(enum rt_syscall syscall, uintptr_t arg0); void rt_syscall_2(enum rt_syscall syscall, uintptr_t arg0, uintptr_t arg1); void rt_syscall_3(enum rt_syscall syscall, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2); static inline void rt_syscall_sleep(unsigned long ticks) { rt_syscall_1(RT_SYSCALL_SLEEP, ticks); } static inline void rt_syscall_sleep_periodic(unsigned long last_wake_tick, unsigned long period) { rt_syscall_2(RT_SYSCALL_SLEEP_PERIODIC, last_wake_tick, period); } static inline void rt_syscall_sem_wait(struct rt_sem *sem) { rt_syscall_1(RT_SYSCALL_SEM_WAIT, (uintptr_t)sem); } static inline bool rt_syscall_sem_timedwait(struct rt_sem *sem, unsigned long ticks) { bool success; rt_syscall_3(RT_SYSCALL_SEM_TIMEDWAIT, (uintptr_t)sem, ticks, (uintptr_t)&success); return success; } static inline void rt_syscall_sem_post(struct rt_sem *sem, int n) { rt_syscall_2(RT_SYSCALL_SEM_POST, (uintptr_t)sem, (uintptr_t)n); } static inline void rt_syscall_mutex_lock(struct rt_mutex *mutex) { rt_syscall_1(RT_SYSCALL_MUTEX_LOCK, (uintptr_t)mutex); } static inline bool rt_syscall_mutex_timedlock(struct rt_mutex *mutex, unsigned long ticks) { bool success; rt_syscall_3(RT_SYSCALL_MUTEX_TIMEDLOCK, (uintptr_t)mutex, ticks, (uintptr_t)&success); return success; } static inline void rt_syscall_mutex_unlock(struct rt_mutex *mutex) { rt_syscall_1(RT_SYSCALL_MUTEX_UNLOCK, (uintptr_t)mutex); } static inline uint32_t rt_syscall_event_wait(struct rt_event *event, uint32_t bits) { rt_syscall_2(RT_SYSCALL_EVENT_WAIT, (uintptr_t)event, (uintptr_t)&bits); return bits; } static inline uint32_t rt_syscall_event_timedwait(struct rt_event *event, uint32_t bits, unsigned long ticks) { rt_syscall_3(RT_SYSCALL_EVENT_TIMEDWAIT, (uintptr_t)event, (uintptr_t)&bits, ticks); return bits; } static inline uint32_t rt_syscall_event_set(struct rt_event *event) { uint32_t bits; rt_syscall_2(RT_SYSCALL_EVENT_SET, (uintptr_t)event, (uintptr_t)&bits); return bits; } /* Enqueue a system call record. It will be processed in the pending syscall * handler, triggered by rt_syscall_pend. */ void rt_syscall_push(struct rt_syscall_record *record); /* Architecture-dependent trigger for the syscall handler that can only be * called from an interrupt. If called from an interrupt, the syscall should * run once no other interrupts are running, but before another task gets to * run. */ void rt_syscall_pend(void); /* Perform a single system call and return a new context to execute or NULL if * no context switch is required. */ void *rt_syscall_run(enum rt_syscall syscall, uintptr_t arg0, uintptr_t arg1, uintptr_t arg2); /* Perform all pending system calls and return a new context to execute or NULL * if no context switch is required. */ void *rt_syscall_run_pending(void); #ifdef __cplusplus } #endif