192 lines
3.3 KiB
C
192 lines
3.3 KiB
C
/*
|
|
* SPDX-License-Identifier: MIT
|
|
*
|
|
* Copyright (C) 2019 Philippe Gerum <rpm@xenomai.org>
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <poll.h>
|
|
#include <fcntl.h>
|
|
#include <evl/flags.h>
|
|
#include <sys/eventfd.h>
|
|
#include "internal.h"
|
|
|
|
#define __FLAGS_ACTIVE_MAGIC 0xb42bb42b
|
|
#define __FLAGS_DEAD_MAGIC 0
|
|
|
|
int evl_create_flags(struct evl_flags *flg, int clockfd,
|
|
int initval, int flags,
|
|
const char *fmt, ...)
|
|
{
|
|
int fd;
|
|
|
|
if (!eshi_is_initialized())
|
|
return -ENXIO;
|
|
|
|
switch (clockfd) {
|
|
case EVL_CLOCK_MONOTONIC:
|
|
flg->active.clock = CLOCK_MONOTONIC;
|
|
break;
|
|
case EVL_CLOCK_REALTIME:
|
|
flg->active.clock = CLOCK_REALTIME;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
fd = eventfd(initval, EFD_CLOEXEC | EFD_NONBLOCK);
|
|
if (fd < 0)
|
|
return -errno;
|
|
|
|
flg->active.fd = fd;
|
|
flg->magic = __FLAGS_ACTIVE_MAGIC;
|
|
|
|
return fd;
|
|
}
|
|
|
|
int evl_close_flags(struct evl_flags *flg)
|
|
{
|
|
if (flg->magic == __FLAGS_UNINIT_MAGIC)
|
|
return 0;
|
|
|
|
if (flg->magic != __FLAGS_ACTIVE_MAGIC)
|
|
return -EINVAL;
|
|
|
|
close(flg->active.fd);
|
|
flg->magic = __FLAGS_DEAD_MAGIC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int check_sanity(struct evl_flags *flg)
|
|
{
|
|
int efd;
|
|
|
|
if (flg->magic == __FLAGS_UNINIT_MAGIC) {
|
|
efd = evl_create_flags(flg,
|
|
flg->uninit.clockfd,
|
|
flg->uninit.initval,
|
|
flg->uninit.flags,
|
|
flg->uninit.name);
|
|
return efd < 0 ? efd : 0;
|
|
}
|
|
|
|
return flg->magic != __FLAGS_ACTIVE_MAGIC ? -EINVAL : 0;
|
|
}
|
|
|
|
static int timedwait_flags(struct evl_flags *flg,
|
|
const struct timespec *timeout,
|
|
int *r_bits)
|
|
{
|
|
const struct timespec *tp = timeout;
|
|
struct timespec ts, now;
|
|
struct pollfd pollfd;
|
|
uint64_t val;
|
|
int ret;
|
|
|
|
if (!tp || (tp->tv_sec == 0 && tp->tv_nsec == 0))
|
|
goto poll;
|
|
|
|
for (;;) {
|
|
if (tp) {
|
|
clock_gettime(flg->active.clock, &now);
|
|
ts = *timeout;
|
|
timespec_sub(&ts, &now);
|
|
if (ts.tv_sec < 0) {
|
|
ts.tv_sec = 0;
|
|
ts.tv_nsec = 0;
|
|
}
|
|
tp = &ts;
|
|
}
|
|
poll:
|
|
pollfd.fd = flg->active.fd;
|
|
pollfd.events = POLLIN;
|
|
pollfd.revents = 0;
|
|
ret = ppoll(&pollfd, 1, tp, NULL);
|
|
if (ret < 0)
|
|
return -errno;
|
|
|
|
if (ret == 0)
|
|
break;
|
|
|
|
if (!(pollfd.revents & POLLIN))
|
|
return -EINVAL;
|
|
|
|
ret = read(flg->active.fd, &val, sizeof(val));
|
|
if (ret > 0) {
|
|
*r_bits = (unsigned int)(val & ~0);
|
|
return 0;
|
|
}
|
|
|
|
if (errno != -EAGAIN)
|
|
return -errno;
|
|
}
|
|
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
int evl_timedwait_flags(struct evl_flags *flg,
|
|
const struct timespec *timeout,
|
|
int *r_bits)
|
|
{
|
|
int ret;
|
|
|
|
if (timeout == NULL)
|
|
return -EINVAL;
|
|
|
|
if (timeout->tv_sec < 0 || timeout->tv_nsec >= 1000000000L)
|
|
return -EINVAL;
|
|
|
|
ret = check_sanity(flg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return timedwait_flags(flg, timeout, r_bits);
|
|
}
|
|
|
|
int evl_wait_flags(struct evl_flags *flg, int *r_bits)
|
|
{
|
|
int ret;
|
|
|
|
ret = check_sanity(flg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return timedwait_flags(flg, NULL, r_bits);
|
|
}
|
|
|
|
int evl_trywait_flags(struct evl_flags *flg, int *r_bits)
|
|
{
|
|
struct timespec zerotime = { .tv_sec = 0, .tv_nsec = 0};
|
|
int ret;
|
|
|
|
ret = check_sanity(flg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = timedwait_flags(flg, &zerotime, r_bits);
|
|
if (ret == -ETIMEDOUT)
|
|
return -EAGAIN;
|
|
|
|
return ret;
|
|
}
|
|
|
|
int evl_post_flags(struct evl_flags *flg, int bits)
|
|
{
|
|
uint64_t val = (uint64_t)(unsigned int)bits;
|
|
int ret;
|
|
|
|
ret = check_sanity(flg);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = write(flg->active.fd, &val, sizeof(val));
|
|
if (ret != sizeof(val))
|
|
return -errno;
|
|
|
|
return 0;
|
|
}
|