pipewire-v4l2: the start of a v4l2 LD_PRELOAD library

This commit is contained in:
Wim Taymans 2021-10-06 20:24:08 +02:00
parent 57b3fe537e
commit e68fd36f90
8 changed files with 910 additions and 0 deletions

View file

@ -450,6 +450,9 @@ configure_file(output : 'config.h',
if not get_option('pipewire-jack').disabled()
subdir('pipewire-jack')
endif
if not get_option('pipewire-v4l2').disabled()
subdir('pipewire-v4l2')
endif
if alsa_dep.found()
subdir('pipewire-alsa/alsa-plugins')

View file

@ -50,6 +50,10 @@ option('pipewire-jack',
description: 'Enable pipewire-jack integration',
type: 'feature',
value: 'enabled')
option('pipewire-v4l2',
description: 'Enable pipewire-v4l2 integration',
type: 'feature',
value: 'enabled')
option('jack-devel',
description: 'Install jack development files',
type: 'boolean',

View file

@ -0,0 +1 @@
subdir('src')

View file

@ -0,0 +1,36 @@
pipewire_v4l2_sources = [
'pipewire-v4l2.c',
'v4l2-func.c',
]
pipewire_v4l2_c_args = [
# Meson enables large file support unconditionally, which redirect file
# operations to 64-bit versions. This results in some symbols being
# renamed, for instance open() being renamed to open64(). As the V4L2
# adaptation wrapper needs to provide both 32-bit and 64-bit versions of
# file operations, disable transparent large file support.
'-U_FILE_OFFSET_BITS',
'-D_FILE_OFFSET_BITS=32',
'-D_LARGEFILE64_SOURCE',
'-fvisibility=hidden',
]
libv4l2_path = modules_install_dir / 'v4l2'
libv4l2_path_dlopen = modules_install_dir_dlopen / 'v4l2'
tools_config = configuration_data()
tools_config.set('LIBV4L2_PATH', libv4l2_path_dlopen)
configure_file(input : 'pw-v4l2.in',
output : 'pw-v4l2',
configuration : tools_config,
install_dir : pipewire_bindir)
pipewire_v4l2 = shared_library('pw-v4l2',
pipewire_v4l2_sources,
c_args : pipewire_v4l2_c_args,
include_directories : [configinc],
dependencies : [pipewire_dep, mathlib, dl_lib],
install : true,
install_dir : libv4l2_path,
)

View file

@ -0,0 +1,623 @@
/* PipeWire
*
* Copyright © 2021 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <linux/videodev2.h>
#include "pipewire-v4l2.h"
#include <spa/utils/result.h>
#include <pipewire/pipewire.h>
PW_LOG_TOPIC_STATIC(v4l2_log_topic, "v4l2");
#define PW_LOG_TOPIC_DEFAULT v4l2_log_topic
struct globals {
pthread_mutex_t lock;
struct fops old_fops;
struct spa_list files;
struct spa_list maps;
};
static struct globals globals;
struct map {
struct spa_list link;
int ref;
void *addr;
struct file *file;
};
struct file {
struct spa_list link;
int ref;
struct pw_properties *props;
struct pw_thread_loop *loop;
struct pw_loop *l;
struct pw_context *context;
struct pw_core *core;
struct spa_hook core_listener;
int pending_sync;
int last_sync;
int last_res;
bool error;
struct pw_registry *registry;
struct spa_hook registry_listener;
int fd;
int other_fd;
};
#define ATOMIC_DEC(s) __atomic_sub_fetch(&(s), 1, __ATOMIC_SEQ_CST)
#define ATOMIC_INC(s) __atomic_add_fetch(&(s), 1, __ATOMIC_SEQ_CST)
static struct file *make_file(void)
{
struct file *file;
file = calloc(1, sizeof(*file));
if (file == NULL)
return NULL;
file->ref = 1;
spa_list_init(&file->link);
return file;
}
static void put_file(struct file *file)
{
pthread_mutex_lock(&globals.lock);
spa_list_append(&globals.files, &file->link);
pthread_mutex_unlock(&globals.lock);
}
static struct file *find_file(int fd)
{
struct file *f, *res = NULL;
pthread_mutex_lock(&globals.lock);
spa_list_for_each(f, &globals.files, link)
if (f->fd == fd) {
res = f;
ATOMIC_INC(f->ref);
break;
}
pthread_mutex_unlock(&globals.lock);
return res;
}
static void free_file(struct file *file)
{
struct map *m, *t;
pthread_mutex_lock(&globals.lock);
spa_list_remove(&file->link);
spa_list_for_each_safe(m, t, &globals.maps, link) {
if (m->file == file) {
spa_list_remove(&m->link);
free(m);
}
}
pthread_mutex_unlock(&globals.lock);
free(file);
}
static void unref_file(struct file *file)
{
if (ATOMIC_DEC(file->ref) <= 0)
free_file(file);
}
static struct map *make_map(struct file *file, void *addr)
{
struct map *map;
map = calloc(1, sizeof(*map));
if (map == NULL)
return NULL;
map->ref = 1;
map->file = file;
map->addr = addr;
spa_list_init(&map->link);
return map;
}
static void put_map(struct map *map)
{
pthread_mutex_lock(&globals.lock);
spa_list_append(&globals.maps, &map->link);
pthread_mutex_unlock(&globals.lock);
}
static struct map *find_map(void *addr)
{
struct map *m, *res = NULL;
pthread_mutex_lock(&globals.lock);
spa_list_for_each(m, &globals.maps, link)
if (m->addr == addr) {
res = m;
ATOMIC_INC(m->ref);
break;
}
pthread_mutex_unlock(&globals.lock);
return res;
}
static void free_map(struct map *map)
{
pthread_mutex_lock(&globals.lock);
spa_list_remove(&map->link);
pthread_mutex_unlock(&globals.lock);
free(map);
}
static void unref_map(struct map *map)
{
if (ATOMIC_DEC(map->ref) <= 0)
free_map(map);
}
static void on_sync_reply(void *data, uint32_t id, int seq)
{
struct file *file = data;
if (id != PW_ID_CORE)
return;
file->last_sync = seq;
if (file->pending_sync == seq)
pw_thread_loop_signal(file->loop, false);
}
static void on_error(void *data, uint32_t id, int seq, int res, const char *message)
{
struct file *file = data;
pw_log_warn("%p: error id:%u seq:%d res:%d (%s): %s", file,
id, seq, res, spa_strerror(res), message);
if (id == PW_ID_CORE) {
file->error = true;
file->last_res = res;
}
pw_thread_loop_signal(file->loop, false);
}
static const struct pw_core_events core_events = {
PW_VERSION_CORE_EVENTS,
.done = on_sync_reply,
.error = on_error,
};
static void registry_event_global(void *data, uint32_t id,
uint32_t permissions, const char *type, uint32_t version,
const struct spa_dict *props)
{
}
static void registry_event_global_remove(void *object, uint32_t id)
{
}
static const struct pw_registry_events registry_events = {
PW_VERSION_REGISTRY_EVENTS,
.global = registry_event_global,
.global_remove = registry_event_global_remove,
};
static int v4l2_openat(int dirfd, const char *path, int oflag, mode_t mode)
{
int res;
int fds[2];
struct file *file;
if (!spa_strstartswith(path, "/dev/video0"))
return globals.old_fops.openat(dirfd, path, oflag, mode);
if ((file = make_file()) == NULL)
return -1;
file->props = pw_properties_new(
PW_KEY_CLIENT_API, "v4l2",
NULL);
file->loop = pw_thread_loop_new("v4l2", NULL);
file->l = pw_thread_loop_get_loop(file->loop);
file->context = pw_context_new(file->l,
pw_properties_copy(file->props), 0);
if (file->context == NULL)
return -1;
pw_thread_loop_start(file->loop);
pw_thread_loop_lock(file->loop);
file->core = pw_context_connect(file->context,
pw_properties_copy(file->props), 0);
if (file->core == NULL)
return -1;
pw_core_add_listener(file->core,
&file->core_listener,
&core_events, file);
file->registry = pw_core_get_registry(file->core,
PW_VERSION_REGISTRY, 0);
pw_registry_add_listener(file->registry,
&file->registry_listener,
&registry_events, file);
pw_thread_loop_unlock(file->loop);
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
return -1;
res = file->fd = fds[0];
file->other_fd = fds[1];
pw_log_info("path:%s oflag:%d mode:%d -> %d (%s)", path, oflag, mode,
res, strerror(res < 0 ? errno : 0));
put_file(file);
return res;
}
static int v4l2_dup(int oldfd)
{
int res;
struct file *file;
if ((file = find_file(oldfd)) == NULL)
return globals.old_fops.dup(oldfd);
res = globals.old_fops.dup(oldfd);
pw_log_info("fd:%d -> %d (%s)", oldfd,
res, strerror(res < 0 ? errno : 0));
unref_file(file);
return res;
}
static int v4l2_close(int fd)
{
int res;
struct file *file;
if ((file = find_file(fd)) == NULL)
return globals.old_fops.close(fd);
res = globals.old_fops.close(file->fd);
res = globals.old_fops.close(file->other_fd);
if (file->loop)
pw_thread_loop_stop(file->loop);
if (file->registry) {
spa_hook_remove(&file->registry_listener);
pw_proxy_destroy((struct pw_proxy*)file->registry);
}
if (file->core) {
spa_hook_remove(&file->core_listener);
pw_core_disconnect(file->core);
}
if (file->context)
pw_context_destroy(file->context);
if (file->loop)
pw_thread_loop_destroy(file->loop);
free_file(file);
pw_log_info("fd:%d -> %d (%s)", fd,
res, strerror(res < 0 ? errno : 0));
return res;
}
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
static int vidioc_querycap(struct file *file, struct v4l2_capability *arg)
{
int res = 0;
spa_scnprintf((char*)arg->driver, sizeof(arg->driver), "%s", "pipewire");
spa_scnprintf((char*)arg->card, sizeof(arg->card), "%s", "cam1");
spa_scnprintf((char*)arg->bus_info, sizeof(arg->bus_info), "%s:%d", "pipewire", 1);
arg->version = KERNEL_VERSION(5, 2, 0);
arg->device_caps = V4L2_CAP_VIDEO_CAPTURE
| V4L2_CAP_STREAMING
| V4L2_CAP_EXT_PIX_FORMAT;
arg->capabilities = arg->device_caps | V4L2_CAP_DEVICE_CAPS;
memset(arg->reserved, 0, sizeof(arg->reserved));
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_enum_framesizes(struct file *file, struct v4l2_frmsizeenum *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_enum_fmt(struct file *file, struct v4l2_fmtdesc *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_g_fmt(struct file *file, struct v4l2_format *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_s_fmt(struct file *file, struct v4l2_format *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_try_fmt(struct file *file, struct v4l2_format *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_g_priority(struct file *file, enum v4l2_priority *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_s_priority(struct file *file, enum v4l2_priority *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_enuminput(struct file *file, struct v4l2_input *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_g_input(struct file *file, int *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_s_input(struct file *file, int *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_reqbufs(struct file *file, struct v4l2_requestbuffers *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_querybuf(struct file *file, struct v4l2_buffer *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_qbuf(struct file *file, struct v4l2_buffer *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_dqbuf(struct file *file, struct v4l2_buffer *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_streamon(struct file *file, int *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int vidioc_streamoff(struct file *file, int *arg)
{
int res = -ENOTTY;
pw_log_info("file:%p -> %d (%s)", file, res, spa_strerror(res));
return res;
}
static int v4l2_ioctl(int fd, unsigned long int request, void *arg)
{
int res;
struct file *file;
if ((file = find_file(fd)) == NULL)
return globals.old_fops.ioctl(fd, request, arg);
if (arg == NULL && (_IOC_DIR(request) & (_IOC_WRITE | _IOC_READ))) {
res = -EFAULT;
goto done;
}
switch (request & 0xffffffff) {
case VIDIOC_QUERYCAP:
res = vidioc_querycap(file, (struct v4l2_capability *)arg);
break;
case VIDIOC_ENUM_FRAMESIZES:
res = vidioc_enum_framesizes(file, (struct v4l2_frmsizeenum *)arg);
break;
case VIDIOC_ENUM_FMT:
res = vidioc_enum_fmt(file, (struct v4l2_fmtdesc *)arg);
break;
case VIDIOC_G_FMT:
res = vidioc_g_fmt(file, (struct v4l2_format *)arg);
break;
case VIDIOC_S_FMT:
res = vidioc_s_fmt(file, (struct v4l2_format *)arg);
break;
case VIDIOC_TRY_FMT:
res = vidioc_try_fmt(file, (struct v4l2_format *)arg);
break;
case VIDIOC_G_PRIORITY:
res = vidioc_g_priority(file, (enum v4l2_priority *)arg);
break;
case VIDIOC_S_PRIORITY:
res = vidioc_s_priority(file, (enum v4l2_priority *)arg);
break;
case VIDIOC_ENUMINPUT:
res = vidioc_enuminput(file, (struct v4l2_input *)arg);
break;
case VIDIOC_G_INPUT:
res = vidioc_g_input(file, (int *)arg);
break;
case VIDIOC_S_INPUT:
res = vidioc_s_input(file, (int *)arg);
break;
case VIDIOC_REQBUFS:
res = vidioc_reqbufs(file, (struct v4l2_requestbuffers *)arg);
break;
case VIDIOC_QUERYBUF:
res = vidioc_querybuf(file, (struct v4l2_buffer *)arg);
break;
case VIDIOC_QBUF:
res = vidioc_qbuf(file, (struct v4l2_buffer *)arg);
break;
case VIDIOC_DQBUF:
res = vidioc_dqbuf(file, (struct v4l2_buffer *)arg);
break;
case VIDIOC_STREAMON:
res = vidioc_streamon(file, (int *)arg);
break;
case VIDIOC_STREAMOFF:
res = vidioc_streamoff(file, (int *)arg);
break;
default:
res = -ENOTTY;
break;
}
done:
if (res < 0) {
errno = -res;
res = -1;
}
pw_log_info("fd:%d request:%lx nr:%d arg:%p -> %d (%s)",
fd, request, (int)_IOC_NR(request), arg,
res, strerror(res < 0 ? errno : 0));
unref_file(file);
return res;
}
static void *v4l2_mmap(void *addr, size_t length, int prot,
int flags, int fd, off64_t offset)
{
void *res;
struct file *file;
struct map *map;
if ((file = find_file(fd)) == NULL)
return globals.old_fops.mmap(addr, length, prot, flags, fd, offset);
if ((map = make_map(addr, file)) == NULL) {
res = MAP_FAILED;
goto exit;
}
res = MAP_FAILED;
errno = ENOTSUP;
pw_log_info("addr:%p length:%zu prot:%d flags:%d fd:%d offset:%"PRIi64" -> %p (%s)" ,
addr, length, prot, flags, fd, offset,
res, strerror(res == MAP_FAILED ? errno : 0));
put_map(map);
exit:
unref_file(file);
return res;
}
static int v4l2_munmap(void *addr, size_t length)
{
int res;
struct map *map;
if ((map = find_map(addr)) == NULL)
return globals.old_fops.munmap(addr, length);
res = globals.old_fops.munmap(addr, length);
pw_log_info("addr:%p length:%zu -> %d (%s)", addr, length,
res, strerror(res < 0 ? errno : 0));
unref_map(map);
return res;
}
const struct fops fops = {
.openat = v4l2_openat,
.dup = v4l2_dup,
.close = v4l2_close,
.ioctl = v4l2_ioctl,
.mmap = v4l2_mmap,
.munmap = v4l2_munmap,
};
static void reg(void) __attribute__ ((constructor));
static void reg(void)
{
globals.old_fops.openat = dlsym(RTLD_NEXT, "openat64");
globals.old_fops.dup = dlsym(RTLD_NEXT, "dup");
globals.old_fops.close = dlsym(RTLD_NEXT, "close");
globals.old_fops.ioctl = dlsym(RTLD_NEXT, "ioctl");
globals.old_fops.mmap = dlsym(RTLD_NEXT, "mmap64");
globals.old_fops.munmap = dlsym(RTLD_NEXT, "munmap");
pw_init(NULL, NULL);
PW_LOG_TOPIC_INIT(v4l2_log_topic);
pthread_mutex_init(&globals.lock, NULL);
spa_list_init(&globals.files);
spa_list_init(&globals.maps);
}

View file

@ -0,0 +1,38 @@
/* PipeWire
*
* Copyright © 2021 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <fcntl.h>
struct fops {
int (*openat)(int dirfd, const char *path, int oflag, mode_t mode);
int (*dup)(int oldfd);
int (*close)(int fd);
int (*ioctl)(int fd, unsigned long request, void *arg);
void *(*mmap)(void *addr, size_t length, int prot,
int flags, int fd, off64_t offset);
int (*munmap)(void *addr, size_t length);
};
extern const struct fops fops;

65
pipewire-v4l2/src/pw-v4l2.in Executable file
View file

@ -0,0 +1,65 @@
#!/bin/sh
# This file is part of PipeWire.
#
# Copyright © 2021 Wim Taymans
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice (including the next
# paragraph) shall be included in all copies or substantial portions of the
# Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
while getopts 'hr:vs:p:' param ; do
case $param in
r)
PIPEWIRE_REMOTE="$OPTARG"
export PIPEWIRE_REMOTE
;;
v)
if [ -z "$PIPEWIRE_DEBUG" ]; then
PIPEWIRE_DEBUG=3
else
PIPEWIRE_DEBUG=$(( PIPEWIRE_DEBUG + 1 ))
fi
export PIPEWIRE_DEBUG
;;
*)
echo "$0 - run v4l2 applications on PipeWire"
echo " "
echo "$0 [options] application [arguments]"
echo " "
echo "options:"
echo " -h show brief help"
echo " -r <remote> remote daemon name"
echo " -v verbose debug info"
exit 0
;;
esac
done
shift $(( OPTIND - 1 ))
if [ x"$LD_PRELOAD" = x ] ; then
LD_PRELOAD="@LIBV4L2_PATH@/libpw-v4l2.so"
else
LD_PRELOAD="$LD_PRELOAD @LIBV4L2_PATH@/libpw-v4l2.so"
fi
export LD_PRELOAD
exec "$@"

View file

@ -0,0 +1,140 @@
/* PipeWire
*
* Copyright © 2021 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "pipewire-v4l2.h"
#define SPA_EXPORT __attribute__((visibility("default")))
#define extract_va_arg(type, arg, last) \
{ \
va_list ap; \
va_start(ap, last); \
arg = va_arg(ap, type); \
va_end(ap); \
}
SPA_EXPORT int open(const char *path, int oflag, ...)
{
mode_t mode = 0;
if (oflag & O_CREAT || oflag & O_TMPFILE)
extract_va_arg(mode_t, mode, oflag);
return fops.openat(AT_FDCWD, path, oflag, mode);
}
/* _FORTIFY_SOURCE redirects open to __open_2 */
SPA_EXPORT int __open_2(const char *path, int oflag)
{
return open(path, oflag);
}
#ifndef open64
SPA_EXPORT int open64(const char *path, int oflag, ...)
{
mode_t mode = 0;
if (oflag & O_CREAT || oflag & O_TMPFILE)
extract_va_arg(mode_t, mode, oflag);
return fops.openat(AT_FDCWD, path, oflag | O_LARGEFILE, mode);
}
SPA_EXPORT int __open64_2(const char *path, int oflag)
{
return open(path, oflag);
}
#endif
SPA_EXPORT int openat(int dirfd, const char *path, int oflag, ...)
{
mode_t mode = 0;
if (oflag & O_CREAT || oflag & O_TMPFILE)
extract_va_arg(mode_t, mode, oflag);
return fops.openat(dirfd, path, oflag, mode);
}
SPA_EXPORT int __openat_2(int dirfd, const char *path, int oflag)
{
return openat(dirfd, path, oflag);
}
#ifndef openat64
SPA_EXPORT int openat64(int dirfd, const char *path, int oflag, ...)
{
mode_t mode = 0;
if (oflag & O_CREAT || oflag & O_TMPFILE)
extract_va_arg(mode_t, mode, oflag);
return fops.openat(dirfd, path, oflag | O_LARGEFILE, mode);
}
SPA_EXPORT int __openat64_2(int dirfd, const char *path, int oflag)
{
return openat(dirfd, path, oflag);
}
#endif
SPA_EXPORT int dup(int oldfd)
{
return fops.dup(oldfd);
}
SPA_EXPORT int close(int fd)
{
return fops.close(fd);
}
SPA_EXPORT void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset)
{
return fops.mmap(addr, length, prot, flags, fd, offset);
}
#ifndef mmap64
SPA_EXPORT void *mmap64(void *addr, size_t length, int prot, int flags,
int fd, off64_t offset)
{
return fops.mmap(addr, length, prot, flags, fd, offset);
}
#endif
SPA_EXPORT int munmap(void *addr, size_t length)
{
return fops.munmap(addr, length);
}
SPA_EXPORT int ioctl(int fd, unsigned long int request, ...)
{
void *arg;
extract_va_arg(void *, arg, request);
return fops.ioctl(fd, request, arg);
}