/* * Xilinx Video Library - Plugin API * * Copyright (C) 2014-2016 Ideas on board Oy * * Contact: Laurent Pinchart * * 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 #include #include #include #include #include #include #include #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 */