summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile26
-rw-r--r--lib/xlnx-buffers.c80
-rw-r--r--lib/xlnx-buffers.h83
-rw-r--r--lib/xlnx-events.c269
-rw-r--r--lib/xlnx-plugin.c220
-rw-r--r--lib/xlnx-priv.h107
-rw-r--r--lib/xlnx-video.c388
7 files changed, 1173 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 0000000..cb91e0e
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,26 @@
+AR := $(CROSS_COMPILE)ar
+CC := $(CROSS_COMPILE)gcc
+CFLAGS := -O2 -W -Wall -Wno-unused-parameter -I$(KDIR)/usr/include -I$(topdir)/3rdparty/media-ctl/include -I$(topdir)/3rdparty/media-enum/include -I$(topdir)/include -I. -fPIC
+LDFLAGS := -L$(topdir)/3rdparty/media-ctl -L$(topdir)/3rdparty/media-enum
+LIBS := -lmediaenum -lmediactl -ldl
+
+OBJECTS := xlnx-buffers.o \
+ xlnx-events.o \
+ xlnx-plugin.o \
+ xlnx-video.o
+
+TARGET := libxlnxvideo.so
+
+all: $(TARGET)
+
+$(TARGET): $(OBJECTS)
+ $(CC) -o $@ -shared $^ $(LDFLAGS) $(LIBS)
+
+%.o : %.c
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+clean:
+ -$(RM) *.o
+ -$(RM) $(TARGET)
+
+install:
diff --git a/lib/xlnx-buffers.c b/lib/xlnx-buffers.c
new file mode 100644
index 0000000..34f873a
--- /dev/null
+++ b/lib/xlnx-buffers.c
@@ -0,0 +1,80 @@
+/*
+ * Xilinx Video Library - Buffers Pool
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xlnx-buffers.h"
+
+struct xlnx_video_buffers_pool *xlnx_video_buffers_pool_new(unsigned int nbufs)
+{
+ struct xlnx_video_buffer *buffers;
+ struct xlnx_video_buffers_pool *pool;
+ unsigned int i;
+
+ pool = malloc(sizeof *pool);
+ if (pool == NULL)
+ return NULL;
+
+ buffers = malloc(sizeof *buffers * nbufs);
+ if (buffers == NULL) {
+ free(pool);
+ return NULL;
+ }
+
+ memset(buffers, 0, sizeof *buffers * nbufs);
+
+ for (i = 0; i < nbufs; ++i)
+ buffers[i].index = i;
+
+ pool->nbufs = nbufs;
+ pool->buffers = buffers;
+
+ return pool;
+}
+
+void xlnx_video_buffers_pool_delete(struct xlnx_video_buffers_pool *pool)
+{
+ unsigned int i;
+
+ for (i = 0; i < pool->nbufs; ++i) {
+ if (pool->buffers[i].allocated)
+ free(pool->buffers[i].mem);
+ }
+
+ free(pool->buffers);
+ free(pool);
+}
+
+int xlnx_video_buffers_pool_alloc(struct xlnx_video_buffers_pool *pool,
+ size_t size, size_t align)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < pool->nbufs; ++i) {
+ ret = posix_memalign(&pool->buffers[i].mem, align, size);
+ if (ret != 0)
+ return -ret;
+
+ pool->buffers[i].size = size;
+ pool->buffers[i].allocated = true;
+ }
+
+ return 0;
+}
diff --git a/lib/xlnx-buffers.h b/lib/xlnx-buffers.h
new file mode 100644
index 0000000..2170150
--- /dev/null
+++ b/lib/xlnx-buffers.h
@@ -0,0 +1,83 @@
+/*
+ * Xilinx Video Library - Buffers Pool
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+#ifndef __XLNX_VIDEO_BUFFERS_POOL_H__
+#define __XLNX_VIDEO_BUFFERS_POOL_H__
+
+#include <stdbool.h>
+#include <time.h>
+
+/*
+ * struct xlnx_video_buffer - Video buffer information
+ * @index: Zero-based buffer index, limited to the number of buffers minus one
+ * @size: Size of the video memory, in bytes
+ * @bytesused: Number of bytes used by video data, smaller or equal to @size
+ * @timestamp: Time stamp at which the buffer has been captured
+ * @error: True if an error occured while capturing video data for the buffer
+ * @allocated: True if memory for the buffer has been allocated by the pool
+ * @mem: Video data memory
+ */
+struct xlnx_video_buffer
+{
+ unsigned int index;
+ unsigned int size;
+ unsigned int bytesused;
+ struct timeval timestamp;
+ bool error;
+ bool allocated;
+ void *mem;
+};
+
+struct xlnx_video_buffers_pool
+{
+ unsigned int nbufs;
+ struct xlnx_video_buffer *buffers;
+};
+
+/*
+ * xlnx_video_buffers_pool_new - Create a new buffers pool
+ * @nbufs: Number of buffers in the pool
+ *
+ * Allocate a new buffers pool with space for @nbufs buffers. Memory for the
+ * buffers is not allocated.
+ */
+struct xlnx_video_buffers_pool *xlnx_video_buffers_pool_new(unsigned int nbufs);
+
+/*
+ * xlnx_video_buffers_pool_delete - Delete a buffers pool
+ * @pool: Buffers pool
+ *
+ * Delete a buffers pool and free buffers memory if it has been allocated by
+ * the pool.
+ */
+void xlnx_video_buffers_pool_delete(struct xlnx_video_buffers_pool *pool);
+
+/*
+ * xlnx_video_buffers_pool_alloc - Allocate memory for buffers in a pool
+ * @pool: Buffers pool
+ * @size: Buffer size
+ * @align: Buffer memory alignment in bytes
+ *
+ * Allocate @size bytes of memory for each buffer aligned to a multiple of
+ * @align bytes. The alignment must be a power of 2.
+ *
+ * Return 0 on success or a negative error code on failure.
+ */
+int xlnx_video_buffers_pool_alloc(struct xlnx_video_buffers_pool *pool,
+ size_t size, size_t align);
+
+#endif /* __XLNX_VIDEO_BUFFERS_POOL_H__ */
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);
+ }
+}
diff --git a/lib/xlnx-plugin.c b/lib/xlnx-plugin.c
new file mode 100644
index 0000000..bd4553d
--- /dev/null
+++ b/lib/xlnx-plugin.c
@@ -0,0 +1,220 @@
+/*
+ * Xilinx Video Library - Plugin API
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+
+#include <dirent.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <mediactl.h>
+
+#include <xlnx-plugin.h>
+#include <xlnx-video.h>
+
+#include "xlnx-priv.h"
+
+#define __stringify(n) #n
+#define _stringify(n) __stringify(n)
+
+#define XLNX_VIDEO_PLUGIN_DIR "/usr/lib/xilinx/video/plugins"
+#define XLNX_VIDEO_PLUGIN_SYMBOL_NAME _stringify(XLNX_VIDEO_PLUGIN_SYMBOL)
+
+struct xlnx_video_plugin {
+ struct list_entry list;
+ void *handle;
+ const struct xlnx_video_plugin_ops *ops;
+ const struct xlnx_video_plugin_info *info;
+};
+
+struct xlnx_video_component *xlnx_video_plugin_scan(struct xlnx_video *xv,
+ struct media_device *mdev)
+{
+ const struct media_device_info *mdev_info = media_get_info(mdev);
+ struct xlnx_video_component *xvcomp;
+ struct xlnx_video_plugin *plugin;
+
+ xlnx_dbg(xv, "Searching plugin for media device %s (%s)\n",
+ mdev_info->model, mdev_info->bus_info);
+
+ list_for_each_entry(plugin, &xv->plugins, list) {
+ xlnx_dbg(xv, "Trying plugin `%s'\n", plugin->info->name);
+ xvcomp = plugin->ops->scan(xv, mdev);
+ if (xvcomp) {
+ xlnx_dbg(xv, "Plugin supports media device\n");
+ return xvcomp;
+ }
+ }
+
+ xlnx_dbg(xv, "No matching plugin found\n");
+ return NULL;
+}
+
+static int xlnx_video_plugin_load_file(struct xlnx_video *xv, const char *filename)
+{
+ const struct xlnx_video_plugin_ops *plugin_ops;
+ struct xlnx_video_plugin *plugin;
+ void *plugin_so;
+ int ret;
+
+ xlnx_dbg(xv, "Loading plugin from file %s\n", filename);
+
+ plugin_so = dlopen(filename, RTLD_LAZY);
+ if (!plugin_so) {
+ xlnx_dbg(xv, "Failed to open shared library (%s)\n", dlerror());
+ return -EINVAL;
+ }
+
+ plugin_ops = dlsym(plugin_so, XLNX_VIDEO_PLUGIN_SYMBOL_NAME);
+ if (!plugin_ops) {
+ xlnx_dbg(xv, "Plugin symbol not found (%s)\n", dlerror());
+ ret = -EINVAL;
+ goto error;
+ }
+
+ plugin = calloc(1, sizeof(*plugin));
+ if (!plugin) {
+ ret = -ENOMEM;
+ goto error;
+ }
+
+ plugin->handle = plugin_so;
+ plugin->ops = plugin_ops;
+ plugin->info = plugin_ops->info();
+
+ list_append(&plugin->list, &xv->plugins);
+
+ xlnx_dbg(xv, "Plugin loaded successfully\n");
+ return 0;
+
+error:
+ dlclose(plugin_so);
+ return ret;
+}
+
+/**
+ * \brief Load plugins from a given directory
+ * \param xv the library context
+ * \param path the full path name to the directory containing plugins
+ *
+ * This function attempts to load all plugins from the \a path directory. A
+ * plugin is a shared object whose name ends with '.so' and contains a symbol
+ * named '__xlnx_video_plugin' pointing to a struct xlnx_video_plugin_ops
+ * instance.
+ *
+ * When a valid plugin is found and loaded, call the init function supplied in
+ * the plugin operations structure.
+ *
+ * Shared objects that fail to load, don't export the plugin operations
+ * structure, don't implement the init function or return an error from their
+ * init function are ignored and not considered as a fatal error.
+ *
+ * \return Return the number of plugins successfully loaded, or a negative error
+ * code if a fatal error occurs.
+ */
+int xlnx_video_plugin_load_directory(struct xlnx_video *xv, const char *path)
+{
+ struct dirent *entry;
+ unsigned int count = 0;
+ size_t pathlen;
+ DIR *dir;
+ int ret = 0;
+
+ xlnx_dbg(xv, "Loading plugins from directory %s\n", path);
+
+ dir = opendir(path);
+ if (!dir) {
+ xlnx_dbg(xv, "Failed to open directory\n");
+ return -errno;
+ }
+
+ pathlen = strlen(path);
+
+ while ((entry = readdir(dir))) {
+ char *filename;
+ size_t len;
+
+ if (entry->d_type != DT_REG && entry->d_type != DT_LNK &&
+ entry->d_type != DT_UNKNOWN)
+ continue;
+
+ len = strlen(entry->d_name);
+ if (strcmp(entry->d_name + len - 3, ".so"))
+ continue;
+
+ filename = malloc(pathlen + 1 + len + 1);
+ if (!filename)
+ break;
+
+ strcpy(filename, path);
+ filename[pathlen] = '/';
+ strcpy(filename + pathlen + 1, entry->d_name);
+
+ ret = xlnx_video_plugin_load_file(xv, filename);
+ free(filename);
+
+ if (ret < 0)
+ continue;
+
+ count++;
+ }
+
+ closedir(dir);
+
+ return ret < 0 ? ret : (int)count;
+}
+
+/** \cond INTERNAL_API */
+
+/**
+ * \brief Initialize plugin support
+ * \param xv the library context
+ *
+ * Initialize plugin support and load plugins from the system default plugin
+ * directory ('/usr/lib/xilinx/video/plugins' by default).
+ *
+ * \return 0 on success or a negative error code if an error occurs.
+ */
+int xlnx_video_plugin_init(struct xlnx_video *xv)
+{
+ xlnx_video_plugin_load_directory(xv, XLNX_VIDEO_PLUGIN_DIR);
+
+ return 0;
+}
+
+/**
+ * \brief Initialize plugin support
+ * \param xv the library context
+ *
+ * Initialize plugin support and load plugins from the system default plugin
+ * directory ('/usr/lib/xilinx/video/plugins' by default).
+ *
+ * \return 0 on success or a negative error code if an error occurs.
+ */
+void xlnx_video_plugin_fini(struct xlnx_video *xv)
+{
+ struct xlnx_video_plugin *plugin, *next;
+
+ list_for_each_entry_safe(plugin, next, &xv->plugins, list) {
+ list_remove(&plugin->list);
+ dlclose(plugin->handle);
+ free(plugin);
+ }
+}
+
+/** \endcond */
diff --git a/lib/xlnx-priv.h b/lib/xlnx-priv.h
new file mode 100644
index 0000000..ca83425
--- /dev/null
+++ b/lib/xlnx-priv.h
@@ -0,0 +1,107 @@
+/*
+ * Xilinx Video Library - Internal API
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+#ifndef __XLNX_PRIV_H__
+#define __XLNX_PRIV_H__
+/** \cond INTERNAL_API */
+
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <xlnx-list.h>
+#include <xlnx-video.h>
+
+struct media_device;
+struct media_entity;
+struct media_entity_desc;
+
+/**
+ * \brief Xilinx video entity in a component
+ */
+struct xlnx_video_entity {
+ /** Entities list */
+ struct list_entry list;
+ /** Media entity */
+ struct media_entity *entity;
+ /** Media entity information */
+ const struct media_entity_desc *info;
+ /** Indicates whether this is the last entity in the component */
+ bool last;
+};
+
+/**
+ * \brief Xilinx video component
+ */
+struct xlnx_video_component {
+ /** The library context */
+ struct xlnx_video *xv;
+ /** List entry in the context's components list */
+ struct list_entry list;
+ /** List of video entities in the component */
+ struct list_entry entities;
+
+ /** Component type */
+ enum xlnx_video_component_type type;
+ /** Component name */
+ const char *name;
+
+ /**
+ * Entity at the input of the component (NULL if the component has no
+ * input entity).
+ */
+ struct xlnx_video_entity *input;
+ /**
+ * Entity at the output of the component (NULL if the component has no
+ * output entity).
+ */
+ struct xlnx_video_entity *output;
+};
+
+/**
+ * \brief Xilinx video library context
+ */
+struct xlnx_video {
+ /** List of video components in the system */
+ struct list_entry components;
+ /** Number of components in the components list */
+ unsigned int num_components;
+
+ /** List of loaded plugins */
+ struct list_entry plugins;
+
+ /** Event handler operations */
+ const struct xlnx_video_event_operations *event_ops;
+ /** Event handler private data */
+ void *event_handler;
+
+ /** Log level for library messages */
+ enum xlnx_log_level log_level;
+};
+
+int xlnx_video_plugin_init(struct xlnx_video *xv);
+void xlnx_video_plugin_fini(struct xlnx_video *xv);
+struct xlnx_video_component *xlnx_video_plugin_scan(struct xlnx_video *xv,
+ struct media_device *mdev);
+
+void xlnx_video_log_print(struct xlnx_video *xv, unsigned int level,
+ const char *func, const char *fmt, ...);
+
+#define xlnx_dbg(xv, fmt...) \
+ xlnx_video_log_print(xv, XLNX_LOG_DEBUG, __func__, fmt)
+
+/** \endcond INTERNAL_API */
+#endif /* __XLNX_PRIV_H__ */
diff --git a/lib/xlnx-video.c b/lib/xlnx-video.c
new file mode 100644
index 0000000..61c9f7c
--- /dev/null
+++ b/lib/xlnx-video.c
@@ -0,0 +1,388 @@
+/*
+ * Xilinx Video Library
+ *
+ * Copyright (C) 2014-2016 Ideas on board Oy
+ *
+ * Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library 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.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <media-enumerate.h>
+#include <mediactl.h>
+
+#include <xlnx-list.h>
+#include <xlnx-plugin.h>
+#include <xlnx-tools.h>
+#include <xlnx-video.h>
+
+#include "xlnx-priv.h"
+
+/**
+ * \mainpage
+ *
+ * The Xilinx video library offers a high-level API to enumerate video-related
+ * components in the system, assemble them in pipelines and stream video in and
+ * out of them.
+ *
+ * To use the library, an application must create a library context with a call
+ * to xlnx_video_create(). The function returns a context pointer that will be
+ * passed to all other library functions. Once a context isn't needed anymore it
+ * should be destroyed by a call to xlnx_video_destroy().
+ *
+ * Multiple contexts can be created and managed separately. However, as contexts
+ * store information about all media devices present in the system, accessing
+ * the same media devices from multiple contexts isn't supported and leads to
+ * undefined behaviour.
+ *
+ * Right after creation contexts are not ready for use. They must be initialized
+ * by registering event handling operations with xlnx_video_set_event_handler().
+ * Optionally logging can be initialized if desired with
+ * xlnx_video_set_log_level(). Finally, to complete initialization applications
+ * must call xlnx_video_setup() on the context. From that point onward the
+ * context can be used with other library functions.
+ */
+
+/* -----------------------------------------------------------------------------
+ * Components Discovery and Management
+ */
+
+/**
+ * \brief Get a list of components in the system
+ * \param xv the library context
+ * \param type the desired component type
+ * \param [out] components array of returned components
+ *
+ * Store a list of components matching the requested \a type in the
+ * \a components array.
+ *
+ * The \a type parameter is a bitmask of components type. Components matching
+ * one of the types in the bitmask will be included in the list, all other
+ * components will be ignored.
+ *
+ * The \a components parameter points to an array of components pointer.
+ * The function allocates an array of component pointers, fills it with
+ * pointers to all components matching the requested \a type, and stores it in
+ * the \a components pointer. The components array is allocated using the C
+ * memory allocator and must be freed by the caller with a call to free().
+ *
+ * The \a components array pointer can be NULL, in which case the function will
+ * not allocate any memory and will only return the number of matching
+ * components.
+ *
+ * If no component matches the requested \a type this function will not allocate
+ * any memory and will not touch the \a components parameter.
+ *
+ * Components must first be enumerated by a call to
+ * xlnx_video_component_enumerate() before calling this function.
+ *
+ * \return the total number of components matching the requested \a type found
+ * in the system and supported by the library (including the loaded plugins) on
+ * success, or a negative error code on failure.
+ */
+int xlnx_video_component_list(struct xlnx_video *xv,
+ enum xlnx_video_component_type type,
+ struct xlnx_video_component ***components)
+{
+ struct xlnx_video_component **comps;
+ struct xlnx_video_component *xvcomp;
+ unsigned int count;
+
+ if (!xv->num_components)
+ return 0;
+
+ count = 0;
+ list_for_each_entry(xvcomp, &xv->components, list) {
+ if (!(xvcomp->type & type))
+ continue;
+
+ count++;
+ }
+
+ if (!count || !components)
+ return count;
+
+ comps = calloc(count, sizeof(*comps));
+ if (comps == NULL)
+ return -ENOMEM;
+
+ count = 0;
+ list_for_each_entry(xvcomp, &xv->components, list) {
+ if (!(xvcomp->type & type))
+ continue;
+
+ comps[count++] = xvcomp;
+ }
+
+ *components = comps;
+ return count;
+}
+
+/**
+ * \brief Get the component name
+ * \param xvcomp the component
+ *
+ * \return the name of the component \a xvcomp. The name string is valid for the
+ * lifetime of the component.
+ */
+const char *xlnx_video_component_name(const struct xlnx_video_component *xvcomp)
+{
+ return xvcomp->name;
+}
+
+/**
+ * \brief Get the component type
+ * \param xvcomp the component
+ *
+ * \return the type of the component \a xvcomp.
+ */
+enum xlnx_video_component_type
+xlnx_video_component_type(const struct xlnx_video_component *xvcomp)
+{
+ return xvcomp->type;
+}
+
+/* -----------------------------------------------------------------------------
+ * Components Discovery
+ */
+
+static int xlnx_video_component_scan_media(struct xlnx_video *xv,
+ struct media_device *media)
+{
+ struct xlnx_video_component *xvcomp;
+
+ xvcomp = xlnx_video_plugin_scan(xv, media);
+ if (xvcomp) {
+ list_append(&xv->components, &xvcomp->list);
+ xv->num_components++;
+ }
+
+ return 0;
+}
+
+static int xlnx_video_component_scan(struct xlnx_video *xv)
+{
+ struct media_enumerate *media_enum;
+ struct media_device **devices;
+ unsigned int num_devices;
+ unsigned int i;
+ int ret;
+
+ /* Start by scanning media controller devices. */
+ media_enum = media_enumerate_new();
+ if (!media_enum)
+ return -ENOMEM;
+
+ ret = media_enumerate_scan(media_enum);
+ if (ret < 0)
+ goto done;
+
+ num_devices = media_enumerate_get_devices_count(media_enum);
+ if (num_devices == 0) {
+ ret = -ENXIO;
+ goto done;
+ }
+
+ devices = media_enumerate_get_devices(media_enum);
+
+ for (i = 0; i < num_devices; ++i) {
+ ret = xlnx_video_component_scan_media(xv, devices[i]);
+ if (ret < 0)
+ goto done;
+ }
+
+done:
+ media_enumerate_unref(media_enum);
+ return ret;
+}
+
+/**
+ * \brief Enumerate components in the system
+ * \param xv the library context
+ *
+ * This function enumerates all supported components in the system and cache
+ * them in the library context \a xv. It must be called once after initializing
+ * the context and before calling any other of the component-related functions.
+ *
+ * \return 0 on success or a negative error code otherwise.
+ */
+int xlnx_video_component_enumerate(struct xlnx_video *xv)
+{
+ int ret;
+
+ ret = xlnx_video_component_scan(xv);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Initialization
+ */
+
+/**
+ * \brief Create a working context for the library
+ *
+ * The Xilinx video library uses context objects to store internal data.
+ * Applications must call this function to create and initialize a context
+ * that can then be passed to other library functions.
+ *
+ * Multiple contexts can be created and managed separately. However, as contexts
+ * store information about all media devices present in the system, accessing
+ * the same media devices from multiple contexts isn't supported and leads to
+ * undefined behaviour.
+ *
+ * When a context isn't needed anymore applications must call the
+ * xlnx_video_destroy() function to destroy of it.
+ *
+ * \return a context pointer on success or NULL if an error occurs.
+ */
+struct xlnx_video *xlnx_video_create(void)
+{
+ struct xlnx_video *xv;
+
+ xv = calloc(1, sizeof *xv);
+ if (!xv)
+ return NULL;
+
+ list_init(&xv->components);
+ list_init(&xv->plugins);
+
+ xv->log_level = XLNX_LOG_NONE;
+
+ return xv;
+}
+
+/**
+ * \brief Setup a library context to make it ready for use
+ * \param xv the library context
+ *
+ * After creating and initializing a library context \a xv, applications must
+ * call this function to finish context setup and make it ready for use.
+ *
+ * \return 0 on success or a negative error code otherwise.
+ */
+int xlnx_video_setup(struct xlnx_video *xv)
+{
+ int ret;
+
+ ret = xlnx_video_plugin_init(xv);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * \brief Destroy a library context
+ * \param xv the library context
+ *
+ * Applications must call this function to destroy library contexts created
+ * by xlnx_video_create() when they're not needed anymore, and at the latest
+ * when the application exits. The context pointer \a xv becomes invalid when
+ * this function is called.
+ */
+void xlnx_video_destroy(struct xlnx_video *xv)
+{
+ xlnx_video_plugin_fini(xv);
+ free(xv);
+}
+
+/**
+ * \brief Register event handler operations
+ * \param xv the library context
+ * \param ops the event handling operations
+ * \param handler the event handler private data
+ *
+ * The library requires the application to provide an implementation of a set of
+ * operations to handle event notifications on file descriptors.
+ *
+ * This function sets the event handler operations \a ops and private data
+ * pointer \a handler for the library context. It must be called after creating
+ * a context with xlnx_video_create() and before initializing it with
+ * xlnx_video_init().
+ */
+void xlnx_video_set_event_handler(struct xlnx_video *xv,
+ const struct xlnx_video_event_operations *ops,
+ void *handler)
+{
+ xv->event_ops = ops;
+ xv->event_handler = handler;
+}
+
+/**
+ * \brief Retrieve the event handler private data registered with the context
+ * \param xv the library context
+ *
+ * \return the event handler private data pointer registered with the library
+ * context by a previous call to xlnx_video_set_event_handler().
+ */
+void *xlnx_video_get_event_handler_data(struct xlnx_video *xv)
+{
+ return xv->event_handler;
+}
+
+/**
+ * \brief Log a library message
+ * \param xv the library context
+ * \param level the message log level
+ * \param fmt the message format
+ * \param ... the message arguments
+ *
+ * Log the message specified by \a format and \a ... arguments using the library
+ * logging infrastructure. The message will be logged only if its \a level is
+ * lower than or equal to the \a xv library context current log level, as set by
+ * xlnx_log_set_level(). Otherwise the message will be dropped silently.
+ */
+void xlnx_video_log_print(struct xlnx_video *xv, unsigned int level,
+ const char *func, const char *fmt, ...)
+{
+ struct timespec ts;
+ va_list ap;
+
+ if (level == XLNX_LOG_NONE || level > xv->log_level)
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ printf("[%lu.%06lu] %s: ", ts.tv_sec, ts.tv_nsec / 1000, func);
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * \brief Set the library context log level
+ * \param xv the library context
+ * \param level the desired log level
+ *
+ * The Xilinx video library can print error, informational and debug messages
+ * to help debugging applications and library internals. Only messages whose log
+ * level is lower than or equal to the library context log level are printed,
+ * other messages are dropped silently
+ *
+ * This function sets the active log level for the context \a xv to \a level.
+ * The default log level is XLNX_LOG_NONE, which drops all log messages.
+ */
+void xlnx_video_set_log_level(struct xlnx_video *xv, enum xlnx_log_level level)
+{
+ xv->log_level = level;
+}