summaryrefslogtreecommitdiff
path: root/lib/xlnx-events.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xlnx-events.c')
-rw-r--r--lib/xlnx-events.c269
1 files changed, 269 insertions, 0 deletions
diff --git a/lib/xlnx-events.c b/lib/xlnx-events.c
new file mode 100644
index 0000000..933f27a
--- /dev/null
+++ b/lib/xlnx-events.c
@@ -0,0 +1,269 @@
+/*
+ * Generic Event Loop
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#define _DEFAULT_SOURCE
+#include <errno.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/select.h>
+
+#include <xlnx-events.h>
+#include <xlnx-list.h>
+#include <xlnx-tools.h>
+
+#include "xlnx-priv.h"
+
+#define SELECT_TIMEOUT 2000 /* in milliseconds */
+
+struct xlnx_events {
+ struct list_entry events;
+ bool done;
+
+ int maxfd;
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+};
+
+struct xlnx_event_fd {
+ struct list_entry list;
+
+ int fd;
+ int type;
+ void (*callback)(void *priv);
+ void *priv;
+};
+
+static void xlnx_events_watch_fd(void *handler, int fd, int event_type,
+ void(*callback)(void *), void *priv)
+{
+ struct xlnx_events *events = handler;
+ struct xlnx_event_fd *event;
+
+ event = malloc(sizeof *event);
+ if (event == NULL)
+ return;
+
+ event->fd = fd;
+ event->type = event_type;
+ event->callback = callback;
+ event->priv = priv;
+
+ switch (event->type) {
+ case POLLIN:
+ FD_SET(fd, &events->rfds);
+ break;
+ case POLLOUT:
+ FD_SET(fd, &events->wfds);
+ break;
+ case POLLERR:
+ FD_SET(fd, &events->efds);
+ break;
+ }
+
+ events->maxfd = max(events->maxfd, fd);
+
+ list_append(&event->list, &events->events);
+}
+
+static void xlnx_events_unwatch_fd(void *handler, int fd)
+{
+ struct xlnx_events *events = handler;
+ struct xlnx_event_fd *event = NULL;
+ struct xlnx_event_fd *entry;
+ int maxfd = 0;
+
+ list_for_each_entry(entry, &events->events, list) {
+ if (entry->fd == fd)
+ event = entry;
+ else
+ maxfd = max(maxfd, entry->fd);
+ }
+
+ if (event == NULL)
+ return;
+
+ switch (event->type) {
+ case POLLIN:
+ FD_CLR(fd, &events->rfds);
+ break;
+ case POLLOUT:
+ FD_CLR(fd, &events->wfds);
+ break;
+ case POLLERR:
+ FD_CLR(fd, &events->efds);
+ break;
+ }
+
+ events->maxfd = maxfd;
+
+ list_remove(&event->list);
+ free(event);
+}
+
+static void xlnx_events_dispatch(struct xlnx_events *events, const fd_set *rfds,
+ const fd_set *wfds, const fd_set *efds)
+{
+ struct xlnx_event_fd *event;
+ struct xlnx_event_fd *next;
+
+ list_for_each_entry_safe(event, next, &events->events, list) {
+ if (event->type == POLLIN && FD_ISSET(event->fd, rfds))
+ event->callback(event->priv);
+ else if (event->type == POLLOUT && FD_ISSET(event->fd, wfds))
+ event->callback(event->priv);
+ else if (event->type == POLLERR && FD_ISSET(event->fd, efds))
+ event->callback(event->priv);
+
+ /* If the callback stopped events processing, we're done. */
+ if (events->done)
+ break;
+ }
+}
+
+/**
+ * \brief Start and run the event loop
+ * \param xv the library context
+ *
+ * This function starts and runs the event loop synchronously, waiting for
+ * events and dispatching them as they arrive. It doesn't return until the loop
+ * is requested to stop by a call to xlnx_events_stop() or a fatal error occurs.
+ *
+ * \return false if the loop exited due to a stop request, or true if it exited
+ * due to a fatal error.
+ */
+bool xlnx_events_loop(struct xlnx_video *xv)
+{
+ struct xlnx_events *events = xlnx_video_get_event_handler_data(xv);
+
+ events->done = false;
+
+ while (!events->done) {
+ struct timeval timeout;
+ fd_set rfds;
+ fd_set wfds;
+ fd_set efds;
+ int ret;
+
+ timeout.tv_sec = SELECT_TIMEOUT / 1000;
+ timeout.tv_usec = (SELECT_TIMEOUT % 1000) * 1000;
+ rfds = events->rfds;
+ wfds = events->wfds;
+ efds = events->efds;
+
+ ret = select(events->maxfd + 1, &rfds, &wfds, &efds, &timeout);
+ 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;
+ }
+ if (ret == 0) {
+ /* select() should never time out as the library is
+ * supposed to process images continuously. A timeout is
+ * thus considered as a fatal error.
+ */
+ printf("error: select timeout\n");
+ break;
+ }
+
+ xlnx_events_dispatch(events, &rfds, &wfds, &efds);
+ }
+
+ return !events->done;
+}
+
+/**
+ * \brief Stop the event loop
+ * \param xv the library context
+ *
+ * Stop the running event loop started with xlnx_events_loop(). The loop is
+ * stopped asynchronously after processing the next event. This function doesn't
+ * wait for the loop to exit before returning.
+ */
+void xlnx_events_stop(struct xlnx_video *xv)
+{
+ struct xlnx_events *events = xlnx_video_get_event_handler_data(xv);
+
+ events->done = true;
+}
+
+static const struct xlnx_video_event_operations xlnx_events_ops = {
+ .watch = xlnx_events_watch_fd,
+ .unwatch = xlnx_events_unwatch_fd,
+};
+
+/**
+ * \brief Initialize event handling
+ * \param xv the library context
+ *
+ * This function registers event handling operations with the library context
+ * \a xv. The event handling implementation is based on a select() loop that is
+ * controlled through the xlnx_events_loop() and xlnx_events_stop() functions.
+ *
+ * Before destroying the library context event handling must be cleaned up with
+ * a call to xlnx_events_fini().
+ *
+ * \return 0 on success or a negative error code on failure.
+ */
+int xlnx_events_init(struct xlnx_video *xv)
+{
+ struct xlnx_events *events;
+
+ events = calloc(1, sizeof(*events));
+ if (!events)
+ return -ENOMEM;
+
+ FD_ZERO(&events->rfds);
+ FD_ZERO(&events->wfds);
+ FD_ZERO(&events->efds);
+ events->maxfd = 0;
+ list_init(&events->events);
+
+ xlnx_video_set_event_handler(xv, &xlnx_events_ops, events);
+ return 0;
+}
+
+/**
+ * \brief Clean up event handling
+ * \param xv the library context
+ *
+ * This function cleans up event handling. It must be called before destroying
+ * any library context previously initialized by a call to xlnx_events_init().
+ */
+void xlnx_events_fini(struct xlnx_video *xv)
+{
+ struct xlnx_events *events = xlnx_video_get_event_handler_data(xv);
+
+ if (events) {
+ xlnx_video_set_event_handler(xv, NULL, NULL);
+ free(events);
+ }
+}