summaryrefslogtreecommitdiff
path: root/lib/events.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/events.c')
-rw-r--r--lib/events.c183
1 files changed, 183 insertions, 0 deletions
diff --git a/lib/events.c b/lib/events.c
new file mode 100644
index 0000000..6840c19
--- /dev/null
+++ b/lib/events.c
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Generic Event Handling
+ *
+ * Copyright (C) 2018 Laurent Pinchart
+ *
+ * This file comes from the omap3-isp-live project
+ * (git://git.ideasonboard.org/omap3-isp-live.git)
+ *
+ * Copyright (C) 2010-2011 Ideas on board SPRL
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ */
+
+#define _DEFAULT_SOURCE
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/select.h>
+
+#include "events.h"
+#include "list.h"
+#include "tools.h"
+
+#define SELECT_TIMEOUT 2000 /* in milliseconds */
+
+struct event_fd {
+ struct list_entry list;
+
+ int fd;
+ enum event_type type;
+ void (*callback)(void *priv);
+ void *priv;
+};
+
+void events_watch_fd(struct events *events, int fd, enum event_type type,
+ void(*callback)(void *), void *priv)
+{
+ struct event_fd *event;
+
+ event = malloc(sizeof *event);
+ if (event == NULL)
+ return;
+
+ event->fd = fd;
+ event->type = type;
+ event->callback = callback;
+ event->priv = priv;
+
+ switch (event->type) {
+ case EVENT_READ:
+ FD_SET(fd, &events->rfds);
+ break;
+ case EVENT_WRITE:
+ FD_SET(fd, &events->wfds);
+ break;
+ case EVENT_EXCEPTION:
+ FD_SET(fd, &events->efds);
+ break;
+ }
+
+ events->maxfd = max(events->maxfd, fd);
+
+ list_append(&event->list, &events->events);
+}
+
+void events_unwatch_fd(struct events *events, int fd, enum event_type type)
+{
+ struct event_fd *event = NULL;
+ struct event_fd *entry;
+ int maxfd = 0;
+
+ list_for_each_entry(entry, &events->events, list) {
+ if (entry->fd == fd && entry->type == type)
+ event = entry;
+ else
+ maxfd = max(maxfd, entry->fd);
+ }
+
+ if (event == NULL)
+ return;
+
+ switch (event->type) {
+ case EVENT_READ:
+ FD_CLR(fd, &events->rfds);
+ break;
+ case EVENT_WRITE:
+ FD_CLR(fd, &events->wfds);
+ break;
+ case EVENT_EXCEPTION:
+ FD_CLR(fd, &events->efds);
+ break;
+ }
+
+ events->maxfd = maxfd;
+
+ list_remove(&event->list);
+ free(event);
+}
+
+static void events_dispatch(struct events *events, const fd_set *rfds,
+ const fd_set *wfds, const fd_set *efds)
+{
+ struct event_fd *event;
+ struct event_fd *next;
+
+ list_for_each_entry_safe(event, next, &events->events, list) {
+ if (event->type == EVENT_READ &&
+ FD_ISSET(event->fd, rfds))
+ event->callback(event->priv);
+ else if (event->type == EVENT_WRITE &&
+ FD_ISSET(event->fd, wfds))
+ event->callback(event->priv);
+ else if (event->type == EVENT_EXCEPTION &&
+ FD_ISSET(event->fd, efds))
+ event->callback(event->priv);
+
+ /* If the callback stopped events processing, we're done. */
+ if (events->done)
+ break;
+ }
+}
+
+bool events_loop(struct events *events)
+{
+ events->done = false;
+
+ while (!events->done) {
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+ int ret;
+
+ rfds = events->rfds;
+ wfds = events->wfds;
+ efds = events->efds;
+
+ ret = select(events->maxfd + 1, &rfds, &wfds, &efds, NULL);
+ if (ret < 0) {
+ /* EINTR means that a signal has been received, continue
+ * to the next iteration in that case.
+ */
+ if (errno == EINTR)
+ continue;
+
+ printf("error: select failed with %d\n", errno);
+ break;
+ }
+
+ events_dispatch(events, &rfds, &wfds, &efds);
+ }
+
+ return !events->done;
+}
+
+void events_stop(struct events *events)
+{
+ events->done = true;
+}
+
+void events_init(struct events *events)
+{
+ memset(events, 0, sizeof *events);
+
+ FD_ZERO(&events->rfds);
+ FD_ZERO(&events->wfds);
+ FD_ZERO(&events->efds);
+ events->maxfd = 0;
+ list_init(&events->events);
+}
+
+void events_cleanup(struct events *events)
+{
+ while (!list_empty(&events->events)) {
+ struct event_fd *event;
+
+ event = list_first_entry(&events->events, typeof(*event), list);
+ list_remove(&event->list);
+ free(event);
+ }
+}