aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am6
-rw-r--r--README8
-rw-r--r--configure.ac15
-rw-r--r--contrib/freebsd/include/linux/input-event-codes.h1
-rw-r--r--contrib/freebsd/include/linux/input.h11
-rw-r--r--contrib/freebsd/include/linux/v4l2-controls.h16
-rw-r--r--contrib/freebsd/include/linux/videodev2.h15
-rwxr-xr-xcontrib/test/test-media47
-rw-r--r--contrib/test/v4l2grab.c8
-rw-r--r--contrib/xc3028-firmware/firmware-tool.c6
-rw-r--r--doxygen_libdvbv5.cfg61
-rw-r--r--include/linux/bpf.h287
-rw-r--r--include/linux/cec-funcs.h14
-rw-r--r--include/linux/cec.h2
-rw-r--r--include/linux/dvb/frontend.h59
-rw-r--r--include/linux/media-bus-format.h9
-rw-r--r--include/linux/v4l2-controls.h16
-rw-r--r--include/linux/videodev2.h15
-rw-r--r--lib/include/libdvbv5/dvb-fe.h10
-rw-r--r--lib/include/libdvbv5/dvb-frontend.h59
-rw-r--r--lib/libdvbv5/descriptors/desc_atsc_service_location.c17
-rw-r--r--lib/libdvbv5/dvb-dev-remote.c4
-rw-r--r--lib/libdvbv5/dvb-file.c11
-rw-r--r--lib/libdvbv5/dvb-v5.c102
-rw-r--r--lib/libdvbv5/dvb-v5.h10
-rw-r--r--lib/libv4lconvert/libv4lconvert-priv.h14
-rw-r--r--lib/libv4lconvert/libv4lconvert.c32
-rw-r--r--lib/libv4lconvert/rgbyuv.c70
-rw-r--r--utils/Makefile.am5
-rw-r--r--utils/cec-follower/cec-follower.1.in6
-rw-r--r--utils/cec-follower/cec-follower.cpp14
-rw-r--r--utils/cec-follower/cec-follower.h4
-rw-r--r--utils/cec-follower/cec-processing.cpp6
-rw-r--r--utils/common/v4l2-info.cpp118
-rw-r--r--utils/common/v4l2-info.h26
-rw-r--r--utils/common/v4l2-pix-formats.h37
-rw-r--r--utils/common/v4l2-tpg-core.c2
-rw-r--r--utils/common/v4l2-tpg.h6
-rw-r--r--utils/common/v4l2-tpg.patch8
-rw-r--r--utils/dvb/dvbv5-zap.c187
-rw-r--r--utils/keytable/bpf_load.c59
-rw-r--r--utils/keytable/keytable.c23
-rw-r--r--utils/keytable/parse.h1
-rw-r--r--utils/keytable/rc_keymaps_userspace/empty.toml2
-rw-r--r--utils/libv4l2util/v4l2_driver.c8
-rw-r--r--utils/qv4l2/capture-win-gl.cpp17
-rw-r--r--utils/qv4l2/qv4l2.cpp2
-rw-r--r--utils/qvidcap/capture.cpp4
-rw-r--r--utils/qvidcap/paint.cpp10
-rw-r--r--utils/qvidcap/v4l2-convert.glsl6
-rw-r--r--utils/rds-ctl/rds-ctl.cpp77
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.cpp1
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.h3
-rw-r--r--utils/v4l2-compliance/v4l2-test-buffers.cpp72
-rw-r--r--utils/v4l2-compliance/v4l2-test-colors.cpp4
-rw-r--r--utils/v4l2-compliance/v4l2-test-controls.cpp24
-rw-r--r--utils/v4l2-compliance/v4l2-test-formats.cpp10
-rw-r--r--utils/v4l2-compliance/v4l2-test-input-output.cpp52
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-common.cpp17
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-tuner.cpp112
-rw-r--r--utils/v4l2-ctl/v4l2-ctl.cpp12
-rw-r--r--utils/v4l2-ctl/v4l2-ctl.h2
-rw-r--r--utils/v4l2-tracer/.gitignore7
-rw-r--r--utils/v4l2-tracer/Makefile.am36
-rw-r--r--utils/v4l2-tracer/libv4l2tracer.cpp303
-rw-r--r--utils/v4l2-tracer/retrace-helper.cpp262
-rw-r--r--utils/v4l2-tracer/retrace.cpp1373
-rw-r--r--utils/v4l2-tracer/retrace.h43
-rw-r--r--utils/v4l2-tracer/trace-helper.cpp500
-rw-r--r--utils/v4l2-tracer/trace.cpp603
-rw-r--r--utils/v4l2-tracer/trace.h75
-rw-r--r--utils/v4l2-tracer/v4l2-tracer-common.cpp476
-rw-r--r--utils/v4l2-tracer/v4l2-tracer-common.h79
-rwxr-xr-xutils/v4l2-tracer/v4l2-tracer-gen.pl879
-rw-r--r--utils/v4l2-tracer/v4l2-tracer.1.in111
-rw-r--r--utils/v4l2-tracer/v4l2-tracer.cpp415
76 files changed, 6433 insertions, 591 deletions
diff --git a/Makefile.am b/Makefile.am
index 7fb443ab..8e924af8 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,7 +2,11 @@ AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
AM_MAKEFLAGS = $(word 1, $(subst 1, -w, $(filter 1, $(V))) --no-print-directory)
-SUBDIRS = v4l-utils-po libdvbv5-po lib
+SUBDIRS = lib
+
+if USE_NLS
+SUBDIRS += v4l-utils-po libdvbv5-po
+endif
if WITH_V4LUTILS
SUBDIRS += utils contrib
diff --git a/README b/README
index a7759171..8c5561ae 100644
--- a/README
+++ b/README
@@ -45,6 +45,8 @@ you'll need also:
alsa-lib-devel doxygen libjpeg-turbo-devel qt5-qtbase-devel
libudev-devel mesa-libGLU-devel
+The v4l2-tracer also needs the json-c library.
+On Debian: libjson-c-dev; on Fedora: json-c-devel.
After downloading and installing the needed packages, you should run:
@@ -227,6 +229,12 @@ v4l2-sysfs-path:
FIXME add description.
Installed by make install under <prefix>/bin.
+v4l2-tracer:
+Tool to trace, record and replay userspace applications
+that implement the v4l2 memory-to-memory stateless video
+decoder interface.
+Installed by 'make install' under <prefix>/bin.
+
xc3028-firmware:
Xceive XC2028/3028 tuner module firmware manipulation tool.
xc3028-firmware does not get installed by make install.
diff --git a/configure.ac b/configure.ac
index 05298981..2b26e3dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,6 +32,8 @@ AC_CONFIG_FILES([Makefile
utils/v4l2-ctl/Makefile
utils/v4l2-dbg/Makefile
utils/v4l2-sysfs-path/Makefile
+ utils/v4l2-tracer/Makefile
+ utils/v4l2-tracer/v4l2-tracer.1
utils/qv4l2/Makefile
utils/libcecutil/Makefile
utils/cec-ctl/Makefile
@@ -99,6 +101,7 @@ DX_INIT_DOXYGEN($PACKAGE_NAME, doxygen_libdvbv5.cfg)
ALL_LINGUAS=""
m4_ifdef(AM_GNU_GETTEXT_REQUIRE_VERSION,[AM_GNU_GETTEXT_REQUIRE_VERSION([0.19.8])],[AM_GNU_GETTEXT_VERSION([0.19.8])])
AM_GNU_GETTEXT([external])
+AM_CONDITIONAL([USE_NLS], [test "$USE_NLS" = "yes"])
LIBDVBV5_DOMAIN="libdvbv5"
AC_DEFINE([LIBDVBV5_DOMAIN], "libdvbv5", [libdvbv5 domain])
@@ -311,6 +314,15 @@ AS_IF([test "x$with_libudev" != xno -o "x$enable_libdvbv5" != xno],
AC_SUBST([JPEG_LIBS])
+JSONC_VERSION_REQUIRED="0.15";
+PKG_CHECK_MODULES(JSONC, [json-c >= $JSONC_VERSION_REQUIRED], [jsonc_pkgconfig=yes], [jsonc_pkgconfig=no])
+AC_SUBST([JSONC_CFLAGS])
+AC_SUBST([JSONC_LIBS])
+AM_CONDITIONAL([HAVE_JSONC], [test x$jsonc_pkgconfig = xyes])
+if test "x$jsonc_pkgconfig" = "xno"; then
+ AC_MSG_WARN(json-c $JSONC_VERSION_REQUIRED or higher required for v4l2-tracer)
+fi
+
# Check for pthread
AS_IF([test x$enable_shared != xno],
@@ -564,7 +576,7 @@ AM_CONDITIONAL([WITH_V4L2_CTL_32], [test x${enable_v4l2_ctl_32} = xyes])
AM_CONDITIONAL([WITH_V4L2_COMPLIANCE], [test x$ac_cv_func_fork = xyes])
AM_CONDITIONAL([WITH_V4L2_COMPLIANCE_LIBV4L], [test x$ac_cv_func_fork = xyes -a x${enable_v4l2_compliance_libv4l} != xno])
AM_CONDITIONAL([WITH_V4L2_COMPLIANCE_32], [test x$ac_cv_func_fork = xyes -a x${enable_v4l2_compliance_32} = xyes])
-PKG_CHECK_MODULES([LIBBPF], [libbpf], [bpf_pc=yes], [bpf_pc=no])
+PKG_CHECK_MODULES([LIBBPF], [libbpf >= 0.7], [bpf_pc=yes], [bpf_pc=no])
AM_CONDITIONAL([WITH_BPF], [test x$enable_bpf != xno -a x$libelf_pkgconfig = xyes -a x$CLANG = xclang -a x$bpf_pc = xyes])
# append -static to libtool compile and link command to enforce static libs
@@ -645,6 +657,7 @@ compile time options summary
QT version : $QT_VERSION
ALSA support : $USE_ALSA
SDL support : $sdl_pc
+ JSON-C : $jsonc_pkgconfig >= $JSONC_VERSION_REQUIRED
build dynamic libs : $enable_shared
build static libs : $enable_static
diff --git a/contrib/freebsd/include/linux/input-event-codes.h b/contrib/freebsd/include/linux/input-event-codes.h
index a63dcd4a..4dc90e95 100644
--- a/contrib/freebsd/include/linux/input-event-codes.h
+++ b/contrib/freebsd/include/linux/input-event-codes.h
@@ -862,6 +862,7 @@
#define ABS_TOOL_WIDTH 0x1c
#define ABS_VOLUME 0x20
+#define ABS_PROFILE 0x21
#define ABS_MISC 0x28
diff --git a/contrib/freebsd/include/linux/input.h b/contrib/freebsd/include/linux/input.h
index 2ff653ac..e1ab29b1 100644
--- a/contrib/freebsd/include/linux/input.h
+++ b/contrib/freebsd/include/linux/input.h
@@ -112,10 +112,13 @@ struct input_id {
* Note that input core does not clamp reported values to the
* [minimum, maximum] limits, such task is left to userspace.
*
- * The default resolution for main axes (ABS_X, ABS_Y, ABS_Z)
- * is reported in units per millimeter (units/mm), resolution
- * for rotational axes (ABS_RX, ABS_RY, ABS_RZ) is reported
- * in units per radian.
+ * The default resolution for main axes (ABS_X, ABS_Y, ABS_Z,
+ * ABS_MT_POSITION_X, ABS_MT_POSITION_Y) is reported in units
+ * per millimeter (units/mm), resolution for rotational axes
+ * (ABS_RX, ABS_RY, ABS_RZ) is reported in units per radian.
+ * The resolution for the size axes (ABS_MT_TOUCH_MAJOR,
+ * ABS_MT_TOUCH_MINOR, ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR)
+ * is reported in units per millimeter (units/mm).
* When INPUT_PROP_ACCELEROMETER is set the resolution changes.
* The main axes (ABS_X, ABS_Y, ABS_Z) are then reported in
* units per g (units/g) and in units per degree per second
diff --git a/contrib/freebsd/include/linux/v4l2-controls.h b/contrib/freebsd/include/linux/v4l2-controls.h
index b70d3ea1..445b7748 100644
--- a/contrib/freebsd/include/linux/v4l2-controls.h
+++ b/contrib/freebsd/include/linux/v4l2-controls.h
@@ -229,6 +229,18 @@ enum v4l2_colorfx {
*/
#define V4L2_CID_USER_ISL7998X_BASE (V4L2_CID_USER_BASE + 0x1180)
+/*
+ * The base for DW100 driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_DW100_BASE (V4L2_CID_USER_BASE + 0x1190)
+
+/*
+ * The base for Aspeed driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_ASPEED_BASE (V4L2_CID_USER_BASE + 0x11a0)
+
/* MPEG-class control IDs */
/* The MPEG controls are applicable to all codec controls
* and the 'MPEG' part of the define is historical */
@@ -1015,6 +1027,8 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_CAMERA_SENSOR_ROTATION (V4L2_CID_CAMERA_CLASS_BASE+35)
+#define V4L2_CID_HDR_SENSOR_MODE (V4L2_CID_CAMERA_CLASS_BASE+36)
+
/* FM Modulator class control IDs */
#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
@@ -1732,7 +1746,7 @@ struct v4l2_vp8_segment {
* @sharpness_level: matches sharpness_level syntax element.
* @level: matches loop_filter_level syntax element.
* @padding: padding field. Should be zeroed by applications.
- * @flags: see V4L2_VP8_LF_FLAG_{}.
+ * @flags: see V4L2_VP8_LF_{}.
*
* This structure contains loop filter related parameters.
* See the 'mb_lf_adjustments()' part of the frame header syntax,
diff --git a/contrib/freebsd/include/linux/videodev2.h b/contrib/freebsd/include/linux/videodev2.h
index 7365b15f..1c15eed6 100644
--- a/contrib/freebsd/include/linux/videodev2.h
+++ b/contrib/freebsd/include/linux/videodev2.h
@@ -509,7 +509,6 @@ struct v4l2_capability {
#define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */
#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
-#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
#define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */
@@ -662,6 +661,8 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_NV12_16L16 v4l2_fourcc('H', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 tiles */
#define V4L2_PIX_FMT_NV12_32L32 v4l2_fourcc('S', 'T', '1', '2') /* 12 Y/CbCr 4:2:0 32x32 tiles */
#define V4L2_PIX_FMT_P010_4L4 v4l2_fourcc('T', '0', '1', '0') /* 12 Y/CbCr 4:2:0 10-bit 4x4 macroblocks */
+#define V4L2_PIX_FMT_NV12_8L128 v4l2_fourcc('A', 'T', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
+#define V4L2_PIX_FMT_NV12_10BE_8L128 v4l2_fourcc_be('A', 'X', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
/* Tiled YUV formats, non contiguous planes */
#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 tiles */
@@ -783,6 +784,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */
#define V4L2_PIX_FMT_QC08C v4l2_fourcc('Q', '0', '8', 'C') /* Qualcomm 8-bit compressed */
#define V4L2_PIX_FMT_QC10C v4l2_fourcc('Q', '1', '0', 'C') /* Qualcomm 10-bit compressed */
+#define V4L2_PIX_FMT_AJPG v4l2_fourcc('A', 'J', 'P', 'G') /* Aspeed JPEG */
/* 10bit raw packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
#define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
@@ -1586,7 +1588,8 @@ struct v4l2_bt_timings {
((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
#define V4L2_DV_BT_BLANKING_HEIGHT(bt) \
((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + \
- (bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch)
+ ((bt)->interlaced ? \
+ ((bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch) : 0))
#define V4L2_DV_BT_FRAME_HEIGHT(bt) \
((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
@@ -1764,6 +1767,8 @@ struct v4l2_ext_control {
uint8_t *p_u8;
uint16_t *p_u16;
uint32_t *p_u32;
+ uint32_t *p_s32;
+ uint32_t *p_s64;
struct v4l2_area *p_area;
struct v4l2_ctrl_h264_sps *p_h264_sps;
struct v4l2_ctrl_h264_pps *p_h264_pps;
@@ -2415,6 +2420,7 @@ struct v4l2_event_vsync {
#define V4L2_EVENT_CTRL_CH_VALUE (1 << 0)
#define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1)
#define V4L2_EVENT_CTRL_CH_RANGE (1 << 2)
+#define V4L2_EVENT_CTRL_CH_DIMENSIONS (1 << 3)
struct v4l2_event_ctrl {
uint32_t changes;
@@ -2657,5 +2663,10 @@ struct v4l2_create_buffers {
/* Deprecated definitions kept for backwards compatibility */
#define V4L2_PIX_FMT_HM12 V4L2_PIX_FMT_NV12_16L16
#define V4L2_PIX_FMT_SUNXI_TILED_NV12 V4L2_PIX_FMT_NV12_32L32
+/*
+ * This capability was never implemented, anyone using this cap should drop it
+ * from their code.
+ */
+#define V4L2_CAP_ASYNCIO 0x02000000
#endif /* __LINUX_VIDEODEV2_H */
diff --git a/contrib/test/test-media b/contrib/test/test-media
index b928c39f..8a6ad86f 100755
--- a/contrib/test/test-media
+++ b/contrib/test/test-media
@@ -18,6 +18,7 @@ unload=0
unbind_time=1
reunbind_time=9
rmmod_time=1
+modprobe_time=3
kobj_rel=0
v4l2_ctl=v4l2-ctl
v4l2_compliance=v4l2-compliance
@@ -173,7 +174,8 @@ if [ $kobj_rel -eq 1 ]; then
echo Detected CONFIG_DEBUG_KOBJECT_RELEASE=y
unbind_time=10
reunbind_time=14
- rmmod_time=5
+ rmmod_time=10
+ modprobe_time=10
fi
if [ $unload -eq 1 ]; then
@@ -187,7 +189,7 @@ fi
rmmod vivid 2&>/dev/null
modprobe vivid n_devs=3 multiplanar=1,2,2 cache_hints=1,0,0 #allocators=0,1,1
-sleep 3
+sleep $modprobe_time
tmp=`mktemp`
@@ -315,7 +317,7 @@ if [ $vivid -eq 1 -a $setup -eq 0 ]; then
fi
modprobe vivid n_devs=3 multiplanar=1,2,2 cache_hints=1,0,0 #allocators=0,1,1
- sleep 3
+ sleep $modprobe_time
$v4l2_ctl -z platform:vivid-002 -d vivid-002-vid-cap -i3 -v width=3840,height=2160,pixelformat=NV24
$v4l2_ctl -z platform:vivid-002 -d vivid-002-vid-out -o1 -x width=3840,height=2160,pixelformat=NM16
@@ -336,7 +338,7 @@ fi
if [ $vim2m -eq 1 ]; then
rmmod vim2m 2&>/dev/null
modprobe vim2m
- sleep 1
+ sleep $modprobe_time
dmesg -n notice
if ! $v4l2_ctl -z platform:vim2m ; then
@@ -412,7 +414,7 @@ fi
if [ $vimc -eq 1 ]; then
rmmod vimc 2&>/dev/null
modprobe vimc
- sleep 1
+ sleep $modprobe_time
dmesg -n notice
if ! $v4l2_ctl -z platform:vimc -d "Sensor A" ; then
@@ -516,7 +518,7 @@ if [ $vicodec -eq 1 ]; then
rmmod vicodec 2&>/dev/null
modprobe vicodec
- sleep 2
+ sleep $modprobe_time
dmesg -n notice
if ! $v4l2_ctl -z platform:vicodec ; then
@@ -580,6 +582,9 @@ if [ $vicodec -eq 1 -a $setup -eq 0 ]; then
decful_opts='-z platform:vicodec -d stateful-decoder-source --stream-mmap --stream-out-mmap'
decless_opts='-z platform:vicodec -d stateless-decoder-source --stream-mmap --stream-out-mmap'
+ vicodec_tests=0
+ vicodec_ok=0
+ vicodec_fail=0
echo
echo stateful decode | tee /dev/kmsg
echo
@@ -588,7 +593,14 @@ if [ $vicodec -eq 1 -a $setup -eq 0 ]; then
echo stateless decode | tee /dev/kmsg
echo
$v4l2_ctl $decless_opts --stream-from-hdr=$tmpdir/comp.hdr.yu12.1280.24 --stream-to=$tmpdir/raw.yu12.1280.24.stateless
- cmp $tmpdir/raw.yu12.1280.24 $tmpdir/raw.yu12.1280.24.stateless
+ vicodec_tests=$((vicodec_tests+1))
+ if cmp $tmpdir/raw.yu12.1280.24 $tmpdir/raw.yu12.1280.24.stateless ; then
+ echo "OK: stateless decode" | tee -a $tmp
+ vicodec_ok=$((vicodec_ok+1))
+ else
+ echo "FAIL: stateless decode: unexpected output" | tee -a $tmp
+ vicodec_fail=$((vicodec_fail+1))
+ fi
echo
echo stateful decode with dynamic resolution changes for every frame | tee /dev/kmsg
@@ -596,7 +608,14 @@ if [ $vicodec -eq 1 -a $setup -eq 0 ]; then
$v4l2_ctl $decful_opts --stream-from-hdr=$tmpdir/comp.hdr.yu12.mix.2.12 --stream-to=$tmpdir/raw.yu12.mix.2.12
# v4l2-ctl does not yet reallocate buffers if they are too small for the new format
$v4l2_ctl $decless_opts -v width=1920,height=1088 --stream-from-hdr=$tmpdir/comp.hdr.yu12.mix.2.12 --stream-to=$tmpdir/raw.yu12.mix.2.12.stateless
- cmp $tmpdir/raw.yu12.mix.2.12 $tmpdir/raw.yu12.mix.2.12.stateless
+ vicodec_tests=$((vicodec_tests+1))
+ if cmp $tmpdir/raw.yu12.mix.2.12 $tmpdir/raw.yu12.mix.2.12.stateless ; then
+ echo "OK: stateful decode with dynamic resolution changes for every frame" | tee -a $tmp
+ vicodec_ok=$((vicodec_ok+1))
+ else
+ echo "FAIL: stateful decode with dynamic resolution changes for every frame: unexpected output" | tee -a $tmp
+ vicodec_fail=$((vicodec_fail+1))
+ fi
echo
echo stateful decode with dynamic resolution changes for every other frame | tee /dev/kmsg
@@ -604,7 +623,15 @@ if [ $vicodec -eq 1 -a $setup -eq 0 ]; then
$v4l2_ctl $decful_opts --stream-from-hdr=$tmpdir/comp.hdr.yu12.mix.4.6 --stream-to=$tmpdir/raw.yu12.mix.4.6
# v4l2-ctl does not yet reallocate buffers if they are too small for the new format
$v4l2_ctl $decless_opts -v width=1920,height=1088 --stream-from-hdr=$tmpdir/comp.hdr.yu12.mix.4.6 --stream-to=$tmpdir/raw.yu12.mix.4.6.stateless
- cmp $tmpdir/raw.yu12.mix.4.6 $tmpdir/raw.yu12.mix.4.6.stateless
+ vicodec_tests=$((vicodec_tests+1))
+ if cmp $tmpdir/raw.yu12.mix.4.6 $tmpdir/raw.yu12.mix.4.6.stateless ; then
+ echo "OK: stateful decode with dynamic resolution changes for every other frame" | tee -a $tmp
+ vicodec_ok=$((vicodec_ok+1))
+ else
+ echo "FAIL: stateful decode with dynamic resolution changes for every other frame: unexpected output" | tee -a $tmp
+ vicodec_fail=$((vicodec_fail+1))
+ fi
+ echo Total for vicodec cmp tests: $vicodec_tests, Succeeded: $vicodec_ok, Failed: $vicodec_fail, Warnings: 0 | tee -a $tmp
rm -rf $tmpdir
echo
@@ -665,7 +692,7 @@ fi
if [ $vidtv -eq 1 ]; then
rmmod dvb_vidtv_bridge dvb_vidtv_tuner dvb_vidtv_demod 2&>/dev/null
modprobe vidtv
- sleep 2
+ sleep $modprobe_time
dmesg -n notice
if ! media-ctl -d platform:vidtv ; then
diff --git a/contrib/test/v4l2grab.c b/contrib/test/v4l2grab.c
index a19f7196..7213a144 100644
--- a/contrib/test/v4l2grab.c
+++ b/contrib/test/v4l2grab.c
@@ -206,8 +206,6 @@ static char *prt_caps(uint32_t caps, char *s)
strcat (s,"RADIO ");
if (V4L2_CAP_READWRITE & caps)
strcat (s,"READWRITE ");
- if (V4L2_CAP_ASYNCIO & caps)
- strcat (s,"ASYNCIO ");
if (V4L2_CAP_STREAMING & caps)
strcat (s,"STREAMING ");
if (V4L2_CAP_EXT_PIX_FORMAT & caps)
@@ -226,12 +224,6 @@ static char *prt_caps(uint32_t caps, char *s)
strcat (s,"SDR_OUTPUT ");
if(V4L2_CAP_META_CAPTURE & caps)
strcat (s,"META_CAPTURE ");
- if(V4L2_CAP_READWRITE & caps)
- strcat (s,"READWRITE ");
- if(V4L2_CAP_ASYNCIO & caps)
- strcat (s,"ASYNCIO ");
- if(V4L2_CAP_STREAMING & caps)
- strcat (s,"STREAMING ");
if(V4L2_CAP_META_OUTPUT & caps)
strcat (s,"META_OUTPUT ");
if(V4L2_CAP_TOUCH & caps)
diff --git a/contrib/xc3028-firmware/firmware-tool.c b/contrib/xc3028-firmware/firmware-tool.c
index 3ae59f87..5dd205e0 100644
--- a/contrib/xc3028-firmware/firmware-tool.c
+++ b/contrib/xc3028-firmware/firmware-tool.c
@@ -677,8 +677,6 @@ static int seek_chunks(struct chunk_hunk *fhunk,
}
}
- free(temp_data);
-
/* Method 2: seek for base firmware */
if (!base_start)
base_start = seek;
@@ -728,6 +726,8 @@ static int seek_chunks(struct chunk_hunk *fhunk,
base_start = p;
+ free(temp_data);
+
return 2;
}
}
@@ -785,6 +785,8 @@ not_found:
#endif
memset(fhunk, 0, sizeof(struct chunk_hunk));
printf("Couldn't find firmware\n");
+
+ free(temp_data);
return 0;
/* Method 4: Seek for first firmware chunks */
diff --git a/doxygen_libdvbv5.cfg b/doxygen_libdvbv5.cfg
index b25301a0..fba16c4a 100644
--- a/doxygen_libdvbv5.cfg
+++ b/doxygen_libdvbv5.cfg
@@ -222,12 +222,6 @@ TAB_SIZE = 8
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ -776,7 +770,6 @@ INPUT = $(SRCDIR)/doc/libdvbv5-index.doc \
$(SRCDIR)/lib/include/libdvbv5/desc_event_extended.h \
$(SRCDIR)/lib/include/libdvbv5/desc_event_short.h \
$(SRCDIR)/lib/include/libdvbv5/desc_extension.h \
- $(SRCDIR)/lib/include/libdvbv5/desc_descriptor_id.h \
$(SRCDIR)/lib/include/libdvbv5/desc_frequency_list.h \
$(SRCDIR)/lib/include/libdvbv5/desc_hierarchy.h \
$(SRCDIR)/lib/include/libdvbv5/desc_isdbt_delivery.h \
@@ -784,6 +777,7 @@ INPUT = $(SRCDIR)/doc/libdvbv5-index.doc \
$(SRCDIR)/lib/include/libdvbv5/desc_logical_channel.h \
$(SRCDIR)/lib/include/libdvbv5/desc_network_name.h \
$(SRCDIR)/lib/include/libdvbv5/desc_partial_reception.h \
+ $(SRCDIR)/lib/include/libdvbv5/desc_registration_id.h \
$(SRCDIR)/lib/include/libdvbv5/desc_sat.h \
$(SRCDIR)/lib/include/libdvbv5/desc_service.h \
$(SRCDIR)/lib/include/libdvbv5/desc_t2_delivery.h \
@@ -1023,13 +1017,6 @@ VERBATIM_HEADERS = YES
ALPHABETICAL_INDEX = YES
-# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
-# which the alphabetical index list will be split.
-# Minimum value: 1, maximum value: 20, default value: 5.
-# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
-
-COLS_IN_ALPHA_INDEX = 5
-
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
@@ -1705,16 +1692,6 @@ LATEX_BATCHMODE = YES
LATEX_HIDE_INDICES = NO
-# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
-# code with syntax highlighting in the LaTeX output.
-#
-# Note that which sources are shown also depends on other settings such as
-# SOURCE_BROWSER.
-# The default value is: NO.
-# This tag requires that the tag GENERATE_LATEX is set to YES.
-
-LATEX_SOURCE_CODE = NO
-
# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
# bibliography, e.g. plainnat, or ieeetr. See
# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
@@ -1834,18 +1811,6 @@ GENERATE_XML = $(GENERATE_XML)
XML_OUTPUT = xml
-# The XML_SCHEMA tag can be used to specify a XML schema, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_SCHEMA =
-
-# The XML_DTD tag can be used to specify a XML DTD, which can be used by a
-# validating XML parser to check the syntax of the XML files.
-# This tag requires that the tag GENERATE_XML is set to YES.
-
-XML_DTD =
-
# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program
# listings (including syntax highlighting and cross-referencing information) to
# the XML output. Note that enabling this will significantly increase the size
@@ -2050,34 +2015,10 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
-# The PERL_PATH should be the absolute path and name of the perl script
-# interpreter (i.e. the result of 'which perl').
-# The default file (with absolute path) is: /usr/bin/perl.
-
-PERL_PATH = /usr/bin/false
-
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
-# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram
-# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
-# NO turns the diagrams off. Note that this option also works with HAVE_DOT
-# disabled, but it is recommended to install and use dot, since it yields more
-# powerful graphs.
-# The default value is: YES.
-
-CLASS_DIAGRAMS = YES
-
-# You can define message sequence charts within doxygen comments using the \msc
-# command. Doxygen will then run the mscgen tool (see:
-# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
-# documentation. The MSCGEN_PATH tag allows you to specify the directory where
-# the mscgen tool resides. If left empty the tool is assumed to be found in the
-# default search path.
-
-MSCGEN_PATH =
-
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 2892794f..5cad2be2 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -87,10 +87,35 @@ struct bpf_cgroup_storage_key {
__u32 attach_type; /* program attach type (enum bpf_attach_type) */
};
+enum bpf_cgroup_iter_order {
+ BPF_CGROUP_ITER_ORDER_UNSPEC = 0,
+ BPF_CGROUP_ITER_SELF_ONLY, /* process only a single object. */
+ BPF_CGROUP_ITER_DESCENDANTS_PRE, /* walk descendants in pre-order. */
+ BPF_CGROUP_ITER_DESCENDANTS_POST, /* walk descendants in post-order. */
+ BPF_CGROUP_ITER_ANCESTORS_UP, /* walk ancestors upward. */
+};
+
union bpf_iter_link_info {
struct {
__u32 map_fd;
} map;
+ struct {
+ enum bpf_cgroup_iter_order order;
+
+ /* At most one of cgroup_fd and cgroup_id can be non-zero. If
+ * both are zero, the walk starts from the default cgroup v2
+ * root. For walking v1 hierarchy, one should always explicitly
+ * specify cgroup_fd.
+ */
+ __u32 cgroup_fd;
+ __u64 cgroup_id;
+ } cgroup;
+ /* Parameters of task iterators. */
+ struct {
+ __u32 tid;
+ __u32 pid;
+ __u32 pid_fd;
+ } task;
};
/* BPF syscall commands, see bpf(2) man-page for more details. */
@@ -909,6 +934,7 @@ enum bpf_map_type {
BPF_MAP_TYPE_INODE_STORAGE,
BPF_MAP_TYPE_TASK_STORAGE,
BPF_MAP_TYPE_BLOOM_FILTER,
+ BPF_MAP_TYPE_USER_RINGBUF,
};
/* Note that tracing related programs such as
@@ -998,6 +1024,7 @@ enum bpf_attach_type {
BPF_SK_REUSEPORT_SELECT_OR_MIGRATE,
BPF_PERF_EVENT,
BPF_TRACE_KPROBE_MULTI,
+ BPF_LSM_CGROUP,
__MAX_BPF_ATTACH_TYPE
};
@@ -1232,7 +1259,7 @@ enum {
/* Query effective (directly attached + inherited from ancestor cgroups)
* programs that will be executed for events within a cgroup.
- * attach_flags with this flag are returned only for directly attached programs.
+ * attach_flags with this flag are always returned 0.
*/
#define BPF_F_QUERY_EFFECTIVE (1U << 0)
@@ -1431,6 +1458,10 @@ union bpf_attr {
__u32 attach_flags;
__aligned_u64 prog_ids;
__u32 prog_cnt;
+ /* output: per-program attach_flags.
+ * not allowed to be set during effective query.
+ */
+ __aligned_u64 prog_attach_flags;
} query;
struct { /* anonymous struct used by BPF_RAW_TRACEPOINT_OPEN command */
@@ -2359,7 +2390,8 @@ union bpf_attr {
* Pull in non-linear data in case the *skb* is non-linear and not
* all of *len* are part of the linear section. Make *len* bytes
* from *skb* readable and writable. If a zero value is passed for
- * *len*, then the whole length of the *skb* is pulled.
+ * *len*, then all bytes in the linear part of *skb* will be made
+ * readable and writable.
*
* This helper is only needed for reading and writing with direct
* packet access.
@@ -2570,10 +2602,12 @@ union bpf_attr {
* There are two supported modes at this time:
*
* * **BPF_ADJ_ROOM_MAC**: Adjust room at the mac layer
- * (room space is added or removed below the layer 2 header).
+ * (room space is added or removed between the layer 2 and
+ * layer 3 headers).
*
* * **BPF_ADJ_ROOM_NET**: Adjust room at the network layer
- * (room space is added or removed below the layer 3 header).
+ * (room space is added or removed between the layer 3 and
+ * layer 4 headers).
*
* The following flags are supported at this time:
*
@@ -3005,8 +3039,18 @@ union bpf_attr {
* **BPF_F_USER_STACK**
* Collect a user space stack instead of a kernel stack.
* **BPF_F_USER_BUILD_ID**
- * Collect buildid+offset instead of ips for user stack,
- * only valid if **BPF_F_USER_STACK** is also specified.
+ * Collect (build_id, file_offset) instead of ips for user
+ * stack, only valid if **BPF_F_USER_STACK** is also
+ * specified.
+ *
+ * *file_offset* is an offset relative to the beginning
+ * of the executable or shared object file backing the vma
+ * which the *ip* falls in. It is *not* an offset relative
+ * to that object's base address. Accordingly, it must be
+ * adjusted by adding (sh_addr - sh_offset), where
+ * sh_{addr,offset} correspond to the executable section
+ * containing *file_offset* in the object, for comparisons
+ * to symbols' st_value to be valid.
*
* **bpf_get_stack**\ () can collect up to
* **PERF_MAX_STACK_DEPTH** both kernel and user frames, subject
@@ -3597,10 +3641,11 @@ union bpf_attr {
*
* *iph* points to the start of the IPv4 or IPv6 header, while
* *iph_len* contains **sizeof**\ (**struct iphdr**) or
- * **sizeof**\ (**struct ip6hdr**).
+ * **sizeof**\ (**struct ipv6hdr**).
*
* *th* points to the start of the TCP header, while *th_len*
- * contains **sizeof**\ (**struct tcphdr**).
+ * contains the length of the TCP header (at least
+ * **sizeof**\ (**struct tcphdr**)).
* Return
* 0 if *iph* and *th* are a valid SYN cookie ACK, or a negative
* error otherwise.
@@ -3783,10 +3828,11 @@ union bpf_attr {
*
* *iph* points to the start of the IPv4 or IPv6 header, while
* *iph_len* contains **sizeof**\ (**struct iphdr**) or
- * **sizeof**\ (**struct ip6hdr**).
+ * **sizeof**\ (**struct ipv6hdr**).
*
* *th* points to the start of the TCP header, while *th_len*
- * contains the length of the TCP header.
+ * contains the length of the TCP header with options (at least
+ * **sizeof**\ (**struct tcphdr**)).
* Return
* On success, lower 32 bits hold the generated SYN cookie in
* followed by 16 bits which hold the MSS value for that cookie,
@@ -4420,7 +4466,7 @@ union bpf_attr {
*
* **-EEXIST** if the option already exists.
*
- * **-EFAULT** on failrue to parse the existing header options.
+ * **-EFAULT** on failure to parse the existing header options.
*
* **-EPERM** if the helper cannot be used under the current
* *skops*\ **->op**.
@@ -4629,7 +4675,7 @@ union bpf_attr {
* a *map* with *task* as the **key**. From this
* perspective, the usage is not much different from
* **bpf_map_lookup_elem**\ (*map*, **&**\ *task*) except this
- * helper enforces the key must be an task_struct and the map must also
+ * helper enforces the key must be a task_struct and the map must also
* be a **BPF_MAP_TYPE_TASK_STORAGE**.
*
* Underneath, the value is stored locally at *task* instead of
@@ -4687,7 +4733,7 @@ union bpf_attr {
*
* long bpf_ima_inode_hash(struct inode *inode, void *dst, u32 size)
* Description
- * Returns the stored IMA hash of the *inode* (if it's avaialable).
+ * Returns the stored IMA hash of the *inode* (if it's available).
* If the hash is larger than *size*, then only *size*
* bytes will be copied to *dst*
* Return
@@ -4711,12 +4757,12 @@ union bpf_attr {
*
* The argument *len_diff* can be used for querying with a planned
* size change. This allows to check MTU prior to changing packet
- * ctx. Providing an *len_diff* adjustment that is larger than the
+ * ctx. Providing a *len_diff* adjustment that is larger than the
* actual packet size (resulting in negative packet size) will in
- * principle not exceed the MTU, why it is not considered a
- * failure. Other BPF-helpers are needed for performing the
- * planned size change, why the responsability for catch a negative
- * packet size belong in those helpers.
+ * principle not exceed the MTU, which is why it is not considered
+ * a failure. Other BPF helpers are needed for performing the
+ * planned size change; therefore the responsibility for catching
+ * a negative packet size belongs in those helpers.
*
* Specifying *ifindex* zero means the MTU check is performed
* against the current net device. This is practical if this isn't
@@ -4914,6 +4960,7 @@ union bpf_attr {
* Get address of the traced function (for tracing and kprobe programs).
* Return
* Address of the traced function.
+ * 0 for kprobes placed within the function (not at the entry).
*
* u64 bpf_get_attach_cookie(void *ctx)
* Description
@@ -5043,12 +5090,12 @@ union bpf_attr {
*
* long bpf_get_func_arg(void *ctx, u32 n, u64 *value)
* Description
- * Get **n**-th argument (zero based) of the traced function (for tracing programs)
+ * Get **n**-th argument register (zero based) of the traced function (for tracing programs)
* returned in **value**.
*
* Return
* 0 on success.
- * **-EINVAL** if n >= arguments count of traced function.
+ * **-EINVAL** if n >= argument register count of traced function.
*
* long bpf_get_func_ret(void *ctx, u64 *value)
* Description
@@ -5061,24 +5108,37 @@ union bpf_attr {
*
* long bpf_get_func_arg_cnt(void *ctx)
* Description
- * Get number of arguments of the traced function (for tracing programs).
+ * Get number of registers of the traced function (for tracing programs) where
+ * function arguments are stored in these registers.
*
* Return
- * The number of arguments of the traced function.
+ * The number of argument registers of the traced function.
*
* int bpf_get_retval(void)
* Description
- * Get the syscall's return value that will be returned to userspace.
+ * Get the BPF program's return value that will be returned to the upper layers.
*
- * This helper is currently supported by cgroup programs only.
+ * This helper is currently supported by cgroup programs and only by the hooks
+ * where BPF program's return value is returned to the userspace via errno.
* Return
- * The syscall's return value.
+ * The BPF program's return value.
*
* int bpf_set_retval(int retval)
* Description
- * Set the syscall's return value that will be returned to userspace.
+ * Set the BPF program's return value that will be returned to the upper layers.
+ *
+ * This helper is currently supported by cgroup programs and only by the hooks
+ * where BPF program's return value is returned to the userspace via errno.
+ *
+ * Note that there is the following corner case where the program exports an error
+ * via bpf_set_retval but signals success via 'return 1':
+ *
+ * bpf_set_retval(-EPERM);
+ * return 1;
+ *
+ * In this case, the BPF program's return value will use helper's -EPERM. This
+ * still holds true for cgroup/bind{4,6} which supports extra 'return 3' success case.
*
- * This helper is currently supported by cgroup programs only.
* Return
* 0 on success, or a negative error in case of failure.
*
@@ -5222,22 +5282,25 @@ union bpf_attr {
* Return
* Nothing. Always succeeds.
*
- * long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset)
+ * long bpf_dynptr_read(void *dst, u32 len, struct bpf_dynptr *src, u32 offset, u64 flags)
* Description
* Read *len* bytes from *src* into *dst*, starting from *offset*
* into *src*.
+ * *flags* is currently unused.
* Return
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
- * of *src*'s data, -EINVAL if *src* is an invalid dynptr.
+ * of *src*'s data, -EINVAL if *src* is an invalid dynptr or if
+ * *flags* is not 0.
*
- * long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len)
+ * long bpf_dynptr_write(struct bpf_dynptr *dst, u32 offset, void *src, u32 len, u64 flags)
* Description
* Write *len* bytes from *src* into *dst*, starting from *offset*
* into *dst*.
+ * *flags* is currently unused.
* Return
* 0 on success, -E2BIG if *offset* + *len* exceeds the length
* of *dst*'s data, -EINVAL if *dst* is an invalid dynptr or if *dst*
- * is a read-only dynptr.
+ * is a read-only dynptr or if *flags* is not 0.
*
* void *bpf_dynptr_data(struct bpf_dynptr *ptr, u32 offset, u32 len)
* Description
@@ -5249,6 +5312,129 @@ union bpf_attr {
* Pointer to the underlying dynptr data, NULL if the dynptr is
* read-only, if the dynptr is invalid, or if the offset and length
* is out of bounds.
+ *
+ * s64 bpf_tcp_raw_gen_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th, u32 th_len)
+ * Description
+ * Try to issue a SYN cookie for the packet with corresponding
+ * IPv4/TCP headers, *iph* and *th*, without depending on a
+ * listening socket.
+ *
+ * *iph* points to the IPv4 header.
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains the length of the TCP header (at least
+ * **sizeof**\ (**struct tcphdr**)).
+ * Return
+ * On success, lower 32 bits hold the generated SYN cookie in
+ * followed by 16 bits which hold the MSS value for that cookie,
+ * and the top 16 bits are unused.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** if *th_len* is invalid.
+ *
+ * s64 bpf_tcp_raw_gen_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th, u32 th_len)
+ * Description
+ * Try to issue a SYN cookie for the packet with corresponding
+ * IPv6/TCP headers, *iph* and *th*, without depending on a
+ * listening socket.
+ *
+ * *iph* points to the IPv6 header.
+ *
+ * *th* points to the start of the TCP header, while *th_len*
+ * contains the length of the TCP header (at least
+ * **sizeof**\ (**struct tcphdr**)).
+ * Return
+ * On success, lower 32 bits hold the generated SYN cookie in
+ * followed by 16 bits which hold the MSS value for that cookie,
+ * and the top 16 bits are unused.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EINVAL** if *th_len* is invalid.
+ *
+ * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * long bpf_tcp_raw_check_syncookie_ipv4(struct iphdr *iph, struct tcphdr *th)
+ * Description
+ * Check whether *iph* and *th* contain a valid SYN cookie ACK
+ * without depending on a listening socket.
+ *
+ * *iph* points to the IPv4 header.
+ *
+ * *th* points to the TCP header.
+ * Return
+ * 0 if *iph* and *th* are a valid SYN cookie ACK.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EACCES** if the SYN cookie is not valid.
+ *
+ * long bpf_tcp_raw_check_syncookie_ipv6(struct ipv6hdr *iph, struct tcphdr *th)
+ * Description
+ * Check whether *iph* and *th* contain a valid SYN cookie ACK
+ * without depending on a listening socket.
+ *
+ * *iph* points to the IPv6 header.
+ *
+ * *th* points to the TCP header.
+ * Return
+ * 0 if *iph* and *th* are a valid SYN cookie ACK.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EACCES** if the SYN cookie is not valid.
+ *
+ * **-EPROTONOSUPPORT** if CONFIG_IPV6 is not builtin.
+ *
+ * u64 bpf_ktime_get_tai_ns(void)
+ * Description
+ * A nonsettable system-wide clock derived from wall-clock time but
+ * ignoring leap seconds. This clock does not experience
+ * discontinuities and backwards jumps caused by NTP inserting leap
+ * seconds as CLOCK_REALTIME does.
+ *
+ * See: **clock_gettime**\ (**CLOCK_TAI**)
+ * Return
+ * Current *ktime*.
+ *
+ * long bpf_user_ringbuf_drain(struct bpf_map *map, void *callback_fn, void *ctx, u64 flags)
+ * Description
+ * Drain samples from the specified user ring buffer, and invoke
+ * the provided callback for each such sample:
+ *
+ * long (\*callback_fn)(struct bpf_dynptr \*dynptr, void \*ctx);
+ *
+ * If **callback_fn** returns 0, the helper will continue to try
+ * and drain the next sample, up to a maximum of
+ * BPF_MAX_USER_RINGBUF_SAMPLES samples. If the return value is 1,
+ * the helper will skip the rest of the samples and return. Other
+ * return values are not used now, and will be rejected by the
+ * verifier.
+ * Return
+ * The number of drained samples if no error was encountered while
+ * draining samples, or 0 if no samples were present in the ring
+ * buffer. If a user-space producer was epoll-waiting on this map,
+ * and at least one sample was drained, they will receive an event
+ * notification notifying them of available space in the ring
+ * buffer. If the BPF_RB_NO_WAKEUP flag is passed to this
+ * function, no wakeup notification will be sent. If the
+ * BPF_RB_FORCE_WAKEUP flag is passed, a wakeup notification will
+ * be sent even if no sample was drained.
+ *
+ * On failure, the returned value is one of the following:
+ *
+ * **-EBUSY** if the ring buffer is contended, and another calling
+ * context was concurrently draining the ring buffer.
+ *
+ * **-EINVAL** if user-space is not properly tracking the ring
+ * buffer due to the producer position not being aligned to 8
+ * bytes, a sample not being aligned to 8 bytes, or the producer
+ * position not matching the advertised length of a sample.
+ *
+ * **-E2BIG** if user-space has tried to publish a sample which is
+ * larger than the size of the ring buffer, or which cannot fit
+ * within a struct bpf_dynptr.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -5455,6 +5641,12 @@ union bpf_attr {
FN(dynptr_read), \
FN(dynptr_write), \
FN(dynptr_data), \
+ FN(tcp_raw_gen_syncookie_ipv4), \
+ FN(tcp_raw_gen_syncookie_ipv6), \
+ FN(tcp_raw_check_syncookie_ipv4), \
+ FN(tcp_raw_check_syncookie_ipv6), \
+ FN(ktime_get_tai_ns), \
+ FN(user_ringbuf_drain), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -5517,6 +5709,11 @@ enum {
BPF_F_SEQ_NUMBER = (1ULL << 3),
};
+/* BPF_FUNC_skb_get_tunnel_key flags. */
+enum {
+ BPF_F_TUNINFO_FLAGS = (1ULL << 4),
+};
+
/* BPF_FUNC_perf_event_output, BPF_FUNC_perf_event_read and
* BPF_FUNC_perf_event_read_value flags.
*/
@@ -5706,7 +5903,10 @@ struct bpf_tunnel_key {
};
__u8 tunnel_tos;
__u8 tunnel_ttl;
- __u16 tunnel_ext; /* Padding, future use. */
+ union {
+ __u16 tunnel_ext; /* compat */
+ __be16 tunnel_flags;
+ };
__u32 tunnel_label;
union {
__u32 local_ipv4;
@@ -5750,6 +5950,11 @@ enum bpf_ret_code {
* represented by BPF_REDIRECT above).
*/
BPF_LWT_REROUTE = 128,
+ /* BPF_FLOW_DISSECTOR_CONTINUE: used by BPF_PROG_TYPE_FLOW_DISSECTOR
+ * to indicate that no custom dissection was performed, and
+ * fallback to standard dissector is requested.
+ */
+ BPF_FLOW_DISSECTOR_CONTINUE = 129,
};
struct bpf_sock {
@@ -5995,6 +6200,8 @@ struct bpf_prog_info {
__u64 run_cnt;
__u64 recursion_misses;
__u32 verified_insns;
+ __u32 attach_btf_obj_id;
+ __u32 attach_btf_id;
} __attribute__((aligned(8)));
struct bpf_map_info {
@@ -6046,11 +6253,26 @@ struct bpf_link_info {
struct {
__aligned_u64 target_name; /* in/out: target_name buffer ptr */
__u32 target_name_len; /* in/out: target_name buffer len */
+
+ /* If the iter specific field is 32 bits, it can be put
+ * in the first or second union. Otherwise it should be
+ * put in the second union.
+ */
union {
struct {
__u32 map_id;
} map;
};
+ union {
+ struct {
+ __u64 cgroup_id;
+ __u32 order;
+ } cgroup;
+ struct {
+ __u32 tid;
+ __u32 pid;
+ } task;
+ };
} iter;
struct {
__u32 netns_ino;
@@ -6702,6 +6924,7 @@ enum bpf_core_relo_kind {
BPF_CORE_TYPE_SIZE = 9, /* type size in bytes */
BPF_CORE_ENUMVAL_EXISTS = 10, /* enum value existence in target kernel */
BPF_CORE_ENUMVAL_VALUE = 11, /* enum value integer value */
+ BPF_CORE_TYPE_MATCHES = 12, /* type match in target kernel */
};
/*
diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h
index 0385d0e7..b0fe9b2e 100644
--- a/include/linux/cec-funcs.h
+++ b/include/linux/cec-funcs.h
@@ -1568,6 +1568,20 @@ static __inline__ void cec_ops_request_short_audio_descriptor(const struct cec_m
}
}
+static __inline__ void cec_msg_set_audio_volume_level(struct cec_msg *msg,
+ __u8 audio_volume_level)
+{
+ msg->len = 3;
+ msg->msg[1] = CEC_MSG_SET_AUDIO_VOLUME_LEVEL;
+ msg->msg[2] = audio_volume_level;
+}
+
+static __inline__ void cec_ops_set_audio_volume_level(const struct cec_msg *msg,
+ __u8 *audio_volume_level)
+{
+ *audio_volume_level = msg->msg[2];
+}
+
/* Audio Rate Control Feature */
static __inline__ void cec_msg_set_audio_rate(struct cec_msg *msg,
diff --git a/include/linux/cec.h b/include/linux/cec.h
index 971a2ec5..9ebbec29 100644
--- a/include/linux/cec.h
+++ b/include/linux/cec.h
@@ -768,6 +768,7 @@ struct cec_event {
#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE 0x08
#define CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX 0x04
#define CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX 0x02
+#define CEC_OP_FEAT_DEV_HAS_SET_AUDIO_VOLUME_LEVEL 0x01
#define CEC_MSG_GIVE_FEATURES 0xa5 /* HDMI 2.0 */
@@ -1059,6 +1060,7 @@ struct cec_event {
#define CEC_OP_AUD_FMT_ID_CEA861 0
#define CEC_OP_AUD_FMT_ID_CEA861_CXT 1
+#define CEC_MSG_SET_AUDIO_VOLUME_LEVEL 0x73
/* Audio Rate Control Feature */
#define CEC_MSG_SET_AUDIO_RATE 0x9a
diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h
index 5f00fc1f..f81e858f 100644
--- a/include/linux/dvb/frontend.h
+++ b/include/linux/dvb/frontend.h
@@ -296,6 +296,22 @@ enum fe_spectral_inversion {
* @FEC_3_5: Forward Error Correction Code 3/5
* @FEC_9_10: Forward Error Correction Code 9/10
* @FEC_2_5: Forward Error Correction Code 2/5
+ * @FEC_1_3: Forward Error Correction Code 1/3
+ * @FEC_1_4: Forward Error Correction Code 1/4
+ * @FEC_5_9: Forward Error Correction Code 5/9
+ * @FEC_7_9: Forward Error Correction Code 7/9
+ * @FEC_8_15: Forward Error Correction Code 8/15
+ * @FEC_11_15: Forward Error Correction Code 11/15
+ * @FEC_13_18: Forward Error Correction Code 13/18
+ * @FEC_9_20: Forward Error Correction Code 9/20
+ * @FEC_11_20: Forward Error Correction Code 11/20
+ * @FEC_23_36: Forward Error Correction Code 23/36
+ * @FEC_25_36: Forward Error Correction Code 25/36
+ * @FEC_13_45: Forward Error Correction Code 13/45
+ * @FEC_26_45: Forward Error Correction Code 26/45
+ * @FEC_28_45: Forward Error Correction Code 28/45
+ * @FEC_32_45: Forward Error Correction Code 32/45
+ * @FEC_77_90: Forward Error Correction Code 77/90
*
* Please note that not all FEC types are supported by a given standard.
*/
@@ -313,6 +329,22 @@ enum fe_code_rate {
FEC_3_5,
FEC_9_10,
FEC_2_5,
+ FEC_1_3,
+ FEC_1_4,
+ FEC_5_9,
+ FEC_7_9,
+ FEC_8_15,
+ FEC_11_15,
+ FEC_13_18,
+ FEC_9_20,
+ FEC_11_20,
+ FEC_23_36,
+ FEC_25_36,
+ FEC_13_45,
+ FEC_26_45,
+ FEC_28_45,
+ FEC_32_45,
+ FEC_77_90,
};
/**
@@ -331,6 +363,13 @@ enum fe_code_rate {
* @APSK_32: 32-APSK modulation
* @DQPSK: DQPSK modulation
* @QAM_4_NR: 4-QAM-NR modulation
+ * @QAM-1024: 1024-QAM modulation
+ * @QAM-4096: 4096-QAM modulation
+ * @APSK_8_L: 8APSK-L modulation
+ * @APSK_16_L: 16APSK-L modulation
+ * @APSK_32_L: 32APSK-L modulation
+ * @APSK_64: 64APSK modulation
+ * @APSK_64_L: 64APSK-L modulation
*
* Please note that not all modulations are supported by a given standard.
*
@@ -350,6 +389,13 @@ enum fe_modulation {
APSK_32,
DQPSK,
QAM_4_NR,
+ QAM_1024,
+ QAM_4096,
+ APSK_8_L,
+ APSK_16_L,
+ APSK_32_L,
+ APSK_64,
+ APSK_64_L,
};
/**
@@ -404,6 +450,7 @@ enum fe_transmit_mode {
* @GUARD_INTERVAL_PN420: PN length 420 (1/4)
* @GUARD_INTERVAL_PN595: PN length 595 (1/6)
* @GUARD_INTERVAL_PN945: PN length 945 (1/9)
+ * @GUARD_INTERVAL_1_64: Guard interval 1/64
*
* Please note that not all guard intervals are supported by a given standard.
*/
@@ -419,6 +466,7 @@ enum fe_guard_interval {
GUARD_INTERVAL_PN420,
GUARD_INTERVAL_PN595,
GUARD_INTERVAL_PN945,
+ GUARD_INTERVAL_1_64,
};
/**
@@ -571,6 +619,9 @@ enum fe_pilot {
* @ROLLOFF_20: Roloff factor: α=20%
* @ROLLOFF_25: Roloff factor: α=25%
* @ROLLOFF_AUTO: Auto-detect the roloff factor.
+ * @ROLLOFF_15: Rolloff factor: α=15%
+ * @ROLLOFF_10: Rolloff factor: α=10%
+ * @ROLLOFF_5: Rolloff factor: α=5%
*
* .. note:
*
@@ -581,6 +632,9 @@ enum fe_rolloff {
ROLLOFF_20,
ROLLOFF_25,
ROLLOFF_AUTO,
+ ROLLOFF_15,
+ ROLLOFF_10,
+ ROLLOFF_5,
};
/**
@@ -594,6 +648,8 @@ enum fe_rolloff {
* Cable TV: DVB-C following ITU-T J.83 Annex B spec (ClearQAM)
* @SYS_DVBC_ANNEX_C:
* Cable TV: DVB-C following ITU-T J.83 Annex C spec
+ * @SYS_DVBC2:
+ * Cable TV: DVB-C2
* @SYS_ISDBC:
* Cable TV: ISDB-C (no drivers yet)
* @SYS_DVBT:
@@ -611,7 +667,7 @@ enum fe_rolloff {
* @SYS_DVBS:
* Satellite TV: DVB-S
* @SYS_DVBS2:
- * Satellite TV: DVB-S2
+ * Satellite TV: DVB-S2 and DVB-S2X
* @SYS_TURBO:
* Satellite TV: DVB-S Turbo
* @SYS_ISDBS:
@@ -645,6 +701,7 @@ enum fe_delivery_system {
SYS_DVBT2,
SYS_TURBO,
SYS_DVBC_ANNEX_C,
+ SYS_DVBC2,
};
/* backward compatibility definitions for delivery systems */
diff --git a/include/linux/media-bus-format.h b/include/linux/media-bus-format.h
index 0dfc11ee..ca9a24c8 100644
--- a/include/linux/media-bus-format.h
+++ b/include/linux/media-bus-format.h
@@ -34,7 +34,7 @@
#define MEDIA_BUS_FMT_FIXED 0x0001
-/* RGB - next is 0x101e */
+/* RGB - next is 0x1022 */
#define MEDIA_BUS_FMT_RGB444_1X12 0x1016
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_BE 0x1001
#define MEDIA_BUS_FMT_RGB444_2X8_PADHI_LE 0x1002
@@ -59,13 +59,17 @@
#define MEDIA_BUS_FMT_RGB888_3X8_DELTA 0x101d
#define MEDIA_BUS_FMT_RGB888_1X7X4_SPWG 0x1011
#define MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA 0x1012
+#define MEDIA_BUS_FMT_RGB666_1X30_CPADLO 0x101e
+#define MEDIA_BUS_FMT_RGB888_1X30_CPADLO 0x101f
#define MEDIA_BUS_FMT_ARGB8888_1X32 0x100d
#define MEDIA_BUS_FMT_RGB888_1X32_PADHI 0x100f
#define MEDIA_BUS_FMT_RGB101010_1X30 0x1018
+#define MEDIA_BUS_FMT_RGB666_1X36_CPADLO 0x1020
+#define MEDIA_BUS_FMT_RGB888_1X36_CPADLO 0x1021
#define MEDIA_BUS_FMT_RGB121212_1X36 0x1019
#define MEDIA_BUS_FMT_RGB161616_1X48 0x101a
-/* YUV (including grey) - next is 0x202e */
+/* YUV (including grey) - next is 0x202f */
#define MEDIA_BUS_FMT_Y8_1X8 0x2001
#define MEDIA_BUS_FMT_UV8_1X8 0x2015
#define MEDIA_BUS_FMT_UYVY8_1_5X8 0x2002
@@ -88,6 +92,7 @@
#define MEDIA_BUS_FMT_YUYV12_2X12 0x201e
#define MEDIA_BUS_FMT_YVYU12_2X12 0x201f
#define MEDIA_BUS_FMT_Y14_1X14 0x202d
+#define MEDIA_BUS_FMT_Y16_1X16 0x202e
#define MEDIA_BUS_FMT_UYVY8_1X16 0x200f
#define MEDIA_BUS_FMT_VYUY8_1X16 0x2010
#define MEDIA_BUS_FMT_YUYV8_1X16 0x2011
diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h
index b70d3ea1..445b7748 100644
--- a/include/linux/v4l2-controls.h
+++ b/include/linux/v4l2-controls.h
@@ -229,6 +229,18 @@ enum v4l2_colorfx {
*/
#define V4L2_CID_USER_ISL7998X_BASE (V4L2_CID_USER_BASE + 0x1180)
+/*
+ * The base for DW100 driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_DW100_BASE (V4L2_CID_USER_BASE + 0x1190)
+
+/*
+ * The base for Aspeed driver controls.
+ * We reserve 16 controls for this driver.
+ */
+#define V4L2_CID_USER_ASPEED_BASE (V4L2_CID_USER_BASE + 0x11a0)
+
/* MPEG-class control IDs */
/* The MPEG controls are applicable to all codec controls
* and the 'MPEG' part of the define is historical */
@@ -1015,6 +1027,8 @@ enum v4l2_auto_focus_range {
#define V4L2_CID_CAMERA_SENSOR_ROTATION (V4L2_CID_CAMERA_CLASS_BASE+35)
+#define V4L2_CID_HDR_SENSOR_MODE (V4L2_CID_CAMERA_CLASS_BASE+36)
+
/* FM Modulator class control IDs */
#define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900)
@@ -1732,7 +1746,7 @@ struct v4l2_vp8_segment {
* @sharpness_level: matches sharpness_level syntax element.
* @level: matches loop_filter_level syntax element.
* @padding: padding field. Should be zeroed by applications.
- * @flags: see V4L2_VP8_LF_FLAG_{}.
+ * @flags: see V4L2_VP8_LF_{}.
*
* This structure contains loop filter related parameters.
* See the 'mb_lf_adjustments()' part of the frame header syntax,
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 1a6a8217..5eb96692 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -475,7 +475,6 @@ struct v4l2_capability {
#define V4L2_CAP_META_CAPTURE 0x00800000 /* Is a metadata capture device */
#define V4L2_CAP_READWRITE 0x01000000 /* read/write systemcalls */
-#define V4L2_CAP_ASYNCIO 0x02000000 /* async I/O */
#define V4L2_CAP_STREAMING 0x04000000 /* streaming I/O ioctls */
#define V4L2_CAP_META_OUTPUT 0x08000000 /* Is a metadata output device */
@@ -628,6 +627,8 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_NV12_16L16 v4l2_fourcc('H', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 16x16 tiles */
#define V4L2_PIX_FMT_NV12_32L32 v4l2_fourcc('S', 'T', '1', '2') /* 12 Y/CbCr 4:2:0 32x32 tiles */
#define V4L2_PIX_FMT_P010_4L4 v4l2_fourcc('T', '0', '1', '0') /* 12 Y/CbCr 4:2:0 10-bit 4x4 macroblocks */
+#define V4L2_PIX_FMT_NV12_8L128 v4l2_fourcc('A', 'T', '1', '2') /* Y/CbCr 4:2:0 8x128 tiles */
+#define V4L2_PIX_FMT_NV12_10BE_8L128 v4l2_fourcc_be('A', 'X', '1', '2') /* Y/CbCr 4:2:0 10-bit 8x128 tiles */
/* Tiled YUV formats, non contiguous planes */
#define V4L2_PIX_FMT_NV12MT v4l2_fourcc('T', 'M', '1', '2') /* 12 Y/CbCr 4:2:0 64x32 tiles */
@@ -749,6 +750,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_HI240 v4l2_fourcc('H', 'I', '2', '4') /* BTTV 8-bit dithered RGB */
#define V4L2_PIX_FMT_QC08C v4l2_fourcc('Q', '0', '8', 'C') /* Qualcomm 8-bit compressed */
#define V4L2_PIX_FMT_QC10C v4l2_fourcc('Q', '1', '0', 'C') /* Qualcomm 10-bit compressed */
+#define V4L2_PIX_FMT_AJPG v4l2_fourcc('A', 'J', 'P', 'G') /* Aspeed JPEG */
/* 10bit raw packed, 32 bytes for every 25 pixels, last LSB 6 bits unused */
#define V4L2_PIX_FMT_IPU3_SBGGR10 v4l2_fourcc('i', 'p', '3', 'b') /* IPU3 packed 10-bit BGGR bayer */
@@ -1552,7 +1554,8 @@ struct v4l2_bt_timings {
((bt)->width + V4L2_DV_BT_BLANKING_WIDTH(bt))
#define V4L2_DV_BT_BLANKING_HEIGHT(bt) \
((bt)->vfrontporch + (bt)->vsync + (bt)->vbackporch + \
- (bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch)
+ ((bt)->interlaced ? \
+ ((bt)->il_vfrontporch + (bt)->il_vsync + (bt)->il_vbackporch) : 0))
#define V4L2_DV_BT_FRAME_HEIGHT(bt) \
((bt)->height + V4L2_DV_BT_BLANKING_HEIGHT(bt))
@@ -1730,6 +1733,8 @@ struct v4l2_ext_control {
__u8 *p_u8;
__u16 *p_u16;
__u32 *p_u32;
+ __u32 *p_s32;
+ __u32 *p_s64;
struct v4l2_area *p_area;
struct v4l2_ctrl_h264_sps *p_h264_sps;
struct v4l2_ctrl_h264_pps *p_h264_pps;
@@ -2381,6 +2386,7 @@ struct v4l2_event_vsync {
#define V4L2_EVENT_CTRL_CH_VALUE (1 << 0)
#define V4L2_EVENT_CTRL_CH_FLAGS (1 << 1)
#define V4L2_EVENT_CTRL_CH_RANGE (1 << 2)
+#define V4L2_EVENT_CTRL_CH_DIMENSIONS (1 << 3)
struct v4l2_event_ctrl {
__u32 changes;
@@ -2623,5 +2629,10 @@ struct v4l2_create_buffers {
/* Deprecated definitions kept for backwards compatibility */
#define V4L2_PIX_FMT_HM12 V4L2_PIX_FMT_NV12_16L16
#define V4L2_PIX_FMT_SUNXI_TILED_NV12 V4L2_PIX_FMT_NV12_32L32
+/*
+ * This capability was never implemented, anyone using this cap should drop it
+ * from their code.
+ */
+#define V4L2_CAP_ASYNCIO 0x02000000
#endif /* __LINUX_VIDEODEV2_H */
diff --git a/lib/include/libdvbv5/dvb-fe.h b/lib/include/libdvbv5/dvb-fe.h
index 96657013..1e143b7b 100644
--- a/lib/include/libdvbv5/dvb-fe.h
+++ b/lib/include/libdvbv5/dvb-fe.h
@@ -752,18 +752,18 @@ int dvb_fe_set_default_country(struct dvb_v5_fe_parms *parms,
extern const unsigned fe_bandwidth_name[8];
extern const char *dvb_v5_name[72];
extern const void *dvb_v5_attr_names[];
-extern const char *delivery_system_name[20];
-extern const char *fe_code_rate_name[14];
-extern const char *fe_modulation_name[15];
+extern const char *delivery_system_name[21];
+extern const char *fe_code_rate_name[30];
+extern const char *fe_modulation_name[22];
extern const char *fe_transmission_mode_name[10];
extern const unsigned fe_bandwidth_name[8];
-extern const char *fe_guard_interval_name[12];
+extern const char *fe_guard_interval_name[13];
extern const char *fe_hierarchy_name[6];
extern const char *fe_voltage_name[4];
extern const char *fe_tone_name[3];
extern const char *fe_inversion_name[4];
extern const char *fe_pilot_name[4];
-extern const char *fe_rolloff_name[5];
+extern const char *fe_rolloff_name[8];
#endif
diff --git a/lib/include/libdvbv5/dvb-frontend.h b/lib/include/libdvbv5/dvb-frontend.h
index 5f00fc1f..f81e858f 100644
--- a/lib/include/libdvbv5/dvb-frontend.h
+++ b/lib/include/libdvbv5/dvb-frontend.h
@@ -296,6 +296,22 @@ enum fe_spectral_inversion {
* @FEC_3_5: Forward Error Correction Code 3/5
* @FEC_9_10: Forward Error Correction Code 9/10
* @FEC_2_5: Forward Error Correction Code 2/5
+ * @FEC_1_3: Forward Error Correction Code 1/3
+ * @FEC_1_4: Forward Error Correction Code 1/4
+ * @FEC_5_9: Forward Error Correction Code 5/9
+ * @FEC_7_9: Forward Error Correction Code 7/9
+ * @FEC_8_15: Forward Error Correction Code 8/15
+ * @FEC_11_15: Forward Error Correction Code 11/15
+ * @FEC_13_18: Forward Error Correction Code 13/18
+ * @FEC_9_20: Forward Error Correction Code 9/20
+ * @FEC_11_20: Forward Error Correction Code 11/20
+ * @FEC_23_36: Forward Error Correction Code 23/36
+ * @FEC_25_36: Forward Error Correction Code 25/36
+ * @FEC_13_45: Forward Error Correction Code 13/45
+ * @FEC_26_45: Forward Error Correction Code 26/45
+ * @FEC_28_45: Forward Error Correction Code 28/45
+ * @FEC_32_45: Forward Error Correction Code 32/45
+ * @FEC_77_90: Forward Error Correction Code 77/90
*
* Please note that not all FEC types are supported by a given standard.
*/
@@ -313,6 +329,22 @@ enum fe_code_rate {
FEC_3_5,
FEC_9_10,
FEC_2_5,
+ FEC_1_3,
+ FEC_1_4,
+ FEC_5_9,
+ FEC_7_9,
+ FEC_8_15,
+ FEC_11_15,
+ FEC_13_18,
+ FEC_9_20,
+ FEC_11_20,
+ FEC_23_36,
+ FEC_25_36,
+ FEC_13_45,
+ FEC_26_45,
+ FEC_28_45,
+ FEC_32_45,
+ FEC_77_90,
};
/**
@@ -331,6 +363,13 @@ enum fe_code_rate {
* @APSK_32: 32-APSK modulation
* @DQPSK: DQPSK modulation
* @QAM_4_NR: 4-QAM-NR modulation
+ * @QAM-1024: 1024-QAM modulation
+ * @QAM-4096: 4096-QAM modulation
+ * @APSK_8_L: 8APSK-L modulation
+ * @APSK_16_L: 16APSK-L modulation
+ * @APSK_32_L: 32APSK-L modulation
+ * @APSK_64: 64APSK modulation
+ * @APSK_64_L: 64APSK-L modulation
*
* Please note that not all modulations are supported by a given standard.
*
@@ -350,6 +389,13 @@ enum fe_modulation {
APSK_32,
DQPSK,
QAM_4_NR,
+ QAM_1024,
+ QAM_4096,
+ APSK_8_L,
+ APSK_16_L,
+ APSK_32_L,
+ APSK_64,
+ APSK_64_L,
};
/**
@@ -404,6 +450,7 @@ enum fe_transmit_mode {
* @GUARD_INTERVAL_PN420: PN length 420 (1/4)
* @GUARD_INTERVAL_PN595: PN length 595 (1/6)
* @GUARD_INTERVAL_PN945: PN length 945 (1/9)
+ * @GUARD_INTERVAL_1_64: Guard interval 1/64
*
* Please note that not all guard intervals are supported by a given standard.
*/
@@ -419,6 +466,7 @@ enum fe_guard_interval {
GUARD_INTERVAL_PN420,
GUARD_INTERVAL_PN595,
GUARD_INTERVAL_PN945,
+ GUARD_INTERVAL_1_64,
};
/**
@@ -571,6 +619,9 @@ enum fe_pilot {
* @ROLLOFF_20: Roloff factor: α=20%
* @ROLLOFF_25: Roloff factor: α=25%
* @ROLLOFF_AUTO: Auto-detect the roloff factor.
+ * @ROLLOFF_15: Rolloff factor: α=15%
+ * @ROLLOFF_10: Rolloff factor: α=10%
+ * @ROLLOFF_5: Rolloff factor: α=5%
*
* .. note:
*
@@ -581,6 +632,9 @@ enum fe_rolloff {
ROLLOFF_20,
ROLLOFF_25,
ROLLOFF_AUTO,
+ ROLLOFF_15,
+ ROLLOFF_10,
+ ROLLOFF_5,
};
/**
@@ -594,6 +648,8 @@ enum fe_rolloff {
* Cable TV: DVB-C following ITU-T J.83 Annex B spec (ClearQAM)
* @SYS_DVBC_ANNEX_C:
* Cable TV: DVB-C following ITU-T J.83 Annex C spec
+ * @SYS_DVBC2:
+ * Cable TV: DVB-C2
* @SYS_ISDBC:
* Cable TV: ISDB-C (no drivers yet)
* @SYS_DVBT:
@@ -611,7 +667,7 @@ enum fe_rolloff {
* @SYS_DVBS:
* Satellite TV: DVB-S
* @SYS_DVBS2:
- * Satellite TV: DVB-S2
+ * Satellite TV: DVB-S2 and DVB-S2X
* @SYS_TURBO:
* Satellite TV: DVB-S Turbo
* @SYS_ISDBS:
@@ -645,6 +701,7 @@ enum fe_delivery_system {
SYS_DVBT2,
SYS_TURBO,
SYS_DVBC_ANNEX_C,
+ SYS_DVBC2,
};
/* backward compatibility definitions for delivery systems */
diff --git a/lib/libdvbv5/descriptors/desc_atsc_service_location.c b/lib/libdvbv5/descriptors/desc_atsc_service_location.c
index 8b423dc5..b3e191c8 100644
--- a/lib/libdvbv5/descriptors/desc_atsc_service_location.c
+++ b/lib/libdvbv5/descriptors/desc_atsc_service_location.c
@@ -34,21 +34,28 @@ int atsc_desc_service_location_init(struct dvb_v5_fe_parms *parms,
int i;
size_t len, dlen = desc->length;
- len = sizeof(*s_loc);
+ // raw data should have one element
+ len = sizeof(u_int16_t) + sizeof(u_int8_t) + sizeof(struct atsc_desc_service_location_elementary);
if (dlen < len) {
dvb_logwarn("ATSC service location descriptor is too small");
return -1;
}
- memcpy(s_loc, p, len);
- p += len;
- dlen -= len;
+ memcpy(&s_loc->bitfield , p, sizeof(u_int16_t));
+ p += sizeof(u_int16_t);
+ dlen -= sizeof(u_int16_t);
+ bswap16(s_loc->bitfield);
+
+ memcpy(&s_loc->number_elements , p, sizeof(u_int8_t));
+ p += sizeof(u_int8_t);
+ dlen -= sizeof(u_int8_t);
bswap16(s_loc->bitfield);
len = s_loc->number_elements * sizeof(*s_loc->elementary);
if (dlen < len) {
- dvb_logwarn("ATSC service location descriptor is too small");
+ dvb_logwarn("ATSC service location descriptor is too small for %d elements",
+ s_loc->number_elements);
return -1;
}
if (dlen > len) {
diff --git a/lib/libdvbv5/dvb-dev-remote.c b/lib/libdvbv5/dvb-dev-remote.c
index cd783858..536f0acf 100644
--- a/lib/libdvbv5/dvb-dev-remote.c
+++ b/lib/libdvbv5/dvb-dev-remote.c
@@ -1497,6 +1497,7 @@ int dvb_remote_fe_set_parms(struct dvb_v5_fe_parms *par)
struct queued_msg *msg = NULL;
int ret, i;
char buf[REMOTE_BUF_SIZE], lnb_name[80] = "", *p = buf;
+ char cmd[CMD_SIZE];
size_t size = sizeof(buf);
if (priv->disconnected)
@@ -1536,7 +1537,8 @@ int dvb_remote_fe_set_parms(struct dvb_v5_fe_parms *par)
size -= ret;
}
- msg = send_buf(dvb, priv->fd, "fe_set_parms", buf, p - buf);
+ strcpy(cmd, "fe_set_parms");
+ msg = send_buf(dvb, priv->fd, cmd, buf, p - buf);
if (!msg)
goto error;
diff --git a/lib/libdvbv5/dvb-file.c b/lib/libdvbv5/dvb-file.c
index ca84866d..4187de9d 100644
--- a/lib/libdvbv5/dvb-file.c
+++ b/lib/libdvbv5/dvb-file.c
@@ -674,21 +674,18 @@ static int fill_entry(struct dvb_entry *entry, char *key, char *value)
if (!type)
return 0;
- len = 0;
-
p = strtok(value," \t");
if (!p)
return 0;
while (p) {
entry->other_el_pid = realloc(entry->other_el_pid,
- (len + 1) *
+ (entry->other_el_pid_len + 1) *
sizeof (*entry->other_el_pid));
- entry->other_el_pid[len].type = type;
- entry->other_el_pid[len].pid = atol(p);
+ entry->other_el_pid[entry->other_el_pid_len].type = type;
+ entry->other_el_pid[entry->other_el_pid_len].pid = atol(p);
p = strtok(NULL, " \t\n");
- len++;
+ entry->other_el_pid_len++;
}
- entry->other_el_pid_len = len;
}
if (!is_video && !is_audio) {
diff --git a/lib/libdvbv5/dvb-v5.c b/lib/libdvbv5/dvb-v5.c
index 37e92125..93c16a85 100644
--- a/lib/libdvbv5/dvb-v5.c
+++ b/lib/libdvbv5/dvb-v5.c
@@ -50,39 +50,62 @@ struct fe_status_name fe_status_name[8] = {
{ FE_TIMEDOUT, "TIMEDOUT" },
};
-const char *fe_code_rate_name[14] = {
- [FEC_1_2] = "1/2",
- [FEC_2_3] = "2/3",
- [FEC_2_5] = "2/5",
- [FEC_3_4] = "3/4",
- [FEC_3_5] = "3/5",
- [FEC_4_5] = "4/5",
- [FEC_5_6] = "5/6",
- [FEC_6_7] = "6/7",
- [FEC_7_8] = "7/8",
- [FEC_8_9] = "8/9",
- [FEC_9_10] = "9/10",
- [FEC_AUTO] = "AUTO",
- [FEC_NONE] = "NONE",
- [13] = NULL,
+const char *fe_code_rate_name[30] = {
+ [FEC_1_2] = "1/2",
+ [FEC_1_3] = "1/3",
+ [FEC_1_4] = "1/4",
+ [FEC_2_3] = "2/3",
+ [FEC_2_5] = "2/5",
+ [FEC_3_4] = "3/4",
+ [FEC_3_5] = "3/5",
+ [FEC_4_5] = "4/5",
+ [FEC_5_6] = "5/6",
+ [FEC_5_9] = "5/9",
+ [FEC_6_7] = "6/7",
+ [FEC_7_8] = "7/8",
+ [FEC_7_9] = "7/9",
+ [FEC_8_9] = "8/9",
+ [FEC_8_15] = "8/15",
+ [FEC_9_10] = "9/10",
+ [FEC_9_20] = "9/20",
+ [FEC_11_15] = "11/15",
+ [FEC_11_20] = "11/20",
+ [FEC_13_18] = "13/18",
+ [FEC_13_45] = "13/45",
+ [FEC_23_36] = "23/36",
+ [FEC_25_36] = "25/36",
+ [FEC_26_45] = "26/45",
+ [FEC_28_45] = "28/45",
+ [FEC_32_45] = "32/45",
+ [FEC_77_90] = "77/90",
+ [FEC_AUTO] = "AUTO",
+ [FEC_NONE] = "NONE",
+ [29] = NULL,
};
-const char *fe_modulation_name[15] = {
- [APSK_16] = "APSK/16",
- [APSK_32] = "APSK/32",
- [DQPSK] = "DQPSK",
- [PSK_8] = "PSK/8",
- [QAM_4_NR] = "QAM/4_NR",
- [QAM_16] = "QAM/16",
- [QAM_32] = "QAM/32",
- [QAM_64] = "QAM/64",
- [QAM_128] = "QAM/128",
- [QAM_256] = "QAM/256",
- [QAM_AUTO] = "QAM/AUTO",
- [QPSK] = "QPSK",
- [VSB_8] = "VSB/8",
- [VSB_16] = "VSB/16",
- [14] = NULL,
+const char *fe_modulation_name[22] = {
+ [APSK_8_L] = "APSK/8_L",
+ [APSK_16] = "APSK/16",
+ [APSK_16_L] = "APSK/16_L",
+ [APSK_32] = "APSK/32",
+ [APSK_32_L] = "APSK/32_L",
+ [APSK_64] = "APSK/64",
+ [APSK_64_L] = "APSK/64_L",
+ [DQPSK] = "DQPSK",
+ [PSK_8] = "PSK/8",
+ [QAM_4_NR] = "QAM/4_NR",
+ [QAM_16] = "QAM/16",
+ [QAM_32] = "QAM/32",
+ [QAM_64] = "QAM/64",
+ [QAM_128] = "QAM/128",
+ [QAM_256] = "QAM/256",
+ [QAM_1024] = "QAM/1024",
+ [QAM_4096] = "QAM/4096",
+ [QAM_AUTO] = "QAM/AUTO",
+ [QPSK] = "QPSK",
+ [VSB_8] = "VSB/8",
+ [VSB_16] = "VSB/16",
+ [21] = NULL,
};
const char *fe_transmission_mode_name[10] = {
@@ -109,11 +132,12 @@ const unsigned fe_bandwidth_name[8] = {
[7] = 0,
};
-const char *fe_guard_interval_name[12] = {
+const char *fe_guard_interval_name[13] = {
[GUARD_INTERVAL_1_4] = "1/4",
[GUARD_INTERVAL_1_8] = "1/8",
[GUARD_INTERVAL_1_16] = "1/16",
[GUARD_INTERVAL_1_32] = "1/32",
+ [GUARD_INTERVAL_1_64] = "1/64",
[GUARD_INTERVAL_1_128] = "1/128",
[GUARD_INTERVAL_19_128] = "19/128",
[GUARD_INTERVAL_19_256] = "19/256",
@@ -121,7 +145,7 @@ const char *fe_guard_interval_name[12] = {
[GUARD_INTERVAL_PN420] = "PN420",
[GUARD_INTERVAL_PN595] = "PN595",
[GUARD_INTERVAL_PN945] = "PN945",
- [11] = NULL,
+ [12] = NULL,
};
const char *fe_hierarchy_name[6] = {
@@ -160,12 +184,15 @@ const char *fe_pilot_name[4] = {
[3] = NULL,
};
-const char *fe_rolloff_name[5] = {
+const char *fe_rolloff_name[8] = {
+ [ROLLOFF_5] = "5",
+ [ROLLOFF_10] = "10",
+ [ROLLOFF_15] = "15",
[ROLLOFF_20] = "20",
[ROLLOFF_25] = "25",
[ROLLOFF_35] = "35",
[ROLLOFF_AUTO] = "AUTO",
- [4] = NULL,
+ [7] = NULL,
};
const char *dvb_v5_name[72] = {
@@ -243,13 +270,14 @@ const char *dvb_v5_name[72] = {
[71] = NULL,
};
-const char *delivery_system_name[20] = {
+const char *delivery_system_name[21] = {
[SYS_ATSC] = "ATSC",
[SYS_ATSCMH] = "ATSCMH",
[SYS_CMMB] = "CMMB",
[SYS_DAB] = "DAB",
[SYS_DSS] = "DSS",
[SYS_DTMB] = "DTMB",
+ [SYS_DVBC2] = "DVBC2",
[SYS_DVBC_ANNEX_A] = "DVBC/ANNEX_A",
[SYS_DVBC_ANNEX_B] = "DVBC/ANNEX_B",
[SYS_DVBC_ANNEX_C] = "DVBC/ANNEX_C",
@@ -263,6 +291,6 @@ const char *delivery_system_name[20] = {
[SYS_ISDBT] = "ISDBT",
[SYS_TURBO] = "TURBO",
[SYS_UNDEFINED] = "UNDEFINED",
- [19] = NULL,
+ [20] = NULL,
};
diff --git a/lib/libdvbv5/dvb-v5.h b/lib/libdvbv5/dvb-v5.h
index 3540bac1..5f7db012 100644
--- a/lib/libdvbv5/dvb-v5.h
+++ b/lib/libdvbv5/dvb-v5.h
@@ -14,17 +14,17 @@ struct fe_status_name {
char *name;
};
extern struct fe_status_name fe_status_name[8];
-extern const char *fe_code_rate_name[14];
-extern const char *fe_modulation_name[15];
+extern const char *fe_code_rate_name[30];
+extern const char *fe_modulation_name[22];
extern const char *fe_transmission_mode_name[10];
extern const unsigned fe_bandwidth_name[8];
-extern const char *fe_guard_interval_name[12];
+extern const char *fe_guard_interval_name[13];
extern const char *fe_hierarchy_name[6];
extern const char *fe_voltage_name[4];
extern const char *fe_tone_name[3];
extern const char *fe_inversion_name[4];
extern const char *fe_pilot_name[4];
-extern const char *fe_rolloff_name[5];
+extern const char *fe_rolloff_name[8];
extern const char *dvb_v5_name[72];
-extern const char *delivery_system_name[20];
+extern const char *delivery_system_name[21];
#endif
diff --git a/lib/libv4lconvert/libv4lconvert-priv.h b/lib/libv4lconvert/libv4lconvert-priv.h
index 6b9128ce..00a03f9e 100644
--- a/lib/libv4lconvert/libv4lconvert-priv.h
+++ b/lib/libv4lconvert/libv4lconvert-priv.h
@@ -118,10 +118,10 @@ void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
const struct v4l2_format *src_fmt, int bgr, int yvu, int bpp);
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dst,
- int width, int height, int yvu);
+ int width, int height, int stride, int yvu);
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dst,
- int width, int height, int yvu);
+ int width, int height, int stride, int yvu);
void v4lconvert_yuyv_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height, int stride);
@@ -133,7 +133,7 @@ void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dst,
int width, int height, int stride, int yvu);
void v4lconvert_nv16_to_yuyv(const unsigned char *src, unsigned char *dest,
- int width, int height);
+ int width, int height, int stride);
void v4lconvert_yvyu_to_rgb24(const unsigned char *src, unsigned char *dst,
int width, int height, int stride);
@@ -178,10 +178,10 @@ int v4lconvert_y10b_to_yuv420(struct v4lconvert_data *data,
const unsigned char *src, unsigned char *dest, int width, int height);
void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height);
+ int width, int height, int stride);
void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest,
- int width, int height);
+ int width, int height, int stride);
void v4lconvert_rgb565_to_yuv420(const unsigned char *src, unsigned char *dest,
const struct v4l2_format *src_fmt, int yvu);
@@ -287,10 +287,10 @@ void v4lconvert_hsv_to_rgb24(const unsigned char *src, unsigned char *dest,
int width, int height, int bgr, int Xin, unsigned char hsv_enc);
void v4lconvert_nv12_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height, int bgr);
+ int width, int height, int stride, int bgr);
void v4lconvert_nv12_to_yuv420(const unsigned char *src, unsigned char *dest,
- int width, int height, int yvu);
+ int width, int height, int stride, int yvu);
void v4lconvert_rotate90(unsigned char *src, unsigned char *dest,
struct v4l2_format *fmt);
diff --git a/lib/libv4lconvert/libv4lconvert.c b/lib/libv4lconvert/libv4lconvert.c
index e794ec00..b07bf3ba 100644
--- a/lib/libv4lconvert/libv4lconvert.c
+++ b/lib/libv4lconvert/libv4lconvert.c
@@ -905,11 +905,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
v4lconvert_yuv420_to_rgb24(data->convert_pixfmt_buf, dest, width,
- height, yvu);
+ height, bytesperline, yvu);
break;
case V4L2_PIX_FMT_BGR24:
v4lconvert_yuv420_to_bgr24(data->convert_pixfmt_buf, dest, width,
- height, yvu);
+ height, bytesperline, yvu);
break;
}
break;
@@ -937,16 +937,16 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
case V4L2_PIX_FMT_NV12:
switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_nv12_to_rgb24(src, dest, width, height, 0);
+ v4lconvert_nv12_to_rgb24(src, dest, width, height, bytesperline, 0);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_nv12_to_rgb24(src, dest, width, height, 1);
+ v4lconvert_nv12_to_rgb24(src, dest, width, height, bytesperline, 1);
break;
case V4L2_PIX_FMT_YUV420:
- v4lconvert_nv12_to_yuv420(src, dest, width, height, 0);
+ v4lconvert_nv12_to_yuv420(src, dest, width, height, bytesperline, 0);
break;
case V4L2_PIX_FMT_YVU420:
- v4lconvert_nv12_to_yuv420(src, dest, width, height, 1);
+ v4lconvert_nv12_to_yuv420(src, dest, width, height, bytesperline, 1);
break;
}
break;
@@ -1282,10 +1282,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
}
switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
- v4lconvert_rgb565_to_rgb24(src, dest, width, height);
+ v4lconvert_rgb565_to_rgb24(src, dest, width, height, bytesperline);
break;
case V4L2_PIX_FMT_BGR24:
- v4lconvert_rgb565_to_bgr24(src, dest, width, height);
+ v4lconvert_rgb565_to_bgr24(src, dest, width, height, bytesperline);
break;
case V4L2_PIX_FMT_YUV420:
v4lconvert_rgb565_to_yuv420(src, dest, fmt, 0);
@@ -1398,11 +1398,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
v4lconvert_yuv420_to_rgb24(src, dest, width,
- height, 0);
+ height, bytesperline, 0);
break;
case V4L2_PIX_FMT_BGR24:
v4lconvert_yuv420_to_bgr24(src, dest, width,
- height, 0);
+ height, bytesperline, 0);
break;
case V4L2_PIX_FMT_YUV420:
memcpy(dest, src, width * height * 3 / 2);
@@ -1422,11 +1422,11 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
switch (dest_pix_fmt) {
case V4L2_PIX_FMT_RGB24:
v4lconvert_yuv420_to_rgb24(src, dest, width,
- height, 1);
+ height, bytesperline, 1);
break;
case V4L2_PIX_FMT_BGR24:
v4lconvert_yuv420_to_bgr24(src, dest, width,
- height, 1);
+ height, bytesperline, 1);
break;
case V4L2_PIX_FMT_YUV420:
v4lconvert_swap_uv(src, dest, fmt);
@@ -1445,10 +1445,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
if (!tmpbuf)
return v4lconvert_oom_error(data);
- v4lconvert_nv16_to_yuyv(src, tmpbuf, width, height);
+ v4lconvert_nv16_to_yuyv(src, tmpbuf, width, height, bytesperline);
src_pix_fmt = V4L2_PIX_FMT_YUYV;
src = tmpbuf;
- bytesperline = bytesperline * 2;
+ bytesperline = width * 2;
/* fall through */
}
case V4L2_PIX_FMT_YUYV:
@@ -1482,10 +1482,10 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data,
return v4lconvert_oom_error(data);
/* Note NV61 is NV16 with U and V swapped so this becomes yvyu. */
- v4lconvert_nv16_to_yuyv(src, tmpbuf, width, height);
+ v4lconvert_nv16_to_yuyv(src, tmpbuf, width, height, bytesperline);
src_pix_fmt = V4L2_PIX_FMT_YVYU;
src = tmpbuf;
- bytesperline = bytesperline * 2;
+ bytesperline = width * 2;
/* fall through */
}
case V4L2_PIX_FMT_YVYU:
diff --git a/lib/libv4lconvert/rgbyuv.c b/lib/libv4lconvert/rgbyuv.c
index b54b4577..ce31a1ba 100644
--- a/lib/libv4lconvert/rgbyuv.c
+++ b/lib/libv4lconvert/rgbyuv.c
@@ -93,7 +93,7 @@ void v4lconvert_rgb24_to_yuv420(const unsigned char *src, unsigned char *dest,
#define CLIP(color) (unsigned char)(((color) > 0xFF) ? 0xff : (((color) < 0) ? 0 : (color)))
void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
- int width, int height, int yvu)
+ int width, int height, int stride, int yvu)
{
int i, j;
@@ -101,11 +101,11 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
const unsigned char *usrc, *vsrc;
if (yvu) {
- vsrc = src + width * height;
- usrc = vsrc + (width * height) / 4;
+ vsrc = src + stride * height;
+ usrc = vsrc + (stride * height) / 4;
} else {
- usrc = src + width * height;
- vsrc = usrc + (width * height) / 4;
+ usrc = src + stride * height;
+ vsrc = usrc + (stride * height) / 4;
}
for (i = 0; i < height; i++) {
@@ -138,16 +138,20 @@ void v4lconvert_yuv420_to_bgr24(const unsigned char *src, unsigned char *dest,
usrc++;
vsrc++;
}
+ ysrc += stride - width;
/* Rewind u and v for next line */
if (!(i & 1)) {
usrc -= width / 2;
vsrc -= width / 2;
+ } else {
+ usrc += (stride - width) / 2;
+ vsrc += (stride - width) / 2;
}
}
}
void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height, int yvu)
+ int width, int height, int stride, int yvu)
{
int i, j;
@@ -155,11 +159,11 @@ void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
const unsigned char *usrc, *vsrc;
if (yvu) {
- vsrc = src + width * height;
- usrc = vsrc + (width * height) / 4;
+ vsrc = src + stride * height;
+ usrc = vsrc + (stride * height) / 4;
} else {
- usrc = src + width * height;
- vsrc = usrc + (width * height) / 4;
+ usrc = src + stride * height;
+ vsrc = usrc + (stride * height) / 4;
}
for (i = 0; i < height; i++) {
@@ -192,10 +196,14 @@ void v4lconvert_yuv420_to_rgb24(const unsigned char *src, unsigned char *dest,
usrc++;
vsrc++;
}
+ ysrc += stride - width;
/* Rewind u and v for next line */
- if (!(i&1)) {
+ if (!(i & 1)) {
usrc -= width / 2;
vsrc -= width / 2;
+ } else {
+ usrc += (stride - width) / 2;
+ vsrc += (stride - width) / 2;
}
}
}
@@ -296,17 +304,21 @@ void v4lconvert_yuyv_to_yuv420(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_nv16_to_yuyv(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int stride)
{
const unsigned char *y, *cbcr;
- int count = 0;
+ int i, j;
y = src;
- cbcr = src + width*height;
+ cbcr = src + stride * height;
- while (count++ < width*height) {
- *dest++ = *y++;
- *dest++ = *cbcr++;
+ for (i = 0; i < height; i++) {
+ for (j = 0; j < width; j++) {
+ *dest++ = *y++;
+ *dest++ = *cbcr++;
+ }
+ y += stride - width;
+ cbcr += stride - width;
}
}
@@ -503,7 +515,7 @@ void v4lconvert_swap_uv(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int stride)
{
int j;
while (--height >= 0) {
@@ -517,11 +529,12 @@ void v4lconvert_rgb565_to_rgb24(const unsigned char *src, unsigned char *dest,
src += 2;
}
+ src += stride - 2 * width;
}
}
void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest,
- int width, int height)
+ int width, int height, int stride)
{
int j;
while (--height >= 0) {
@@ -535,6 +548,7 @@ void v4lconvert_rgb565_to_bgr24(const unsigned char *src, unsigned char *dest,
src += 2;
}
+ src += stride - 2 * width;
}
}
@@ -847,11 +861,11 @@ void v4lconvert_hsv_to_rgb24(const unsigned char *src, unsigned char *dest,
}
void v4lconvert_nv12_to_rgb24(const unsigned char *src, unsigned char *dest,
- int width, int height, int bgr)
+ int width, int height, int stride, int bgr)
{
int i, j;
const unsigned char *ysrc = src;
- const unsigned char *uvsrc = src + width * height;
+ const unsigned char *uvsrc = src + stride * height;
for (i = 0; i < height; i++) {
for (j = 0; j < width; j ++) {
@@ -869,18 +883,21 @@ void v4lconvert_nv12_to_rgb24(const unsigned char *src, unsigned char *dest,
uvsrc += 2;
}
+ ysrc += stride - width;
/* Rewind u and v for next line */
if (!(i&1))
uvsrc -= width;
+ else
+ uvsrc += stride - width;
}
}
void v4lconvert_nv12_to_yuv420(const unsigned char *src, unsigned char *dest,
- int width, int height, int yvu)
+ int width, int height, int stride, int yvu)
{
int i, j;
const unsigned char *ysrc = src;
- const unsigned char *uvsrc = src + width * height;
+ const unsigned char *uvsrc = src + stride * height;
unsigned char *ydst = dest;
unsigned char *udst, *vdst;
@@ -892,7 +909,7 @@ void v4lconvert_nv12_to_yuv420(const unsigned char *src, unsigned char *dest,
vdst = udst + ((width / 2) * (height / 2));
}
- for (i = 0; i < height; i++)
+ for (i = 0; i < height; i++) {
for (j = 0; j < width; j++) {
*ydst++ = *ysrc++;
if (((i % 2) == 0) && ((j % 2) == 0)) {
@@ -900,4 +917,9 @@ void v4lconvert_nv12_to_yuv420(const unsigned char *src, unsigned char *dest,
*vdst++ = *uvsrc++;
}
}
+
+ ysrc += stride - width;
+ if ((i % 2) == 0)
+ uvsrc += stride - width;
+ }
}
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 0e68a612..6f59515e 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -15,6 +15,11 @@ SUBDIRS = \
cec-follower \
rds-ctl
+if HAVE_JSONC
+SUBDIRS += \
+ v4l2-tracer
+endif
+
if WITH_LIBDVBV5
SUBDIRS += \
dvb
diff --git a/utils/cec-follower/cec-follower.1.in b/utils/cec-follower/cec-follower.1.in
index 281651ea..b1477224 100644
--- a/utils/cec-follower/cec-follower.1.in
+++ b/utils/cec-follower/cec-follower.1.in
@@ -96,6 +96,12 @@ is emulating a TV.
Ignore messages from logical address \fI<la>\fR and opcode
\fI<opcode>\fR. 'all' can be used for \fI<la>\fR or \fI<opcode>\fR to match
all logical addresses or opcodes.
+.TP
+\fB\-\-ignore\-standby\fR \fI<n>\fR
+Ignore every <n>th received Standby message.
+.TP
+\fB\-\-ignore\-view\-on\fR \fI<n>\fR
+Ignore every <n>th received Image/Text View On message.
.SH EXIT STATUS
On success, it returns 0. Otherwise, it will return the error code.
diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
index 0adf6ce8..07bbba04 100644
--- a/utils/cec-follower/cec-follower.cpp
+++ b/utils/cec-follower/cec-follower.cpp
@@ -36,6 +36,8 @@ enum Option {
OptServiceByDigID = 128,
OptStandby,
OptTogglePowerStatus,
+ OptIgnoreStandby,
+ OptIgnoreViewOn,
OptVersion,
OptLast = 256
};
@@ -63,6 +65,8 @@ static struct option long_options[] = {
{ "service-by-dig-id", no_argument, nullptr, OptServiceByDigID },
{ "standby", no_argument, nullptr, OptStandby },
{ "toggle-power-status", required_argument, nullptr, OptTogglePowerStatus },
+ { "ignore-standby", required_argument, nullptr, OptIgnoreStandby },
+ { "ignore-view-on", required_argument, nullptr, OptIgnoreViewOn },
{ "ignore", required_argument, nullptr, OptIgnore },
{ "version", no_argument, nullptr, OptVersion },
@@ -90,6 +94,10 @@ static void usage()
" --standby Start in Standby state\n"
" --toggle-power-status <secs>\n"
" Toggle the power status every <secs> seconds\n"
+ " --ignore-standby <n>\n"
+ " Ignore every <n>th received Standby message\n"
+ " --ignore-view-on <n>\n"
+ " Ignore every <n>th received Image/Text View On message\n"
" -i, --ignore <la>,<opcode>\n"
" Ignore messages from logical address <la> and opcode\n"
" <opcode>. 'all' can be used for <la> or <opcode> to match\n"
@@ -459,6 +467,12 @@ int main(int argc, char **argv)
case OptTogglePowerStatus:
toggle_power_status = strtoul(optarg, nullptr, 0);
break;
+ case OptIgnoreStandby:
+ node.ignore_standby = strtoul(optarg, nullptr, 0);
+ break;
+ case OptIgnoreViewOn:
+ node.ignore_view_on = strtoul(optarg, nullptr, 0);
+ break;
case OptIgnore: {
bool all_la = !strncmp(optarg, "all", 3);
bool all_opcodes = true;
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 945e65ff..54f4df52 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -86,6 +86,10 @@ struct node {
bool ignore_la[16];
unsigned short ignore_opcode[256];
+ unsigned standby_cnt;
+ unsigned ignore_standby;
+ unsigned view_on_cnt;
+ unsigned ignore_view_on;
};
struct Timer {
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index 661cbf42..15fe774b 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -332,7 +332,8 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me, __u8
/* Standby */
case CEC_MSG_STANDBY:
- enter_standby(node);
+ if (!node->ignore_standby || (++node->standby_cnt % node->ignore_standby))
+ enter_standby(node);
return;
@@ -366,7 +367,8 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me, __u8
case CEC_MSG_TEXT_VIEW_ON:
if (!cec_has_tv(1 << me))
break;
- exit_standby(node);
+ if (!node->ignore_view_on || (++node->view_on_cnt % node->ignore_view_on))
+ exit_standby(node);
return;
case CEC_MSG_SET_STREAM_PATH: {
__u16 phys_addr;
diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp
index 00a5fada..b297d7ce 100644
--- a/utils/common/v4l2-info.cpp
+++ b/utils/common/v4l2-info.cpp
@@ -16,12 +16,7 @@ static std::string num2s(unsigned num, bool is_hex = true)
return buf;
}
-struct flag_def {
- unsigned flag;
- const char *str;
-};
-
-static std::string flags2s(unsigned val, const flag_def *def)
+std::string flags2s(unsigned val, const flag_def *def)
{
std::string s;
@@ -92,10 +87,10 @@ static std::string cap2s(unsigned cap)
s += "\t\tAudio\n";
if (cap & V4L2_CAP_RADIO)
s += "\t\tRadio\n";
+ if (cap & V4L2_CAP_IO_MC)
+ s += "\t\tI/O MC\n";
if (cap & V4L2_CAP_READWRITE)
s += "\t\tRead/Write\n";
- if (cap & V4L2_CAP_ASYNCIO)
- s += "\t\tAsync I/O\n";
if (cap & V4L2_CAP_STREAMING)
s += "\t\tStreaming\n";
if (cap & V4L2_CAP_EXT_PIX_FORMAT)
@@ -347,6 +342,7 @@ std::string quantization2s(int val)
static constexpr flag_def pixflags_def[] = {
{ V4L2_PIX_FMT_FLAG_PREMUL_ALPHA, "premultiplied-alpha" },
+ { V4L2_PIX_FMT_FLAG_SET_CSC, "set-csc" },
{ 0, nullptr }
};
@@ -768,3 +764,109 @@ std::string vbiflags2s(__u32 flags)
{
return flags2s(flags, vbi_def);
}
+
+std::string ttype2s(int type)
+{
+ switch (type) {
+ case V4L2_TUNER_RADIO: return "radio";
+ case V4L2_TUNER_ANALOG_TV: return "Analog TV";
+ case V4L2_TUNER_DIGITAL_TV: return "Digital TV";
+ case V4L2_TUNER_SDR: return "SDR";
+ case V4L2_TUNER_RF: return "RF";
+ default: return "unknown";
+ }
+}
+
+std::string audmode2s(int audmode)
+{
+ switch (audmode) {
+ case V4L2_TUNER_MODE_STEREO: return "stereo";
+ case V4L2_TUNER_MODE_LANG1: return "lang1";
+ case V4L2_TUNER_MODE_LANG2: return "lang2";
+ case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
+ case V4L2_TUNER_MODE_MONO: return "mono";
+ default: return "unknown";
+ }
+}
+
+std::string rxsubchans2s(int rxsubchans)
+{
+ std::string s;
+
+ if (rxsubchans & V4L2_TUNER_SUB_MONO)
+ s += "mono ";
+ if (rxsubchans & V4L2_TUNER_SUB_STEREO)
+ s += "stereo ";
+ if (rxsubchans & V4L2_TUNER_SUB_LANG1)
+ s += "lang1 ";
+ if (rxsubchans & V4L2_TUNER_SUB_LANG2)
+ s += "lang2 ";
+ if (rxsubchans & V4L2_TUNER_SUB_RDS)
+ s += "rds ";
+ return s;
+}
+
+std::string txsubchans2s(int txsubchans)
+{
+ std::string s;
+
+ if (txsubchans & V4L2_TUNER_SUB_MONO)
+ s += "mono ";
+ if (txsubchans & V4L2_TUNER_SUB_STEREO)
+ s += "stereo ";
+ if (txsubchans & V4L2_TUNER_SUB_LANG1)
+ s += "bilingual ";
+ if (txsubchans & V4L2_TUNER_SUB_SAP)
+ s += "sap ";
+ if (txsubchans & V4L2_TUNER_SUB_RDS)
+ s += "rds ";
+ return s;
+}
+
+std::string tcap2s(unsigned cap)
+{
+ std::string s;
+
+ if (cap & V4L2_TUNER_CAP_LOW)
+ s += "62.5 Hz ";
+ else if (cap & V4L2_TUNER_CAP_1HZ)
+ s += "1 Hz ";
+ else
+ s += "62.5 kHz ";
+ if (cap & V4L2_TUNER_CAP_NORM)
+ s += "multi-standard ";
+ if (cap & V4L2_TUNER_CAP_HWSEEK_BOUNDED)
+ s += "hwseek-bounded ";
+ if (cap & V4L2_TUNER_CAP_HWSEEK_WRAP)
+ s += "hwseek-wrap ";
+ if (cap & V4L2_TUNER_CAP_STEREO)
+ s += "stereo ";
+ if (cap & V4L2_TUNER_CAP_LANG1)
+ s += "lang1 ";
+ if (cap & V4L2_TUNER_CAP_LANG2)
+ s += "lang2 ";
+ if (cap & V4L2_TUNER_CAP_RDS)
+ s += "rds ";
+ if (cap & V4L2_TUNER_CAP_RDS_BLOCK_IO)
+ s += "rds-block-I/O ";
+ if (cap & V4L2_TUNER_CAP_RDS_CONTROLS)
+ s += "rds-controls ";
+ if (cap & V4L2_TUNER_CAP_FREQ_BANDS)
+ s += "freq-bands ";
+ if (cap & V4L2_TUNER_CAP_HWSEEK_PROG_LIM)
+ s += "hwseek-prog-lim ";
+ return s;
+}
+
+std::string modulation2s(unsigned modulation)
+{
+ switch (modulation) {
+ case V4L2_BAND_MODULATION_VSB:
+ return "VSB";
+ case V4L2_BAND_MODULATION_FM:
+ return "FM";
+ case V4L2_BAND_MODULATION_AM:
+ return "AM";
+ }
+ return "Unknown";
+}
diff --git a/utils/common/v4l2-info.h b/utils/common/v4l2-info.h
index 6ddd2ee5..4a9aa3e8 100644
--- a/utils/common/v4l2-info.h
+++ b/utils/common/v4l2-info.h
@@ -11,6 +11,14 @@
#include <linux/videodev2.h>
#include <linux/v4l2-subdev.h>
+struct flag_def {
+ unsigned flag;
+ const char *str;
+};
+
+/* Return a comma-separated string of flags or hex value if unknown */
+std::string flags2s(unsigned val, const flag_def *def);
+
/* Print capability information */
void v4l2_info_capability(const v4l2_capability &cap);
void v4l2_info_subdev_capability(const v4l2_subdev_capability &subdevcap);
@@ -118,4 +126,22 @@ std::string bufferflags2s(__u32 flags);
/* Return vbi flags description */
std::string vbiflags2s(__u32 flags);
+/* Return tuner type description */
+std::string ttype2s(int type);
+
+/* Return audio mode description */
+std::string audmode2s(int audmode);
+
+/* Return RX subchannels description */
+std::string rxsubchans2s(int rxsubchans);
+
+/* Return TX subchannels description */
+std::string txsubchans2s(int txsubchans);
+
+/* Return tuner capabilities description */
+std::string tcap2s(unsigned cap);
+
+/* Return band modulation description */
+std::string modulation2s(unsigned modulation);
+
#endif
diff --git a/utils/common/v4l2-pix-formats.h b/utils/common/v4l2-pix-formats.h
index f2066dec..263dac39 100644
--- a/utils/common/v4l2-pix-formats.h
+++ b/utils/common/v4l2-pix-formats.h
@@ -78,23 +78,23 @@
case V4L2_PIX_FMT_YUV420: return "Planar YUV 4:2:0";
case V4L2_PIX_FMT_HI240: return "8-bit Dithered RGB (BTTV)";
case V4L2_PIX_FMT_M420: return "YUV 4:2:0 (M420)";
- case V4L2_PIX_FMT_NV12: return "Y/CbCr 4:2:0";
- case V4L2_PIX_FMT_NV21: return "Y/CrCb 4:2:0";
- case V4L2_PIX_FMT_NV16: return "Y/CbCr 4:2:2";
- case V4L2_PIX_FMT_NV61: return "Y/CrCb 4:2:2";
- case V4L2_PIX_FMT_NV24: return "Y/CbCr 4:4:4";
- case V4L2_PIX_FMT_NV42: return "Y/CrCb 4:4:4";
- case V4L2_PIX_FMT_P010: return "10-bit Y/CbCr 4:2:0";
- case V4L2_PIX_FMT_NV12_4L4: return "Y/CbCr 4:2:0 (4x4 Linear)";
- case V4L2_PIX_FMT_NV12_16L16: return "Y/CbCr 4:2:0 (16x16 Linear)";
- case V4L2_PIX_FMT_NV12_32L32: return "Y/CbCr 4:2:0 (32x32 Linear)";
- case V4L2_PIX_FMT_P010_4L4: return "10-bit Y/CbCr 4:2:0 (4x4 Linear)";
- case V4L2_PIX_FMT_NV12M: return "Y/CbCr 4:2:0 (N-C)";
- case V4L2_PIX_FMT_NV21M: return "Y/CrCb 4:2:0 (N-C)";
- case V4L2_PIX_FMT_NV16M: return "Y/CbCr 4:2:2 (N-C)";
- case V4L2_PIX_FMT_NV61M: return "Y/CrCb 4:2:2 (N-C)";
- case V4L2_PIX_FMT_NV12MT: return "Y/CbCr 4:2:0 (64x32 MB, N-C)";
- case V4L2_PIX_FMT_NV12MT_16X16: return "Y/CbCr 4:2:0 (16x16 MB, N-C)";
+ case V4L2_PIX_FMT_NV12: return "Y/UV 4:2:0";
+ case V4L2_PIX_FMT_NV21: return "Y/VU 4:2:0";
+ case V4L2_PIX_FMT_NV16: return "Y/UV 4:2:2";
+ case V4L2_PIX_FMT_NV61: return "Y/VU 4:2:2";
+ case V4L2_PIX_FMT_NV24: return "Y/UV 4:4:4";
+ case V4L2_PIX_FMT_NV42: return "Y/VU 4:4:4";
+ case V4L2_PIX_FMT_P010: return "10-bit Y/UV 4:2:0";
+ case V4L2_PIX_FMT_NV12_4L4: return "Y/UV 4:2:0 (4x4 Linear)";
+ case V4L2_PIX_FMT_NV12_16L16: return "Y/UV 4:2:0 (16x16 Linear)";
+ case V4L2_PIX_FMT_NV12_32L32: return "Y/UV 4:2:0 (32x32 Linear)";
+ case V4L2_PIX_FMT_P010_4L4: return "10-bit Y/UV 4:2:0 (4x4 Linear)";
+ case V4L2_PIX_FMT_NV12M: return "Y/UV 4:2:0 (N-C)";
+ case V4L2_PIX_FMT_NV21M: return "Y/VU 4:2:0 (N-C)";
+ case V4L2_PIX_FMT_NV16M: return "Y/UV 4:2:2 (N-C)";
+ case V4L2_PIX_FMT_NV61M: return "Y/VU 4:2:2 (N-C)";
+ case V4L2_PIX_FMT_NV12MT: return "Y/UV 4:2:0 (64x32 MB, N-C)";
+ case V4L2_PIX_FMT_NV12MT_16X16: return "Y/UV 4:2:0 (16x16 MB, N-C)";
case V4L2_PIX_FMT_YUV420M: return "Planar YUV 4:2:0 (N-C)";
case V4L2_PIX_FMT_YVU420M: return "Planar YVU 4:2:0 (N-C)";
case V4L2_PIX_FMT_YUV422M: return "Planar YUV 4:2:2 (N-C)";
@@ -175,7 +175,9 @@
case V4L2_META_FMT_VIVID: return "Vivid Metadata";
case V4L2_META_FMT_RK_ISP1_PARAMS: return "Rockchip ISP1 3A Parameters";
case V4L2_META_FMT_RK_ISP1_STAT_3A: return "Rockchip ISP1 3A Statistics";
+ case V4L2_PIX_FMT_NV12_8L128: return "NV12 (8x128 Linear)";
case V4L2_PIX_FMT_NV12M_8L128: return "NV12M (8x128 Linear)";
+ case V4L2_PIX_FMT_NV12_10BE_8L128: return "10-bit NV12 (8x128 Linear, BE)";
case V4L2_PIX_FMT_NV12M_10BE_8L128: return "10-bit NV12M (8x128 Linear, BE)";
case V4L2_PIX_FMT_MJPEG: return "Motion-JPEG";
case V4L2_PIX_FMT_JPEG: return "JFIF JPEG";
@@ -221,3 +223,4 @@
case V4L2_PIX_FMT_MT21C: return "Mediatek Compressed Format";
case V4L2_PIX_FMT_QC08C: return "QCOM Compressed 8-bit Format";
case V4L2_PIX_FMT_QC10C: return "QCOM Compressed 10-bit Format";
+ case V4L2_PIX_FMT_AJPG: return "Aspeed JPEG";
diff --git a/utils/common/v4l2-tpg-core.c b/utils/common/v4l2-tpg-core.c
index 3cfd0f1d..920fe805 100644
--- a/utils/common/v4l2-tpg-core.c
+++ b/utils/common/v4l2-tpg-core.c
@@ -861,7 +861,7 @@ static void precalculate_color(struct tpg_data *tpg, int k)
g = tpg_colors[col].g;
b = tpg_colors[col].b;
} else if (tpg->pattern == TPG_PAT_NOISE) {
- r = g = b = prandom_u32_max(256);
+ r = g = b = get_random_u8();
} else if (k == TPG_COLOR_RANDOM) {
r = g = b = tpg->qual_offset + prandom_u32_max(196);
} else if (k >= TPG_COLOR_RAMP) {
diff --git a/utils/common/v4l2-tpg.h b/utils/common/v4l2-tpg.h
index 5760854e..7bc98b1b 100644
--- a/utils/common/v4l2-tpg.h
+++ b/utils/common/v4l2-tpg.h
@@ -61,6 +61,12 @@ static inline u32 prandom_u32_max(u32 ep_ro)
return rand() % ep_ro;
}
+static inline u32 get_random_u8(void)
+{
+ return prandom_u32_max(256);
+}
+
+
struct tpg_rbg_color8 {
unsigned char r, g, b;
};
diff --git a/utils/common/v4l2-tpg.patch b/utils/common/v4l2-tpg.patch
index 2381ebd9..50b72096 100644
--- a/utils/common/v4l2-tpg.patch
+++ b/utils/common/v4l2-tpg.patch
@@ -167,7 +167,7 @@ diff --git a/utils/common/v4l2-tpg.h b/utils/common/v4l2-tpg.h
index 0b0ddb87..91da74ec 100644
--- a/utils/common/v4l2-tpg.h
+++ b/utils/common/v4l2-tpg.h
-@@ -8,13 +8,59 @@
+@@ -8,13 +8,65 @@
#ifndef _V4L2_TPG_H_
#define _V4L2_TPG_H_
@@ -229,6 +229,12 @@ index 0b0ddb87..91da74ec 100644
+ return rand() % ep_ro;
+}
+
++static inline u32 get_random_u8(void)
++{
++ return prandom_u32_max(256);
++}
++
++
struct tpg_rbg_color8 {
unsigned char r, g, b;
};
diff --git a/utils/dvb/dvbv5-zap.c b/utils/dvb/dvbv5-zap.c
index 8e752297..1cd6989f 100644
--- a/utils/dvb/dvbv5-zap.c
+++ b/utils/dvb/dvbv5-zap.c
@@ -92,7 +92,7 @@ struct arguments {
int lna, lnb, sat_number;
unsigned diseqc_wait, silent, verbose, frontend_only, freq_bpf;
unsigned timeout, dvr, rec_psi, exit_after_tuning;
- unsigned n_apid, n_vpid, all_pids;
+ unsigned n_apid, n_vpid, extra_pids, all_pids;
enum dvb_file_formats input_format, output_format;
unsigned traffic_monitor, low_traffic, non_human, port;
char *search, *server;
@@ -106,6 +106,7 @@ static const struct argp_option options[] = {
{"adapter", 'a', N_("adapter#"), 0, N_("use given adapter (default 0)"), 0},
{"audio_pid", 'A', N_("audio_pid#"), 0, N_("audio pid program to use (default 0)"), 0},
{"channels", 'c', N_("file"), 0, N_("read channels list from 'file'"), 0},
+ {"extra-pids", 'E', NULL, 0, N_("output all channel pids"), 0 },
{"demux", 'd', N_("demux#"), 0, N_("use given demux (default 0)"), 0},
{"frontend", 'f', N_("frontend#"), 0, N_("use given frontend (default 0)"), 0},
{"input-format", 'I', N_("format"), 0, N_("Input format: ZAP, CHANNEL, DVBV5 (default: DVBV5)"), 0},
@@ -165,16 +166,24 @@ static int timeout_flag = 0;
} while (0)
+/*
+ * Find channel configuration.
+ * On success, the caller must dvb_file_free(*out_file).
+ */
static int parse(struct arguments *args,
struct dvb_v5_fe_parms *parms,
- char *channel,
- int *vpid, int *apid, int *sid)
+ const char *channel,
+ struct dvb_file **out_file,
+ const struct dvb_entry **out_entry)
{
struct dvb_file *dvb_file;
struct dvb_entry *entry;
int i;
uint32_t sys;
+ *out_file = NULL;
+ *out_entry = NULL;
+
/* This is used only when reading old formats */
switch (parms->current_sys) {
case SYS_DVBT:
@@ -265,18 +274,6 @@ static int parse(struct arguments *args,
if (parms->sat_number < 0 && entry->sat_number >= 0)
parms->sat_number = entry->sat_number;
- if (entry->video_pid) {
- if (args->n_vpid < entry->video_pid_len)
- *vpid = entry->video_pid[args->n_vpid];
- else
- *vpid = entry->video_pid[0];
- }
- if (entry->audio_pid) {
- if (args->n_apid < entry->audio_pid_len)
- *apid = entry->audio_pid[args->n_apid];
- else
- *apid = entry->audio_pid[0];
- }
if (entry->other_el_pid) {
int i, type = -1;
for (i = 0; i < entry->other_el_pid_len; i++) {
@@ -290,7 +287,6 @@ static int parse(struct arguments *args,
}
fprintf(stderr, "\n");
}
- *sid = entry->service_id;
/* First of all, set the delivery system */
dvb_retrieve_entry_prop(entry, DTV_DELIVERY_SYSTEM, &sys);
@@ -335,7 +331,8 @@ static int parse(struct arguments *args,
}
}
- dvb_file_free(dvb_file);
+ *out_file = dvb_file;
+ *out_entry = entry;
return 0;
}
@@ -691,8 +688,11 @@ static error_t parse_opt(int k, char *optarg, struct argp_state *state)
case 'V':
args->n_vpid = strtoul(optarg, NULL, 0);
break;
+ case 'E':
+ args->extra_pids = 1;
+ break;
case 'P':
- args->all_pids++;
+ args->all_pids = 1;
break;
case 'm':
args->traffic_monitor = 1;
@@ -1032,12 +1032,12 @@ int main(int argc, char **argv)
char *homedir = getenv("HOME");
char *channel = NULL;
int lnb = -1, idx = -1;
- int vpid = -1, apid = -1, sid = -1;
int pmtpid = 0;
+ struct dvb_file *dvb_file = NULL;
+ const struct dvb_entry *dvb_entry = NULL;
struct dvb_open_descriptor *pat_fd = NULL, *pmt_fd = NULL;
struct dvb_open_descriptor *sdt_fd = NULL;
struct dvb_open_descriptor *sid_fd = NULL, *dvr_fd = NULL;
- struct dvb_open_descriptor *audio_fd = NULL, *video_fd = NULL;
int file_fd = -1;
int err = -1;
int r, ret;
@@ -1167,7 +1167,7 @@ int main(int argc, char **argv)
if (r < 0)
fprintf(stderr, _("Failed to set the country code:%s\n"), args.cc);
- if (parse(&args, parms, channel, &vpid, &apid, &sid))
+ if (parse(&args, parms, channel, &dvb_file, &dvb_entry))
goto err;
if (setup_frontend(&args, parms) < 0)
@@ -1197,23 +1197,16 @@ int main(int argc, char **argv)
}
if (args.rec_psi) {
- if (sid < 0) {
- fprintf(stderr, _("Service id 0x%04x was not specified at the file\n"),
- sid);
- goto err;
- }
-
sid_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
if (!sid_fd) {
ERROR("opening sid demux failed");
return -1;
}
- pmtpid = dvb_dev_dmx_get_pmt_pid(sid_fd, sid);
+ pmtpid = dvb_dev_dmx_get_pmt_pid(sid_fd, dvb_entry->service_id);
dvb_dev_close(sid_fd);
if (pmtpid <= 0) {
fprintf(stderr, _("couldn't find pmt-pid for sid %04x\n"),
- sid);
-
+ dvb_entry->service_id);
goto err;
}
@@ -1251,58 +1244,112 @@ int main(int argc, char **argv)
goto err;
}
- if (args.all_pids++) {
- vpid = 0x2000;
- apid = 0;
- }
- if (vpid >= 0) {
- if (args.silent < 2) {
- if (vpid == 0x2000)
- fprintf(stderr, _("pass all PID's to TS\n"));
- else
- fprintf(stderr, _("video pid %d\n"), vpid);
- }
- video_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
+ if (args.all_pids) {
+ struct dvb_open_descriptor *video_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
if (!video_fd) {
ERROR("failed opening '%s'", args.demux_dev);
goto err;
}
- if (args.silent < 2)
- fprintf(stderr, _(" dvb_set_pesfilter %d\n"), vpid);
-
fprintf(stderr, _("dvb_dev_set_bufsize: buffer set to %d\n"), DVB_BUF_SIZE);
dvb_dev_set_bufsize(video_fd, DVB_BUF_SIZE);
- if (vpid == 0x2000) {
- if (dvb_dev_dmx_set_pesfilter(video_fd, vpid, DMX_PES_OTHER,
- DMX_OUT_TS_TAP, 0) < 0)
- goto err;
- } else {
- if (dvb_dev_dmx_set_pesfilter(video_fd, vpid, DMX_PES_VIDEO,
- args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER,
- args.dvr ? 64 * 1024 : 0) < 0)
- goto err;
+ if (args.silent < 2) {
+ fprintf(stderr, _("pass all PIDs to TS\n"));
+ fprintf(stderr, _(" dvb_set_pesfilter %d\n"), 0x2000);
}
- }
- if (apid > 0) {
- if (args.silent < 2)
- fprintf(stderr, _("audio pid %d\n"), apid);
- audio_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
- if (!audio_fd) {
- ERROR("failed opening '%s'", args.demux_dev);
+ if (dvb_dev_dmx_set_pesfilter(video_fd, 0x2000, DMX_PES_OTHER,
+ DMX_OUT_TS_TAP, 0) < 0) {
goto err;
}
- if (args.silent < 2)
- fprintf(stderr, _(" dvb_set_pesfilter %d\n"), apid);
- if (dvb_dev_dmx_set_pesfilter(audio_fd, apid, DMX_PES_AUDIO,
- args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER,
- args.dvr ? 64 * 1024 : 0) < 0)
- goto err;
+ } else {
+ if (dvb_entry->video_pid_len) {
+ struct dvb_open_descriptor *video_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
+ if (!video_fd) {
+ ERROR("failed opening '%s'", args.demux_dev);
+ goto err;
+ }
+
+ fprintf(stderr, _("dvb_dev_set_bufsize: buffer set to %d\n"), DVB_BUF_SIZE);
+ dvb_dev_set_bufsize(video_fd, DVB_BUF_SIZE);
+
+ if (args.n_vpid >= dvb_entry->video_pid_len) {
+ args.n_vpid = 0;
+ }
+
+ for (int i = 0; i < dvb_entry->video_pid_len; i++) {
+ if (!args.extra_pids && i != args.n_vpid) {
+ continue;
+ }
+
+ if (args.silent < 2) {
+ fprintf(stderr, _("video%2$s pid %1$d\n"),
+ dvb_entry->video_pid[i], i == args.n_vpid ? "" : "+");
+ }
+
+ if (dvb_dev_dmx_set_pesfilter(video_fd, dvb_entry->video_pid[i],
+ i == args.n_vpid ? DMX_PES_VIDEO : DMX_PES_OTHER,
+ args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER,
+ args.dvr ? 1024 * 1024 : 0) < 0) {
+ goto err;
+ }
+ }
+ }
+
+ if (dvb_entry->audio_pid_len) {
+ if (args.n_apid >= dvb_entry->audio_pid_len) {
+ args.n_apid = 0;
+ }
+
+ for (int i = 0; i < dvb_entry->audio_pid_len; i++) {
+ if (!args.extra_pids && i != args.n_apid) {
+ continue;
+ }
+
+ struct dvb_open_descriptor *audio_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
+ if (!audio_fd) {
+ ERROR("failed opening '%s'", args.demux_dev);
+ goto err;
+ }
+
+ if (args.silent < 2) {
+ fprintf(stderr, _("audio%2$s pid %1$d\n"),
+ dvb_entry->audio_pid[i], i == args.n_apid ? "" : "+");
+ }
+
+ if (dvb_dev_dmx_set_pesfilter(audio_fd, dvb_entry->audio_pid[i],
+ i == args.n_apid ? DMX_PES_AUDIO : DMX_PES_OTHER,
+ args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER,
+ args.dvr ? 64 * 1024 : 0) < 0) {
+ goto err;
+ }
+ }
+ }
+
+ if (args.extra_pids && dvb_entry->other_el_pid_len) {
+ for (int i = dvb_entry->other_el_pid_len - 1; i >= 0; i--) {
+ struct dvb_open_descriptor * other_fd = dvb_dev_open(dvb, args.demux_dev, O_RDWR);
+ if (!other_fd) {
+ ERROR("failed opening '%s'", args.demux_dev);
+ goto err;
+ }
+
+ if (args.silent < 2) {
+ fprintf(stderr, _("other pid %d (%d)\n"),
+ dvb_entry->other_el_pid[i].pid, dvb_entry->other_el_pid[i].type);
+ }
+
+ if (dvb_dev_dmx_set_pesfilter(other_fd, dvb_entry->other_el_pid[i].pid, DMX_PES_OTHER,
+ args.dvr ? DMX_OUT_TS_TAP : DMX_OUT_DECODER,
+ args.dvr ? 64 * 1024 : 0) < 0) {
+ goto err;
+ }
+ }
+ }
}
- if (((vpid >= 0 && vpid != 0x2000) || apid) && !args.rec_psi) {
+ if ((dvb_entry->video_pid_len || dvb_entry->audio_pid_len) && !args.all_pids && !args.rec_psi) {
printf(_("PMT record is disabled.\n"
"Please notice that some streams can only be decoded with PMT data.\n"
"Use '-p' option to also record PMT.\n"));
@@ -1404,6 +1451,10 @@ int main(int argc, char **argv)
err:
dvb_dev_free(dvb);
+ if (dvb_file) {
+ dvb_file_free(dvb_file);
+ }
+
/*
* Just to make Valgrind happier. It should be noticed
* That, if an error happens or if the program exits via
diff --git a/utils/keytable/bpf_load.c b/utils/keytable/bpf_load.c
index 7c633dac..06098fc3 100644
--- a/utils/keytable/bpf_load.c
+++ b/utils/keytable/bpf_load.c
@@ -63,19 +63,17 @@ struct bpf_file {
static int load_and_attach(int lirc_fd, struct bpf_file *bpf_file, struct bpf_insn *prog, int size)
{
- struct bpf_load_program_attr load_attr;
- int fd, err;
+ LIBBPF_OPTS(bpf_prog_load_opts, opts);
+ int fd, err, insn_cnt;
- memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+ insn_cnt = size / sizeof(struct bpf_insn);
- load_attr.prog_type = BPF_PROG_TYPE_LIRC_MODE2;
- load_attr.expected_attach_type = BPF_LIRC_MODE2;
- load_attr.name = bpf_file->name;
- load_attr.insns = prog;
- load_attr.insns_cnt = size / sizeof(struct bpf_insn);
- load_attr.license = bpf_file->license;
+ opts.expected_attach_type = BPF_LIRC_MODE2;
+ opts.log_buf = bpf_log_buf;
+ opts.log_size = LOG_BUF_SIZE;
- fd = bpf_load_program_xattr(&load_attr, bpf_log_buf, LOG_BUF_SIZE);
+ fd = bpf_prog_load(BPF_PROG_TYPE_LIRC_MODE2, bpf_file->name,
+ bpf_file->license, prog, insn_cnt, &opts);
if (fd < 0) {
printf("bpf_load_program() err=%m\n%s", bpf_log_buf);
return -1;
@@ -95,6 +93,9 @@ static int build_raw_map(struct bpf_map_data *map, struct raw_entry *raw, int nu
int no_patterns, value_size, fd, key, i;
struct raw_entry *e;
struct raw_pattern *p;
+ LIBBPF_OPTS(bpf_map_create_opts, opts,
+ .map_flags = map->def.map_flags,
+ );
no_patterns = 0;
@@ -110,14 +111,13 @@ static int build_raw_map(struct bpf_map_data *map, struct raw_entry *raw, int nu
value_size = sizeof(struct raw_pattern) + max_length * sizeof(short);
- fd = bpf_create_map_node(map->def.type,
- map->name,
- map->def.key_size,
- value_size,
- no_patterns,
- map->def.map_flags,
- numa_node);
-
+ opts.numa_node = numa_node;
+ fd = bpf_map_create(map->def.type,
+ map->name,
+ map->def.key_size,
+ value_size,
+ no_patterns,
+ &opts);
if (fd < 0) {
printf(_("failed to create a map: %d %s\n"),
errno, strerror(errno));
@@ -174,27 +174,34 @@ static int load_maps(struct bpf_file *bpf_file, struct raw_entry *raw)
if (maps[i].def.type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
maps[i].def.type == BPF_MAP_TYPE_HASH_OF_MAPS) {
- int inner_map_fd = bpf_file->map_fd[maps[i].def.inner_map_idx];
+ LIBBPF_OPTS(bpf_map_create_opts, opts,
+ .inner_map_fd = bpf_file->map_fd[maps[i].def.inner_map_idx],
+ .map_flags = maps[i].def.map_flags,
+ .numa_node = numa_node,
+ );
- bpf_file->map_fd[i] = bpf_create_map_in_map_node(
+ bpf_file->map_fd[i] = bpf_map_create(
maps[i].def.type,
maps[i].name,
maps[i].def.key_size,
- inner_map_fd,
+ 4,
maps[i].def.max_entries,
- maps[i].def.map_flags,
- numa_node);
+ &opts);
} else if (!strcmp(maps[i].name, "raw_map")) {
bpf_file->map_fd[i] = build_raw_map(&maps[i], raw, numa_node);
} else {
- bpf_file->map_fd[i] = bpf_create_map_node(
+ LIBBPF_OPTS(bpf_map_create_opts, opts,
+ .map_flags = maps[i].def.map_flags,
+ .numa_node = numa_node,
+ );
+
+ bpf_file->map_fd[i] = bpf_map_create(
maps[i].def.type,
maps[i].name,
maps[i].def.key_size,
maps[i].def.value_size,
maps[i].def.max_entries,
- maps[i].def.map_flags,
- numa_node);
+ &opts);
}
if (bpf_file->map_fd[i] < 0) {
diff --git a/utils/keytable/keytable.c b/utils/keytable/keytable.c
index 248493a9..6cd1244f 100644
--- a/utils/keytable/keytable.c
+++ b/utils/keytable/keytable.c
@@ -382,14 +382,16 @@ static int add_keymap(struct keymap *map, const char *fname)
protocol = parse_sysfs_protocol(map->protocol, false);
if (protocol == SYSFS_INVALID) {
- struct bpf_protocol *b;
-
- b = malloc(sizeof(*b));
- b->name = strdup(map->protocol);
- b->param = map->param;
- /* steal param */
- map->param = NULL;
- add_bpf_protocol(b);
+ if (strcmp(map->protocol, "none")) {
+ struct bpf_protocol *b;
+
+ b = malloc(sizeof(*b));
+ b->name = strdup(map->protocol);
+ b->param = map->param;
+ /* steal param */
+ map->param = NULL;
+ add_bpf_protocol(b);
+ }
} else {
ch_proto |= protocol;
}
@@ -2105,11 +2107,6 @@ int main(int argc, char *argv[])
}
add_keymap(map, fname);
free_keymap(map);
- if (!keytable) {
- fprintf(stderr, _("Empty keymap %s\n"), fname);
- free(fname);
- return -1;
- }
free(fname);
clear = 1;
matches++;
diff --git a/utils/keytable/parse.h b/utils/keytable/parse.h
index c70664f0..a6f0c3ba 100644
--- a/utils/keytable/parse.h
+++ b/utils/keytable/parse.h
@@ -700,6 +700,7 @@ struct parse_event abs_events[] = {
{"ABS_TILT_Y", 0x1b},
{"ABS_TOOL_WIDTH", 0x1c},
{"ABS_VOLUME", 0x20},
+ {"ABS_PROFILE", 0x21},
{"ABS_MISC", 0x28},
{"ABS_RESERVED", 0x2e},
{"ABS_MT_SLOT", 0x2f},
diff --git a/utils/keytable/rc_keymaps_userspace/empty.toml b/utils/keytable/rc_keymaps_userspace/empty.toml
new file mode 100644
index 00000000..0c0b5e69
--- /dev/null
+++ b/utils/keytable/rc_keymaps_userspace/empty.toml
@@ -0,0 +1,2 @@
+[[protocols]]
+protocol = "none"
diff --git a/utils/libv4l2util/v4l2_driver.c b/utils/libv4l2util/v4l2_driver.c
index 98345e8d..6b6366fa 100644
--- a/utils/libv4l2util/v4l2_driver.c
+++ b/utils/libv4l2util/v4l2_driver.c
@@ -142,8 +142,6 @@ static char *prt_caps(uint32_t caps)
strcat (s,"RADIO ");
if (V4L2_CAP_READWRITE & caps)
strcat (s,"READWRITE ");
- if (V4L2_CAP_ASYNCIO & caps)
- strcat (s,"ASYNCIO ");
if (V4L2_CAP_STREAMING & caps)
strcat (s,"STREAMING ");
if (V4L2_CAP_EXT_PIX_FORMAT & caps)
@@ -162,12 +160,6 @@ static char *prt_caps(uint32_t caps)
strcat (s,"SDR_OUTPUT ");
if(V4L2_CAP_META_CAPTURE & caps)
strcat (s,"META_CAPTURE ");
- if(V4L2_CAP_READWRITE & caps)
- strcat (s,"READWRITE ");
- if(V4L2_CAP_ASYNCIO & caps)
- strcat (s,"ASYNCIO ");
- if(V4L2_CAP_STREAMING & caps)
- strcat (s,"STREAMING ");
if(V4L2_CAP_META_OUTPUT & caps)
strcat (s,"META_OUTPUT ");
if(V4L2_CAP_TOUCH & caps)
diff --git a/utils/qv4l2/capture-win-gl.cpp b/utils/qv4l2/capture-win-gl.cpp
index 05659259..6cbeb426 100644
--- a/utils/qv4l2/capture-win-gl.cpp
+++ b/utils/qv4l2/capture-win-gl.cpp
@@ -196,6 +196,8 @@ void CaptureWinGLEngine::setColorspace(unsigned colorspace, unsigned xfer_func,
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
case V4L2_PIX_FMT_HSV24:
case V4L2_PIX_FMT_HSV32:
is_rgb = false;
@@ -415,6 +417,8 @@ bool CaptureWinGLEngine::hasNativeFormat(__u32 format)
V4L2_PIX_FMT_XYUV32,
V4L2_PIX_FMT_VUYA32,
V4L2_PIX_FMT_VUYX32,
+ V4L2_PIX_FMT_YUVA32,
+ V4L2_PIX_FMT_YUVX32,
V4L2_PIX_FMT_GREY,
V4L2_PIX_FMT_Z16,
V4L2_PIX_FMT_INZI,
@@ -483,6 +487,8 @@ void CaptureWinGLEngine::changeShader()
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
shader_YUV_packed(m_frameFormat);
break;
@@ -651,6 +657,8 @@ void CaptureWinGLEngine::paintGL()
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
render_YUV_packed(m_frameFormat);
break;
@@ -2100,6 +2108,13 @@ void CaptureWinGLEngine::shader_YUV_packed(__u32 format)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_frameWidth, m_frameHeight, 0,
GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL);
break;
+ case V4L2_PIX_FMT_YUVA32:
+ hasAlpha = true;
+ // fall-through
+ case V4L2_PIX_FMT_YUVX32:
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_frameWidth, m_frameHeight, 0,
+ GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
+ break;
}
checkError("Packed YUV shader");
@@ -2173,6 +2188,8 @@ void CaptureWinGLEngine::render_YUV_packed(__u32 format)
break;
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frameWidth, m_frameHeight,
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, m_frameData);
break;
diff --git a/utils/qv4l2/qv4l2.cpp b/utils/qv4l2/qv4l2.cpp
index d9141ad1..4cbaa98e 100644
--- a/utils/qv4l2/qv4l2.cpp
+++ b/utils/qv4l2/qv4l2.cpp
@@ -1567,12 +1567,14 @@ void ApplicationWindow::capStart(bool start)
case V4L2_PIX_FMT_YUV32:
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVX32:
dstFmt = QImage::Format_RGB32;
break;
case V4L2_PIX_FMT_ARGB32:
case V4L2_PIX_FMT_ABGR32:
case V4L2_PIX_FMT_AYUV32:
case V4L2_PIX_FMT_VUYA32:
+ case V4L2_PIX_FMT_YUVA32:
dstFmt = QImage::Format_ARGB32;
break;
case V4L2_PIX_FMT_INZI:
diff --git a/utils/qvidcap/capture.cpp b/utils/qvidcap/capture.cpp
index cfcbb896..0b4c4115 100644
--- a/utils/qvidcap/capture.cpp
+++ b/utils/qvidcap/capture.cpp
@@ -57,6 +57,8 @@ const __u32 formats[] = {
V4L2_PIX_FMT_XYUV32,
V4L2_PIX_FMT_VUYA32,
V4L2_PIX_FMT_VUYX32,
+ V4L2_PIX_FMT_YUVA32,
+ V4L2_PIX_FMT_YUVX32,
V4L2_PIX_FMT_RGB32,
V4L2_PIX_FMT_XRGB32,
V4L2_PIX_FMT_ARGB32,
@@ -882,6 +884,8 @@ bool CaptureWin::updateV4LFormat(const cv4l_fmt &fmt)
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
m_is_rgb = false;
m_accepts_srgb = false;
break;
diff --git a/utils/qvidcap/paint.cpp b/utils/qvidcap/paint.cpp
index 745e4003..c5aadb09 100644
--- a/utils/qvidcap/paint.cpp
+++ b/utils/qvidcap/paint.cpp
@@ -159,6 +159,8 @@ void CaptureWin::paintGL()
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
render_YUV_packed(m_v4l_fmt.g_pixelformat());
break;
@@ -355,6 +357,8 @@ static const struct define defines[] = {
DEF(V4L2_PIX_FMT_XYUV32),
DEF(V4L2_PIX_FMT_VUYA32),
DEF(V4L2_PIX_FMT_VUYX32),
+ DEF(V4L2_PIX_FMT_YUVA32),
+ DEF(V4L2_PIX_FMT_YUVX32),
DEF(V4L2_PIX_FMT_RGB32),
DEF(V4L2_PIX_FMT_XRGB32),
DEF(V4L2_PIX_FMT_ARGB32),
@@ -595,6 +599,8 @@ void CaptureWin::changeShader()
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
shader_YUV_packed();
break;
@@ -945,6 +951,8 @@ void CaptureWin::shader_YUV_packed()
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_v4l_fmt.g_width(), m_v4l_fmt.g_height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
break;
@@ -1310,6 +1318,8 @@ void CaptureWin::render_YUV_packed(__u32 format)
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_v4l_fmt.g_width(), m_v4l_fmt.g_height(),
GL_RGBA, GL_UNSIGNED_BYTE, m_curData[0]);
break;
diff --git a/utils/qvidcap/v4l2-convert.glsl b/utils/qvidcap/v4l2-convert.glsl
index 458901c4..8bd5694b 100644
--- a/utils/qvidcap/v4l2-convert.glsl
+++ b/utils/qvidcap/v4l2-convert.glsl
@@ -292,6 +292,12 @@ void main()
yuv.r = color.b;
yuv.g = color.g;
yuv.b = color.r;
+#elif PIXFMT == V4L2_PIX_FMT_YUVA32 || PIXFMT == V4L2_PIX_FMT_YUVX32
+ vec4 color = texture(tex, xy);
+#if PIXFMT == V4L2_PIX_FMT_YUVA32
+ alpha = color.a;
+#endif
+ yuv = color.rgb;
#elif PIXFMT == V4L2_PIX_FMT_YUV565
yuv = texture(tex, xy).rgb;
#elif PIXFMT == V4L2_PIX_FMT_YUV422P || PIXFMT == V4L2_PIX_FMT_YUV420 || PIXFMT == V4L2_PIX_FMT_YVU420 || \
diff --git a/utils/rds-ctl/rds-ctl.cpp b/utils/rds-ctl/rds-ctl.cpp
index 45770083..813b4f28 100644
--- a/utils/rds-ctl/rds-ctl.cpp
+++ b/utils/rds-ctl/rds-ctl.cpp
@@ -187,81 +187,6 @@ static int doioctl_name(int fd, unsigned long int request, void *parm, const cha
#define doioctl(n, r, p) doioctl_name(n, r, p, #r)
-static const char *audmode2s(int audmode)
-{
- switch (audmode) {
- case V4L2_TUNER_MODE_STEREO: return "stereo";
- case V4L2_TUNER_MODE_LANG1: return "lang1";
- case V4L2_TUNER_MODE_LANG2: return "lang2";
- case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
- case V4L2_TUNER_MODE_MONO: return "mono";
- default: return "unknown";
- }
-}
-
-static std::string rxsubchans2s(int rxsubchans)
-{
- std::string s;
-
- if (rxsubchans & V4L2_TUNER_SUB_MONO)
- s += "mono ";
- if (rxsubchans & V4L2_TUNER_SUB_STEREO)
- s += "stereo ";
- if (rxsubchans & V4L2_TUNER_SUB_LANG1)
- s += "lang1 ";
- if (rxsubchans & V4L2_TUNER_SUB_LANG2)
- s += "lang2 ";
- if (rxsubchans & V4L2_TUNER_SUB_RDS)
- s += "rds ";
- return s;
-}
-
-static std::string tcap2s(unsigned cap)
-{
- std::string s;
-
- if (cap & V4L2_TUNER_CAP_LOW)
- s += "62.5 Hz ";
- else
- s += "62.5 kHz ";
- if (cap & V4L2_TUNER_CAP_NORM)
- s += "multi-standard ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_BOUNDED)
- s += "hwseek-bounded ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_WRAP)
- s += "hwseek-wrap ";
- if (cap & V4L2_TUNER_CAP_STEREO)
- s += "stereo ";
- if (cap & V4L2_TUNER_CAP_LANG1)
- s += "lang1 ";
- if (cap & V4L2_TUNER_CAP_LANG2)
- s += "lang2 ";
- if (cap & V4L2_TUNER_CAP_RDS)
- s += "rds ";
- if (cap & V4L2_TUNER_CAP_RDS_BLOCK_IO)
- s += "rds-block-I/O ";
- if (cap & V4L2_TUNER_CAP_RDS_CONTROLS)
- s += "rds-controls ";
- if (cap & V4L2_TUNER_CAP_FREQ_BANDS)
- s += "freq-bands ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_PROG_LIM)
- s += "hwseek-prog-lim ";
- return s;
-}
-
-static std::string modulation2s(unsigned modulation)
-{
- switch (modulation) {
- case V4L2_BAND_MODULATION_VSB:
- return "VSB";
- case V4L2_BAND_MODULATION_FM:
- return "FM";
- case V4L2_BAND_MODULATION_AM:
- return "AM";
- }
- return "Unknown";
-}
-
static bool is_radio_dev(const char *name)
{
return !memcmp(name, "radio", 5);
@@ -866,7 +791,7 @@ static void get_options(const int fd, const int capabilities, struct v4l2_freque
vt.rangelow / 16.0, vt.rangehigh / 16.0);
printf("\tSignal strength/AFC : %ld%%/%d\n",
lround(vt.signal / 655.25), vt.afc);
- printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode));
+ printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode).c_str());
printf("\tAvailable subchannels: %s\n",
rxsubchans2s(vt.rxsubchans).c_str());
}
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index 183a9497..8aebae2e 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -667,7 +667,6 @@ static int testCap(struct node *node)
dcaps = vcap.device_caps;
node->is_m2m = dcaps & m2m_caps;
fail_on_test(caps == 0);
- fail_on_test(caps & V4L2_CAP_ASYNCIO);
fail_on_test(!(caps & V4L2_CAP_DEVICE_CAPS));
fail_on_test(dcaps & V4L2_CAP_DEVICE_CAPS);
fail_on_test(dcaps & ~caps);
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index d9efe2cd..f408d414 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -89,6 +89,9 @@ enum poll_mode {
#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
#define VIVID_CID_RO_INTEGER (VIVID_CID_CUSTOM_BASE + 12)
#define VIVID_CID_U32_DYN_ARRAY (VIVID_CID_CUSTOM_BASE + 13)
+#define VIVID_CID_U8_PIXEL_ARRAY (VIVID_CID_CUSTOM_BASE + 14)
+
+#define PIXEL_ARRAY_DIV 16
struct test_query_ext_ctrl: v4l2_query_ext_ctrl {
__u64 menu_mask;
diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp
index f2d2ee75..a097a0ff 100644
--- a/utils/v4l2-compliance/v4l2-test-buffers.cpp
+++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp
@@ -2023,14 +2023,13 @@ int testRequests(struct node *node, bool test_streaming)
const unsigned elem_size = sizeof(vivid_dyn_array[0]);
v4l2_ext_control vivid_dyn_array_ctrl = {
.id = VIVID_CID_U32_DYN_ARRAY,
- .size = elem_size,
- .p_u32 = vivid_dyn_array,
};
- v4l2_ext_controls vivid_dyn_array_ctrls = {
- .which = V4L2_CTRL_WHICH_REQUEST_VAL,
- .count = 1,
- .controls = &vivid_dyn_array_ctrl,
+ v4l2_ext_controls vivid_dyn_array_ctrls = {};
+ unsigned vivid_pixel_array_size = 0;
+ v4l2_ext_control vivid_pixel_array_ctrl = {
+ .id = VIVID_CID_U8_PIXEL_ARRAY,
};
+ v4l2_ext_controls vivid_pixel_array_ctrls = {};
bool have_controls;
int ret;
@@ -2042,6 +2041,16 @@ int testRequests(struct node *node, bool test_streaming)
vivid_ro_ctrls.count = 1;
vivid_ro_ctrls.controls = &vivid_ro_ctrl;
+ if (is_vivid) {
+ v4l2_query_ext_ctrl qec = { .id = VIVID_CID_U8_PIXEL_ARRAY };
+ node->query_ext_ctrl(qec);
+ vivid_pixel_array_size = qec.elems;
+ }
+ __u8 vivid_pixel_array[vivid_pixel_array_size + 1];
+ vivid_pixel_array[vivid_pixel_array_size] = 0xff;
+ vivid_pixel_array_ctrl.size = vivid_pixel_array_size;
+ vivid_pixel_array_ctrl.p_u8 = vivid_pixel_array;
+
// If requests are supported, then there must be a media device
if (node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS)
fail_on_test(media_fd < 0);
@@ -2343,10 +2352,24 @@ int testRequests(struct node *node, bool test_streaming)
if (i % 3 < 2)
fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
if (is_vivid) {
+ // For vivid, check modifiable array support
+ memset(vivid_pixel_array, i, vivid_pixel_array_size);
+ vivid_pixel_array_ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+ vivid_pixel_array_ctrls.count = 1;
+ vivid_pixel_array_ctrls.controls = &vivid_pixel_array_ctrl;
+ vivid_pixel_array_ctrls.request_fd = buf_req_fds[i];
+ fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS,
+ &vivid_pixel_array_ctrls));
+ fail_on_test(vivid_pixel_array[vivid_pixel_array_size] != 0xff);
+
// For vivid, check dynamic array support:
- vivid_dyn_array_ctrls.request_fd = buf_req_fds[i];
vivid_dyn_array_ctrl.size = sizeof(vivid_dyn_array);
+ vivid_dyn_array_ctrl.p_u32 = vivid_dyn_array;
memset(vivid_dyn_array, 0xff, sizeof(vivid_dyn_array));
+ vivid_dyn_array_ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+ vivid_dyn_array_ctrls.count = 1;
+ vivid_dyn_array_ctrls.controls = &vivid_dyn_array_ctrl;
+ vivid_dyn_array_ctrls.request_fd = buf_req_fds[i];
// vivid_dyn_array_ctrl.size is too large, must return ENOSPC
fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS,
&vivid_dyn_array_ctrls) != ENOSPC);
@@ -2383,6 +2406,11 @@ int testRequests(struct node *node, bool test_streaming)
if (i % 3 < 2)
fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
if (is_vivid && i % 3 < 2) {
+ // Set the pixel array control again
+ memset(vivid_pixel_array, i, vivid_pixel_array_size);
+ vivid_pixel_array_ctrls.request_fd = buf_req_fds[i];
+ fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS,
+ &vivid_pixel_array_ctrls));
// Set the dynamic array control again
vivid_dyn_array_ctrls.request_fd = buf_req_fds[i];
vivid_dyn_array_ctrl.size = (2 + 2 * (i % 8)) * elem_size;
@@ -2511,6 +2539,20 @@ int testRequests(struct node *node, bool test_streaming)
fail_on_test(memcmp(vivid_dyn_array, vivid_dyn_array_clamped,
vivid_dyn_array_ctrl.size));
fail_on_test(vivid_dyn_array[size / elem_size] != 0xffffffff);
+ // Check that the pixel array control is set as
+ // expected and with the correct values.
+ vivid_pixel_array_ctrls.request_fd = buf_req_fds[i];
+ memset(vivid_pixel_array, 0xfe, vivid_pixel_array_size);
+ fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &vivid_pixel_array_ctrls));
+ bool ok = true;
+ __u8 expected = (i % 3 == 2) ? i - 1 : i;
+ for (unsigned j = 0; j < vivid_pixel_array_size; j++)
+ if (vivid_pixel_array[j] != expected) {
+ ok = false;
+ break;
+ }
+ fail_on_test(!ok);
+ fail_on_test(vivid_pixel_array[vivid_pixel_array_size] != 0xff);
}
fail_on_test(buf.querybuf(node, i));
// Check that all the buffers of the stopped stream are
@@ -2554,7 +2596,7 @@ int testRequests(struct node *node, bool test_streaming)
fail_on_test(ctrl.value != (is_max ? valid_qctrl.maximum :
valid_qctrl.minimum));
if (is_vivid) {
- // For vivid check the final read-only value
+ // For vivid check the final read-only value,
vivid_ro_ctrls.which = 0;
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &vivid_ro_ctrls));
if (node->is_video && !node->can_output &&
@@ -2562,7 +2604,7 @@ int testRequests(struct node *node, bool test_streaming)
warn("vivid_ro_ctrl.value (%d) != num_bufs - 1 (%d)\n",
vivid_ro_ctrl.value, num_bufs - 1);
- // and the final dynamic array value
+ // the final dynamic array value,
v4l2_query_ext_ctrl q_dyn_array = {
.id = VIVID_CID_U32_DYN_ARRAY,
};
@@ -2576,6 +2618,18 @@ int testRequests(struct node *node, bool test_streaming)
fail_on_test(vivid_dyn_array_ctrl.size != elems * elem_size);
fail_on_test(memcmp(vivid_dyn_array, vivid_dyn_array_clamped,
vivid_dyn_array_ctrl.size));
+
+ // and the final pixel array value.
+ vivid_pixel_array_ctrls.which = 0;
+ fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &vivid_pixel_array_ctrls));
+ bool ok = true;
+ __u8 expected = (num_bufs - 1) % 3 == 2 ? num_bufs - 2 : num_bufs - 1;
+ for (unsigned j = 0; j < vivid_pixel_array_size; j++)
+ if (vivid_pixel_array[j] != expected) {
+ ok = false;
+ break;
+ }
+ fail_on_test(!ok);
}
}
diff --git a/utils/v4l2-compliance/v4l2-test-colors.cpp b/utils/v4l2-compliance/v4l2-test-colors.cpp
index 887b2fd4..87bf0cd7 100644
--- a/utils/v4l2-compliance/v4l2-test-colors.cpp
+++ b/utils/v4l2-compliance/v4l2-test-colors.cpp
@@ -200,6 +200,8 @@ static void getColor(const cv4l_fmt &fmt, __u8 * const planes[3],
break;
case V4L2_PIX_FMT_RGBX32:
case V4L2_PIX_FMT_RGBA32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
v32 = p8[4 * x + 2] + (p8[4 * x + 1] << 8) +
(p8[4 * x] << 16) + (p8[4 * x + 3] << 24);
break;
@@ -386,6 +388,8 @@ static void getColor(const cv4l_fmt &fmt, __u8 * const planes[3],
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
case V4L2_PIX_FMT_YUYV:
case V4L2_PIX_FMT_UYVY:
case V4L2_PIX_FMT_YVYU:
diff --git a/utils/v4l2-compliance/v4l2-test-controls.cpp b/utils/v4l2-compliance/v4l2-test-controls.cpp
index 999dbcd7..1b07f7dc 100644
--- a/utils/v4l2-compliance/v4l2-test-controls.cpp
+++ b/utils/v4l2-compliance/v4l2-test-controls.cpp
@@ -220,7 +220,8 @@ int testQueryExtControls(struct node *node)
}
if (which == V4L2_CTRL_CLASS_USER &&
- (qctrl.type < V4L2_CTRL_COMPOUND_TYPES) &&
+ qctrl.type < V4L2_CTRL_COMPOUND_TYPES &&
+ !qctrl.nr_of_dims &&
qctrl.type != V4L2_CTRL_TYPE_INTEGER64 &&
qctrl.type != V4L2_CTRL_TYPE_STRING &&
qctrl.type != V4L2_CTRL_TYPE_CTRL_CLASS) {
@@ -363,7 +364,8 @@ int testQueryControls(struct node *node)
break;
id = qctrl.id;
fail_on_test(node->controls.find(qctrl.id) == node->controls.end());
- fail_on_test(qctrl.step || qctrl.minimum || qctrl.maximum || qctrl.default_value);
+ if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES)
+ fail_on_test(qctrl.step || qctrl.minimum || qctrl.maximum || qctrl.default_value);
compound_controls++;
}
fail_on_test(compound_controls != num_compound_ctrls);
@@ -439,7 +441,7 @@ int testSimpleControls(struct node *node)
for (iter = node->controls.begin(); iter != node->controls.end(); ++iter) {
test_query_ext_ctrl &qctrl = iter->second;
- if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES)
+ if (qctrl.type >= V4L2_CTRL_COMPOUND_TYPES || qctrl.nr_of_dims)
continue;
if (is_vivid && V4L2_CTRL_ID2WHICH(qctrl.id) == V4L2_CTRL_CLASS_VIVID)
continue;
@@ -593,6 +595,16 @@ static int checkExtendedCtrl(const struct v4l2_ext_control &ctrl, const struct t
if (ctrl.id != qctrl.id)
return fail("control id mismatch\n");
+
+ if (qctrl.flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) {
+ fail_on_test(qctrl.nr_of_dims != 1);
+ unsigned tot_elems = qctrl.dims[0];
+ fail_on_test(qctrl.elems > tot_elems);
+ fail_on_test(!qctrl.elems);
+ }
+ if (qctrl.nr_of_dims)
+ return 0;
+
switch (qctrl.type) {
case V4L2_CTRL_TYPE_INTEGER:
case V4L2_CTRL_TYPE_INTEGER64:
@@ -627,12 +639,6 @@ static int checkExtendedCtrl(const struct v4l2_ext_control &ctrl, const struct t
default:
break;
}
- if (qctrl.flags & V4L2_CTRL_FLAG_DYNAMIC_ARRAY) {
- fail_on_test(qctrl.nr_of_dims != 1);
- unsigned tot_elems = qctrl.dims[0];
- fail_on_test(qctrl.elems > tot_elems);
- fail_on_test(!qctrl.elems);
- }
return 0;
}
diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp
index 269a3832..ed1a919c 100644
--- a/utils/v4l2-compliance/v4l2-test-formats.cpp
+++ b/utils/v4l2-compliance/v4l2-test-formats.cpp
@@ -258,7 +258,17 @@ static int testEnumFormatsType(struct node *node, unsigned type)
// Check that the driver does not overwrites the kernel pixelformat description
std::string descr = pixfmt2s(fmtdesc.pixelformat);
+ // In v6.2 the Y/CbCr and Y/CrCb strings were replaced by
+ // Y/UV and Y/VU. Accept both variants for now.
+ std::string descr_alt = descr;
+ size_t idx = descr_alt.find("Y/UV", 0);
+ if (idx != std::string::npos)
+ descr_alt.replace(idx, 4, "Y/CbCr");
+ idx = descr_alt.find("Y/VU", 0);
+ if (idx != std::string::npos)
+ descr_alt.replace(idx, 4, "Y/CrCb");
if (strcmp(descr.c_str(), (const char *)fmtdesc.description) &&
+ strcmp(descr_alt.c_str(), (const char *)fmtdesc.description) &&
memcmp(descr.c_str(), "Unknown", 7))
return fail("fmtdesc.description mismatch: was '%s', expected '%s'\n",
fmtdesc.description, descr.c_str());
diff --git a/utils/v4l2-compliance/v4l2-test-input-output.cpp b/utils/v4l2-compliance/v4l2-test-input-output.cpp
index 006e05ec..d6a8c71f 100644
--- a/utils/v4l2-compliance/v4l2-test-input-output.cpp
+++ b/utils/v4l2-compliance/v4l2-test-input-output.cpp
@@ -448,6 +448,48 @@ static int checkInput(struct node *node, const struct v4l2_input &descr, unsigne
return 0;
}
+static unsigned roundup(unsigned v, unsigned mult)
+{
+ return mult * ((v + mult - 1) / mult);
+}
+
+static int checkVividPixelArray(struct node *node)
+{
+ struct v4l2_query_ext_ctrl qextctrl = {
+ .id = VIVID_CID_U8_PIXEL_ARRAY
+ };
+ cv4l_fmt fmt;
+
+ fail_on_test(node->query_ext_ctrl(qextctrl));
+ fail_on_test(node->g_fmt(fmt));
+ fail_on_test(qextctrl.nr_of_dims != 2);
+ fail_on_test(qextctrl.dims[0] != roundup(fmt.g_width(), PIXEL_ARRAY_DIV));
+ fail_on_test(qextctrl.dims[1] != roundup(fmt.g_height(), PIXEL_ARRAY_DIV));
+ fail_on_test(qextctrl.minimum == qextctrl.default_value);
+
+ struct v4l2_ext_control ctrl = {
+ .id = VIVID_CID_U8_PIXEL_ARRAY
+ };
+ struct v4l2_ext_controls ctrls = {};
+
+ ctrl.size = qextctrl.elems * qextctrl.elem_size;
+ ctrl.p_u8 = new unsigned char[ctrl.size];
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+ fail_on_test(node->g_ext_ctrls(ctrls));
+ for (unsigned i = 0; i < qextctrl.elems; i++) {
+ fail_on_test(ctrl.p_u8[i] != qextctrl.default_value);
+ ctrl.p_u8[i] = qextctrl.minimum;
+ }
+ fail_on_test(node->s_ext_ctrls(ctrls));
+ fail_on_test(node->g_ext_ctrls(ctrls));
+ for (unsigned i = 0; i < qextctrl.elems; i++) {
+ fail_on_test(ctrl.p_u8[i] != qextctrl.minimum);
+ }
+ delete [] ctrl.p_u8;
+ return 0;
+}
+
int testInput(struct node *node)
{
struct v4l2_input descr;
@@ -479,6 +521,14 @@ int testInput(struct node *node)
return fail("VIDIOC_G_INPUT didn't fill in the input\n");
if (node->is_radio)
return fail("radio can't have input support\n");
+ if (is_vivid && cur_input == 0) {
+ // for vivid start off with a different input than the
+ // current one. This ensures that the checkVividPixelArray()
+ // call later succeeds since switching to input 0 will reset
+ // that control to the default values.
+ input = 1;
+ doioctl(node, VIDIOC_S_INPUT, &input);
+ }
for (;;) {
memset(&descr, 0xff, sizeof(descr));
descr.index = i;
@@ -494,6 +544,8 @@ int testInput(struct node *node)
return fail("input set to %d, but becomes %d?!\n", i, input);
if (checkInput(node, descr, i))
return fail("invalid attributes for input %d\n", i);
+ if (is_vivid && node->is_video && checkVividPixelArray(node))
+ return fail("vivid pixel array control test failed\n");
node->inputs++;
i++;
}
diff --git a/utils/v4l2-ctl/v4l2-ctl-common.cpp b/utils/v4l2-ctl/v4l2-ctl-common.cpp
index 72def972..a1cc93c8 100644
--- a/utils/v4l2-ctl/v4l2-ctl-common.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-common.cpp
@@ -723,6 +723,7 @@ static int print_control(int fd, struct v4l2_query_ext_ctrl &qctrl, bool show_me
ctrls.controls = &ext_ctrl;
if (qctrl.type == V4L2_CTRL_TYPE_INTEGER64 ||
qctrl.type == V4L2_CTRL_TYPE_STRING ||
+ qctrl.nr_of_dims ||
qctrl.type >= V4L2_CTRL_COMPOUND_TYPES ||
(V4L2_CTRL_ID2WHICH(qctrl.id) != V4L2_CTRL_CLASS_USER &&
qctrl.id < V4L2_CID_PRIVATE_BASE)) {
@@ -902,7 +903,7 @@ void common_process_controls(cv4l_fd &fd)
}
}
-void common_control_event(const struct v4l2_event *ev)
+void common_control_event(int fd, const struct v4l2_event *ev)
{
const struct v4l2_event_ctrl *ctrl;
@@ -926,6 +927,18 @@ void common_control_event(const struct v4l2_event *ev)
printf("\trange: min=%d max=%d step=%d default=%d\n",
ctrl->minimum, ctrl->maximum, ctrl->step, ctrl->default_value);
}
+ if (ctrl->changes & V4L2_EVENT_CTRL_CH_DIMENSIONS) {
+ v4l2_query_ext_ctrl qctrl = {};
+
+ qctrl.id = ev->id;
+ if (!query_ext_ctrl_ioctl(fd, qctrl)) {
+ ctrl_str2q[name2var(qctrl.name)] = qctrl;
+ printf("\tdimensions: ");
+ for (unsigned i = 0; i < qctrl.nr_of_dims; i++)
+ printf("[%u]", qctrl.dims[i]);
+ printf("\n");
+ }
+ }
}
static bool parse_subset(char *optarg)
@@ -1253,7 +1266,7 @@ void common_get(cv4l_fd &_fd)
if (qc.nr_of_dims) {
print_value(fd, qc, ctrl, true, true);
- return;
+ continue;
}
printf("%s: ", name.c_str());
diff --git a/utils/v4l2-ctl/v4l2-ctl-tuner.cpp b/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
index 177aa8e6..2b0553d0 100644
--- a/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
@@ -45,112 +45,6 @@ void tuner_usage()
);
}
-static const char *audmode2s(int audmode)
-{
- switch (audmode) {
- case V4L2_TUNER_MODE_STEREO: return "stereo";
- case V4L2_TUNER_MODE_LANG1: return "lang1";
- case V4L2_TUNER_MODE_LANG2: return "lang2";
- case V4L2_TUNER_MODE_LANG1_LANG2: return "bilingual";
- case V4L2_TUNER_MODE_MONO: return "mono";
- default: return "unknown";
- }
-}
-
-static const char *ttype2s(int type)
-{
- switch (type) {
- case V4L2_TUNER_RADIO: return "radio";
- case V4L2_TUNER_ANALOG_TV: return "Analog TV";
- case V4L2_TUNER_DIGITAL_TV: return "Digital TV";
- case V4L2_TUNER_SDR: return "SDR";
- case V4L2_TUNER_RF: return "RF";
- default: return "unknown";
- }
-}
-
-static std::string rxsubchans2s(int rxsubchans)
-{
- std::string s;
-
- if (rxsubchans & V4L2_TUNER_SUB_MONO)
- s += "mono ";
- if (rxsubchans & V4L2_TUNER_SUB_STEREO)
- s += "stereo ";
- if (rxsubchans & V4L2_TUNER_SUB_LANG1)
- s += "lang1 ";
- if (rxsubchans & V4L2_TUNER_SUB_LANG2)
- s += "lang2 ";
- if (rxsubchans & V4L2_TUNER_SUB_RDS)
- s += "rds ";
- return s;
-}
-
-static std::string txsubchans2s(int txsubchans)
-{
- std::string s;
-
- if (txsubchans & V4L2_TUNER_SUB_MONO)
- s += "mono ";
- if (txsubchans & V4L2_TUNER_SUB_STEREO)
- s += "stereo ";
- if (txsubchans & V4L2_TUNER_SUB_LANG1)
- s += "bilingual ";
- if (txsubchans & V4L2_TUNER_SUB_SAP)
- s += "sap ";
- if (txsubchans & V4L2_TUNER_SUB_RDS)
- s += "rds ";
- return s;
-}
-
-static std::string tcap2s(unsigned cap)
-{
- std::string s;
-
- if (cap & V4L2_TUNER_CAP_LOW)
- s += "62.5 Hz ";
- else if (cap & V4L2_TUNER_CAP_1HZ)
- s += "1 Hz ";
- else
- s += "62.5 kHz ";
- if (cap & V4L2_TUNER_CAP_NORM)
- s += "multi-standard ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_BOUNDED)
- s += "hwseek-bounded ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_WRAP)
- s += "hwseek-wrap ";
- if (cap & V4L2_TUNER_CAP_STEREO)
- s += "stereo ";
- if (cap & V4L2_TUNER_CAP_LANG1)
- s += "lang1 ";
- if (cap & V4L2_TUNER_CAP_LANG2)
- s += "lang2 ";
- if (cap & V4L2_TUNER_CAP_RDS)
- s += "rds ";
- if (cap & V4L2_TUNER_CAP_RDS_BLOCK_IO)
- s += "rds-block-I/O ";
- if (cap & V4L2_TUNER_CAP_RDS_CONTROLS)
- s += "rds-controls ";
- if (cap & V4L2_TUNER_CAP_FREQ_BANDS)
- s += "freq-bands ";
- if (cap & V4L2_TUNER_CAP_HWSEEK_PROG_LIM)
- s += "hwseek-prog-lim ";
- return s;
-}
-
-static std::string modulation2s(unsigned modulation)
-{
- switch (modulation) {
- case V4L2_BAND_MODULATION_VSB:
- return "VSB";
- case V4L2_BAND_MODULATION_FM:
- return "FM";
- case V4L2_BAND_MODULATION_AM:
- return "AM";
- }
- return "Unknown";
-}
-
static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
{
char *value;
@@ -395,7 +289,7 @@ void tuner_get(cv4l_fd &_fd)
if (doioctl(fd, VIDIOC_G_TUNER, &vt) == 0) {
printf("Tuner %d:\n", vt.index);
printf("\tName : %s\n", vt.name);
- printf("\tType : %s\n", ttype2s(vt.type));
+ printf("\tType : %s\n", ttype2s(vt.type).c_str());
printf("\tCapabilities : %s\n", tcap2s(vt.capability).c_str());
if (vt.capability & V4L2_TUNER_CAP_LOW)
printf("\tFrequency range : %.3f MHz - %.3f MHz\n",
@@ -410,7 +304,7 @@ void tuner_get(cv4l_fd &_fd)
if (vt.type != V4L2_TUNER_SDR && vt.type != V4L2_TUNER_RF) {
printf("\tSignal strength/AFC : %ld%%/%d\n",
lround(vt.signal / 655.35), vt.afc);
- printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode));
+ printf("\tCurrent audio mode : %s\n", audmode2s(vt.audmode).c_str());
printf("\tAvailable subchannels: %s\n", rxsubchans2s(vt.rxsubchans).c_str());
}
}
@@ -424,7 +318,7 @@ void tuner_get(cv4l_fd &_fd)
if (doioctl(fd, VIDIOC_G_MODULATOR, &mt) == 0) {
printf("Modulator %d:\n", modulator.index);
printf("\tName : %s\n", mt.name);
- printf("\tType : %s\n", ttype2s(mt.type));
+ printf("\tType : %s\n", ttype2s(mt.type).c_str());
printf("\tCapabilities : %s\n", tcap2s(mt.capability).c_str());
if (mt.capability & V4L2_TUNER_CAP_LOW)
printf("\tFrequency range : %.1f MHz - %.1f MHz\n",
diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp
index 6bf0a1c7..8585278f 100644
--- a/utils/v4l2-ctl/v4l2-ctl.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl.cpp
@@ -345,6 +345,8 @@ static bool is_rgb_or_hsv(__u32 pixelformat)
case V4L2_PIX_FMT_XYUV32:
case V4L2_PIX_FMT_VUYA32:
case V4L2_PIX_FMT_VUYX32:
+ case V4L2_PIX_FMT_YUVA32:
+ case V4L2_PIX_FMT_YUVX32:
case V4L2_PIX_FMT_YUV410:
case V4L2_PIX_FMT_YUV420:
case V4L2_PIX_FMT_HI240:
@@ -890,7 +892,7 @@ int parse_selection_target(const char *s, unsigned int &target)
}
-static void print_event(const struct v4l2_event *ev)
+static void print_event(int fd, const struct v4l2_event *ev)
{
printf("%lld.%06ld: event %u, pending %u: ",
static_cast<__u64>(ev->timestamp.tv_sec), ev->timestamp.tv_nsec / 1000,
@@ -903,7 +905,7 @@ static void print_event(const struct v4l2_event *ev)
printf("eos\n");
break;
case V4L2_EVENT_CTRL:
- common_control_event(ev);
+ common_control_event(fd, ev);
break;
case V4L2_EVENT_FRAME_SYNC:
printf("frame_sync %d\n", ev->u.frame_sync.frame_sequence);
@@ -1524,7 +1526,7 @@ int main(int argc, char **argv)
sub.id = e.id;
if (!doioctl(fd, VIDIOC_SUBSCRIBE_EVENT, &sub))
if (!doioctl(fd, VIDIOC_DQEVENT, &ev))
- print_event(&ev);
+ print_event(fd, &ev);
doioctl(fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub);
}
@@ -1556,7 +1558,7 @@ int main(int argc, char **argv)
break;
if (doioctl(fd, VIDIOC_DQEVENT, &ev))
break;
- print_event(&ev);
+ print_event(fd, &ev);
if (ev.sequence > seq)
printf("\tMissed %d events\n", ev.sequence - seq);
seq = ev.sequence + 1;
@@ -1583,7 +1585,7 @@ int main(int argc, char **argv)
break;
if (doioctl(fd, VIDIOC_DQEVENT, &ev))
break;
- print_event(&ev);
+ print_event(fd, &ev);
if (ev.sequence > seq)
printf("\tMissed %d events\n", ev.sequence - seq);
seq = ev.sequence + 1;
diff --git a/utils/v4l2-ctl/v4l2-ctl.h b/utils/v4l2-ctl/v4l2-ctl.h
index 39161466..70a80ade 100644
--- a/utils/v4l2-ctl/v4l2-ctl.h
+++ b/utils/v4l2-ctl/v4l2-ctl.h
@@ -340,7 +340,7 @@ void common_set(cv4l_fd &fd);
void common_get(cv4l_fd &fd);
void common_list(cv4l_fd &fd);
void common_process_controls(cv4l_fd &fd);
-void common_control_event(const struct v4l2_event *ev);
+void common_control_event(int fd, const struct v4l2_event *ev);
int common_find_ctrl_id(const char *name);
// v4l2-ctl-tuner.cpp
diff --git a/utils/v4l2-tracer/.gitignore b/utils/v4l2-tracer/.gitignore
new file mode 100644
index 00000000..48fbfc47
--- /dev/null
+++ b/utils/v4l2-tracer/.gitignore
@@ -0,0 +1,7 @@
+trace-gen.h
+trace-gen.cpp
+retrace-gen.cpp
+retrace-gen.h
+v4l2-tracer-info-gen.h
+v4l2-tracer
+v4l2-tracer.1
diff --git a/utils/v4l2-tracer/Makefile.am b/utils/v4l2-tracer/Makefile.am
new file mode 100644
index 00000000..b72ee1e9
--- /dev/null
+++ b/utils/v4l2-tracer/Makefile.am
@@ -0,0 +1,36 @@
+if HAVE_JSONC
+
+lib_LTLIBRARIES = libv4l2tracer.la
+libv4l2tracer_la_SOURCES = libv4l2tracer.cpp trace-gen.cpp trace-helper.cpp trace.cpp \
+v4l2-tracer-common.cpp $(top_srcdir)/utils/common/v4l2-info.cpp \
+$(top_srcdir)/utils/common/media-info.cpp
+libv4l2tracer_la_CPPFLAGS = -I$(top_srcdir)/utils/common $(JSONC_CFLAGS)
+libv4l2tracer_la_LDFLAGS = -avoid-version -module -shared -export-dynamic -ldl $(JSONC_LIBS)
+libv4l2tracer_la_LIBTOOLFLAGS = --tag=disable-static
+
+bin_PROGRAMS = v4l2-tracer
+man_MANS = v4l2-tracer.1
+
+v4l2_tracer_SOURCES = v4l2-tracer.cpp retrace-gen.cpp retrace-helper.cpp retrace.cpp \
+v4l2-tracer-common.cpp $(top_srcdir)/utils/common/v4l2-info.cpp \
+$(top_srcdir)/utils/common/media-info.cpp
+v4l2_tracer_CPPFLAGS = -I$(top_srcdir)/utils/common -DLIBTRACER_PATH=$(libdir) $(JSONC_CFLAGS) \
+$(GIT_SHA) $(GIT_COMMIT_CNT) $(GIT_COMMIT_DATE)
+v4l2_tracer_LDFLAGS = -lrt -lpthread $(JSONC_LIBS)
+
+V4L2_TRACER_GEN_PREREQS = $(top_srcdir)/include/linux/v4l2-controls.h \
+$(top_srcdir)/include/linux/videodev2.h $(top_srcdir)/include/linux/media.h \
+$(top_srcdir)/include/linux/v4l2-common.h
+V4L2_TRACER_GEN = trace-gen.cpp trace-gen.h retrace-gen.cpp retrace-gen.h v4l2-tracer-info-gen.h
+v4l2-tracer.cpp: $(V4L2_TRACER_GEN)
+$(V4L2_TRACER_GEN): gen.intermediate;
+.INTERMEDIATE: gen.intermediate
+gen.intermediate: v4l2-tracer-gen.pl $(V4L2_TRACER_GEN_PREREQS)
+ $(AM_V_GEN) $(top_srcdir)/utils/v4l2-tracer/v4l2-tracer-gen.pl $(V4L2_TRACER_GEN_PREREQS)
+
+clean-local:
+ -rm -vf $(V4L2_TRACER_GEN)
+
+EXTRA_DIST = v4l2-tracer.1, v4l2-tracer-gen.pl
+
+endif
diff --git a/utils/v4l2-tracer/libv4l2tracer.cpp b/utils/v4l2-tracer/libv4l2tracer.cpp
new file mode 100644
index 00000000..090a43d7
--- /dev/null
+++ b/utils/v4l2-tracer/libv4l2tracer.cpp
@@ -0,0 +1,303 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "trace.h"
+#include <config.h> /* For PROMOTED_MODE_T */
+#include <dlfcn.h>
+#include <stdarg.h>
+
+extern struct trace_context ctx_trace;
+
+const std::list<unsigned long> ioctls = {
+ VIDIOC_QUERYCAP,
+ VIDIOC_STREAMON,
+ VIDIOC_STREAMOFF,
+ VIDIOC_ENUM_FMT,
+ VIDIOC_G_FMT,
+ VIDIOC_S_FMT,
+ VIDIOC_REQBUFS,
+ VIDIOC_QUERYBUF,
+ VIDIOC_QBUF,
+ VIDIOC_EXPBUF,
+ VIDIOC_DQBUF,
+ VIDIOC_G_PARM,
+ VIDIOC_S_PARM,
+ VIDIOC_G_CTRL,
+ VIDIOC_S_CTRL,
+ VIDIOC_QUERYCTRL,
+ VIDIOC_G_CROP,
+ VIDIOC_S_CROP,
+ VIDIOC_TRY_FMT,
+ VIDIOC_G_EXT_CTRLS,
+ VIDIOC_S_EXT_CTRLS,
+ VIDIOC_TRY_EXT_CTRLS,
+ VIDIOC_ENCODER_CMD,
+ VIDIOC_TRY_ENCODER_CMD,
+ VIDIOC_CREATE_BUFS,
+ VIDIOC_PREPARE_BUF,
+ VIDIOC_G_SELECTION,
+ VIDIOC_S_SELECTION,
+ VIDIOC_DECODER_CMD,
+ VIDIOC_TRY_DECODER_CMD,
+ VIDIOC_QUERY_EXT_CTRL,
+ MEDIA_IOC_REQUEST_ALLOC,
+ MEDIA_REQUEST_IOC_QUEUE,
+ MEDIA_REQUEST_IOC_REINIT,
+};
+
+int open(const char *path, int oflag, ...)
+{
+ errno = 0;
+ mode_t mode = 0;
+
+ if ((oflag & O_CREAT) != 0) {
+ va_list argp;
+ va_start(argp, oflag);
+ mode = va_arg(argp, PROMOTED_MODE_T);
+ va_end(argp);
+ }
+
+ int (*original_open)(const char *path, int oflag, ...) = nullptr;
+ original_open = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open");
+ int fd = (*original_open)(path, oflag, mode);
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "fd: %d, path: %s\n", fd, path);
+ }
+
+ if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
+ return fd;
+
+ if (is_video_or_media_device(path)) {
+ trace_open(fd, path, oflag, mode, false);
+ add_device(fd, path);
+ }
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_devices();
+ }
+
+ return fd;
+}
+
+int open64(const char *path, int oflag, ...)
+{
+ errno = 0;
+ mode_t mode = 0;
+ if ((oflag & O_CREAT) != 0) {
+ va_list argp;
+ va_start(argp, oflag);
+ mode = va_arg(argp, PROMOTED_MODE_T);
+ va_end(argp);
+ }
+
+ int (*original_open64)(const char *path, int oflag, ...) = nullptr;
+ original_open64 = (int (*)(const char*, int, ...)) dlsym(RTLD_NEXT, "open64");
+ int fd = (*original_open64)(path, oflag, mode);
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "fd: %d, path: %s\n", fd, path);
+ }
+
+ if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
+ return fd;
+
+ if (is_video_or_media_device(path)) {
+ add_device(fd, path);
+ trace_open(fd, path, oflag, mode, true);
+ }
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_devices();
+ }
+
+ return fd;
+}
+
+int close(int fd)
+{
+ errno = 0;
+ int (*original_close)(int fd) = nullptr;
+ original_close = (int (*)(int)) dlsym(RTLD_NEXT, "close");
+
+ if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
+ return (*original_close)(fd);
+
+ std::string path = get_device(fd);
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "fd: %d, path: %s\n", fd, path.c_str());
+ }
+
+ /* Only trace the close if a corresponding open was also traced. */
+ if (!path.empty()) {
+ json_object *close_obj = json_object_new_object();
+ json_object_object_add(close_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(close_obj, "close", json_object_new_string(path.c_str()));
+ write_json_object_to_json_file(close_obj);
+ json_object_put(close_obj);
+ ctx_trace.devices.erase(fd);
+
+ /* If we removed the last device, close the json trace file. */
+ if (!ctx_trace.devices.size())
+ close_json_file();
+ }
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_devices();
+ }
+
+ return (*original_close)(fd);
+}
+
+void *mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+ errno = 0;
+ void *(*original_mmap)(void *addr, size_t len, int prot, int flags, int fildes, off_t off) = nullptr;
+ original_mmap = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap");
+ void *buf_address_pointer = (*original_mmap)(addr, len, prot, flags, fildes, off);
+
+ set_buffer_address_trace(fildes, off, (unsigned long) buf_address_pointer);
+
+ if (buffer_in_trace_context(fildes, off))
+ trace_mmap(addr, len, prot, flags, fildes, off, (unsigned long) buf_address_pointer, false);
+
+ return buf_address_pointer;
+}
+
+void *mmap64(void *addr, size_t len, int prot, int flags, int fildes, off_t off)
+{
+ errno = 0;
+ void *(*original_mmap64)(void *addr, size_t len, int prot, int flags, int fildes, off_t off) = nullptr;
+ original_mmap64 = (void*(*)(void*, size_t, int, int, int, off_t)) dlsym(RTLD_NEXT, "mmap64");
+ void *buf_address_pointer = (*original_mmap64)(addr, len, prot, flags, fildes, off);
+
+ set_buffer_address_trace(fildes, off, (unsigned long) buf_address_pointer);
+
+ if (buffer_in_trace_context(fildes, off))
+ trace_mmap(addr, len, prot, flags, fildes, off, (unsigned long) buf_address_pointer, true);
+
+ return buf_address_pointer;
+}
+
+int munmap(void *start, size_t length)
+{
+ errno = 0;
+ int(*original_munmap)(void *start, size_t length) = nullptr;
+ original_munmap = (int(*)(void *, size_t)) dlsym(RTLD_NEXT, "munmap");
+ int ret = (*original_munmap)(start, length);
+
+ /* Only trace the unmapping if the original mapping was traced. */
+ if (!buffer_is_mapped((unsigned long) start))
+ return ret;
+
+ json_object *munmap_obj = json_object_new_object();
+
+ if (errno)
+ json_object_object_add(munmap_obj, "errno", json_object_new_string(strerrorname_np(errno)));
+
+ json_object *munmap_args = json_object_new_object();
+ json_object_object_add(munmap_args, "start", json_object_new_int64((int64_t)start));
+ json_object_object_add(munmap_args, "length", json_object_new_uint64(length));
+ json_object_object_add(munmap_obj, "munmap", munmap_args);
+
+ write_json_object_to_json_file(munmap_obj);
+ json_object_put(munmap_obj);
+
+ return ret;
+}
+
+int ioctl(int fd, unsigned long cmd, ...)
+{
+ errno = 0;
+ va_list argp;
+ va_start(argp, cmd);
+ void *arg = va_arg(argp, void *);
+ va_end(argp);
+
+ int (*original_ioctl)(int fd, unsigned long cmd, ...) = nullptr;
+ original_ioctl = (int (*)(int, long unsigned int, ...)) dlsym(RTLD_NEXT, "ioctl");
+
+ if (getenv("V4L2_TRACER_PAUSE_TRACE") != nullptr)
+ return (*original_ioctl)(fd, cmd, arg);
+
+ /* Don't trace ioctls that are not in the specified ioctls list. */
+ if (find(ioctls.begin(), ioctls.end(), cmd) == ioctls.end())
+ return (*original_ioctl)(fd, cmd, arg);
+
+ json_object *ioctl_obj = json_object_new_object();
+ json_object_object_add(ioctl_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(ioctl_obj, "ioctl",
+ json_object_new_string(val2s(cmd, ioctl_val_def).c_str()));
+
+ /* Don't attempt to trace a nullptr. */
+ if (arg == nullptr) {
+ int ret = (*original_ioctl)(fd, cmd, arg);
+ if (errno)
+ json_object_object_add(ioctl_obj, "errno",
+ json_object_new_string(strerrorname_np(errno)));
+ write_json_object_to_json_file(ioctl_obj);
+ json_object_put(ioctl_obj);
+ return ret;
+ }
+
+ /* Get info needed for writing the decoded video data to a yuv file. */
+ if (cmd == VIDIOC_S_EXT_CTRLS)
+ s_ext_ctrls_setup(static_cast<struct v4l2_ext_controls*>(arg));
+ if (cmd == VIDIOC_QBUF)
+ qbuf_setup(static_cast<struct v4l2_buffer*>(arg));
+ if (cmd == VIDIOC_STREAMOFF)
+ streamoff_cleanup(*(static_cast<v4l2_buf_type*>(arg)));
+
+ /* Trace userspace arguments if driver will be reading them i.e. _IOW or _IOWR ioctls */
+ if ((cmd & IOC_IN) != 0U) {
+ json_object *ioctl_args_userspace = trace_ioctl_args(cmd, arg);
+ /* Some ioctls won't have arguments to trace e.g. MEDIA_REQUEST_IOC_QUEUE. */
+ if (json_object_object_length(ioctl_args_userspace))
+ json_object_object_add(ioctl_obj, "from_userspace", ioctl_args_userspace);
+ else
+ json_object_put(ioctl_args_userspace);
+ }
+
+ /* Make the original ioctl call. */
+ int ret = (*original_ioctl)(fd, cmd, arg);
+
+ if (errno)
+ json_object_object_add(ioctl_obj, "errno", json_object_new_string(strerrorname_np(errno)));
+
+ /* Trace driver arguments if userspace will be reading them i.e. _IOR or _IOWR ioctls */
+ if ((cmd & IOC_OUT) != 0U) {
+ json_object *ioctl_args_driver = trace_ioctl_args(cmd, arg);
+ /* Some ioctls won't have arguments to trace e.g. MEDIA_REQUEST_IOC_QUEUE. */
+ if (json_object_object_length(ioctl_args_driver))
+ json_object_object_add(ioctl_obj, "from_driver", ioctl_args_driver);
+ else
+ json_object_put(ioctl_args_driver);
+ }
+
+ write_json_object_to_json_file(ioctl_obj);
+ json_object_put(ioctl_obj);
+
+ /* Get additional info from driver for writing the decoded video data to a yuv file. */
+ if (cmd == VIDIOC_G_FMT)
+ g_fmt_setup_trace(static_cast<struct v4l2_format*>(arg));
+ if (cmd == VIDIOC_S_FMT)
+ s_fmt_setup(static_cast<struct v4l2_format*>(arg));
+ if (cmd == VIDIOC_EXPBUF)
+ expbuf_setup(static_cast<struct v4l2_exportbuffer*>(arg));
+ if (cmd == VIDIOC_QUERYBUF)
+ querybuf_setup(fd, static_cast<struct v4l2_buffer*>(arg));
+
+ /* Get info needed for tracing dynamic arrays */
+ if (cmd == VIDIOC_QUERY_EXT_CTRL)
+ query_ext_ctrl_setup(fd, static_cast<struct v4l2_query_ext_ctrl*>(arg));
+
+ return ret;
+}
diff --git a/utils/v4l2-tracer/retrace-helper.cpp b/utils/v4l2-tracer/retrace-helper.cpp
new file mode 100644
index 00000000..03b0466b
--- /dev/null
+++ b/utils/v4l2-tracer/retrace-helper.cpp
@@ -0,0 +1,262 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retrace.h"
+
+struct retrace_context ctx_retrace = {};
+
+bool buffer_in_retrace_context(int fd, __u32 offset)
+{
+ bool buffer_in_retrace_context = false;
+ for (auto &b : ctx_retrace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ buffer_in_retrace_context = true;
+ break;
+ }
+ }
+ return buffer_in_retrace_context;
+}
+
+int get_buffer_fd_retrace(__u32 type, __u32 index)
+{
+ int fd = -1;
+ for (auto &b : ctx_retrace.buffers) {
+ if ((b.type == type) && (b.index == index)) {
+ fd = b.fd;
+ break;
+ }
+ }
+ return fd;
+}
+
+void add_buffer_retrace(int fd, __u32 type, __u32 index, __u32 offset)
+{
+ struct buffer_retrace buf = {};
+ buf.fd = fd;
+ buf.type = type;
+ buf.index = index;
+ buf.offset = offset;
+ ctx_retrace.buffers.push_front(buf);
+}
+
+void remove_buffer_retrace(int fd)
+{
+ for (auto it = ctx_retrace.buffers.begin(); it != ctx_retrace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ ctx_retrace.buffers.erase(it);
+ break;
+ }
+ }
+}
+
+void set_buffer_address_retrace(int fd, __u32 offset, long address_trace, long address_retrace)
+{
+ for (auto &b : ctx_retrace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ b.address_trace = address_trace;
+ b.address_retrace = address_retrace;
+ break;
+ }
+ }
+}
+
+long get_retrace_address_from_trace_address(long address_trace)
+{
+ long address_retrace = 0;
+ for (auto &b : ctx_retrace.buffers) {
+ if (b.address_trace == address_trace) {
+ address_retrace = b.address_retrace;
+ break;
+ }
+ }
+ return address_retrace;
+}
+
+void print_buffers_retrace(void)
+{
+ for (auto &b : ctx_retrace.buffers) {
+ fprintf(stderr, "fd: %d, offset: %d, address_trace:%ld, address_retrace:%ld\n",
+ b.fd, b.offset, b.address_trace, b.address_retrace);
+ }
+}
+
+void add_fd(int fd_trace, int fd_retrace)
+{
+ std::pair<int, int> new_pair;
+ new_pair = std::make_pair(fd_trace, fd_retrace);
+ ctx_retrace.retrace_fds.insert(new_pair);
+}
+
+int get_fd_retrace_from_fd_trace(int fd_trace)
+{
+ int fd_retrace = -1;
+ std::unordered_map<int, int>::const_iterator it;
+ it = ctx_retrace.retrace_fds.find(fd_trace);
+ if (it != ctx_retrace.retrace_fds.end())
+ fd_retrace = it->second;
+ return fd_retrace;
+}
+
+void print_fds(void)
+{
+ if (ctx_retrace.retrace_fds.empty())
+ fprintf(stderr, "all devices closed\n");
+ for (auto it = ctx_retrace.retrace_fds.cbegin(); it != ctx_retrace.retrace_fds.cend(); ++it)
+ fprintf(stderr, "fd_trace: %d, fd_retrace: %d\n", it->first, it->second);
+}
+
+std::string get_path_retrace_from_path_trace(std::string path_trace, json_object *open_obj)
+{
+ bool is_media = path_trace.find("media") != std::string::npos;
+ bool is_video = path_trace.find("video") != std::string::npos;
+
+ std::string path_media;
+ std::string path_video;
+
+ /* If user set the media or video path just return that path. */
+ if (is_media && (getenv("V4L2_TRACER_OPTION_SET_MEDIA_DEVICE") != nullptr)) {
+ path_media = getenv("V4L2_TRACER_OPTION_SET_MEDIA_DEVICE");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "Use path set by user: %s ", path_media.c_str());
+ }
+ return path_media;
+ }
+ if (is_video && (getenv("V4L2_TRACER_OPTION_SET_VIDEO_DEVICE") != nullptr)) {
+ path_video = getenv("V4L2_TRACER_OPTION_SET_VIDEO_DEVICE");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "Use path set by user: %s ", path_video.c_str());
+ }
+ return path_video;
+ }
+
+ std::string driver;
+ json_object *driver_obj;
+ if (json_object_object_get_ex(open_obj, "driver", &driver_obj))
+ if (json_object_get_string(driver_obj) != nullptr)
+ driver = json_object_get_string(driver_obj);
+ if (driver.empty())
+ return "";
+
+ path_media = get_path_media(driver);
+ if (path_media.empty()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: driver: %s not found\n", driver.c_str());
+ return "";
+ }
+
+ if (is_media)
+ return path_media;
+
+ if (is_video) {
+ std::list<std::string> linked_entities;
+ json_object *le_obj;
+ if (json_object_object_get_ex(open_obj, "linked_entities", &le_obj)) {
+ for (size_t i = 0; i < array_list_length(json_object_get_array(le_obj)); i++) {
+ std::string ename;
+ if (json_object_get_string(json_object_array_get_idx(le_obj, i)) != nullptr)
+ ename = json_object_get_string(json_object_array_get_idx(le_obj, i));
+ linked_entities.push_back(ename);
+ }
+ }
+ if (linked_entities.size() == 0)
+ return "";
+
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ int media_fd = open(path_media.c_str(), O_RDONLY);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+
+ std::string path_video = get_path_video(media_fd, linked_entities);
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ close(media_fd);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ return path_video;
+ }
+
+ return "";
+}
+
+void write_to_output_buffer(unsigned char *buffer_pointer, int bytesused, json_object *mem_obj)
+{
+ int byteswritten = 0;
+ const int hex_base = 16;
+ json_object *line_obj;
+ size_t number_of_lines;
+ std::string compressed_video_data;
+
+ json_object *mem_array_obj;
+ json_object_object_get_ex(mem_obj, "mem_array", &mem_array_obj);
+ number_of_lines = json_object_array_length(mem_array_obj);
+
+ for (long unsigned int i = 0; i < number_of_lines; i++) {
+ line_obj = json_object_array_get_idx(mem_array_obj, i);
+ if (json_object_get_string(line_obj) != nullptr)
+ compressed_video_data = json_object_get_string(line_obj);
+
+ for (long unsigned i = 0; i < compressed_video_data.length(); i++) {
+ if (std::isspace(compressed_video_data[i]) != 0)
+ continue;
+ try {
+ /* Two values from the string e.g. "D9" are needed to write one byte. */
+ *buffer_pointer = (char) std::stoi(compressed_video_data.substr(i,2), nullptr, hex_base);
+ buffer_pointer++;
+ i++;
+ byteswritten++;
+ } catch (std::invalid_argument& ia) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "\'%s\' is an invalid argument.\n",
+ compressed_video_data.substr(i,2).c_str());
+ } catch (std::out_of_range& oor) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "\'%s\' is out of range.\n",
+ compressed_video_data.substr(i,2).c_str());
+ }
+ }
+ }
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "bytesused: %d, byteswritten: %d\n", bytesused, byteswritten);
+ }
+}
+
+void compare_program_versions(json_object *v4l2_tracer_info_obj)
+{
+ json_object *package_version_obj;
+ json_object_object_get_ex(v4l2_tracer_info_obj, "package_version", &package_version_obj);
+ std::string package_version_trace;
+ if (json_object_get_string(package_version_obj) != nullptr)
+ package_version_trace = json_object_get_string(package_version_obj);
+ std::string package_version_retrace = PACKAGE_VERSION;
+ if (package_version_trace != package_version_retrace) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: trace package version \'%s\' does not match current: \'%s\':\n",
+ package_version_trace.c_str(), package_version_retrace.c_str());
+ print_v4l2_tracer_info();
+ return;
+ }
+
+ json_object *git_sha_obj;
+ json_object_object_get_ex(v4l2_tracer_info_obj, "git_sha", &git_sha_obj);
+ std::string git_sha_trace;
+ if (json_object_get_string(git_sha_obj) != nullptr)
+ git_sha_trace = json_object_get_string(git_sha_obj);
+ std::string git_sha_retrace = (STRING(GIT_SHA));
+ if (git_sha_trace != git_sha_retrace) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: sha in trace file \'%s\' does not match current sha: \'%s\'\n",
+ git_sha_trace.c_str(), git_sha_retrace.c_str());
+ print_v4l2_tracer_info();
+ return;
+ }
+}
+
+void print_context(void)
+{
+ print_fds();
+ print_buffers_retrace();
+ fprintf(stderr, "\n");
+}
diff --git a/utils/v4l2-tracer/retrace.cpp b/utils/v4l2-tracer/retrace.cpp
new file mode 100644
index 00000000..36a218d3
--- /dev/null
+++ b/utils/v4l2-tracer/retrace.cpp
@@ -0,0 +1,1373 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retrace.h"
+
+extern struct retrace_context ctx_retrace;
+
+void retrace_mmap(json_object *mmap_obj, bool is_mmap64)
+{
+ json_object *mmap_args_obj;
+ if (is_mmap64)
+ json_object_object_get_ex(mmap_obj, "mmap64", &mmap_args_obj);
+ else
+ json_object_object_get_ex(mmap_obj, "mmap", &mmap_args_obj);
+
+ json_object *len_obj;
+ json_object_object_get_ex(mmap_args_obj, "len", &len_obj);
+ size_t len = (size_t) json_object_get_int(len_obj);
+
+ json_object *prot_obj;
+ json_object_object_get_ex(mmap_args_obj, "prot", &prot_obj);
+ int prot = json_object_get_int(prot_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(mmap_args_obj, "flags", &flags_obj);
+ int flags = s2number(json_object_get_string(flags_obj));
+
+ json_object *fildes_obj;
+ json_object_object_get_ex(mmap_args_obj, "fildes", &fildes_obj);
+ int fd_trace = json_object_get_int(fildes_obj);
+
+ json_object *off_obj;
+ json_object_object_get_ex(mmap_args_obj, "off", &off_obj);
+ off_t off = (off_t) json_object_get_int64(off_obj);
+
+ int fd_retrace = get_fd_retrace_from_fd_trace(fd_trace);
+ if (fd_retrace < 0) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "bad or missing file descriptor.\n");
+ return;
+ }
+
+ /* Only retrace mmap calls that map a buffer. */
+ if (!buffer_in_retrace_context(fd_retrace, off))
+ return;
+
+ void *buf_address_retrace_pointer = nullptr;
+ if (is_mmap64)
+ buf_address_retrace_pointer = mmap64(0, len, prot, flags, fd_retrace, off);
+ else
+ buf_address_retrace_pointer = mmap(0, len, prot, flags, fd_retrace, off);
+
+ if (buf_address_retrace_pointer == MAP_FAILED) {
+ if (is_mmap64)
+ perror("mmap64");
+ else
+ perror("mmap");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Get and store the original trace address so that it can be matched
+ * with the munmap address later.
+ */
+ json_object *buffer_address_obj;
+ json_object_object_get_ex(mmap_obj, "buffer_address", &buffer_address_obj);
+ long buf_address_trace = json_object_get_int64(buffer_address_obj);
+ set_buffer_address_retrace(fd_retrace, off, buf_address_trace,
+ (long) buf_address_retrace_pointer);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "fd: %d, offset: %ld, ", fd_retrace, off);
+ if (is_mmap64)
+ perror("mmap64");
+ else
+ perror("mmap");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+}
+
+void retrace_munmap(json_object *syscall_obj)
+{
+ json_object *munmap_args_obj;
+ json_object_object_get_ex(syscall_obj, "munmap", &munmap_args_obj);
+
+ json_object *start_obj;
+ json_object_object_get_ex(munmap_args_obj, "start", &start_obj);
+ long start = json_object_get_int64(start_obj);
+
+ json_object *length_obj;
+ json_object_object_get_ex(munmap_args_obj, "length", &length_obj);
+ size_t length = (size_t) json_object_get_int(length_obj);
+
+ long buffer_address_retrace = get_retrace_address_from_trace_address(start);
+
+ if (buffer_address_retrace < 0)
+ return;
+
+ munmap((void *)buffer_address_retrace, length);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "unmapped: %ld, ", buffer_address_retrace);
+ perror("munmap");
+ }
+}
+
+void retrace_open(json_object *jobj, bool is_open64)
+{
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(jobj, "fd", &fd_trace_obj);
+ int fd_trace = json_object_get_int(fd_trace_obj);
+
+ json_object *open_args_obj;
+ if (is_open64)
+ json_object_object_get_ex(jobj, "open64", &open_args_obj);
+ else
+ json_object_object_get_ex(jobj, "open", &open_args_obj);
+
+ json_object *path_obj;
+ std::string path_trace;
+ json_object_object_get_ex(open_args_obj, "path", &path_obj);
+ if (json_object_get_string(path_obj) != nullptr)
+ path_trace = json_object_get_string(path_obj);
+ std::string path_retrace = get_path_retrace_from_path_trace(path_trace, jobj);
+
+ /*
+ * Don't fail if a retrace path can't be found.
+ * Try using the same path as in the trace file.
+ */
+ if (path_retrace.empty()) {
+ if (is_verbose()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: can't find retrace device. Attempting to use: %s\n",
+ path_trace.c_str());
+ }
+ path_retrace = path_trace;
+ }
+
+ json_object *oflag_obj;
+ json_object_object_get_ex(open_args_obj, "oflag", &oflag_obj);
+ int oflag = s2val(json_object_get_string(oflag_obj), open_val_def);
+
+ int mode = 0;
+ json_object *mode_obj;
+ if (json_object_object_get_ex(open_args_obj, "mode", &mode_obj))
+ mode = s2number(json_object_get_string(mode_obj));
+
+ int fd_retrace = 0;
+ if (is_open64)
+ fd_retrace = open64(path_retrace.c_str(), oflag, mode);
+ else
+ fd_retrace = open(path_retrace.c_str(), oflag, mode);
+
+ if (fd_retrace <= 0) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot open: %s\n", path_retrace.c_str());
+ exit(fd_retrace);
+ }
+
+ add_fd(fd_trace, fd_retrace);
+
+ if (is_verbose() || errno != 0) {
+ fprintf(stderr, "path: %s ", path_retrace.c_str());
+ if (is_open64)
+ perror("open64");
+ else
+ perror("open");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+}
+
+void retrace_close(json_object *jobj)
+{
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(jobj, "fd", &fd_trace_obj);
+ int fd_retrace = get_fd_retrace_from_fd_trace(json_object_get_int(fd_trace_obj));
+
+ /* Only close devices that were opened in the retrace context. */
+ if (fd_retrace < 0)
+ return;
+
+ close(fd_retrace);
+ ctx_retrace.retrace_fds.erase(json_object_get_int(fd_trace_obj));
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "fd: %d ", fd_retrace);
+ perror("close");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+}
+
+void retrace_vidioc_reqbufs(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_requestbuffers *ptr = retrace_v4l2_requestbuffers_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_REQBUFS, ptr);
+ free(ptr);
+}
+
+struct v4l2_plane *retrace_v4l2_plane(json_object *plane_obj, __u32 memory)
+{
+ struct v4l2_plane *ptr = (struct v4l2_plane *) calloc(1, sizeof(v4l2_plane));
+
+ json_object *bytesused_obj;
+ json_object_object_get_ex(plane_obj, "bytesused", &bytesused_obj);
+ ptr->bytesused = (__u32) json_object_get_int64(bytesused_obj);
+
+ json_object *length_obj;
+ json_object_object_get_ex(plane_obj, "length", &length_obj);
+ ptr->length = (__u32) json_object_get_int64(length_obj);
+
+ json_object *m_obj;
+ json_object_object_get_ex(plane_obj, "m", &m_obj);
+ if (memory == V4L2_MEMORY_MMAP) {
+ json_object *mem_offset_obj;
+ json_object_object_get_ex(m_obj, "mem_offset", &mem_offset_obj);
+ ptr->m.mem_offset = (__u32) json_object_get_int64(mem_offset_obj);
+ }
+
+ json_object *data_offset_obj;
+ json_object_object_get_ex(plane_obj, "data_offset", &data_offset_obj);
+ ptr->data_offset = (__u32) json_object_get_int64(data_offset_obj);
+
+ return ptr;
+}
+
+struct v4l2_buffer *retrace_v4l2_buffer(json_object *ioctl_args)
+{
+ struct v4l2_buffer *buf = (struct v4l2_buffer *) calloc(1, sizeof(struct v4l2_buffer));
+
+ json_object *buf_obj;
+ json_object_object_get_ex(ioctl_args, "v4l2_buffer", &buf_obj);
+
+ json_object *index_obj;
+ json_object_object_get_ex(buf_obj, "index", &index_obj);
+ buf->index = (__u32) json_object_get_uint64(index_obj);
+
+ json_object *type_obj;
+ json_object_object_get_ex(buf_obj, "type", &type_obj);
+ buf->type = s2val(json_object_get_string(type_obj), v4l2_buf_type_val_def);
+
+ json_object *bytesused_obj;
+ json_object_object_get_ex(buf_obj, "bytesused", &bytesused_obj);
+ buf->bytesused = (__u32) json_object_get_uint64(bytesused_obj);
+
+ json_object *flags_obj;
+ json_object_object_get_ex(buf_obj, "flags", &flags_obj);
+ buf->flags = (__u32) s2flags_buffer(json_object_get_string(flags_obj));
+
+ json_object *field_obj;
+ json_object_object_get_ex(buf_obj, "field", &field_obj);
+ buf->field = (__u32) s2val(json_object_get_string(field_obj), v4l2_field_val_def);
+
+ json_object *timestamp_obj;
+ json_object_object_get_ex(buf_obj, "timestamp", &timestamp_obj);
+
+ struct timeval tval = {};
+ json_object *tv_sec_obj;
+ json_object_object_get_ex(timestamp_obj, "tv_sec", &tv_sec_obj);
+ tval.tv_sec = json_object_get_int64(tv_sec_obj);
+ json_object *tv_usec_obj;
+ json_object_object_get_ex(timestamp_obj, "tv_usec", &tv_usec_obj);
+ tval.tv_usec = json_object_get_int64(tv_usec_obj);
+ buf->timestamp = tval;
+
+ json_object *sequence_obj;
+ json_object_object_get_ex(buf_obj, "sequence", &sequence_obj);
+ buf->sequence = (__u32) json_object_get_uint64(sequence_obj);
+
+ json_object *memory_obj;
+ json_object_object_get_ex(buf_obj, "memory", &memory_obj);
+ buf->memory = s2val(json_object_get_string(memory_obj), v4l2_memory_val_def);
+
+ json_object *length_obj;
+ json_object_object_get_ex(buf_obj, "length", &length_obj);
+ buf->length = (__u32) json_object_get_uint64(length_obj);
+
+ json_object *m_obj;
+ json_object_object_get_ex(buf_obj, "m", &m_obj);
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ json_object *planes_obj;
+ json_object_object_get_ex(m_obj, "planes", &planes_obj);
+ /* TODO add planes > 0 */
+ json_object *plane_obj = json_object_array_get_idx(planes_obj, 0);
+ buf->m.planes = retrace_v4l2_plane(plane_obj, buf->memory);
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (buf->memory == V4L2_MEMORY_MMAP) {
+ json_object *offset_obj;
+ json_object_object_get_ex(m_obj, "offset", &offset_obj);
+ buf->m.offset = (__u32) json_object_get_uint64(offset_obj);
+ }
+ }
+
+ if ((buf->flags & V4L2_BUF_FLAG_REQUEST_FD) != 0U) {
+ json_object *request_fd_obj;
+ json_object_object_get_ex(buf_obj, "request_fd", &request_fd_obj);
+ buf->request_fd = (__s32) get_fd_retrace_from_fd_trace(json_object_get_int(request_fd_obj));
+ if (buf->request_fd < 0) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "bad or missing file descriptor\n");
+ }
+ }
+
+ return buf;
+}
+
+void retrace_vidioc_querybuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer *buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_QUERYBUF, buf);
+
+ if (buf->memory == V4L2_MEMORY_MMAP) {
+ __u32 offset = 0;
+ if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ offset = buf->m.offset;
+ if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ||
+ (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ offset = buf->m.planes->m.mem_offset;
+ if (get_buffer_fd_retrace(buf->type, buf->index) == -1)
+ add_buffer_retrace(fd_retrace, buf->type, buf->index, offset);
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (buf->m.planes != nullptr) {
+ free(buf->m.planes);
+ }
+ }
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, index: %d, fd: %d, ",
+ buftype2s((int) buf->type).c_str(), buf->index, fd_retrace);
+ perror("VIDIOC_QUERYBUF");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+
+ free(buf);
+}
+
+void retrace_vidioc_qbuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer *ptr = retrace_v4l2_buffer(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_QBUF, ptr);
+
+ if (ptr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ ptr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ if (ptr->m.planes != nullptr) {
+ free(ptr->m.planes);
+ }
+ }
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, index: %d, fd: %d, ",
+ buftype2s((int) ptr->type).c_str(), ptr->index, fd_retrace);
+ perror("VIDIOC_QBUF");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+
+ free(ptr);
+}
+
+void retrace_vidioc_dqbuf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer *buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ const int poll_timeout_ms = 5000;
+ struct pollfd *pfds = (struct pollfd *) calloc(1, sizeof(struct pollfd));
+ if (pfds == nullptr)
+ exit(EXIT_FAILURE);
+ pfds[0].fd = fd_retrace;
+ pfds[0].events = POLLIN;
+ int ret = poll(pfds, 1, poll_timeout_ms);
+ free(pfds);
+ if (ret == -1) {
+ fprintf(stderr, "%s:%s:%d: poll error: ", __FILE__, __func__, __LINE__);
+ perror("");
+ exit(EXIT_FAILURE);
+ }
+ if (ret == 0) {
+ fprintf(stderr, "%s:%s:%d: poll timed out\n", __FILE__, __func__, __LINE__);
+ exit(EXIT_FAILURE);
+ }
+
+ ioctl(fd_retrace, VIDIOC_DQBUF, buf);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, index: %d, fd: %d, ",
+ buftype2s((int) buf->type).c_str(), buf->index, fd_retrace);
+ perror("VIDIOC_DQBUF");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ free(buf->m.planes);
+
+ free(buf);
+}
+
+void retrace_vidioc_prepare_buf(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_buffer *buf = retrace_v4l2_buffer(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_PREPARE_BUF, buf);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, index: %d, fd: %d, ",
+ buftype2s((int) buf->type).c_str(), buf->index, fd_retrace);
+ perror("VIDIOC_PREPARE_BUF");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ free(buf->m.planes);
+
+ free(buf);
+}
+
+void retrace_vidioc_create_bufs(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_create_buffers *ptr = retrace_v4l2_create_buffers_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_CREATE_BUFS, ptr);
+
+ if (is_verbose() || (errno != 0)) {
+ perror("VIDIOC_CREATE_BUFS");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+
+ free(ptr);
+}
+
+void retrace_vidioc_expbuf(int fd_retrace, json_object *ioctl_args_user, json_object *ioctl_args_driver)
+{
+ struct v4l2_exportbuffer *ptr = retrace_v4l2_exportbuffer_gen(ioctl_args_user);
+ ioctl(fd_retrace, VIDIOC_EXPBUF, ptr);
+
+ int buf_fd_retrace = ptr->fd;
+
+ /*
+ * If a buffer was previously added to the retrace context using the video device
+ * file descriptor, replace the video fd with the more specific buffer fd from EXPBUF.
+ */
+ int fd_found_in_retrace_context = get_buffer_fd_retrace(ptr->type, ptr->index);
+ if (fd_found_in_retrace_context != -1)
+ remove_buffer_retrace(fd_found_in_retrace_context);
+
+ add_buffer_retrace(buf_fd_retrace, ptr->type, ptr->index);
+
+ /* Retrace again to associate the original fd with the current buffer fd. */
+ memset(ptr, 0, sizeof(v4l2_exportbuffer));
+ ptr = retrace_v4l2_exportbuffer_gen(ioctl_args_driver);
+ int buf_fd_trace = ptr->fd;
+ add_fd(buf_fd_trace, buf_fd_retrace);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_EXPBUF");
+
+ free(ptr);
+}
+
+void retrace_vidioc_streamon(int fd_retrace, json_object *ioctl_args)
+{
+ json_object *type_obj;
+ json_object_object_get_ex(ioctl_args, "type", &type_obj);
+ v4l2_buf_type buf_type = (v4l2_buf_type) s2val(json_object_get_string(type_obj),
+ v4l2_buf_type_val_def);
+
+ ioctl(fd_retrace, VIDIOC_STREAMON, &buf_type);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, ", buftype2s(buf_type).c_str());
+ perror("VIDIOC_STREAMON");
+ }
+}
+
+void retrace_vidioc_streamoff(int fd_retrace, json_object *ioctl_args)
+{
+ json_object *type_obj;
+ json_object_object_get_ex(ioctl_args, "type", &type_obj);
+ v4l2_buf_type buf_type = (v4l2_buf_type) s2val(json_object_get_string(type_obj),
+ v4l2_buf_type_val_def);
+
+ ioctl(fd_retrace, VIDIOC_STREAMOFF, &buf_type);
+
+ if (is_verbose() || (errno != 0)) {
+ fprintf(stderr, "%s, ", buftype2s(buf_type).c_str());
+ perror("VIDIOC_STREAMOFF");
+ }
+}
+
+void retrace_vidioc_try_fmt(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_format *ptr = retrace_v4l2_format_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_TRY_FMT, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_TRY_FMT");
+
+ free(ptr);
+}
+
+void retrace_vidioc_g_fmt(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_format *ptr = retrace_v4l2_format_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_G_FMT, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_FMT");
+
+ free(ptr);
+}
+
+void retrace_vidioc_s_fmt(int fd_retrace, json_object *ioctl_args_user)
+{
+ struct v4l2_format *ptr = retrace_v4l2_format_gen(ioctl_args_user);
+
+ ioctl(fd_retrace, VIDIOC_S_FMT, ptr);
+
+ if (is_verbose() || (errno != 0)) {
+ perror("VIDIOC_S_FMT");
+ }
+
+ free(ptr);
+}
+
+struct v4l2_streamparm *retrace_v4l2_streamparm(json_object *parent_obj, std::string key_name = "")
+{
+ struct v4l2_streamparm *ptr = (struct v4l2_streamparm *) calloc(1, sizeof(v4l2_streamparm));
+
+ json_object *v4l2_streamparm_obj;
+ json_object_object_get_ex(parent_obj, "v4l2_streamparm", &v4l2_streamparm_obj);
+
+ json_object *type_obj;
+ if (json_object_object_get_ex(v4l2_streamparm_obj, "type", &type_obj))
+ ptr->type = (__u32) s2val(json_object_get_string(type_obj), v4l2_buf_type_val_def);
+
+ if ((ptr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || (ptr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+ ptr->parm.capture = *retrace_v4l2_captureparm_gen(v4l2_streamparm_obj);
+
+ if ((ptr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || (ptr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ ptr->parm.output = *retrace_v4l2_outputparm_gen(v4l2_streamparm_obj);
+
+ return ptr;
+}
+
+void retrace_vidioc_g_parm (int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_streamparm *ptr = retrace_v4l2_streamparm(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_G_PARM, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_PARM");
+
+ free(ptr);
+}
+
+void retrace_vidioc_s_parm (int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_streamparm *ptr = retrace_v4l2_streamparm(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_S_PARM, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_S_PARM");
+
+ free(ptr);
+}
+
+void retrace_vidioc_queryctrl(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_queryctrl *ptr = retrace_v4l2_queryctrl_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_QUERYCTRL, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_QUERYCTRL");
+
+ free(ptr);
+}
+
+void retrace_vidioc_g_control(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_control *ptr = retrace_v4l2_control_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_G_CTRL, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_CTRL");
+
+ free(ptr);
+}
+
+void retrace_vidioc_s_control(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_control *ptr = retrace_v4l2_control_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_S_CTRL, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_S_CTRL");
+
+ free(ptr);
+}
+
+void retrace_vidioc_g_crop(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_crop *ptr = retrace_v4l2_crop_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_G_CROP, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_CROP");
+
+ free(ptr);
+}
+
+void retrace_vidioc_s_crop(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_crop *ptr = retrace_v4l2_crop_gen(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_S_CROP, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_S_CROP");
+
+ free(ptr);
+}
+
+int retrace_v4l2_ext_control_value(json_object *ctrl_obj, const val_def *def)
+{
+ __s32 value = -1;
+
+ json_object *value_obj;
+ if (json_object_object_get_ex(ctrl_obj, "value", &value_obj))
+ value = (__s32) s2val(json_object_get_string(value_obj), def);
+
+ return value;
+}
+
+__u32 *retrace_v4l2_dynamic_array(json_object *v4l2_ext_control_obj)
+{
+ __u32 elems = 0;
+ json_object *elems_obj;
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "elems", &elems_obj))
+ elems = (__u32) json_object_get_int64(elems_obj);
+
+ __u32 *ptr = static_cast<__u32 *>(calloc(elems, sizeof(__u32)));
+ if (ptr == nullptr) {
+ fprintf(stderr, "%s:%s:%d: memory allocation failed.\n", __FILE__, __func__, __LINE__);
+ return ptr;
+ }
+
+ json_object *p_u32_obj;
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "p_u32", &p_u32_obj)) {
+ for (size_t i = 0; i < elems; i++) {
+ if (json_object_array_get_idx(p_u32_obj, i))
+ ptr[i] = (__u32) json_object_get_int64(json_object_array_get_idx(p_u32_obj, i));
+ }
+ }
+
+ return ptr;
+}
+
+struct v4l2_ext_control *retrace_v4l2_ext_control(json_object *parent_obj, int ctrl_idx)
+{
+ struct v4l2_ext_control *p = (struct v4l2_ext_control *) calloc(1, sizeof(v4l2_ext_control));
+
+ json_object *v4l2_ext_control_obj = json_object_array_get_idx(parent_obj, ctrl_idx);
+ if (!v4l2_ext_control_obj)
+ return p;
+
+ json_object *id_obj;
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "id", &id_obj))
+ p->id = s2val(json_object_get_string(id_obj), control_val_def);
+
+ json_object *size_obj;
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "size", &size_obj))
+ p->size = json_object_get_int64(size_obj);
+
+ switch (p->id) {
+ case V4L2_CID_STATELESS_H264_DECODE_MODE:
+ p->value = retrace_v4l2_ext_control_value(v4l2_ext_control_obj,
+ v4l2_stateless_h264_decode_mode_val_def);
+ break;
+ case V4L2_CID_STATELESS_H264_START_CODE:
+ p->value = retrace_v4l2_ext_control_value(v4l2_ext_control_obj,
+ v4l2_stateless_h264_start_code_val_def);
+ break;
+ case V4L2_CID_STATELESS_HEVC_DECODE_MODE:
+ p->value = retrace_v4l2_ext_control_value(v4l2_ext_control_obj,
+ v4l2_stateless_hevc_decode_mode_val_def);
+ break;
+ case V4L2_CID_STATELESS_HEVC_START_CODE:
+ p->value = retrace_v4l2_ext_control_value(v4l2_ext_control_obj,
+ v4l2_stateless_hevc_start_code_val_def);
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+ case V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR:
+ case V4L2_CID_PIXEL_RATE: {
+ json_object *value64_obj;
+
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "value64", &value64_obj))
+ p->value64 = json_object_get_int64(value64_obj);
+ break;
+ }
+ default:
+ if (!p->size) {
+ json_object *value_obj;
+
+ if (json_object_object_get_ex(v4l2_ext_control_obj, "value", &value_obj))
+ p->value = json_object_get_int(value_obj);
+ }
+ break;
+ }
+
+ /* Don't retrace pointers that were not traced because they were null. */
+ if (p->size == 0)
+ return p;
+
+ switch (p->id) {
+ case V4L2_CID_STATELESS_VP8_FRAME:
+ p->ptr = retrace_v4l2_ctrl_vp8_frame_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SPS:
+ p->ptr = retrace_v4l2_ctrl_h264_sps_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_PPS:
+ p->ptr = retrace_v4l2_ctrl_h264_pps_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SCALING_MATRIX:
+ p->ptr = retrace_v4l2_ctrl_h264_scaling_matrix_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_PRED_WEIGHTS:
+ p->ptr = retrace_v4l2_ctrl_h264_pred_weights_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SLICE_PARAMS:
+ p->ptr = retrace_v4l2_ctrl_h264_slice_params_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_DECODE_PARAMS:
+ p->ptr = retrace_v4l2_ctrl_h264_decode_params_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_FWHT_PARAMS:
+ p->ptr = retrace_v4l2_ctrl_fwht_params_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_VP9_FRAME:
+ p->ptr = retrace_v4l2_ctrl_vp9_frame_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR:
+ p->ptr = retrace_v4l2_ctrl_vp9_compressed_hdr_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SPS:
+ p->ptr = retrace_v4l2_ctrl_hevc_sps_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_PPS:
+ p->ptr = retrace_v4l2_ctrl_hevc_pps_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS:
+ p->ptr = retrace_v4l2_ctrl_hevc_slice_params_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX:
+ p->ptr = retrace_v4l2_ctrl_hevc_scaling_matrix_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS:
+ p->ptr = retrace_v4l2_ctrl_hevc_decode_params_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS:
+ p->p_u32 = retrace_v4l2_dynamic_array(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_MPEG2_SEQUENCE:
+ p->ptr = retrace_v4l2_ctrl_mpeg2_sequence_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_MPEG2_PICTURE:
+ p->ptr = retrace_v4l2_ctrl_mpeg2_picture_gen(v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_MPEG2_QUANTISATION:
+ p->ptr = retrace_v4l2_ctrl_mpeg2_quantisation_gen(v4l2_ext_control_obj);
+ break;
+ default:
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: cannot retrace control: %s\n",
+ val2s(p->id, control_val_def).c_str());
+ break;
+ }
+
+ return p;
+}
+
+struct v4l2_ext_controls *retrace_v4l2_ext_controls(json_object *parent_obj)
+{
+ struct v4l2_ext_controls *ptr = (struct v4l2_ext_controls *) calloc(1, sizeof(v4l2_ext_controls));
+
+ json_object *v4l2_ext_controls_obj;
+ json_object_object_get_ex(parent_obj, "v4l2_ext_controls", &v4l2_ext_controls_obj);
+
+ json_object *which_obj;
+ if (json_object_object_get_ex(v4l2_ext_controls_obj, "which", &which_obj))
+ ptr->which = (__u32) s2val(json_object_get_string(which_obj), which_val_def);
+
+ json_object *count_obj;
+ if (json_object_object_get_ex(v4l2_ext_controls_obj, "count", &count_obj))
+ ptr->count = (__u32) json_object_get_int64(count_obj);
+
+ json_object *error_idx_obj;
+ if (json_object_object_get_ex(v4l2_ext_controls_obj, "error_idx", &error_idx_obj))
+ ptr->error_idx = (__u32) json_object_get_int64(error_idx_obj);
+
+ /* request_fd is only valid for V4L2_CTRL_WHICH_REQUEST_VAL */
+ if (ptr->which == V4L2_CTRL_WHICH_REQUEST_VAL) {
+ json_object *request_fd_obj;
+ if (json_object_object_get_ex(v4l2_ext_controls_obj, "request_fd", &request_fd_obj)) {
+
+ int request_fd_trace = json_object_get_int(request_fd_obj);
+ int request_fd_retrace = get_fd_retrace_from_fd_trace(request_fd_trace);
+ if (request_fd_retrace < 0) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "bad file descriptor\n");
+ return ptr;
+ }
+ ptr->request_fd = (__s32) request_fd_retrace;
+ }
+ }
+
+ json_object *controls_obj;
+ if (json_object_object_get_ex(v4l2_ext_controls_obj, "controls", &controls_obj)) {
+ ptr->controls = (struct v4l2_ext_control *) calloc(ptr->count, sizeof(v4l2_ext_control));
+ for (__u32 i = 0; i < ptr->count; i++) {
+ void *temp = retrace_v4l2_ext_control(controls_obj, i);
+ if (temp != nullptr) {
+ ptr->controls[i] = *(static_cast<struct v4l2_ext_control*>(temp));
+ free(temp);
+ }
+ }
+ }
+
+ return ptr;
+}
+
+void retrace_vidioc_try_ext_ctrls(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_ext_controls *ptr = retrace_v4l2_ext_controls(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_TRY_EXT_CTRLS, ptr);
+
+ free(ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_TRY_EXT_CTRLS");
+}
+
+void retrace_vidioc_g_ext_ctrls(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_ext_controls *ptr = retrace_v4l2_ext_controls(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_G_EXT_CTRLS, ptr);
+
+ free(ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_EXT_CTRLS");
+}
+
+void retrace_vidioc_s_ext_ctrls(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_ext_controls *ptr = retrace_v4l2_ext_controls(ioctl_args);
+ ioctl(fd_retrace, VIDIOC_S_EXT_CTRLS, ptr);
+
+ free(ptr);
+
+ if (is_verbose() || (errno != 0)) {
+ perror("VIDIOC_S_EXT_CTRLS");
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_context();
+ }
+ }
+}
+
+void retrace_vidioc_try_encoder_cmd(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_encoder_cmd *ptr = retrace_v4l2_encoder_cmd_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_TRY_ENCODER_CMD, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_TRY_ENCODER_CMD");
+
+ free(ptr);
+}
+
+void retrace_vidioc_encoder_cmd(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_encoder_cmd *ptr = retrace_v4l2_encoder_cmd_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_ENCODER_CMD, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_ENCODER_CMD");
+
+ free(ptr);
+}
+
+void retrace_vidioc_g_selection(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_selection *ptr = retrace_v4l2_selection_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_G_SELECTION, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_G_SELECTION");
+
+ free(ptr);
+
+}
+
+void retrace_vidioc_s_selection(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_selection *ptr = retrace_v4l2_selection_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_S_SELECTION, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_S_SELECTION");
+
+ free(ptr);
+}
+
+struct v4l2_decoder_cmd *retrace_v4l2_decoder_cmd(json_object *parent_obj)
+{
+ struct v4l2_decoder_cmd *ptr = (struct v4l2_decoder_cmd *) calloc(1, sizeof(v4l2_decoder_cmd));
+
+ json_object *v4l2_decoder_cmd_obj;
+ json_object_object_get_ex(parent_obj, "v4l2_decoder_cmd", &v4l2_decoder_cmd_obj);
+
+ json_object *cmd_obj;
+ /* Since V4L2_DEC_CMD_START is 0, an empty key will be retraced as V4L2_DEC_CMD_START. */
+ if (json_object_object_get_ex(v4l2_decoder_cmd_obj, "cmd", &cmd_obj))
+ ptr->cmd = (__u32) s2val(json_object_get_string(cmd_obj), decoder_cmd_val_def);
+
+ std::string flags;
+ json_object *flags_obj;
+ if (json_object_object_get_ex(v4l2_decoder_cmd_obj, "flags", &flags_obj))
+ if (json_object_get_string(flags_obj) != nullptr)
+ flags = json_object_get_string(flags_obj);
+
+ switch (ptr->cmd) {
+ case V4L2_DEC_CMD_START: {
+ if (flags == "V4L2_DEC_CMD_START_MUTE_AUDIO")
+ ptr->flags = V4L2_DEC_CMD_START_MUTE_AUDIO;
+
+ json_object *start_obj;
+ json_object_object_get_ex(v4l2_decoder_cmd_obj, "start", &start_obj);
+
+ json_object *speed_obj;
+ if (json_object_object_get_ex(start_obj, "speed", &speed_obj))
+ ptr->start.speed = json_object_get_int(speed_obj);
+
+ std::string format;
+ json_object *format_obj;
+ if (json_object_object_get_ex(start_obj, "format", &format_obj))
+ if (json_object_get_string(format_obj) != nullptr)
+ format = json_object_get_string(format_obj);
+
+ if (format == "V4L2_DEC_START_FMT_GOP")
+ ptr->start.format = V4L2_DEC_START_FMT_GOP;
+ else if (format == "V4L2_DEC_START_FMT_NONE")
+ ptr->start.format = V4L2_DEC_START_FMT_NONE;
+ break;
+ }
+ case V4L2_DEC_CMD_STOP: {
+ if (flags == "V4L2_DEC_CMD_STOP_TO_BLACK")
+ ptr->flags = V4L2_DEC_CMD_STOP_TO_BLACK;
+ else if (flags == "V4L2_DEC_CMD_STOP_IMMEDIATELY")
+ ptr->flags = V4L2_DEC_CMD_STOP_IMMEDIATELY;
+
+ json_object *stop_obj;
+ json_object_object_get_ex(v4l2_decoder_cmd_obj, "stop", &stop_obj);
+
+ json_object *pts_obj;
+ if (json_object_object_get_ex(stop_obj, "pts", &pts_obj))
+ ptr->stop.pts = (__u64) json_object_get_uint64(pts_obj);
+ break;
+ }
+ case V4L2_DEC_CMD_PAUSE: {
+ if (flags == "V4L2_DEC_CMD_PAUSE_TO_BLACK")
+ ptr->flags = V4L2_DEC_CMD_PAUSE_TO_BLACK;
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ptr;
+}
+
+void retrace_vidioc_try_decoder_cmd(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_decoder_cmd *ptr = retrace_v4l2_decoder_cmd(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_TRY_DECODER_CMD, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_TRY_DECODER_CMD");
+
+ free (ptr);
+}
+
+void retrace_vidioc_decoder_cmd(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_decoder_cmd *ptr = retrace_v4l2_decoder_cmd(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_DECODER_CMD, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_DECODER_CMD");
+
+ free (ptr);
+}
+
+void retrace_vidioc_query_ext_ctrl(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_query_ext_ctrl *ptr = retrace_v4l2_query_ext_ctrl_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_QUERY_EXT_CTRL, ptr);
+
+ if (is_verbose())
+ perror("VIDIOC_QUERY_EXT_CTRL");
+
+ free(ptr);
+}
+
+void retrace_vidioc_enum_fmt(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_fmtdesc *ptr = retrace_v4l2_fmtdesc_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_ENUM_FMT, ptr);
+
+ if (is_verbose())
+ perror("VIDIOC_ENUM_FMT");
+
+ free(ptr);
+}
+
+void retrace_vidioc_querycap(int fd_retrace, json_object *ioctl_args)
+{
+ struct v4l2_capability *ptr = retrace_v4l2_capability_gen(ioctl_args);
+
+ ioctl(fd_retrace, VIDIOC_QUERYCAP, ptr);
+
+ if (is_verbose() || (errno != 0))
+ perror("VIDIOC_QUERYCAP");
+
+ free(ptr);
+}
+
+void retrace_media_ioc_request_alloc(int fd_retrace, json_object *ioctl_args)
+{
+ /* Get the original request file descriptor from the original trace file. */
+ json_object *request_fd_trace_obj;
+ json_object_object_get_ex(ioctl_args, "request_fd", &request_fd_trace_obj);
+ int request_fd_trace = json_object_get_int(request_fd_trace_obj);
+
+ /* Allocate a request in the retrace context. */
+ __s32 request_fd_retrace = 0;
+ ioctl(fd_retrace, MEDIA_IOC_REQUEST_ALLOC, &request_fd_retrace);
+
+ /* Associate the original request file descriptor with the current request file descriptor. */
+ add_fd(request_fd_trace, request_fd_retrace);
+}
+
+void retrace_ioctl(json_object *syscall_obj)
+{
+ long cmd = 0;
+ int fd_retrace = 0;
+
+ json_object *fd_trace_obj;
+ json_object_object_get_ex(syscall_obj, "fd", &fd_trace_obj);
+ fd_retrace = get_fd_retrace_from_fd_trace(json_object_get_int(fd_trace_obj));
+ if (fd_retrace < 0) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "bad file descriptor\n");
+ return;
+ }
+
+ json_object *cmd_obj;
+ json_object_object_get_ex(syscall_obj, "ioctl", &cmd_obj);
+ cmd = s2val(json_object_get_string(cmd_obj), ioctl_val_def);
+
+ json_object *ioctl_args_user;
+ json_object_object_get_ex(syscall_obj, "from_userspace", &ioctl_args_user);
+
+ json_object *ioctl_args_driver;
+ json_object_object_get_ex(syscall_obj, "from_driver", &ioctl_args_driver);
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ retrace_vidioc_querycap(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_ENUM_FMT:
+ retrace_vidioc_enum_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_TRY_FMT:
+ retrace_vidioc_try_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_FMT:
+ retrace_vidioc_g_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_FMT:
+ retrace_vidioc_s_fmt(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_REQBUFS:
+ retrace_vidioc_reqbufs(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QUERYBUF:
+ retrace_vidioc_querybuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QBUF:
+ retrace_vidioc_qbuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_EXPBUF:
+ retrace_vidioc_expbuf(fd_retrace, ioctl_args_user, ioctl_args_driver);
+ break;
+ case VIDIOC_DQBUF:
+ retrace_vidioc_dqbuf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_STREAMON:
+ retrace_vidioc_streamon(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_STREAMOFF:
+ retrace_vidioc_streamoff(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_PARM:
+ retrace_vidioc_g_parm(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_PARM:
+ retrace_vidioc_s_parm(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_CTRL:
+ retrace_vidioc_g_control(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_CTRL:
+ retrace_vidioc_s_control(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QUERYCTRL:
+ retrace_vidioc_queryctrl(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_CROP:
+ retrace_vidioc_g_crop(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_CROP:
+ retrace_vidioc_s_crop(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_EXT_CTRLS:
+ retrace_vidioc_g_ext_ctrls(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_TRY_EXT_CTRLS:
+ retrace_vidioc_try_ext_ctrls(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_EXT_CTRLS:
+ retrace_vidioc_s_ext_ctrls(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_TRY_ENCODER_CMD:
+ retrace_vidioc_try_encoder_cmd(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_ENCODER_CMD:
+ retrace_vidioc_encoder_cmd(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_CREATE_BUFS:
+ retrace_vidioc_create_bufs(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_G_SELECTION:
+ retrace_vidioc_g_selection(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_S_SELECTION:
+ retrace_vidioc_s_selection(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_PREPARE_BUF:
+ retrace_vidioc_prepare_buf(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_TRY_DECODER_CMD:
+ retrace_vidioc_try_decoder_cmd(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_DECODER_CMD:
+ retrace_vidioc_decoder_cmd(fd_retrace, ioctl_args_user);
+ break;
+ case VIDIOC_QUERY_EXT_CTRL:
+ retrace_vidioc_query_ext_ctrl(fd_retrace, ioctl_args_user);
+ break;
+ case MEDIA_IOC_REQUEST_ALLOC:
+ retrace_media_ioc_request_alloc(fd_retrace, ioctl_args_driver);
+ break;
+ case MEDIA_REQUEST_IOC_QUEUE:
+ ioctl(fd_retrace, MEDIA_REQUEST_IOC_QUEUE);
+ break;
+ case MEDIA_REQUEST_IOC_REINIT:
+ ioctl(fd_retrace, MEDIA_REQUEST_IOC_REINIT);
+ break;
+ default:
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: cannot retrace ioctl");
+ if (json_object_get_string(cmd_obj) != nullptr)
+ fprintf(stderr, ": \'%s\'\n", json_object_get_string(cmd_obj));
+ else
+ fprintf(stderr, "\n");
+ break;
+ }
+}
+
+void retrace_mem(json_object *mem_obj)
+{
+ json_object *type_obj;
+ json_object_object_get_ex(mem_obj, "mem_dump", &type_obj);
+ v4l2_buf_type type = (v4l2_buf_type) s2val(json_object_get_string(type_obj),
+ v4l2_buf_type_val_def);
+ json_object *bytesused_obj;
+ json_object_object_get_ex(mem_obj, "bytesused", &bytesused_obj);
+ int bytesused = json_object_get_int64(bytesused_obj);
+ if (bytesused == 0)
+ return;
+
+ json_object *offset_obj;
+ json_object_object_get_ex(mem_obj, "offset", &offset_obj);
+ __u32 offset = json_object_get_int64(offset_obj);
+
+ json_object *address_obj;
+ json_object_object_get_ex(mem_obj, "address", &address_obj);
+ long buffer_address_trace = json_object_get_int64(address_obj);
+
+ long buffer_address_retrace = get_retrace_address_from_trace_address(buffer_address_trace);
+
+ unsigned char *buffer_pointer = (unsigned char *) buffer_address_retrace;
+
+ /* Get the encoded data from the json file and write it to output buffer memory. */
+ if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ write_to_output_buffer(buffer_pointer, bytesused, mem_obj);
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "%s, bytesused: %d, offset: %d, addr: %ld\n",
+ buftype2s(type).c_str(), bytesused, offset, buffer_address_retrace);
+ print_context();
+ }
+}
+
+void retrace_object(json_object *jobj)
+{
+ errno = 0;
+ json_object *temp_obj;
+ if (json_object_object_get_ex(jobj, "ioctl", &temp_obj)) {
+ retrace_ioctl(jobj);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "open", &temp_obj)) {
+ retrace_open(jobj, false);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "open64", &temp_obj)) {
+ retrace_open(jobj, true);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "close", &temp_obj)) {
+ retrace_close(jobj);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "mmap", &temp_obj)) {
+ retrace_mmap(jobj, false);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "mmap64", &temp_obj)) {
+ retrace_mmap(jobj, true);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "munmap", &temp_obj)) {
+ retrace_munmap(jobj);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "mem_dump", &temp_obj)) {
+ retrace_mem(jobj);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "package_version", &temp_obj)) {
+ compare_program_versions(jobj);
+ return;
+ }
+
+ if (json_object_object_get_ex(jobj, "Trace", &temp_obj)) {
+ return;
+ }
+
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: unexpected JSON object in trace file.\n");
+}
+
+void retrace_array(json_object *root_array_obj)
+{
+ json_object *jobj;
+ struct array_list *array_list_pointer = json_object_get_array(root_array_obj);
+ size_t json_objects_in_file = array_list_length(array_list_pointer);
+
+ if (json_objects_in_file < 3) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: trace file may be empty.\n");
+ }
+
+ for (size_t i = 0; i < json_objects_in_file; i++) {
+ jobj = (json_object *) array_list_get_idx(array_list_pointer, i);
+ retrace_object(jobj);
+ }
+}
+
+int retrace(std::string trace_filename)
+{
+ FILE *trace_file = fopen(trace_filename.c_str(), "r");
+ if (trace_file == nullptr) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "Trace file error: \'%s\'\n", trace_filename.c_str());
+ return 1;
+ }
+ fclose(trace_file);
+
+ fprintf(stderr, "Retracing: %s\n", trace_filename.c_str());
+
+ json_object *root_array_obj = json_object_from_file(trace_filename.c_str());
+
+ if (root_array_obj == nullptr) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot get JSON-object from file: %s\n", trace_filename.c_str());
+ return 1;
+ }
+
+ retrace_array(root_array_obj);
+ json_object_put(root_array_obj);
+
+ return 0;
+}
diff --git a/utils/v4l2-tracer/retrace.h b/utils/v4l2-tracer/retrace.h
new file mode 100644
index 00000000..01157336
--- /dev/null
+++ b/utils/v4l2-tracer/retrace.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef RETRACE_H
+#define RETRACE_H
+
+#include "v4l2-tracer-common.h"
+#include "retrace-gen.h"
+
+struct buffer_retrace {
+ int fd;
+ __u32 type;
+ __u32 index;
+ __u32 offset;
+ long address_trace;
+ long address_retrace;
+};
+
+struct retrace_context {
+ /* Key is a file descriptor from the trace, value is the corresponding fd in the retrace. */
+ std::unordered_map<int, int> retrace_fds;
+ /* List of output and capture buffers being retraced. */
+ std::list<struct buffer_retrace> buffers;
+};
+
+int retrace(std::string trace_filename);
+
+bool buffer_in_retrace_context(int fd, __u32 offset = 0);
+int get_buffer_fd_retrace(__u32 type, __u32 index);
+void add_buffer_retrace(int fd, __u32 type, __u32 index, __u32 offset = 0);
+void remove_buffer_retrace(int fd);
+void set_buffer_address_retrace(int fd, __u32 offset, long address_trace, long address_retrace);
+long get_retrace_address_from_trace_address(long address_trace);
+void add_fd(int fd_trace, int fd_retrace);
+int get_fd_retrace_from_fd_trace(int fd_trace);
+std::string get_path_retrace_from_path_trace(std::string path_trace, json_object *jobj);
+void write_to_output_buffer(unsigned char *buffer_pointer, int bytesused, json_object *mem_obj);
+void compare_program_versions(json_object *v4l2_tracer_info_obj);
+void print_context(void);
+
+#endif
diff --git a/utils/v4l2-tracer/trace-helper.cpp b/utils/v4l2-tracer/trace-helper.cpp
new file mode 100644
index 00000000..0030272b
--- /dev/null
+++ b/utils/v4l2-tracer/trace-helper.cpp
@@ -0,0 +1,500 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "trace.h"
+#include <math.h>
+
+struct trace_context ctx_trace = {};
+
+bool is_video_or_media_device(const char *path)
+{
+ std::string dev_path_video = "/dev/video";
+ std::string dev_path_media = "/dev/media";
+ bool is_video = strncmp(path, dev_path_video.c_str(), dev_path_video.length()) == 0;
+ bool is_media = strncmp(path, dev_path_media.c_str(), dev_path_media.length()) == 0;
+ return (is_video || is_media);
+}
+
+void add_device(int fd, std::string path)
+{
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "fd: %d, path: %s\n", fd, path.c_str());
+ }
+ std::pair<int, std::string> new_pair = std::make_pair(fd, path);
+ ctx_trace.devices.insert(new_pair);
+}
+
+std::string get_device(int fd)
+{
+ std::string path;
+ std::unordered_map<int, std::string>::const_iterator it;
+ it = ctx_trace.devices.find(fd);
+ if (it != ctx_trace.devices.end())
+ path = it->second;
+ return path;
+}
+
+void print_devices(void)
+{
+ if (ctx_trace.devices.size())
+ fprintf(stderr, "Devices:\n");
+ for (auto &device_pair : ctx_trace.devices)
+ fprintf(stderr, "fd: %d, path: %s\n", device_pair.first, device_pair.second.c_str());
+}
+
+void print_decode_order(void)
+{
+ fprintf(stderr, "Decode order: ");
+ for (auto &num : ctx_trace.decode_order)
+ fprintf(stderr, "%ld, ", num);
+ fprintf(stderr, ".\n");
+}
+
+void set_decode_order(long decode_order)
+{
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "%ld\n", decode_order);
+ }
+
+ std::list<long>::iterator it;
+ it = find(ctx_trace.decode_order.begin(), ctx_trace.decode_order.end(), decode_order);
+ if (it == ctx_trace.decode_order.end())
+ ctx_trace.decode_order.push_front(decode_order);
+
+ if (is_debug())
+ print_decode_order();
+}
+
+long get_decode_order(void)
+{
+ long decode_order = 0;
+ if (!ctx_trace.decode_order.empty())
+ decode_order = ctx_trace.decode_order.front();
+ return decode_order;
+}
+
+void add_buffer_trace(int fd, __u32 type, __u32 index, __u32 offset = 0)
+{
+ struct buffer_trace buf = {};
+ buf.fd = fd;
+ buf.type = type;
+ buf.index = index;
+ buf.offset = offset;
+ buf.display_order = -1;
+ ctx_trace.buffers.push_front(buf);
+}
+
+void remove_buffer_trace(int fd)
+{
+ for (auto it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->fd == fd) {
+ ctx_trace.buffers.erase(it);
+ break;
+ }
+ }
+}
+
+bool buffer_in_trace_context(int fd, __u32 offset)
+{
+ bool buffer_in_trace_context = false;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ buffer_in_trace_context = true;
+ break;
+ }
+ }
+ return buffer_in_trace_context;
+}
+
+int get_buffer_fd_trace(__u32 type, __u32 index)
+{
+ int fd = 0;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.type == type) && (b.index == index)) {
+ fd = b.fd;
+ break;
+ }
+ }
+ return fd;
+}
+
+__u32 get_buffer_type_trace(int fd, __u32 offset)
+{
+ __u32 type = 0;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ type = b.type;
+ break;
+ }
+ }
+ return type;
+}
+
+int get_buffer_index_trace(int fd, __u32 offset)
+{
+ int index = -1;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ index = b.index;
+ break;
+ }
+ }
+ return index;
+}
+
+__u32 get_buffer_offset_trace(__u32 type, __u32 index)
+{
+ __u32 offset = 0;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.type == type) && (b.index == index)) {
+ offset = b.offset;
+ break;
+ }
+ }
+ return offset;
+}
+
+void set_buffer_bytesused_trace(int fd, __u32 offset, __u32 bytesused)
+{
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ b.bytesused = bytesused;
+ break;
+ }
+ }
+}
+
+long get_buffer_bytesused_trace(int fd, __u32 offset)
+{
+ long bytesused = 0;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ bytesused = b.bytesused;
+ break;
+ }
+ }
+ return bytesused;
+}
+
+void set_buffer_display_order(int fd, __u32 offset, long display_order)
+{
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "%ld\n", display_order);
+ }
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ b.display_order = display_order;
+ break;
+ }
+ }
+}
+
+void set_buffer_address_trace(int fd, __u32 offset, unsigned long address)
+{
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ b.address = address;
+ break;
+ }
+ }
+}
+
+unsigned long get_buffer_address_trace(int fd, __u32 offset)
+{
+ unsigned long address = 0;
+ for (auto &b : ctx_trace.buffers) {
+ if ((b.fd == fd) && (b.offset == offset)) {
+ address = b.address;
+ break;
+ }
+ }
+ return address;
+}
+
+bool buffer_is_mapped(unsigned long buffer_address)
+{
+ bool ret = false;
+ for (auto &b : ctx_trace.buffers) {
+ if (b.address == buffer_address) {
+ ret = true;
+ break;
+ }
+ }
+ return ret;
+}
+
+void print_buffers_trace(void)
+{
+ for (auto &b : ctx_trace.buffers) {
+ fprintf(stderr, "fd: %d, %s, index: %d, display_order: %ld, bytesused: %d, ",
+ b.fd, buftype2s(b.type).c_str(), b.index, b.display_order, b.bytesused);
+ fprintf(stderr, "address: %lu, offset: %u \n", b.address, b.offset);
+ }
+}
+
+unsigned get_expected_length_trace()
+{
+ /*
+ * TODO: this assumes that the stride is equal to the real width and that the
+ * padding follows the end of the chroma plane. It could be improved by
+ * following the model in v4l2-ctl-streaming.cpp read_write_padded_frame()
+ */
+ unsigned expected_length = ctx_trace.width * ctx_trace.height;
+ if (ctx_trace.pixelformat == V4L2_PIX_FMT_NV12 || ctx_trace.pixelformat == V4L2_PIX_FMT_YUV420) {
+ expected_length *= 3;
+ expected_length /= 2;
+ expected_length += (expected_length % 2);
+ }
+ return expected_length;
+}
+
+void s_ext_ctrls_setup(struct v4l2_ext_controls *ext_controls)
+{
+ if (ext_controls->which != V4L2_CTRL_WHICH_REQUEST_VAL)
+ return;
+
+ if (is_debug())
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+
+ /*
+ * Since userspace sends H264 frames out of order, get information
+ * about the correct display order of each frame so that v4l2-tracer
+ * can write the decoded frames to a file.
+ */
+ for (__u32 i = 0; i < ext_controls->count; i++) {
+ struct v4l2_ext_control ctrl = ext_controls->controls[i];
+
+ switch (ctrl.id) {
+ case V4L2_CID_STATELESS_H264_SPS: {
+ ctx_trace.fmt.h264.max_pic_order_cnt_lsb = pow(2, ctrl.p_h264_sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
+ break;
+ }
+ case V4L2_CID_STATELESS_H264_DECODE_PARAMS: {
+ long pic_order_cnt_msb;
+ int max = ctx_trace.fmt.h264.max_pic_order_cnt_lsb;
+ long prev_pic_order_cnt_msb = get_decode_order();
+ int prev_pic_order_cnt_lsb = ctx_trace.fmt.h264.pic_order_cnt_lsb;
+ int pic_order_cnt_lsb = ctrl.p_h264_decode_params->pic_order_cnt_lsb;
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "\tprev_pic_order_cnt_lsb: %d\n", prev_pic_order_cnt_lsb);
+ fprintf(stderr, "\tprev_pic_order_cnt_msb: %ld\n", prev_pic_order_cnt_msb);
+ fprintf(stderr, "\tpic_order_cnt_lsb: %d\n", pic_order_cnt_lsb);
+ }
+
+ /*
+ * TODO: improve the displaying of decoded frames following H264 specification
+ * 8.2.1.1. For now, dump all the previously decoded frames when an IDR_PIC is
+ * received to avoid losing frames although this will still sometimes result
+ * in frames out of order.
+ */
+ if ((ctrl.p_h264_decode_params->flags & V4L2_H264_DECODE_PARAM_FLAG_IDR_PIC) != 0U) {
+ if (ctx_trace.compressed_frame_count != 0)
+ trace_mem_decoded();
+ }
+
+ /*
+ * When pic_order_cnt_lsb wraps around to zero, adjust the total count using
+ * max to keep the correct display order.
+ */
+ if ((pic_order_cnt_lsb < prev_pic_order_cnt_lsb) &&
+ ((prev_pic_order_cnt_lsb - pic_order_cnt_lsb) >= (max / 2))) {
+ pic_order_cnt_msb = prev_pic_order_cnt_msb + max;
+ } else if ((pic_order_cnt_lsb > prev_pic_order_cnt_lsb) &&
+ ((pic_order_cnt_lsb - prev_pic_order_cnt_lsb) > (max / 2))) {
+ pic_order_cnt_msb = prev_pic_order_cnt_msb - max;
+ } else {
+ pic_order_cnt_msb = prev_pic_order_cnt_msb + (pic_order_cnt_lsb - prev_pic_order_cnt_lsb);
+ }
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "\tpic_order_cnt_msb: %ld\n", pic_order_cnt_msb);
+ }
+ ctx_trace.fmt.h264.pic_order_cnt_lsb = pic_order_cnt_lsb;
+ set_decode_order(pic_order_cnt_msb);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void qbuf_setup(struct v4l2_buffer *buf)
+{
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "%s, index: %d\n", buftype2s((int) buf->type).c_str(), buf->index);
+ }
+
+ int buf_fd = get_buffer_fd_trace(buf->type, buf->index);
+ __u32 buf_offset = get_buffer_offset_trace(buf->type, buf->index);
+
+ __u32 bytesused = 0;
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ bytesused = buf->m.planes[0].bytesused;
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT || buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ bytesused = buf->bytesused;
+ set_buffer_bytesused_trace(buf_fd, buf_offset, bytesused);
+
+ /* The output buffer should have compressed data just before it is queued, so trace it. */
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ trace_mem_encoded(buf_fd, buf_offset);
+ ctx_trace.compressed_frame_count = ctx_trace.compressed_frame_count + 1;
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+
+ /* If the capture buffer is queued for reuse, trace it before it is reused. */
+ if (ctx_trace.compressed_frame_count != 0)
+ trace_mem_decoded();
+
+ /* H264 sets display order in controls, otherwise display just in the order queued. */
+ if (ctx_trace.compression_format != V4L2_PIX_FMT_H264_SLICE)
+ set_decode_order(get_decode_order() + 1);
+
+ set_buffer_display_order(buf_fd, buf_offset, get_decode_order());
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_decode_order();
+ print_buffers_trace();
+ }
+ }
+}
+
+void streamoff_cleanup(v4l2_buf_type buf_type)
+{
+
+ if (is_debug())
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ if (is_verbose() || (getenv("V4L2_TRACER_OPTION_WRITE_DECODED_TO_YUV_FILE") != nullptr)) {
+ fprintf(stderr, "VIDIOC_STREAMOFF: %s\n", buftype2s(buf_type).c_str());
+ fprintf(stderr, "%s, %s %s, width: %d, height: %d\n",
+ val2s(ctx_trace.compression_format, v4l2_pix_fmt_val_def).c_str(),
+ val2s(ctx_trace.pixelformat, v4l2_pix_fmt_val_def).c_str(),
+ fcc2s(ctx_trace.pixelformat).c_str(), ctx_trace.width, ctx_trace.height);
+ }
+
+ /*
+ * Before turning off the stream, trace any remaining capture buffers that were missed
+ * because they were not queued for reuse.
+ */
+ if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (ctx_trace.compressed_frame_count != 0)
+ trace_mem_decoded();
+ }
+}
+
+void g_fmt_setup_trace(struct v4l2_format *format)
+{
+ if (format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ ctx_trace.width = format->fmt.pix.width;
+ ctx_trace.height = format->fmt.pix.height;
+ ctx_trace.pixelformat = format->fmt.pix.pixelformat;
+ }
+ if (format->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ ctx_trace.width = format->fmt.pix_mp.width;
+ ctx_trace.height = format->fmt.pix_mp.height;
+ ctx_trace.pixelformat = format->fmt.pix_mp.pixelformat;
+ }
+ if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ctx_trace.compression_format = format->fmt.pix.pixelformat;
+ if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ ctx_trace.compression_format = format->fmt.pix_mp.pixelformat;
+}
+
+void s_fmt_setup(struct v4l2_format *format)
+{
+ if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ ctx_trace.compression_format = format->fmt.pix.pixelformat;
+ if (format->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ ctx_trace.compression_format = format->fmt.pix_mp.pixelformat;
+}
+
+void expbuf_setup(struct v4l2_exportbuffer *export_buffer)
+{
+ __u32 type = export_buffer->type;
+ __u32 index = export_buffer->index;
+ int fd_found_in_trace_context = get_buffer_fd_trace(type, index);
+
+ /* If the buffer was already added to the trace context don't add it again. */
+ if (fd_found_in_trace_context == export_buffer->fd)
+ return;
+
+ /*
+ * If a buffer was previously added to the trace context using the video device
+ * file descriptor, replace the video fd with the more specific buffer fd from EXPBUF.
+ */
+ if (fd_found_in_trace_context != 0)
+ remove_buffer_trace(fd_found_in_trace_context);
+
+ add_buffer_trace(export_buffer->fd, type, index);
+}
+
+void querybuf_setup(int fd, struct v4l2_buffer *buf)
+{
+ /* If the buffer was already added to the trace context don't add it again. */
+ if (get_buffer_fd_trace(buf->type, buf->index) != 0)
+ return;
+
+ if (buf->memory == V4L2_MEMORY_MMAP) {
+ __u32 offset = 0;
+ if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT))
+ offset = buf->m.offset;
+ if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) ||
+ (buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ offset = buf->m.planes->m.mem_offset;
+ add_buffer_trace(fd, buf->type, buf->index, offset);
+ }
+}
+
+void query_ext_ctrl_setup(int fd, struct v4l2_query_ext_ctrl *ptr)
+{
+ if (ptr->flags & (V4L2_CTRL_FLAG_HAS_PAYLOAD|V4L2_CTRL_FLAG_DYNAMIC_ARRAY)) {
+ if (ptr->id == V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS)
+ ctx_trace.elems = ptr->elems;
+ }
+}
+
+void write_json_object_to_json_file(json_object *jobj)
+{
+ std::string json_str;
+ if (getenv("V4L2_TRACER_OPTION_COMPACT_PRINT") != nullptr)
+ json_str = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PLAIN);
+ else
+ json_str = json_object_to_json_string_ext(jobj, JSON_C_TO_STRING_PRETTY);
+
+ if (ctx_trace.trace_file == nullptr) {
+ std::string filename;
+ if (getenv("TRACE_ID") != nullptr)
+ filename = getenv("TRACE_ID");
+ ctx_trace.trace_filename = filename;
+ ctx_trace.trace_filename += ".json";
+ ctx_trace.trace_file = fopen(ctx_trace.trace_filename.c_str(), "a");
+ }
+
+ fwrite(json_str.c_str(), sizeof(char), json_str.length(), ctx_trace.trace_file);
+ fputs(",\n", ctx_trace.trace_file);
+ fflush(ctx_trace.trace_file);
+}
+
+void close_json_file(void)
+{
+ if (ctx_trace.trace_file != nullptr) {
+ fclose(ctx_trace.trace_file);
+ ctx_trace.trace_file = 0;
+ }
+}
diff --git a/utils/v4l2-tracer/trace.cpp b/utils/v4l2-tracer/trace.cpp
new file mode 100644
index 00000000..d5a09ad7
--- /dev/null
+++ b/utils/v4l2-tracer/trace.cpp
@@ -0,0 +1,603 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "trace.h"
+
+extern struct trace_context ctx_trace;
+
+void trace_open(int fd, const char *path, int oflag, mode_t mode, bool is_open64)
+{
+ json_object *open_obj = json_object_new_object();
+ json_object_object_add(open_obj, "fd", json_object_new_int(fd));
+
+ json_object *open_args = json_object_new_object();
+ json_object_object_add(open_args, "path", json_object_new_string(path));
+ json_object_object_add(open_args, "oflag",
+ json_object_new_string(val2s(oflag, open_val_def).c_str()));
+ json_object_object_add(open_args, "mode", json_object_new_string(number2s_oct(mode).c_str()));
+ if (is_open64)
+ json_object_object_add(open_obj, "open64", open_args);
+ else
+ json_object_object_add(open_obj, "open", open_args);
+
+ /* Add additional topology information about device. */
+ std::string path_str = path;
+ bool is_media = path_str.find("media") != std::string::npos;
+ bool is_video = path_str.find("video") != std::string::npos;
+
+ int media_fd = -1;
+ if (is_media)
+ media_fd = fd;
+
+ std::string driver;
+ if (is_video) {
+ struct v4l2_capability cap = {};
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ ioctl(fd, VIDIOC_QUERYCAP, &cap);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+
+ std::string path_media = get_path_media(reinterpret_cast<const char *>(cap.driver));
+
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ media_fd = open(path_media.c_str(), O_RDONLY);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ }
+
+ struct media_device_info info = {};
+ ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &info);
+
+ json_object_object_add(open_obj, "driver", json_object_new_string(info.driver));
+ json_object_object_add(open_obj, "bus_info", json_object_new_string(info.bus_info));
+
+ if (is_video) {
+ std::list<std::string> linked_entities = get_linked_entities(media_fd, path_str);
+ json_object *linked_entities_obj = json_object_new_array();
+ for (auto &name : linked_entities)
+ json_object_array_add(linked_entities_obj, json_object_new_string(name.c_str()));
+ json_object_object_add(open_obj, "linked_entities", linked_entities_obj);
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ close(media_fd);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ }
+
+ write_json_object_to_json_file(open_obj);
+ json_object_put(open_obj);
+}
+
+void trace_mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off, unsigned long buf_address, bool is_mmap64)
+{
+ json_object *mmap_obj = json_object_new_object();
+
+ if (errno)
+ json_object_object_add(mmap_obj, "errno", json_object_new_string(strerrorname_np(errno)));
+
+ json_object *mmap_args = json_object_new_object();
+ json_object_object_add(mmap_args, "addr", json_object_new_int64((int64_t)addr));
+ json_object_object_add(mmap_args, "len", json_object_new_uint64(len));
+ json_object_object_add(mmap_args, "prot", json_object_new_int(prot));
+ json_object_object_add(mmap_args, "flags", json_object_new_string(number2s(flags).c_str()));
+ json_object_object_add(mmap_args, "fildes", json_object_new_int(fildes));
+ json_object_object_add(mmap_args, "off", json_object_new_int64(off));
+
+ if (is_mmap64)
+ json_object_object_add(mmap_obj, "mmap64", mmap_args);
+ else
+ json_object_object_add(mmap_obj, "mmap", mmap_args);
+
+ json_object_object_add(mmap_obj, "buffer_address", json_object_new_uint64(buf_address));
+
+ write_json_object_to_json_file(mmap_obj);
+ json_object_put(mmap_obj);
+}
+
+json_object *trace_buffer(unsigned char *buffer_pointer, __u32 bytesused)
+{
+ const int BUF_SIZE = 5;
+ const int MAX_BYTES_PER_LINE = 32;
+ char buf[BUF_SIZE];
+ std::string str;
+ int byte_count_per_line = 0;
+ json_object *mem_array_obj = json_object_new_array();
+
+ for (__u32 i = 0; i < bytesused; i++) {
+ memset(buf, 0, BUF_SIZE);
+ /* Each byte e.g. D9 will write a string of two characters "D9". */
+ sprintf(buf, "%02x", buffer_pointer[i]);
+ str += buf;
+ byte_count_per_line++;
+
+ /* Add a newline every 32 bytes. */
+ if (byte_count_per_line == MAX_BYTES_PER_LINE) {
+ byte_count_per_line = 0;
+ json_object_array_add(mem_array_obj, json_object_new_string(str.c_str()));
+ str.clear();
+ } else if (getenv("V4L2_TRACER_OPTION_COMPACT_PRINT") == nullptr) {
+ /* Add a space every byte e.g. "01 2A 40 01" */
+ str += " ";
+ }
+ }
+
+ /* Trace the last line if it was less than a full line. */
+ if (byte_count_per_line)
+ json_object_array_add(mem_array_obj, json_object_new_string(str.c_str()));
+
+ return mem_array_obj;
+}
+
+void trace_mem(int fd, __u32 offset, __u32 type, int index, __u32 bytesused, unsigned long start)
+{
+ json_object *mem_obj = json_object_new_object();
+ json_object_object_add(mem_obj, "mem_dump",
+ json_object_new_string(val2s(type, v4l2_buf_type_val_def).c_str()));
+ json_object_object_add(mem_obj, "fd", json_object_new_int(fd));
+ json_object_object_add(mem_obj, "offset", json_object_new_uint64(offset));
+ json_object_object_add(mem_obj, "index", json_object_new_int(index));
+ json_object_object_add(mem_obj, "bytesused", json_object_new_uint64(bytesused));
+ json_object_object_add(mem_obj, "address", json_object_new_uint64(start));
+
+ if ((type == V4L2_BUF_TYPE_VIDEO_OUTPUT || type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ||
+ (getenv("V4L2_TRACER_OPTION_WRITE_DECODED_TO_JSON_FILE") != nullptr)) {
+ json_object *mem_array_obj = trace_buffer((unsigned char*) start, bytesused);
+ json_object_object_add(mem_obj, "mem_array", mem_array_obj);
+ }
+
+ write_json_object_to_json_file(mem_obj);
+
+ json_object_put(mem_obj);
+}
+
+void trace_mem_encoded(int fd, __u32 offset)
+{
+ unsigned long start = get_buffer_address_trace(fd, offset);
+ if (start == 0U)
+ return;
+
+ __u32 bytesused = get_buffer_bytesused_trace(fd, offset);
+ __u32 type = get_buffer_type_trace(fd, offset);
+ int index = get_buffer_index_trace(fd, offset);
+ trace_mem(fd, offset, type, index, bytesused, start);
+}
+
+void trace_mem_decoded(void)
+{
+ int displayed_count = 0;
+ unsigned expected_length = get_expected_length_trace();
+
+ while (!ctx_trace.decode_order.empty()) {
+ std::list<buffer_trace>::iterator it;
+ long next_frame_to_be_displayed = *std::min_element(ctx_trace.decode_order.begin(),
+ ctx_trace.decode_order.end());
+ for (it = ctx_trace.buffers.begin(); it != ctx_trace.buffers.end(); ++it) {
+ if (it->display_order != next_frame_to_be_displayed)
+ continue;
+ if (!it->address)
+ break;
+ /*
+ * If bytesused exceeds the expected length of the decoded video data,
+ * then assume that this is extraneous padding or info added by the driver
+ * and do not trace it.
+ */
+ if (it->bytesused < expected_length)
+ break;
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "displaying: %ld, %s, index: %d\n",
+ it->display_order, buftype2s(it->type).c_str(), it->index);
+ }
+ displayed_count++;
+
+ if (getenv("V4L2_TRACER_OPTION_WRITE_DECODED_TO_YUV_FILE") != nullptr) {
+ std::string filename;
+ if (getenv("TRACE_ID") != nullptr)
+ filename = getenv("TRACE_ID");
+ filename += ".yuv";
+ FILE *fp = fopen(filename.c_str(), "a");
+ unsigned char *buffer_pointer = (unsigned char*) it->address;
+ for (__u32 i = 0; i < expected_length; i++)
+ fwrite(&buffer_pointer[i], sizeof(unsigned char), 1, fp);
+ fclose(fp);
+ }
+ trace_mem(it->fd, it->offset, it->type, it->index, it->bytesused, it->address);
+ ctx_trace.decode_order.remove(next_frame_to_be_displayed);
+ it->display_order = -1;
+ break;
+ }
+ if (!it->address || it == ctx_trace.buffers.end() || it->bytesused < expected_length)
+ break;
+ }
+ ctx_trace.compressed_frame_count = ctx_trace.compressed_frame_count - displayed_count;
+}
+
+json_object *trace_v4l2_plane(struct v4l2_plane *ptr, __u32 memory)
+{
+ json_object *plane_obj = json_object_new_object();
+
+ json_object_object_add(plane_obj, "bytesused", json_object_new_int64(ptr->bytesused));
+ json_object_object_add(plane_obj, "length", json_object_new_int64(ptr->length));
+
+ json_object *m_obj = json_object_new_object();
+
+ if (memory == V4L2_MEMORY_MMAP)
+ json_object_object_add(m_obj, "mem_offset", json_object_new_int64(ptr->m.mem_offset));
+ json_object_object_add(plane_obj, "m", m_obj);
+
+ json_object_object_add(plane_obj, "data_offset", json_object_new_int64(ptr->data_offset));
+
+ return plane_obj;
+}
+
+void trace_v4l2_buffer(void *arg, json_object *ioctl_args)
+{
+ json_object *buf_obj = json_object_new_object();
+ struct v4l2_buffer *buf = static_cast<struct v4l2_buffer*>(arg);
+
+ json_object_object_add(buf_obj, "index", json_object_new_uint64(buf->index));
+ json_object_object_add(buf_obj, "type",
+ json_object_new_string(val2s(buf->type, v4l2_buf_type_val_def).c_str()));
+ json_object_object_add(buf_obj, "bytesused", json_object_new_uint64(buf->bytesused));
+ json_object_object_add(buf_obj, "flags", json_object_new_string(fl2s_buffer(buf->flags).c_str()));
+ json_object_object_add(buf_obj, "field",
+ json_object_new_string(val2s(buf->field, v4l2_field_val_def).c_str()));
+ json_object *timestamp_obj = json_object_new_object();
+ json_object_object_add(timestamp_obj, "tv_sec", json_object_new_int64(buf->timestamp.tv_sec));
+ json_object_object_add(timestamp_obj, "tv_usec",
+ json_object_new_int64(buf->timestamp.tv_usec));
+ json_object_object_add(buf_obj, "timestamp", timestamp_obj);
+ json_object_object_add(buf_obj, "timestamp_ns",
+ json_object_new_uint64(v4l2_timeval_to_ns(&buf->timestamp)));
+
+ json_object_object_add(buf_obj, "sequence", json_object_new_uint64(buf->sequence));
+ json_object_object_add(buf_obj, "memory",
+ json_object_new_string(val2s(buf->memory, v4l2_memory_val_def).c_str()));
+
+ json_object *m_obj = json_object_new_object();
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+ json_object *planes_obj = json_object_new_array();
+ /* TODO add planes > 0 */
+ json_object_array_add(planes_obj, trace_v4l2_plane(buf->m.planes, buf->memory));
+ json_object_object_add(m_obj, "planes", planes_obj);
+ }
+
+ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+ buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
+ if (buf->memory == V4L2_MEMORY_MMAP)
+ json_object_object_add(m_obj, "offset", json_object_new_uint64(buf->m.offset));
+ }
+ json_object_object_add(buf_obj, "m", m_obj);
+ json_object_object_add(buf_obj, "length", json_object_new_uint64(buf->length));
+
+ if (buf->flags & V4L2_BUF_FLAG_REQUEST_FD)
+ json_object_object_add(buf_obj, "request_fd", json_object_new_int(buf->request_fd));
+
+ json_object_object_add(ioctl_args, "v4l2_buffer", buf_obj);
+}
+
+void trace_vidioc_stream(void *arg, json_object *ioctl_args)
+{
+ v4l2_buf_type buf_type = *(static_cast<v4l2_buf_type*>(arg));
+ json_object_object_add(ioctl_args, "type",
+ json_object_new_string(val2s(buf_type, v4l2_buf_type_val_def).c_str()));
+}
+
+void trace_v4l2_streamparm(void *arg, json_object *ioctl_args)
+{
+ json_object *v4l2_streamparm_obj = json_object_new_object();
+ struct v4l2_streamparm *streamparm = static_cast<struct v4l2_streamparm*>(arg);
+
+ json_object_object_add(v4l2_streamparm_obj, "type",
+ json_object_new_string(val2s(streamparm->type, v4l2_buf_type_val_def).c_str()));
+
+ if ((streamparm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+ (streamparm->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
+ trace_v4l2_captureparm_gen(&streamparm->parm, v4l2_streamparm_obj);
+
+ if ((streamparm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) ||
+ (streamparm->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
+ trace_v4l2_outputparm_gen(&streamparm->parm, v4l2_streamparm_obj);
+
+ json_object_object_add(ioctl_args, "v4l2_streamparm", v4l2_streamparm_obj);
+}
+
+void trace_v4l2_ext_control(void *arg, json_object *parent_obj, std::string key_name = "")
+{
+ json_object *v4l2_ext_control_obj = json_object_new_object();
+ struct v4l2_ext_control *p = static_cast<struct v4l2_ext_control*>(arg);
+
+ json_object_object_add(v4l2_ext_control_obj, "id",
+ json_object_new_string(val2s(p->id, control_val_def).c_str()));
+ json_object_object_add(v4l2_ext_control_obj, "size", json_object_new_uint64(p->size));
+
+ /* trace controls of type V4L2_CTRL_TYPE_MENU */
+ switch (p->id) {
+ case V4L2_CID_STATELESS_H264_DECODE_MODE: {
+ json_object_object_add(v4l2_ext_control_obj, "value",
+ json_object_new_string(val2s(p->value, v4l2_stateless_h264_decode_mode_val_def).c_str()));
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+ return;
+ }
+ case V4L2_CID_STATELESS_H264_START_CODE: {
+ json_object_object_add(v4l2_ext_control_obj, "value",
+ json_object_new_string(val2s(p->value, v4l2_stateless_h264_start_code_val_def).c_str()));
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+ return;
+ }
+ case V4L2_CID_STATELESS_HEVC_DECODE_MODE: {
+ json_object_object_add(v4l2_ext_control_obj, "value",
+ json_object_new_string(val2s(p->value, v4l2_stateless_hevc_decode_mode_val_def).c_str()));
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+ return;
+ }
+ case V4L2_CID_STATELESS_HEVC_START_CODE: {
+ json_object_object_add(v4l2_ext_control_obj, "value",
+ json_object_new_string(val2s(p->value, v4l2_stateless_hevc_start_code_val_def).c_str()));
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+ return;
+ }
+ default:
+ break;
+ }
+
+ if (p->ptr == nullptr) {
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+ return;
+ }
+
+ switch (p->id) {
+ case V4L2_CID_STATELESS_VP8_FRAME:
+ trace_v4l2_ctrl_vp8_frame_gen(p->p_vp8_frame, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SPS:
+ trace_v4l2_ctrl_h264_sps_gen(p->p_h264_sps, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_PPS:
+ trace_v4l2_ctrl_h264_pps_gen(p->p_h264_pps, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SCALING_MATRIX:
+ trace_v4l2_ctrl_h264_scaling_matrix_gen(p->p_h264_scaling_matrix, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_PRED_WEIGHTS:
+ trace_v4l2_ctrl_h264_pred_weights_gen(p->p_h264_pred_weights, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_SLICE_PARAMS:
+ trace_v4l2_ctrl_h264_slice_params_gen(p->p_h264_slice_params, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_H264_DECODE_PARAMS:
+ trace_v4l2_ctrl_h264_decode_params_gen(p->p_h264_decode_params, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_FWHT_PARAMS:
+ trace_v4l2_ctrl_fwht_params_gen(p->p_fwht_params, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_VP9_FRAME:
+ trace_v4l2_ctrl_vp9_frame_gen(p->p_vp9_frame, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_VP9_COMPRESSED_HDR:
+ trace_v4l2_ctrl_vp9_compressed_hdr_gen(p->p_vp9_compressed_hdr_probs, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SPS:
+ trace_v4l2_ctrl_hevc_sps_gen(p->p_hevc_sps, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_PPS:
+ trace_v4l2_ctrl_hevc_pps_gen(p->p_hevc_pps, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SLICE_PARAMS:
+ trace_v4l2_ctrl_hevc_slice_params_gen(p->p_hevc_slice_params, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_SCALING_MATRIX:
+ trace_v4l2_ctrl_hevc_scaling_matrix_gen(p->p_hevc_scaling_matrix, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_DECODE_PARAMS:
+ trace_v4l2_ctrl_hevc_decode_params_gen(p->p_hevc_decode_params, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_HEVC_ENTRY_POINT_OFFSETS: {
+ /* V4L2_CTRL_TYPE_U32, V4L2_CTRL_FLAG_DYNAMIC_ARRAY */
+ __u32 elems = ctx_trace.elems;
+ json_object_object_add(v4l2_ext_control_obj, "elems", json_object_new_int64(elems));
+ json_object *hevc_entry_point_offsets_obj = json_object_new_array();
+ for (__u32 i = 0; i < elems; i++)
+ json_object_array_add(hevc_entry_point_offsets_obj, json_object_new_int64(p->p_u32[i]));
+ json_object_object_add(v4l2_ext_control_obj, "p_u32", hevc_entry_point_offsets_obj);
+ break;
+ }
+ case V4L2_CID_STATELESS_MPEG2_SEQUENCE:
+ trace_v4l2_ctrl_mpeg2_sequence_gen(p->p_mpeg2_sequence, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_MPEG2_PICTURE:
+ trace_v4l2_ctrl_mpeg2_picture_gen(p->p_mpeg2_picture, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_STATELESS_MPEG2_QUANTISATION:
+ trace_v4l2_ctrl_mpeg2_quantisation_gen(p->p_mpeg2_quantisation, v4l2_ext_control_obj);
+ break;
+ case V4L2_CID_MPEG_VIDEO_DEC_PTS:
+ case V4L2_CID_MPEG_VIDEO_DEC_FRAME:
+ case V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR:
+ case V4L2_CID_PIXEL_RATE:
+ json_object_object_add(v4l2_ext_control_obj, "value64", json_object_new_int64(p->value64));
+ break;
+ default:
+ if (p->size) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "warning: cannot trace control: %s\n", val2s(p->id, control_val_def).c_str());
+ } else {
+ json_object_object_add(v4l2_ext_control_obj, "value", json_object_new_int(p->value));
+ }
+ break;
+ }
+
+ json_object_array_add(parent_obj, v4l2_ext_control_obj);
+}
+
+void trace_v4l2_ext_controls(void *arg, json_object *ioctl_args)
+{
+ json_object *ext_controls_obj = json_object_new_object();
+ struct v4l2_ext_controls *ext_controls = static_cast<struct v4l2_ext_controls*>(arg);
+
+ json_object_object_add(ext_controls_obj, "which",
+ json_object_new_string(val2s(ext_controls->which, which_val_def).c_str()));
+
+ json_object_object_add(ext_controls_obj, "count", json_object_new_int64(ext_controls->count));
+
+ /* error_idx is defined only if the ioctl returned an error */
+ if (errno)
+ json_object_object_add(ext_controls_obj, "error_idx",
+ json_object_new_uint64(ext_controls->error_idx));
+
+ /* request_fd is only valid when "which" == V4L2_CTRL_WHICH_REQUEST_VAL */
+ if (ext_controls->which == V4L2_CTRL_WHICH_REQUEST_VAL)
+ json_object_object_add(ext_controls_obj, "request_fd",
+ json_object_new_int(ext_controls->request_fd));
+
+ json_object *controls_obj = json_object_new_array();
+ for (__u32 i = 0; i < ext_controls->count; i++) {
+ if ((void *) ext_controls->controls == nullptr)
+ break;
+ trace_v4l2_ext_control((void *) &ext_controls->controls[i], controls_obj);
+ }
+ json_object_object_add(ext_controls_obj, "controls", controls_obj);
+
+ json_object_object_add(ioctl_args, "v4l2_ext_controls", ext_controls_obj);
+}
+
+void trace_v4l2_decoder_cmd(void *arg, json_object *ioctl_args)
+{
+ json_object *v4l2_decoder_cmd_obj = json_object_new_object();
+ struct v4l2_decoder_cmd *ptr = static_cast<struct v4l2_decoder_cmd*>(arg);
+
+ json_object_object_add(v4l2_decoder_cmd_obj, "cmd",
+ json_object_new_string(val2s(ptr->cmd, decoder_cmd_val_def).c_str()));
+
+ std::string flags;
+
+ switch (ptr->cmd) {
+ case V4L2_DEC_CMD_START: {
+ /* This command has one flag: V4L2_DEC_CMD_START_MUTE_AUDIO. */
+ if (ptr->flags == V4L2_DEC_CMD_START_MUTE_AUDIO)
+ flags = "V4L2_DEC_CMD_START_MUTE_AUDIO";
+
+ /* struct start */
+ json_object *start_obj = json_object_new_object();
+ json_object_object_add(start_obj, "speed", json_object_new_int(ptr->start.speed));
+
+ std::string format;
+ /* possible values V4L2_DEC_START_FMT_NONE, V4L2_DEC_START_FMT_GOP */
+ if (ptr->start.format == V4L2_DEC_START_FMT_GOP)
+ format = "V4L2_DEC_START_FMT_GOP";
+ else if (ptr->start.format == V4L2_DEC_START_FMT_NONE)
+ format = "V4L2_DEC_START_FMT_NONE";
+ json_object_object_add(start_obj, "format", json_object_new_string(format.c_str()));
+
+ json_object_object_add(v4l2_decoder_cmd_obj, "start", start_obj);
+ break;
+ }
+ case V4L2_DEC_CMD_STOP: {
+ /* This command has two flags */
+ if (ptr->flags == V4L2_DEC_CMD_STOP_TO_BLACK)
+ flags = "V4L2_DEC_CMD_STOP_TO_BLACK";
+ else if (ptr->flags == V4L2_DEC_CMD_STOP_IMMEDIATELY)
+ flags = "V4L2_DEC_CMD_STOP_IMMEDIATELY";
+
+ json_object *stop_obj = json_object_new_object();
+ json_object_object_add(stop_obj, "pts", json_object_new_uint64(ptr->stop.pts));
+
+ json_object_object_add(v4l2_decoder_cmd_obj, "stop", stop_obj);
+ break;
+ }
+
+ case V4L2_DEC_CMD_PAUSE: {
+ if (ptr->flags == V4L2_DEC_CMD_PAUSE_TO_BLACK)
+ flags = "V4L2_DEC_CMD_PAUSE_TO_BLACK";
+ break;
+ }
+ case V4L2_DEC_CMD_RESUME:
+ case V4L2_DEC_CMD_FLUSH:
+ default:
+ break;
+ }
+ json_object_object_add(v4l2_decoder_cmd_obj, "flags", json_object_new_string(flags.c_str()));
+
+ json_object_object_add(ioctl_args, "v4l2_decoder_cmd", v4l2_decoder_cmd_obj);
+}
+
+json_object *trace_ioctl_args(unsigned long cmd, void *arg)
+{
+ json_object *ioctl_args = json_object_new_object();
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ trace_v4l2_capability_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_ENUM_FMT:
+ trace_v4l2_fmtdesc_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_G_FMT:
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ trace_v4l2_format_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_REQBUFS:
+ trace_v4l2_requestbuffers_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_PREPARE_BUF:
+ case VIDIOC_QUERYBUF:
+ case VIDIOC_QBUF:
+ case VIDIOC_DQBUF:
+ trace_v4l2_buffer(arg, ioctl_args);
+ break;
+ case VIDIOC_EXPBUF:
+ trace_v4l2_exportbuffer_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_STREAMON:
+ case VIDIOC_STREAMOFF:
+ trace_vidioc_stream(arg, ioctl_args);
+ break;
+ case VIDIOC_G_PARM:
+ case VIDIOC_S_PARM:
+ trace_v4l2_streamparm(arg, ioctl_args);
+ break;
+ case VIDIOC_G_CTRL:
+ case VIDIOC_S_CTRL:
+ trace_v4l2_control_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_QUERYCTRL:
+ trace_v4l2_queryctrl_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP:
+ trace_v4l2_crop_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_G_EXT_CTRLS:
+ case VIDIOC_TRY_EXT_CTRLS:
+ case VIDIOC_S_EXT_CTRLS:
+ trace_v4l2_ext_controls(arg, ioctl_args);
+ break;
+ case VIDIOC_TRY_ENCODER_CMD:
+ case VIDIOC_ENCODER_CMD:
+ trace_v4l2_encoder_cmd_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_CREATE_BUFS:
+ trace_v4l2_create_buffers_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_G_SELECTION:
+ case VIDIOC_S_SELECTION:
+ trace_v4l2_selection_gen(arg, ioctl_args);
+ break;
+ case VIDIOC_TRY_DECODER_CMD:
+ case VIDIOC_DECODER_CMD:
+ trace_v4l2_decoder_cmd(arg, ioctl_args);
+ break;
+ case VIDIOC_QUERY_EXT_CTRL:
+ trace_v4l2_query_ext_ctrl_gen(arg, ioctl_args);
+ break;
+ case MEDIA_IOC_REQUEST_ALLOC: {
+ __s32 *request_fd = static_cast<__s32*>(arg);
+ json_object_object_add(ioctl_args, "request_fd", json_object_new_int(*request_fd));
+ break;
+ }
+ default:
+ break;
+ }
+
+ return ioctl_args;
+}
diff --git a/utils/v4l2-tracer/trace.h b/utils/v4l2-tracer/trace.h
new file mode 100644
index 00000000..1e8b17e7
--- /dev/null
+++ b/utils/v4l2-tracer/trace.h
@@ -0,0 +1,75 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#include "v4l2-tracer-common.h"
+#include "trace-gen.h"
+
+struct buffer_trace {
+ int fd;
+ __u32 type;
+ __u32 index;
+ __u32 offset;
+ __u32 bytesused;
+ long display_order;
+ unsigned long address;
+};
+
+struct h264_info {
+ int pic_order_cnt_lsb;
+ int max_pic_order_cnt_lsb;
+};
+
+struct trace_context {
+ __u32 elems;
+ __u32 width;
+ __u32 height;
+ FILE *trace_file;
+ __u32 pixelformat;
+ std::string media_device;
+ __u32 compression_format;
+ union {
+ struct h264_info h264;
+ } fmt;
+ std::string trace_filename;
+ int compressed_frame_count;
+ std::list<long> decode_order;
+ std::list<struct buffer_trace> buffers;
+ std::unordered_map<int, std::string> devices; /* key:fd, value: path of the device */
+};
+
+void trace_open(int fd, const char *path, int oflag, mode_t mode, bool is_open64);
+void trace_mmap(void *addr, size_t len, int prot, int flags, int fildes, off_t off, unsigned long buf_address, bool is_mmap64);
+void trace_mem(int fd, __u32 offset, __u32 type, int index, __u32 bytesused, unsigned long start);
+void trace_mem_encoded(int fd, __u32 offset);
+void trace_mem_decoded(void);
+json_object *trace_ioctl_args(unsigned long cmd, void *arg);
+
+bool is_video_or_media_device(const char *path);
+void add_device(int fd, std::string path);
+std::string get_device(int fd);
+void print_devices(void);
+bool buffer_in_trace_context(int fd, __u32 offset = 0);
+__u32 get_buffer_type_trace(int fd, __u32 offset = 0);
+int get_buffer_index_trace(int fd, __u32 offset);
+long get_buffer_bytesused_trace(int fd, __u32 offset);
+void set_buffer_address_trace(int fd, __u32 offset, unsigned long address);
+unsigned long get_buffer_address_trace(int fd, __u32 offset);
+bool buffer_is_mapped(unsigned long buffer_address);
+unsigned get_expected_length_trace(void);
+void s_ext_ctrls_setup(struct v4l2_ext_controls *ext_controls);
+void qbuf_setup(struct v4l2_buffer *buf);
+void streamoff_cleanup(v4l2_buf_type buf_type);
+void g_fmt_setup_trace(struct v4l2_format *format);
+void s_fmt_setup(struct v4l2_format *format);
+void expbuf_setup(struct v4l2_exportbuffer *export_buffer);
+void querybuf_setup(int fd, struct v4l2_buffer *buf);
+void query_ext_ctrl_setup(int fd, struct v4l2_query_ext_ctrl *ptr);
+void write_json_object_to_json_file(json_object *jobj);
+void close_json_file(void);
+
+#endif
diff --git a/utils/v4l2-tracer/v4l2-tracer-common.cpp b/utils/v4l2-tracer/v4l2-tracer-common.cpp
new file mode 100644
index 00000000..869347a1
--- /dev/null
+++ b/utils/v4l2-tracer/v4l2-tracer-common.cpp
@@ -0,0 +1,476 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "v4l2-tracer-common.h"
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+bool is_debug(void)
+{
+ return (getenv("V4L2_TRACER_OPTION_DEBUG") != nullptr);
+}
+
+bool is_verbose(void)
+{
+ return (getenv("V4L2_TRACER_OPTION_VERBOSE") != nullptr);
+}
+
+void print_v4l2_tracer_info(void)
+{
+ fprintf(stderr, "v4l2-tracer %s%s\n", PACKAGE_VERSION, STRING(GIT_COMMIT_CNT));
+ if (strlen(STRING(GIT_SHA)) != 0U)
+ fprintf(stderr, "v4l2-tracer SHA: '%s' %s\n", STRING(GIT_SHA), STRING(GIT_COMMIT_DATE));
+}
+
+void print_usage(void)
+{
+ print_v4l2_tracer_info();
+ fprintf(stderr, "Usage:\n\tv4l2-tracer [options] trace <tracee>\n"
+ "\tv4l2-tracer [options] retrace <trace_file>.json\n"
+ "\tv4l2-tracer clean <trace_file>.json\n\n"
+
+ "\tCommon options:\n"
+ "\t\t-c, --compact Write minimal whitespace in JSON file.\n"
+ "\t\t-g, --debug Turn on verbose reporting plus additional debug info.\n"
+ "\t\t-h, --help Display this message.\n"
+ "\t\t-r --raw Write decoded video frame data to JSON file.\n"
+ "\t\t-v, --verbose Turn on verbose reporting.\n"
+ "\t\t-y, --yuv Write decoded video frame data to yuv file.\n\n"
+
+ "\tRetrace options:\n"
+ "\t\t-d, --video_device <dev> Retrace with a specific video device.\n"
+ "\t\t <dev> must be a digit corresponding to\n"
+ "\t\t /dev/video<dev> \n\n"
+ "\t\t-m, --media_device <dev> Retrace with a specific media device.\n"
+ "\t\t <dev> must be a digit corresponding to\n"
+ "\t\t /dev/media<dev> \n\n");
+}
+
+void add_separator(std::string &str)
+{
+ if (!str.empty())
+ str += "|";
+}
+
+void clean_string(size_t idx, std::string substring_to_erase, std::string &str)
+{
+ std::string temp = substring_to_erase + '|';
+ if (str.find(temp) != std::string::npos)
+ str.erase(idx, temp.length());
+ else
+ str.erase(idx, substring_to_erase.length());
+}
+
+std::string ver2s(unsigned int version)
+{
+ const int mask = 0xff;
+ const int BUF_SIZE = 16;
+ char buf[BUF_SIZE];
+ sprintf(buf, "%d.%d.%d", version >> BUF_SIZE, (version >> (BUF_SIZE / 2)) & mask, version & mask);
+ return buf;
+}
+
+/* Convert a number to an octal string. If num is 0, return an empty string. */
+std::string number2s_oct(long num)
+{
+ const int min_width = 5;
+ std::stringstream stream;
+ stream << std::setfill ('0') << std::setw(min_width) << std::oct << num;
+ return stream.str();
+}
+
+/* Convert a number to a hex string. If num is 0, return an empty string. */
+std::string number2s(long num)
+{
+ if (num == 0)
+ return "";
+ std::stringstream stream;
+ stream << std::hex << num;
+ return "0x" + stream.str();
+}
+
+std::string val2s(long val, const val_def *def)
+{
+ if (def == nullptr)
+ return number2s(val);
+
+ while ((def->val != -1) && (def->val != val))
+ def++;
+
+ if (def->val == val)
+ return def->str;
+
+ return number2s(val);
+}
+
+std::string fl2s(unsigned val, const flag_def *def)
+{
+ std::string str;
+
+ if (def == nullptr)
+ return number2s(val);
+
+ while ((def->flag) != 0U) {
+ if ((val & def->flag) != 0U) {
+ add_separator(str);
+ str += def->str;
+ val &= ~def->flag;
+ }
+ def++;
+ }
+ if (val != 0U) {
+ add_separator(str);
+ str += number2s(val);
+ }
+
+ return str;
+}
+
+std::string fl2s_buffer(__u32 flags)
+{
+ std::string str;
+
+ switch (flags & V4L2_BUF_FLAG_TIMESTAMP_MASK) {
+ case V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN:
+ str += "V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN";
+ flags &= ~V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN;
+ break;
+ case V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC:
+ str += "V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC";
+ flags &= ~V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ break;
+ case V4L2_BUF_FLAG_TIMESTAMP_COPY:
+ str += "V4L2_BUF_FLAG_TIMESTAMP_COPY";
+ flags &= ~V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ break;
+ default:
+ break;
+ }
+
+ /* Since V4L2_BUF_FLAG_TSTAMP_SRC_EOF == 0, at least this flag will always be added. */
+ add_separator(str);
+ switch (flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK) {
+ case V4L2_BUF_FLAG_TSTAMP_SRC_EOF:
+ str += "V4L2_BUF_FLAG_TSTAMP_SRC_EOF";
+ flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+ break;
+ case V4L2_BUF_FLAG_TSTAMP_SRC_SOE:
+ str += "V4L2_BUF_FLAG_TSTAMP_SRC_SOE";
+ flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
+ break;
+ default:
+ break;
+ }
+
+ if (flags != 0U) {
+ add_separator(str);
+ const unsigned ts_mask = V4L2_BUF_FLAG_TIMESTAMP_MASK | V4L2_BUF_FLAG_TSTAMP_SRC_MASK;
+ str += fl2s(flags & ~ts_mask, v4l2_buf_flag_def);
+ }
+
+ return str;
+}
+
+std::string fl2s_fwht(__u32 flags)
+{
+ std::string str;
+ switch (flags & V4L2_FWHT_FL_PIXENC_MSK) {
+ case V4L2_FWHT_FL_PIXENC_YUV:
+ str += "V4L2_FWHT_FL_PIXENC_YUV";
+ flags &= ~V4L2_FWHT_FL_PIXENC_YUV;
+ break;
+ case V4L2_FWHT_FL_PIXENC_RGB:
+ str += "V4L2_FWHT_FL_PIXENC_RGB";
+ flags &= ~V4L2_FWHT_FL_PIXENC_RGB;
+ break;
+ case V4L2_FWHT_FL_PIXENC_HSV:
+ str += "V4L2_FWHT_FL_PIXENC_HSV";
+ flags &= ~V4L2_FWHT_FL_PIXENC_HSV;
+ break;
+ default:
+ break;
+ }
+ add_separator(str);
+ str += fl2s(flags, v4l2_ctrl_fwht_params_flag_def);
+ return str;
+}
+
+long s2number(const char *char_str)
+{
+ if (char_str == nullptr)
+ return 0;
+
+ std::string str = char_str;
+
+ long num = 0;
+ if (str.empty())
+ return 0;
+ try {
+ num = std::strtol(str.c_str(), nullptr, 0); /* base is auto-detected */
+ } catch (std::invalid_argument& ia) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "string \'%s\' is invalid\n", str.c_str());
+ } catch (std::out_of_range& oor) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "string \'%s\' is out of range\n", str.c_str());
+ }
+ return num;
+}
+
+long s2val(const char *char_str, const val_def *def)
+{
+ if (char_str == nullptr)
+ return 0;
+ std::string str = char_str;
+
+ if (str.empty())
+ return 0;
+
+ if (def == nullptr)
+ return s2number(char_str);
+
+ while ((def->val != -1) && (def->str != str))
+ def++;
+
+ if (def->str == str)
+ return def->val;
+
+ return s2number(char_str);
+}
+
+unsigned long s2flags(const char *char_str, const flag_def *def)
+{
+ if (char_str == nullptr)
+ return 0;
+ std::string str = char_str;
+
+ size_t idx = 0;
+ unsigned long flags = 0;
+
+ if (def == nullptr)
+ return s2number(char_str);
+
+ while ((def->flag) != 0U) {
+ idx = str.find(def->str);
+ if (idx == std::string::npos) {
+ def++;
+ continue;
+ }
+ /* Stop false substring matches e.g. in V4L2_BUF_CAP_SUPPORTS_MMAP_CACHE_HINTS */
+ std::string check = def->str;
+ if (check.length() != str.length()) {
+ idx = str.find(check + '|');
+ if (idx == std::string::npos) {
+ def++;
+ continue;
+ }
+ }
+ flags += def->flag;
+ clean_string(idx, def->str, str);
+ def++;
+ }
+ if (!str.empty())
+ flags += s2number(str.c_str());
+
+ return flags;
+}
+
+unsigned long s2flags_buffer(const char *char_str)
+{
+ if (char_str == nullptr)
+ return 0;
+ std::string str = char_str;
+
+ size_t idx = 0;
+ unsigned long flags = 0;
+
+ idx = str.find("V4L2_BUF_FLAG_TIMESTAMP_COPY");
+ if (idx != std::string::npos) {
+ flags += V4L2_BUF_FLAG_TIMESTAMP_COPY;
+ clean_string(idx, "V4L2_BUF_FLAG_TIMESTAMP_COPY", str);
+ }
+ idx = str.find("V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC");
+ if (idx != std::string::npos) {
+ flags += V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
+ clean_string(idx, "V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC", str);
+ }
+ idx = str.find("V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN");
+ if (idx != std::string::npos) {
+ flags += V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN;
+ clean_string(idx, "V4L2_BUF_FLAG_TIMESTAMP_UNKNOWN", str);
+ }
+ idx = str.find("V4L2_BUF_FLAG_TSTAMP_SRC_SOE");
+ if (idx != std::string::npos) {
+ flags += V4L2_BUF_FLAG_TSTAMP_SRC_SOE;
+ clean_string(idx, "V4L2_BUF_FLAG_TSTAMP_SRC_SOE", str);
+ }
+ idx = str.find("V4L2_BUF_FLAG_TSTAMP_SRC_EOF");
+ if (idx != std::string::npos) {
+ flags += V4L2_BUF_FLAG_TSTAMP_SRC_EOF;
+ clean_string(idx, "V4L2_BUF_FLAG_TSTAMP_SRC_EOF", str);
+ }
+ if (!str.empty())
+ flags += s2flags(str.c_str(), v4l2_buf_flag_def);
+ return flags;
+}
+
+unsigned long s2flags_fwht(const char *char_str)
+{
+ if (char_str == nullptr)
+ return 0;
+ std::string str = char_str;
+
+ size_t idx = 0;
+ unsigned long flags = 0;
+ idx = str.find("V4L2_FWHT_FL_PIXENC_YUV");
+ if (idx != std::string::npos) {
+ flags += V4L2_FWHT_FL_PIXENC_YUV;
+ clean_string(idx, "V4L2_FWHT_FL_PIXENC_YUV", str);
+ }
+ idx = str.find("V4L2_FWHT_FL_PIXENC_RGB");
+ if (idx != std::string::npos) {
+ flags += V4L2_FWHT_FL_PIXENC_RGB;
+ clean_string(idx, "V4L2_FWHT_FL_PIXENC_RGB", str);
+ }
+ idx = str.find("V4L2_FWHT_FL_PIXENC_HSV");
+ if (idx != std::string::npos) {
+ flags += V4L2_FWHT_FL_PIXENC_HSV;
+ clean_string(idx, "V4L2_FWHT_FL_PIXENC_HSV", str);
+ }
+ if (!str.empty())
+ flags += s2flags(str.c_str(), v4l2_ctrl_fwht_params_flag_def);
+ return flags;
+}
+
+std::string get_path_media(std::string driver)
+{
+ struct dirent *entry_pointer = nullptr;
+ std::string path_media;
+ DIR *directory_pointer = opendir("/dev");
+ if (directory_pointer == nullptr)
+ return path_media;
+
+ while ((entry_pointer = readdir(directory_pointer)) != nullptr) {
+ std::string media = "media";
+ const char *name = entry_pointer->d_name;
+ if ((memcmp(name, media.c_str(), media.length()) != 0) || (isdigit(name[media.length()]) == 0))
+ continue;
+
+ std::string media_devname = std::string("/dev/") + name;
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ int media_fd = open(media_devname.c_str(), O_RDONLY);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ if (media_fd < 0)
+ continue;
+
+ struct media_device_info info = {};
+ if (ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &info) || info.driver != driver) {
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ close(media_fd);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ continue;
+ }
+ path_media = media_devname;
+ setenv("V4L2_TRACER_PAUSE_TRACE", "true", 0);
+ close(media_fd);
+ unsetenv("V4L2_TRACER_PAUSE_TRACE");
+ }
+ closedir(directory_pointer);
+ return path_media;
+}
+
+std::string get_path_video(int media_fd, std::list<std::string> linked_entities)
+{
+ int err = 0;
+ std::string path_video;
+ struct media_v2_topology topology = {};
+
+ err = ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology);
+ if (err < 0)
+ return path_video;
+
+ std::vector<media_v2_interface> ifaces(topology.num_interfaces);
+ topology.ptr_interfaces = (uintptr_t) ifaces.data();
+
+ std::vector<media_v2_link> links(topology.num_links);
+ topology.ptr_links = (uintptr_t) links.data();
+
+ std::vector<media_v2_entity> ents(topology.num_entities);
+ topology.ptr_entities = (uintptr_t) ents.data();
+
+ err = ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology);
+ if (err < 0)
+ return path_video;
+
+ for (auto &name : linked_entities) {
+ /* Find an entity listed in the video device's linked_entities. */
+ for (__u32 i = 0; i < topology.num_entities; i++) {
+ if (ents[i].name != name)
+ continue;
+ /* Find the first link connected to that entity. */
+ for (__u32 j = 0; j < topology.num_links; j++) {
+ if (links[j].sink_id != ents[i].id)
+ continue;
+ /* Find the interface connected to that link. */
+ for (__u32 k = 0; k < topology.num_interfaces; k++) {
+ if (ifaces[k].id != links[j].source_id)
+ continue;
+ std::string video_devname = mi_media_get_device(ifaces[k].devnode.major,
+ ifaces[k].devnode.minor);
+ if (!video_devname.empty()) {
+ path_video = video_devname;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return path_video;
+}
+
+std::list<std::string> get_linked_entities(int media_fd, std::string path_video)
+{
+ int err = 0;
+ std::list<std::string> linked_entities;
+ struct media_v2_topology topology = {};
+
+ err = ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology);
+ if (err < 0)
+ return linked_entities;
+
+ std::vector<media_v2_interface> ifaces(topology.num_interfaces);
+ topology.ptr_interfaces = (uintptr_t) ifaces.data();
+
+ std::vector<media_v2_link> links(topology.num_links);
+ topology.ptr_links = (uintptr_t) links.data();
+
+ std::vector<media_v2_entity> ents(topology.num_entities);
+ topology.ptr_entities = (uintptr_t) ents.data();
+
+ err = ioctl(media_fd, MEDIA_IOC_G_TOPOLOGY, &topology);
+ if (err < 0)
+ return linked_entities;
+
+ /* find the interface corresponding to the path_video */
+ for (__u32 i = 0; i < topology.num_interfaces; i++) {
+ if (path_video != mi_media_get_device(ifaces[i].devnode.major, ifaces[i].devnode.minor))
+ continue;
+ /* find the links from that interface */
+ for (__u32 j = 0; j < topology.num_links; j++) {
+ if (links[j].source_id != ifaces[i].id)
+ continue;
+ /* find the entities connected by that link to the interface */
+ for (__u32 k = 0; k < topology.num_entities; k++) {
+ if (ents[k].id != links[j].sink_id)
+ continue;
+ linked_entities.push_back(ents[k].name);
+ }
+ }
+ if (linked_entities.size())
+ break;
+ }
+ return linked_entities;
+}
diff --git a/utils/v4l2-tracer/v4l2-tracer-common.h b/utils/v4l2-tracer/v4l2-tracer-common.h
new file mode 100644
index 00000000..362a7ba6
--- /dev/null
+++ b/utils/v4l2-tracer/v4l2-tracer-common.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#ifndef V4L2_TRACER_COMMON_H
+#define V4L2_TRACER_COMMON_H
+
+#include "v4l2-info.h"
+#include "codec-fwht.h"
+#include "config.h"
+#include "media-info.h"
+#include <algorithm>
+#include <dirent.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <json.h>
+#include <linux/media.h>
+#include <linux/videodev2.h>
+#include <list>
+#include <poll.h>
+#include <pthread.h>
+#include <stdexcept>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <unordered_map>
+#include <vector>
+
+#define STR(x) #x
+#define STRING(x) STR(x)
+
+struct val_def {
+ long val;
+ const char *str;
+};
+
+bool is_debug(void);
+bool is_verbose(void);
+void print_v4l2_tracer_info(void);
+void print_usage(void);
+std::string ver2s(unsigned int version);
+std::string number2s_oct(long num);
+std::string number2s(long num);
+std::string val2s(long val, const val_def *def);
+std::string fl2s(unsigned val, const flag_def *def);
+std::string fl2s_buffer(__u32 flags);
+std::string fl2s_fwht(__u32 flags);
+long s2number(const char *char_str);
+long s2val(const char *char_str, const val_def *def);
+unsigned long s2flags(const char *char_str, const flag_def *def);
+unsigned long s2flags_buffer(const char *char_str);
+unsigned long s2flags_fwht(const char *char_str);
+std::string which2s(unsigned long which);
+std::string get_path_media(std::string driver);
+std::string get_path_video(int media_fd, std::list<std::string> linked_entities);
+std::list<std::string> get_linked_entities(int media_fd, std::string path_video);
+
+constexpr val_def which_val_def[] = {
+ { V4L2_CTRL_WHICH_CUR_VAL, "V4L2_CTRL_WHICH_CUR_VAL" },
+ { V4L2_CTRL_WHICH_DEF_VAL, "V4L2_CTRL_WHICH_DEF_VAL" },
+ { V4L2_CTRL_WHICH_REQUEST_VAL, "V4L2_CTRL_WHICH_REQUEST_VAL" },
+ { -1, "" }
+};
+
+constexpr val_def open_val_def[] = {
+ { O_RDONLY, "O_RDONLY" },
+ { O_WRONLY, "O_WRONLY" },
+ { O_RDWR, "O_RDWR" },
+ { -1, "" }
+};
+
+#include "v4l2-tracer-info-gen.h"
+
+#endif
diff --git a/utils/v4l2-tracer/v4l2-tracer-gen.pl b/utils/v4l2-tracer/v4l2-tracer-gen.pl
new file mode 100755
index 00000000..fe648182
--- /dev/null
+++ b/utils/v4l2-tracer/v4l2-tracer-gen.pl
@@ -0,0 +1,879 @@
+#!/usr/bin/perl
+# SPDX-License-Identifier: GPL-2.0-only */
+# Copyright 2022 Collabora Ltd.
+
+sub convert_type_to_json_type {
+ my $type = shift;
+ if ($type eq __u8 || $type eq char || $type eq __u16 || $type eq __s8 || $type eq __s16 || $type eq __s32 || $type eq 'int') {
+ return "int";
+ }
+ if ($type eq __u32 || $type eq __le32 || $type eq __s64) {
+ return "int64";
+ }
+
+ # unsigned appears just twice in videodev2.h and in both cases it is 'unsigned long'
+ if ($type eq __u64 || $type eq 'v4l2_std_id' || $type eq 'unsigned') {
+ return "uint64";
+ }
+ if ($type eq struct || $type eq union || $type eq void) {
+ return;
+ }
+ print "v4l2_tracer: error: couldn't convert \'$type\' to json_object type.\n";
+ return;
+}
+
+sub get_index_letter {
+ my $index = shift;
+ if ($index eq 0) {return "i";}
+ if ($index eq 1) {return "j";}
+ if ($index eq 2) {return "k";}
+ if ($index eq 3) {return "l";}
+ if ($index eq 4) {return "m";}
+ if ($index eq 5) {return "n";}
+ if ($index eq 6) {return "o";} # "p" is saved for pointer
+ if ($index eq 8) {return "q";}
+ return "z";
+}
+
+$flag_func_name;
+
+sub flag_gen {
+ my $flag_type = shift;
+
+ if ($flag_type =~ /fwht/) {
+ $flag_func_name = v4l2_ctrl_fwht_params_;
+ } elsif ($flag_type =~ /vp8_loop_filter/) {
+ $flag_func_name = v4l2_vp8_loop_filter_;
+ } else {
+ ($flag_func_name) = ($_) =~ /#define (\w+_)FL.+/;
+ $flag_func_name = lc $flag_func_name;
+ }
+
+ printf $fh_common_info_h "constexpr flag_def %sflag_def[] = {\n", $flag_func_name;
+
+ ($flag) = ($_) =~ /#define\s+(\w+)\s+.+/;
+ printf $fh_common_info_h "\t{ $flag, \"$flag\" },\n"; # get the first flag
+
+ while (<>) {
+ next if ($_ =~ /^\/?\s?\*.*/); # skip comments between flags if any
+ next if $_ =~ /^\s*$/; # skip blank lines between flags if any
+ last if ((grep {!/^#define\s+\w+_FL/} $_) && (grep {!/^#define V4L2_VP8_LF/} $_));
+ ($flag) = ($_) =~ /#\s*define\s+(\w+)\s+.+/;
+
+ # don't include flags that are masks
+ next if ($flag_func_name eq v4l2_buf_) && ($flag =~ /.*TIMESTAMP.*/ || $flag =~ /.*TSTAMP.*/);
+ next if ($flag_func_name eq v4l2_ctrl_fwht_params_) && ($flag =~ /.*COMPONENTS.*/ || $flag =~ /.*PIXENC.*/);
+ next if ($flag =~ /.*MEDIA_LNK_FL_LINK_TYPE.*/);
+ next if ($flag =~ /.*MEDIA_ENT_ID_FLAG_NEXT.*/);
+
+ printf $fh_common_info_h "\t{ $flag, \"$flag\" },\n";
+ }
+ printf $fh_common_info_h "\t{ 0, \"\" }\n};\n\n";
+}
+
+sub enum_gen {
+ ($enum_name) = ($_) =~ /enum (\w+) {/;
+ printf $fh_common_info_h "constexpr val_def %s_val_def[] = {\n", $enum_name;
+ while (<>) {
+ last if $_ =~ /};/;
+ ($name) = ($_) =~ /\s+(\w+)\s?.*/;
+ next if ($name ne uc $name); # skip comments that don't start with *
+ next if ($_ =~ /^\s*\/?\s?\*.*/); # skip comments
+ next if $name =~ /^\s*$/; # skip blank lines
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $name, $name;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+}
+
+sub clean_up_line {
+ my $line = shift;
+ chomp($line);
+ $line =~ s/^\s+//; # remove leading whitespace
+ $line =~ s/.*\# define.*//; # zero out line if it has defines inside a structure (e.g. v4l2_jpegcompression)
+ $line =~ s/^\s*\/?\s?\*.*//; # zero out line if it has comments where the line starts with start with /* / * or just *
+ $line =~ s/\s*\/\*.*//; # remove comments at the end of a line following a member
+ $line =~ s/\*\/$//; # zero out line if it has comments that begin without any slashs or asterisks but end with */
+ # zero out lines that don't have a ; or { because they are comments but without any identifying slashes or asteriks
+ if ($line !~ /.*[\;|\{].*/) {
+ $line =~ s/.*//;
+ }
+ $line =~ s/.*reserved.*//; # zero out lines with reserved members, they will segfault on retracing
+ $line =~ s/.*raw_data.*//;
+ # don't remove semi-colon at end because some functions will look for it
+ return $line;
+}
+
+sub get_val_def_name {
+ my @params = @_;
+ my $member = @params[0];
+ my $struct_name = @params[1];
+
+ $val_def_name = "";
+ if ($member =~ /type/) {
+ if ($struct_name =~ /.*fmt|format|buf|parm|crop|selection|vbi.*/) {
+ $val_def_name = "v4l2_buf_type_val_def";
+ } elsif ($struct_name =~ /ctrl$/) {
+ $val_def_name = "v4l2_ctrl_type_val_def";
+ } else {
+ $val_def_name = "nullptr"; # will print as hex string
+ }
+ }
+ if ($member =~ /pixelformat/) {
+ $val_def_name = "v4l2_pix_fmt_val_def";
+ }
+ if ($member =~ /cmd/) {
+ if ($struct_name =~ /v4l2_decoder_cmd/) {
+ $val_def_name = "decoder_cmd_val_def";
+ }
+ if ($struct_name =~ /v4l2_encoder_cmd/) {
+ $val_def_name = "encoder_cmd_val_def";
+ }
+ }
+ if ($member =~ /memory/) {
+ $val_def_name = "v4l2_memory_val_def";
+ }
+ if ($member =~ /field/) {
+ if ($struct_name =~ /.*pix|buffer.*/) {
+ $val_def_name = "v4l2_field_val_def";
+ } else {
+ $val_def_name = "nullptr"; # will print as hex string
+ }
+ }
+ if ($member =~ /^id$/) {
+ if ($struct_name =~ /.*control|query.*/) {
+ $val_def_name = "control_val_def";
+ } else {
+ $val_def_name = "nullptr"; # will print as hex string
+ }
+ }
+ if ($member =~ /capability|outputmode|capturemode/) {
+ if ($struct_name =~ /.*v4l2_captureparm|v4l2_outputparm.*/) {
+ $val_def_name = "streamparm_val_def";
+ }
+ }
+ if ($member =~ /colorspace/) {
+ $val_def_name = "v4l2_colorspace_val_def";
+ }
+ if ($member =~ /ycbcr_enc/) {
+ $val_def_name = "v4l2_ycbcr_encoding_val_def";
+ }
+ if ($member =~ /quantization/) {
+ $val_def_name = "v4l2_quantization_val_def";
+ }
+ if ($member =~ /xfer_func/) {
+ $val_def_name = "v4l2_xfer_func_val_def";
+ }
+
+ return $val_def_name;
+}
+
+sub get_flag_def_name {
+ my @params = @_;
+ my $member = @params[0];
+ my $struct_name = @params[1];
+
+ $flag_def_name = "";
+ if ($member =~ /flags/) {
+ if ($struct_name =~ /buffers$/) {
+ $flag_def_name = "v4l2_memory_flag_def";
+ } elsif ($struct_name =~ /.*pix_format.*/) {
+ $flag_def_name = "v4l2_pix_fmt_flag_def";
+ } elsif ($struct_name =~ /.*ctrl$/) {
+ $flag_def_name = "v4l2_ctrl_flag_def";
+ } elsif ($struct_name =~ /.*fmtdesc$/) {
+ $flag_def_name = "v4l2_fmt_flag_def";
+ } elsif ($struct_name =~ /.*selection$/) {
+ $flag_def_name = "v4l2_sel_flag_def";
+ } else {
+ $flag_def_name = "nullptr"
+ }
+ }
+
+ if ($member =~ /.*cap.*/) {
+ # v4l2_requestbuffers, v4l2_create_buffers
+ if ($struct_name =~ /buffers$/) {
+ $flag_def_name = "v4l2_buf_cap_flag_def";
+ }
+ # v4l2_capability
+ if ($struct_name =~ /capability$/) {
+ $flag_def_name = "v4l2_cap_flag_def";
+ }
+ }
+
+ return $flag_def_name;
+}
+
+# trace a struct nested in another struct in videodev2.h
+sub handle_struct {
+ printf $fh_trace_cpp "\t//$line\n";
+ printf $fh_retrace_cpp "\t//$line\n";
+
+ # this is a multi-lined nested struct so iterate through it
+ if ($line !~ /.*;$/) {
+ $suppress_struct = true;
+ return;
+ }
+
+ # don't trace struct pointers
+ if ($line =~ /\*/) {
+ return;
+ }
+ # don't trace struct arrays
+ if ($line =~ /\[/) {
+ return;
+ }
+
+ my ($struct_tag) = ($line) =~ /\s*struct (\w+)\s+.*/;
+
+ # structs defined outside of videodev2.h
+ if ($struct_tag =~ /v4l2_ctrl|timeval|timespec/) {
+ return;
+ }
+
+ # e.g. $struct_tag_parent = v4l2_captureparm, $struct_tag = v4l2_fract, $struct_var = timeperframe
+ my $struct_tag_parent = $struct_name;
+ my ($struct_var) = ($line) =~ /(\w+)\;$/;
+ printf $fh_trace_cpp "\ttrace_%s_gen(&p->%s, %s_obj, \"%s\");\n", $struct_tag, $struct_var, $struct_tag_parent, $struct_var;
+
+ printf $fh_retrace_cpp "\tvoid *%s_ptr = (void *) retrace_%s_gen(%s_obj, \"%s\");\n", $struct_var, $struct_tag, $struct_tag_parent, $struct_var;
+ printf $fh_retrace_cpp "\tp->$struct_var = *static_cast<struct %s*>(%s_ptr);\n", $struct_tag, $struct_var;
+ printf $fh_retrace_cpp "\tfree(%s_ptr);\n\n", $struct_var;
+}
+
+# trace a union in videodev2.h
+sub handle_union {
+ my @params = @_;
+ my $struct_name = @params[0];
+
+ $in_union = true;
+ $suppress_union = true;
+ printf $fh_trace_cpp "\t//union\n";
+ printf $fh_retrace_cpp "\t//union\n";
+
+ if ($struct_name =~ /^v4l2_pix_format/) {
+ $suppress_union = false;
+ }
+
+ if ($struct_name =~ /^v4l2_format$/) {
+ printf $fh_trace_cpp "\tswitch (p->type) {\n";
+ printf $fh_trace_cpp "\tcase V4L2_BUF_TYPE_VIDEO_CAPTURE:\n\tcase V4L2_BUF_TYPE_VIDEO_OUTPUT:\n";
+ printf $fh_trace_cpp "\t\ttrace_v4l2_pix_format_gen(&p->fmt.pix, %s_obj);\n\t\tbreak;\n", $struct_name;
+ printf $fh_trace_cpp "\tcase V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:\n\tcase V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:\n";
+ printf $fh_trace_cpp "\t\ttrace_v4l2_pix_format_mplane_gen(&p->fmt.pix, %s_obj);\n\t\tbreak;\n", $struct_name;
+ printf $fh_trace_cpp "\tdefault:\n\t\tbreak;\n\t}\n";
+
+ printf $fh_retrace_cpp "\tswitch (p->type) {\n";
+ printf $fh_retrace_cpp "\tcase V4L2_BUF_TYPE_VIDEO_CAPTURE:\n\tcase V4L2_BUF_TYPE_VIDEO_OUTPUT: {\n";
+ printf $fh_retrace_cpp "\t\tvoid *pix_ptr = (void *) retrace_v4l2_pix_format_gen(v4l2_format_obj);\n";
+ printf $fh_retrace_cpp "\t\tp->fmt.pix = *static_cast<struct v4l2_pix_format*>(pix_ptr);\n";
+ printf $fh_retrace_cpp "\t\tfree(pix_ptr);\n\t\tbreak;\n\t}\n";
+
+ printf $fh_retrace_cpp "\tcase V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:\n\tcase V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: {\n";
+ printf $fh_retrace_cpp "\t\tvoid *pix_mp_ptr = (void *) retrace_v4l2_pix_format_mplane_gen(v4l2_format_obj);\n";
+ printf $fh_retrace_cpp "\t\tp->fmt.pix_mp = *static_cast<struct v4l2_pix_format_mplane*>(pix_mp_ptr);\n";
+ printf $fh_retrace_cpp "\t\tfree(pix_mp_ptr);\n\t\tbreak;\n\t}\n";
+
+ printf $fh_retrace_cpp "\tdefault:\n\t\tbreak;\n\t}\n";
+ }
+
+ return $suppress_union;
+}
+
+# generate functions for structs in videodev2.h
+sub struct_gen {
+ ($struct_name) = ($_) =~ /struct (\w+) {/;
+
+ # it's not being used and was generating a warning
+ if ($struct_name =~ /v4l2_mpeg_vbi_ITV0/) {
+ return;
+ }
+ printf $fh_trace_cpp "void trace_%s_gen(void *arg, json_object *parent_obj, std::string key_name = \"\")\n{\n", $struct_name;
+ printf $fh_trace_h "void trace_%s_gen(void *arg, json_object *parent_obj, std::string key_name = \"\");\n", $struct_name;
+ printf $fh_trace_cpp "\tjson_object *%s_obj = json_object_new_object();\n", $struct_name;
+ printf $fh_trace_cpp "\tstruct %s *p = static_cast<struct %s*>(arg);\n\n", $struct_name, $struct_name;
+
+ printf $fh_retrace_h "struct %s *retrace_%s_gen(json_object *parent_obj, std::string key_name = \"\");\n", $struct_name, $struct_name;
+ printf $fh_retrace_cpp "struct %s *retrace_%s_gen(json_object *parent_obj, std::string key_name = \"\")\n{\n", $struct_name, $struct_name;
+
+ printf $fh_retrace_cpp "\tstruct %s *p = (struct %s *) calloc(1, sizeof(%s));\n\n", $struct_name, $struct_name, $struct_name;
+ printf $fh_retrace_cpp "\tjson_object *%s_obj;\n", $struct_name;
+ printf $fh_retrace_cpp "\tif (key_name.empty())\n";
+ printf $fh_retrace_cpp "\t\tjson_object_object_get_ex(parent_obj, \"%s\", &%s_obj);\n", $struct_name, $struct_name;
+ printf $fh_retrace_cpp "\telse\n";
+ printf $fh_retrace_cpp "\t\tjson_object_object_get_ex(parent_obj, key_name.c_str(), &%s_obj);\n\n", $struct_name;
+
+ $suppress_union = false;
+ $suppress_struct = false;
+ while ($line = <>) {
+ chomp($line);
+ $member = "";
+ if ($line =~ /}.*;/) {
+ if ($suppress_struct eq true) {
+ printf $fh_trace_cpp "\t//end of struct $line\n";
+ printf $fh_retrace_cpp "\t//end of struct $line\n";
+ $suppress_struct = false;
+ next;
+ } elsif ($in_union eq true) {
+ if ($suppress_union eq true) {
+ $suppress_union = false; # end of union
+ }
+ printf $fh_trace_cpp "\t//end of union $line\n";
+ printf $fh_retrace_cpp "\t//end of union $line\n";
+ $in_union = false;
+ next;
+ } else {
+ last;
+ }
+ }
+ last if $line =~ /^} __attribute__.*/;
+
+ $line = clean_up_line($line);
+ next if $line =~ /^\s*$/; # ignore blank lines
+
+ @words = split /[\s\[]/, $line; # split on whitespace and also'[' to get char arrays
+ @words = grep {/^\D/} @words; # remove values that start with digit from inside []
+ @words = grep {!/\]/} @words; # remove values with brackets e.g. V4L2_H264_REF_LIST_LEN]
+
+ ($type) = $words[0];
+
+ # unions inside the struct
+ if ($type eq 'union') {
+ handle_union($struct_name);
+ next;
+ }
+ # suppress anything inside a union including structs nested inside a union
+ if ($suppress_union eq true) {
+ printf $fh_trace_cpp "\t//$line\n";
+ printf $fh_retrace_cpp "\t//$line\n";
+ next;
+ }
+
+ # struct members inside the struct
+ if ($type eq 'struct') {
+ handle_struct();
+ next;
+ }
+ if ($suppress_struct eq true) {
+ printf $fh_trace_cpp "\t//$line\n";
+ printf $fh_retrace_cpp "\t//$line\n";
+ next;
+ }
+
+ $json_type = convert_type_to_json_type($type);
+
+ ($member) = $words[scalar @words - 1];
+ $member =~ s/;//; # remove the ;
+
+ if ($member =~ /service_lines/) {
+ printf $fh_trace_cpp "\t//$line\n";
+ printf $fh_retrace_cpp "\t//$line\n";
+ next;
+ }
+
+ # Don't trace members that are pointers
+ if ($member =~ /\*/) {
+ printf $fh_trace_cpp "\t//$line\n";
+ printf $fh_retrace_cpp "\t//$line\n";
+ next;
+ }
+
+ if ($line =~ /dims\[V4L2_CTRL_MAX_DIMS\]/) {
+ printf $fh_trace_cpp "\t\/\* $line \*\/\n"; # add comment
+ printf $fh_trace_cpp "\tjson_object *dims_obj = json_object_new_array();\n";
+ printf $fh_trace_cpp "\tfor (int i = 0; i < (std::min((int) p->nr_of_dims, V4L2_CTRL_MAX_DIMS)); i++) {\n";
+ printf $fh_trace_cpp "\t\tjson_object_array_add(dims_obj, json_object_new_int64(p->dims[i]));\n\t}\n";
+ printf $fh_trace_cpp "\tjson_object_object_add(%s_obj, \"$member\", dims_obj);\n", $struct_name;
+
+ printf $fh_retrace_cpp "\t\/\* $line \*\/\n"; # add comment
+ printf $fh_retrace_cpp "\tjson_object *dims_obj;\n";
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(%s_obj, \"$member\", &%s_obj)) {\n", $struct_name, $member;
+ printf $fh_retrace_cpp "\t\tfor (int i = 0; i < (std::min((int) p->nr_of_dims, V4L2_CTRL_MAX_DIMS)); i++) {\n";
+ printf $fh_retrace_cpp "\t\t\tif (json_object_array_get_idx(dims_obj, i))\n";
+ printf $fh_retrace_cpp "\t\t\t\tp->dims[i] = (__u32) json_object_get_int64(json_object_array_get_idx(dims_obj, i));\n\t\t}\n\t}\n";
+ next;
+ }
+
+ printf $fh_retrace_cpp "\tjson_object *%s_obj;\n", $member;
+
+ # struct v4l2_pix_format
+ if ($member =~ /priv/) {
+ printf $fh_trace_cpp "\tif (p->priv == V4L2_PIX_FMT_PRIV_MAGIC)\n";
+ printf $fh_trace_cpp "\t\tjson_object_object_add(%s_obj, \"%s\", json_object_new_string(\"V4L2_PIX_FMT_PRIV_MAGIC\"));\n", $struct_name, $member;
+ printf $fh_trace_cpp "\telse\n";
+ printf $fh_trace_cpp "\t\tjson_object_object_add(%s_obj, \"%s\", json_object_new_string(\"\"));\n", $struct_name, $member;
+
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(%s_obj, \"$member\", &%s_obj)) {\n", $struct_name, $member;
+ printf $fh_retrace_cpp "\t\tif (json_object_get_string(priv_obj) == nullptr)\n\t\t\treturn p;\n";
+ printf $fh_retrace_cpp "\t\tstd::string priv_str = json_object_get_string(priv_obj);\n";
+ printf $fh_retrace_cpp "\t\tif (!priv_str.empty())\n";
+ printf $fh_retrace_cpp "\t\t\tp->priv = V4L2_PIX_FMT_PRIV_MAGIC;\n\t}\n";
+ next;
+ }
+
+ printf $fh_trace_cpp "\tjson_object_object_add(%s_obj, \"%s\", json_object_new_", $struct_name, $member;
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(%s_obj, \"$member\", &%s_obj))\n", $struct_name, $member;
+
+ # Convert char array members to string
+ if ($line =~ /.*\[.*/) {
+ if ($member =~ /.*name|driver|card|bus_info|description|model|magic|serial|userbits|APP_data|COM_data|linemask|data|start|count|raw|.*/) {
+ printf $fh_trace_cpp "string(reinterpret_cast<const char *>(p->$member)));\n";
+
+ printf $fh_retrace_cpp "\t\tif (json_object_get_string(%s_obj) != nullptr)\n", $member;
+ my @char_array_size = ($line) =~ /\[(\w+)\]/g;
+ printf $fh_retrace_cpp "\t\t\tmemcpy(p->$member, json_object_get_string(%s_obj), @char_array_size);\n\n", $member;
+ next;
+ }
+ }
+
+ # special strings
+ if ($member =~ /version/) {
+ printf $fh_trace_cpp "string(ver2s(p->$member).c_str()));\n";
+ printf $fh_retrace_cpp "\t\tmemset(&p->$member, 0, sizeof(p->$member));\n\n"; # driver can fill in version
+ next;
+ }
+
+ printf $fh_retrace_cpp "\t\tp->$member = ";
+
+ if ($struct_name =~ /^v4l2_buffer$/) {
+ if ($member =~ /flags/) {
+ printf $fh_trace_cpp "string(fl2s_buffer(p->$member).c_str()));\n";
+ printf $fh_retrace_cpp "(%s) s2flags_buffer(json_object_get_string(%s_obj));\n\n", $type, $member, $flag_def_name;
+ next;
+ }
+ }
+
+ # strings
+ $val_def_name = get_val_def_name($member, $struct_name);
+ if ($val_def_name !~ /^\s*$/) {
+ printf $fh_trace_cpp "string(val2s(p->$member, %s).c_str()));\n", $val_def_name;
+ printf $fh_retrace_cpp "(%s) s2val(json_object_get_string(%s_obj), %s);\n", $type, $member, $val_def_name;
+ next;
+ }
+
+ $flag_def_name = get_flag_def_name($member, $struct_name);
+ if ($flag_def_name !~ /^\s*$/) {
+ printf $fh_trace_cpp "string(fl2s(p->$member, %s).c_str()));\n", $flag_def_name;
+ printf $fh_retrace_cpp "(%s) s2flags(json_object_get_string(%s_obj), %s);\n\n", $type, $member, $flag_def_name;
+ next;
+ }
+
+ # integers
+ printf $fh_trace_cpp "$json_type(p->$member));\n";
+ printf $fh_retrace_cpp "(%s) json_object_get_%s(%s_obj);\n\n", $type, $json_type, $member;
+
+ # special treatment for v4l2_pix_format_mplane member plane_fmt[VIDEO_MAX_PLANES]
+ # it can only be traced after num_planes is known
+ if ($member =~ /num_planes/) {
+ printf $fh_trace_cpp "\tjson_object *plane_fmt_obj = json_object_new_array();\n";
+ printf $fh_trace_cpp "\tfor (int i = 0; i < (std::min((int) p->num_planes, VIDEO_MAX_PLANES)); i++) {\n";
+ printf $fh_trace_cpp "\t\tjson_object *element_obj = json_object_new_object();\n";
+ printf $fh_trace_cpp "\t\ttrace_v4l2_plane_pix_format_gen(&(p->plane_fmt[i]), element_obj);\n";
+ printf $fh_trace_cpp "\t\tjson_object *element_no_key_obj;\n";
+ printf $fh_trace_cpp "\t\tjson_object_object_get_ex(element_obj, \"v4l2_plane_pix_format\", &element_no_key_obj);\n";
+ printf $fh_trace_cpp "\t\tjson_object_array_add(plane_fmt_obj, element_no_key_obj);\n\t}\n";
+ printf $fh_trace_cpp "\tjson_object_object_add(v4l2_pix_format_mplane_obj, \"plane_fmt\", plane_fmt_obj);\n\n";
+
+ printf $fh_retrace_cpp "\tjson_object *plane_fmt_obj;\n";
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(v4l2_pix_format_mplane_obj, \"plane_fmt\", &plane_fmt_obj)) {\n";
+ printf $fh_retrace_cpp "\t\tfor (int i = 0; i < (std::min((int) p->num_planes, VIDEO_MAX_PLANES)); i++) {\n";
+ printf $fh_retrace_cpp "\t\t\tif (json_object_array_get_idx(plane_fmt_obj, i)) {\n";
+ printf $fh_retrace_cpp "\t\t\t\tjson_object *element_obj = json_object_new_object();\n";
+ printf $fh_retrace_cpp "\t\t\t\tjson_object_object_add(element_obj, \"v4l2_plane_pix_format\", json_object_array_get_idx(plane_fmt_obj, i));\n";
+ printf $fh_retrace_cpp "\t\t\t\tvoid *ptr = (void *) retrace_v4l2_plane_pix_format_gen(element_obj);\n";
+ printf $fh_retrace_cpp "\t\t\t\tp->plane_fmt[i] = *static_cast<struct v4l2_plane_pix_format *>(ptr);\n";
+ printf $fh_retrace_cpp "\t\t\t\tfree(ptr);\n";
+ printf $fh_retrace_cpp "\t\t\t}\n\t\t}\n\t}\n\n";
+ }
+ }
+
+ # The key name option allows a struct to be traced when it is nested inside another struct.
+ printf $fh_trace_cpp "\n\tif (key_name.empty())\n";
+ printf $fh_trace_cpp "\t\tjson_object_object_add(parent_obj, \"%s\", %s_obj);\n", $struct_name, $struct_name;
+ printf $fh_trace_cpp "\telse\n";
+ printf $fh_trace_cpp "\t\tjson_object_object_add(parent_obj, key_name.c_str(), %s_obj);\n", $struct_name;
+ printf $fh_trace_cpp "}\n\n";
+
+ printf $fh_retrace_cpp "\treturn p;\n}\n";
+}
+
+# generate functions for structs in v4l2-controls.h
+sub struct_gen_ctrl {
+ ($struct_name) = ($_) =~ /struct (\w+) {/;
+
+ printf $fh_trace_h "void trace_%s_gen(void *ptr, json_object *parent_obj);\n", $struct_name;
+ printf $fh_trace_cpp "void trace_%s_gen(void *ptr, json_object *parent_obj)\n{\n", $struct_name;
+ printf $fh_trace_cpp "\tjson_object *%s_obj = json_object_new_object();\n", $struct_name;
+ printf $fh_trace_cpp "\tstruct %s *p = static_cast<struct %s*>(ptr);\n", $struct_name, $struct_name;
+
+ printf $fh_retrace_h "struct %s *retrace_%s_gen(json_object *ctrl_obj);\n", $struct_name, $struct_name;
+ printf $fh_retrace_cpp "struct %s *retrace_%s_gen(json_object *ctrl_obj)\n{\n", $struct_name, $struct_name;
+ printf $fh_retrace_cpp "\tstruct %s *p = (struct %s *) calloc(1, sizeof(%s));\n", $struct_name, $struct_name, $struct_name;
+ printf $fh_retrace_cpp "\tjson_object *%s_obj;\n", $struct_name;
+ # if a key value isn't found, assume it is retracing an element of an array
+ # e.g. in struct v4l2_ctrl_h264_pred_weights
+ printf $fh_retrace_cpp "\tif (!json_object_object_get_ex(ctrl_obj, \"%s\", &%s_obj))\n", $struct_name, $struct_name;
+ printf $fh_retrace_cpp "\t\t%s_obj = ctrl_obj;\n", $struct_name;
+
+ while ($line = <>) {
+ chomp($line);
+ last if $line =~ /};/;
+ $line = clean_up_line($line);
+ next if $line =~ /^\s*$/; # ignore blank lines
+ $line =~ s/;$//; # remove semi-colon at the end
+ @words = split /[\s\[]/, $line; # also split on '[' to avoid arrays
+ @words = grep {/^\D/} @words; # remove values that start with digit from inside []
+ @words = grep {!/\]/} @words; # remove values with brackets e.g. V4L2_H264_REF_LIST_LEN]
+
+ ($type) = $words[0];
+ $json_type = convert_type_to_json_type($type);
+
+ ($member) = $words[scalar @words - 1];
+
+ # generate members that are arrays
+ if ($line =~ /.*\[.*/) {
+ printf $fh_trace_cpp "\t\/\* %s \*\/\n", $line; # add comment
+ printf $fh_trace_cpp "\tjson_object *%s_obj = json_object_new_array();\n", $member;
+ printf $fh_retrace_cpp "\n\t\/\* %s \*\/\n", $line; # add comment
+
+ my @dimensions = ($line) =~ /\[(\w+)\]/g;
+ $dimensions_count = scalar @dimensions;
+
+ if ($dimensions_count > 1) {
+ printf $fh_retrace_cpp "\tint count_%s = 0;\n", $member;
+ }
+ printf $fh_retrace_cpp "\tjson_object *%s_obj;\n", $member;
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(%s_obj, \"%s\", &%s_obj)) {\n", $struct_name, $member, $member;
+
+ for (my $idx = 0; $idx < $dimensions_count; $idx = $idx + 1) {
+ $size = $dimensions[$idx];
+ $index_letter = get_index_letter($idx);
+ printf $fh_trace_cpp "\t" x ($idx + 1);
+ printf $fh_trace_cpp "for (size_t %s = 0; %s < %s\; %s++) {\n", $index_letter, $index_letter, $size, $index_letter;
+
+ printf $fh_retrace_cpp "\t" x ($idx + 1);
+ printf $fh_retrace_cpp "\t";
+ printf $fh_retrace_cpp "for (size_t %s = 0; %s < %s\; %s++) {\n", $index_letter, $index_letter, $size, $index_letter;
+ }
+ printf $fh_trace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_retrace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_retrace_cpp "\t";
+
+ # handle arrays of structs e.g. struct v4l2_ctrl_h264_pred_weights weight_factors
+ if ($type =~ /struct/) {
+ my $struct_tag = @words[1];
+ my $struct_var = $member;
+ printf $fh_trace_cpp "json_object *element_obj = json_object_new_object();\n";
+ printf $fh_trace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_trace_cpp "trace_%s_gen(&(p->%s", $struct_tag, $struct_var;
+ for (my $idx = 0; $idx < $dimensions_count; $idx = $idx + 1) {
+ printf $fh_trace_cpp "[%s]", get_index_letter($idx);
+ }
+ printf $fh_trace_cpp "), element_obj);\n";
+ printf $fh_trace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_trace_cpp "json_object *element_no_key_obj;\n";
+ printf $fh_trace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_trace_cpp "json_object_object_get_ex(element_obj, \"%s\", &element_no_key_obj);\n", $struct_tag;
+ printf $fh_trace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_trace_cpp "json_object_array_add(%s_obj, element_no_key_obj);\n", $struct_var;
+
+ printf $fh_retrace_cpp "void *%s_ptr", $struct_var;
+ printf $fh_retrace_cpp " = (void *) retrace_%s_gen(json_object_array_get_idx(%s_obj, ", $struct_tag, $struct_var;
+ if ($dimensions_count > 1) {
+ printf $fh_retrace_cpp "count_%s++", $struct_var;
+ } else {
+ printf $fh_retrace_cpp "i";
+ }
+ printf $fh_retrace_cpp "));\n";
+
+ printf $fh_retrace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_retrace_cpp "\tp->%s", $struct_var;
+ for (my $idx = 0; $idx < $dimensions_count; $idx = $idx + 1) {
+ printf $fh_retrace_cpp "[%s]", get_index_letter($idx);
+ }
+ printf $fh_retrace_cpp " = *static_cast<struct %s*>(%s_ptr);\n", $struct_tag, $struct_var;
+
+ printf $fh_retrace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_retrace_cpp "\tfree(%s_ptr);\n", $struct_var;
+ } else {
+ # handle arrays of ints
+ printf $fh_trace_cpp "json_object_array_add(%s_obj, json_object_new_%s(p->%s", $member, $json_type, $member;
+ for (my $idx = 0; $idx < $dimensions_count; $idx = $idx + 1) {
+ printf $fh_trace_cpp "[%s]", get_index_letter($idx);
+ }
+ printf $fh_trace_cpp "));\n";
+
+ # add a check to avoid accessing a null array index
+ printf $fh_retrace_cpp "if (json_object_array_get_idx(%s_obj, ", $member;
+ if ($dimensions_count > 1) {
+ printf $fh_retrace_cpp "count_%s", $member;
+ } else {
+ printf $fh_retrace_cpp "i";
+ }
+ printf $fh_retrace_cpp "))\n";
+
+ printf $fh_retrace_cpp "\t" x ($dimensions_count + 1);
+ printf $fh_retrace_cpp "\t\t";
+ printf $fh_retrace_cpp "p->%s", $member;
+ for (my $idx = 0; $idx < $dimensions_count; $idx = $idx + 1) {
+ printf $fh_retrace_cpp "[%s]", get_index_letter($idx);
+ }
+
+ printf $fh_retrace_cpp " = ($type) json_object_get_%s(json_object_array_get_idx(%s_obj, ", $json_type, $member;
+ if ($dimensions_count > 1) {
+ printf $fh_retrace_cpp "count_%s++", $member;
+ } else {
+ printf $fh_retrace_cpp "i";
+ }
+ printf $fh_retrace_cpp "));\n";
+ }
+ # closing brackets for all array types
+ for (my $idx = $dimensions_count - 1; $idx >= 0 ; $idx = $idx - 1) {
+ printf $fh_trace_cpp "\t" x ($idx + 1);
+ printf $fh_trace_cpp "}\n";
+
+ printf $fh_retrace_cpp "\t" x ($idx + 1);
+ printf $fh_retrace_cpp "\t";
+ printf $fh_retrace_cpp "}\n";
+ }
+ printf $fh_retrace_cpp "\t}\n";
+ printf $fh_trace_cpp "\tjson_object_object_add(%s_obj, \"%s\", %s_obj);\n\n", $struct_name, $member, $member;
+ next;
+ }
+
+ # member that is a struct but not an array of structs
+ # e.g. $struct_tag_parent = v4l2_ctrl_vp8_frame, $struct_tag = v4l2_vp8_segment, $struct_var = segment
+ if ($type =~ /struct/) {
+ my $struct_tag_parent = $struct_name;
+ my ($struct_tag) = ($line) =~ /\s*struct (\w+)\s+.*/;
+ my ($struct_var) = $member;
+ printf $fh_trace_cpp "\t\/\* %s \*\/\n", $line;
+ printf $fh_trace_cpp "\ttrace_%s_gen(&p->%s, %s_obj);\n", $struct_tag, $struct_var, $struct_tag_parent;
+
+ printf $fh_retrace_cpp "\n\t\/\* %s \*\/\n", $line;
+ printf $fh_retrace_cpp "\tjson_object *%s_obj;\n", $struct_var;
+ printf $fh_retrace_cpp "\tif (!json_object_object_get_ex(%s_obj, \"%s\", &%s_obj))\n", $struct_tag_parent, $struct_tag, $struct_var;
+ printf $fh_retrace_cpp "\t\treturn p;\n", $struct_tag_parent, $struct_tag, $struct_var;
+
+ printf $fh_retrace_cpp "\tvoid *%s_ptr = (void *) retrace_%s_gen(%s_obj);\n", $struct_var, $struct_tag, $struct_var;
+ printf $fh_retrace_cpp "\tp->$struct_var = *static_cast<struct %s*>(%s_ptr);\n", $struct_tag, $struct_var;
+ printf $fh_retrace_cpp "\tfree(%s_ptr);\n", $struct_var;
+ next;
+ }
+
+ printf $fh_trace_cpp "\tjson_object_object_add(%s_obj, \"%s\", json_object_new_", $struct_name, $member;
+ printf $fh_retrace_cpp "\n\tjson_object *%s_obj;\n", $member;
+ printf $fh_retrace_cpp "\tif (json_object_object_get_ex(%s_obj, \"%s\", &%s_obj))\n", $struct_name, $member, $member;
+
+ # strings
+ if ($member =~ /flags/) {
+ if ($struct_name eq "v4l2_ctrl_fwht_params") {
+ printf $fh_trace_cpp "string(fl2s_fwht(p->$member).c_str()));\n";
+ printf $fh_retrace_cpp "\t\tp->%s = ($type) s2flags_fwht(json_object_get_string(%s_obj));\n", $member, $member, $flag_func_name;
+ } else {
+ printf $fh_trace_cpp "string(fl2s(p->$member, %sflag_def).c_str()));\n", $flag_func_name;
+ printf $fh_retrace_cpp "\t\tp->%s = ($type) s2flags(json_object_get_string(%s_obj), %sflag_def);\n", $member, $member, $flag_func_name;
+ }
+ next;
+ }
+
+ # Add members with a single string value (e.g. enums, #define)
+ $val_def_name = get_val_def_name($member, $struct_name);
+ if ($val_def_name !~ /^\s*$/) {
+ printf $fh_trace_cpp "string(val2s(p->$member, %s).c_str()));\n", $val_def_name;
+ printf $fh_retrace_cpp "\t\tp->%s = ($type) s2val(json_object_get_string(%s_obj), $val_def_name);\n", $member, $member;
+ next;
+ }
+
+ # integers
+ printf $fh_trace_cpp "%s(p->%s));\n", $json_type, $member;
+ printf $fh_retrace_cpp "\t\tp->%s = ($type) json_object_get_%s(%s_obj);\n", $member, $json_type, $member;
+ }
+
+ printf $fh_trace_cpp "\tjson_object_object_add(parent_obj, \"%s\", %s_obj);\n", $struct_name, $struct_name;
+ printf $fh_trace_cpp "}\n\n";
+
+ printf $fh_retrace_cpp "\n\treturn p;\n";
+ printf $fh_retrace_cpp "}\n\n";
+}
+
+open($fh_trace_cpp, '>', 'trace-gen.cpp') or die "Could not open trace-gen.cpp for writing";
+printf $fh_trace_cpp "/* SPDX-License-Identifier: GPL-2.0-only */\n/*\n * Copyright 2022 Collabora Ltd.\n";
+printf $fh_trace_cpp " *\n * AUTOMATICALLY GENERATED BY \'%s\' DO NOT EDIT\n */\n\n", __FILE__;
+printf $fh_trace_cpp "#include \"v4l2-tracer-common.h\"\n\n";
+
+open($fh_trace_h, '>', 'trace-gen.h') or die "Could not open trace-gen.h for writing";
+printf $fh_trace_h "/* SPDX-License-Identifier: GPL-2.0-only */\n/*\n * Copyright 2022 Collabora Ltd.\n";
+printf $fh_trace_h " *\n * AUTOMATICALLY GENERATED BY \'%s\' DO NOT EDIT\n */\n\n", __FILE__;
+printf $fh_trace_h "\#ifndef TRACE_GEN_H\n";
+printf $fh_trace_h "\#define TRACE_GEN_H\n\n";
+
+open($fh_retrace_cpp, '>', 'retrace-gen.cpp') or die "Could not open retrace-gen.cpp for writing";
+printf $fh_retrace_cpp "/* SPDX-License-Identifier: GPL-2.0-only */\n/*\n * Copyright 2022 Collabora Ltd.\n";
+printf $fh_retrace_cpp " *\n * AUTOMATICALLY GENERATED BY \'%s\' DO NOT EDIT\n */\n\n", __FILE__;
+printf $fh_retrace_cpp "#include \"v4l2-tracer-common.h\"\n\n";
+
+open($fh_retrace_h, '>', 'retrace-gen.h') or die "Could not open retrace-gen.h for writing";
+printf $fh_retrace_h "/* SPDX-License-Identifier: GPL-2.0-only */\n/*\n * Copyright 2022 Collabora Ltd.\n";
+printf $fh_retrace_h " *\n * AUTOMATICALLY GENERATED BY \'%s\' DO NOT EDIT\n */\n\n", __FILE__;
+printf $fh_retrace_h "\#ifndef RETRACE_GEN_H\n";
+printf $fh_retrace_h "\#define RETRACE_GEN_H\n\n";
+
+open($fh_common_info_h, '>', 'v4l2-tracer-info-gen.h') or die "Could not open v4l2-tracer-info-gen.h for writing";
+printf $fh_common_info_h "/* SPDX-License-Identifier: GPL-2.0-only */\n/*\n * Copyright 2022 Collabora Ltd.\n";
+printf $fh_common_info_h " *\n * AUTOMATICALLY GENERATED BY \'%s\' DO NOT EDIT\n */\n\n", __FILE__;
+printf $fh_common_info_h "\#ifndef V4L2_TRACER_INFO_GEN_H\n";
+printf $fh_common_info_h "\#define V4L2_TRACER_INFO_GEN_H\n\n";
+printf $fh_common_info_h "#include \"v4l2-tracer-common.h\"\n\n";
+
+$in_v4l2_controls = true;
+
+while (<>) {
+ if (grep {/#define __LINUX_VIDEODEV2_H/} $_) {
+ $in_v4l2_controls = false;
+ }
+
+ if (grep {/^#define.+FWHT_FL_.+/} $_) {
+ flag_gen("fwht");
+ } elsif (grep {/^#define V4L2_VP8_LF.*/} $_) {
+ flag_gen("vp8_loop_filter");
+ } elsif (grep {/^#define.+_FL_.+/} $_) { #use to get media flags
+ flag_gen();
+ } elsif (grep {/^#define.+_FLAG_.+/} $_) {
+ flag_gen();
+ }
+
+ if ($in_v4l2_controls eq true) {
+ if (grep {/^struct/} $_) {
+ struct_gen_ctrl();
+ }
+ } else {
+ if (grep {/^struct/} $_) {
+ struct_gen();
+ }
+ }
+
+ if (grep {/^enum/} $_) {
+ enum_gen();
+ }
+
+ if (grep {/^#define\s+(V4L2_CID\w*)\s*.*/} $_) {
+ push (@controls, $_);
+ }
+
+ if (grep {/^\/\* Control classes \*\//} $_) {
+ printf $fh_common_info_h "constexpr val_def ctrlclass_val_def[] = {\n";
+ while (<>) {
+ last if $_ =~ /^\s*$/; # last if blank line
+ ($ctrl_class) = ($_) =~ /#define\s*(\w+)\s+.*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $ctrl_class, $ctrl_class;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+ }
+
+ if (grep {/\/\* Values for 'capabilities' field \*\//} $_) {
+ printf $fh_common_info_h "constexpr flag_def v4l2_cap_flag_def[] = {\n";
+ while (<>) {
+ last if $_ =~ /.*V I D E O I M A G E F O R M A T.*/;
+ next if ($_ =~ /^\/?\s?\*.*/); # skip comments
+ next if $_ =~ /^\s*$/; # skip blank lines
+ ($cap) = ($_) =~ /#define\s+(\w+)\s+.+/;
+ printf $fh_common_info_h "\t{ $cap, \"$cap\" },\n"
+ }
+ printf $fh_common_info_h "\t{ 0, \"\" }\n};\n\n";
+ }
+
+ if (grep {/\* Pixel format FOURCC depth Description \*\//} $_) {
+ printf $fh_common_info_h "constexpr val_def v4l2_pix_fmt_val_def[] = {\n";
+ while (<>) {
+ last if $_ =~ /.*SDR formats - used only for Software Defined Radio devices.*/;
+ next if ($_ =~ /^\s*\/\*.*/); # skip comments
+ next if $_ =~ /^\s*$/; # skip blank lines
+ ($pixfmt) = ($_) =~ /#define (\w+)\s+.*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $pixfmt, $pixfmt;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+ }
+
+ if (grep {/^#define V4L2_BUF_CAP_SUPPORTS_MMAP.*/} $_) {
+ printf $fh_common_info_h "constexpr flag_def v4l2_buf_cap_flag_def[] = {\n";
+ ($buf_cap) = ($_) =~ /#define (\w+)\s+.*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $buf_cap, $buf_cap;
+ while (<>) {
+ last if $_ =~ /^\s*$/; # blank line
+ ($buf_cap) = ($_) =~ /#define (\w+)\s+.*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $buf_cap, $buf_cap;
+ }
+ printf $fh_common_info_h "\t{ 0, \"\" }\n};\n\n";
+ }
+
+ if (grep {/.*Flags for 'capability' and 'capturemode' fields.*/} $_) {
+ printf $fh_common_info_h "constexpr val_def streamparm_val_def[] = {\n";
+ while (<>) {
+ last if $_ =~ /^\s*$/; # blank line
+ ($streamparm) = ($_) =~ /^#define\s*(\w+)\s*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $streamparm, $streamparm;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+ }
+
+ if (grep {/.*V4L2_ENC_CMD_START.*/} $_) {
+ printf $fh_common_info_h "constexpr val_def encoder_cmd_val_def[] = {\n";
+ ($enc_cmd) = ($_) =~ /^#define\s*(\w+)\s*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $enc_cmd, $enc_cmd;
+ while (<>) {
+ last if $_ =~ /^\s*$/; # blank line
+ ($enc_cmd) = ($_) =~ /^#define\s*(\w+)\s*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $enc_cmd, $enc_cmd;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+ }
+
+ if (grep {/.*Decoder commands.*/} $_) {
+ printf $fh_common_info_h "constexpr val_def decoder_cmd_val_def[] = {\n";
+ while (<>) {
+ last if $_ =~ /^\s*$/; # blank line
+ ($dec_cmd) = ($_) =~ /^#define\s*(\w+)\s*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $dec_cmd, $dec_cmd;
+ }
+ printf $fh_common_info_h "\t{ -1, \"\" }\n};\n\n";
+ }
+
+ if (grep {/^#define\s+(VIDIOC_\w*)\s*.*/} $_) {
+ push (@ioctls, $_);
+ }
+
+ if (grep {/^#define\s+(MEDIA_IOC\w*)\s*.*/} $_) {
+ push (@ioctls, $_);
+ }
+
+ if (grep {/^#define\s+(MEDIA_REQUEST_IOC\w*)\s*.*/} $_) {
+ push (@ioctls, $_);
+ }
+}
+
+printf $fh_common_info_h "constexpr val_def control_val_def[] = {\n";
+foreach (@controls) {
+ ($control) = ($_) =~ /^#define\s*(\w+)\s*/;
+ next if ($control =~ /BASE$/);
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $control, $control;
+}
+printf $fh_common_info_h "\t{ -1, \"\" }\n};\n";
+
+printf $fh_common_info_h "constexpr val_def ioctl_val_def[] = {\n";
+foreach (@ioctls) {
+ ($ioctl) = ($_) =~ /^#define\s*(\w+)\s*/;
+ printf $fh_common_info_h "\t{ %s,\t\"%s\" },\n", $ioctl, $ioctl;
+}
+printf $fh_common_info_h "\t{ -1, \"\" }\n};\n";
+
+
+printf $fh_trace_h "\n#endif\n";
+close $fh_trace_h;
+close $fh_trace_cpp;
+
+printf $fh_retrace_h "\n#endif\n";
+close $fh_retrace_h;
+close $fh_retrace_cpp;
+
+printf $fh_common_info_h "\n#endif\n";
+close $fh_common_info_h;
diff --git a/utils/v4l2-tracer/v4l2-tracer.1.in b/utils/v4l2-tracer/v4l2-tracer.1.in
new file mode 100644
index 00000000..13942eba
--- /dev/null
+++ b/utils/v4l2-tracer/v4l2-tracer.1.in
@@ -0,0 +1,111 @@
+.TH "V4L2-TRACER" "1" "November 2022" "v4l-utils @PACKAGE_VERSION@" "User Commands"
+.SH NAME
+v4l2-tracer - An application to trace and replay stateless video decoding.
+.SH SYNOPSIS
+\fBv4l2-tracer \fR[options] \fBtrace\fR <\fItracee\fR>
+.RS
+.RE
+\fBv4l2-tracer \fR[options] \fBretrace\fR <\fItrace_file\fR>\fB.json\fR
+.RS
+.RE
+\fBv4l2-tracer clean\fR <\fIfile\fR>\fB.json\fR
+.RS
+.RE
+
+.SH DESCRIPTION
+The v4l2-tracer utility traces, records and replays userspace applications
+that implement the v4l2 memory-to-memory stateless video decoder interface.
+.SS Trace
+Trace system calls and video frame data passed by userspace application <\fItracee\fR> to kernel driver.
+All stateless codec controls in user-space API can be traced. Outputs a JSON-formatted trace file.
+.SS Retrace
+Read the JSON-formatted <\fItrace_file\fR>\fB.json\fR. Replay the same system calls and pass the same video frame data to kernel driver.
+Outputs a JSON-formatted retrace file.
+
+.SS Clean
+Remove lines with irrelevant differences (e.g. file descriptors and memory addresses) from JSON files.
+Outputs a clean copy, not necessarily still in JSON-format.
+
+.SH OPTIONS
+.SS Common Options
+.TP
+\fB\-c\fR, \fB\-\-compact\fR
+Write minimal whitespace in JSON file.
+.TP
+\fB\-g\fR, \fB\-\-debug\fR
+Turn on verbose reporting plus additional debug info.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display this message.
+.TP
+\fB\-r\fR, \fB\-\-raw\fR
+Write decoded video frame data to JSON file.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Turn on verbose reporting.
+.TP
+\fB\-y\fR, \fB\-\-yuv\fR
+Write decoded video frame data to yuv file.
+
+.SS Retrace Options
+.TP
+\fB\-d\fR, \fB\-\-device\fR <\fIdev\fR>
+Use a different video device than specified in the trace file.
+.RS
+<\fIdev\fR> must be a digit corresponding to an existing /dev/video<\fIdev\fR>
+.RE
+.TP
+\fB\-m\fR, \fB\-\-media\fR <\fIdev\fR>
+Use a different media device than specified in the trace file.
+.RS
+<\fIdev\fR> must be a digit corresponding to an existing /dev/media<\fIdev\fR>
+.RE
+
+.SH EXIT STATUS
+On success, it returns 0. Otherwise, it will return 1 or an error code.
+
+.SH EXAMPLE
+.TP
+Trace an application decoding VP8 video:
+.EX
+\fIv4l2-tracer trace gst-launch-1.0 -- filesrc location=test-25fps.vp8 ! parsebin ! v4l2slvp8dec ! videocodectestsink\fR
+.EE
+.TP
+A trace file is generated:
+.EE
+\fI71827_trace.json\fR
+.TP
+Retrace the trace file:
+.EX
+\fIv4l2-tracer retrace 71827_trace.json\fR
+.EE
+.TP
+Specify device nodes if retracing on a different driver:
+.EX
+\fIv4l2-tracer -d0 -m0 retrace 71827_trace.json\fR
+.EE
+.TP
+A retrace file is generated:
+.EE
+\fI71827_trace_retrace.json\fR
+.EX
+.TP
+Remove file descriptors and addresses (optional):
+.EX
+\fIv4l2-tracer clean 71827_trace.json\fR
+.EE
+.EX
+\fIv4l2-tracer clean 71827_trace_retrace.json\fR
+.EE
+.TP
+Clean files are generated for comparison:
+.EX
+\fIclean_71827_trace.json\fR
+.EE
+.EE
+\fIclean_71827_trace_retrace.json\fR
+.EX
+
+.SH BUGS
+Bug reports or questions about this utility should be sent to the
+linux-media@vger.kernel.org mailinglist.
diff --git a/utils/v4l2-tracer/v4l2-tracer.cpp b/utils/v4l2-tracer/v4l2-tracer.cpp
new file mode 100644
index 00000000..3afcd34f
--- /dev/null
+++ b/utils/v4l2-tracer/v4l2-tracer.cpp
@@ -0,0 +1,415 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright 2022 Collabora Ltd.
+ */
+
+#include "retrace.h"
+#include <climits>
+#include <sys/wait.h>
+#include <time.h>
+
+int tracer(int argc, char *argv[], bool retrace = false);
+
+enum Options {
+ V4l2TracerOptCompactPrint = 'c',
+ V4l2TracerOptSetVideoDevice = 'd',
+ V4l2TracerOptDebug = 'g',
+ V4l2TracerOptHelp = 'h',
+ V4l2TracerOptSetMediaDevice = 'm',
+ V4l2TracerOptWriteDecodedToJson = 'r',
+ V4l2TracerOptVerbose = 'v',
+ V4l2TracerOptWriteDecodedToYUVFile = 'y',
+};
+
+const static struct option long_options[] = {
+ { "compact", no_argument, nullptr, V4l2TracerOptCompactPrint },
+ { "video_device", required_argument, nullptr, V4l2TracerOptSetVideoDevice },
+ { "debug", no_argument, nullptr, V4l2TracerOptDebug },
+ { "help", no_argument, nullptr, V4l2TracerOptHelp },
+ { "media_device", required_argument, nullptr, V4l2TracerOptSetMediaDevice },
+ { "raw", no_argument, nullptr, V4l2TracerOptWriteDecodedToJson },
+ { "verbose", no_argument, nullptr, V4l2TracerOptVerbose },
+ { "yuv", no_argument, nullptr, V4l2TracerOptWriteDecodedToYUVFile },
+ { nullptr, 0, nullptr, 0 }
+};
+
+const char short_options[] = {
+ V4l2TracerOptCompactPrint,
+ V4l2TracerOptSetVideoDevice, ':',
+ V4l2TracerOptDebug,
+ V4l2TracerOptHelp,
+ V4l2TracerOptSetMediaDevice, ':',
+ V4l2TracerOptWriteDecodedToJson,
+ V4l2TracerOptVerbose,
+ V4l2TracerOptWriteDecodedToYUVFile
+};
+
+int get_options(int argc, char *argv[])
+{
+ int option = 0;
+
+ do {
+ /* If there are no commands after the valid options, return err. */
+ if (optind == argc) {
+ print_usage();
+ return -1;
+ }
+
+ /* Avoid reading the tracee's options. */
+ if ((strcmp(argv[optind], "trace") == 0) || (strcmp(argv[optind], "retrace") == 0))
+ return 0;
+
+ option = getopt_long(argc, argv, short_options, long_options, NULL);
+ switch (option) {
+ case V4l2TracerOptCompactPrint: {
+ setenv("V4L2_TRACER_OPTION_COMPACT_PRINT", "true", 0);
+ break;
+ }
+ case V4l2TracerOptSetVideoDevice: {
+ std::string device_num = optarg;
+ try {
+ std::stoi(device_num, nullptr, 0);
+ } catch (std::exception& e) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "can't convert <dev> \'%s\' to integer\n", device_num.c_str());
+ return -1;
+ }
+ if (device_num[0] >= '0' && device_num[0] <= '9' && device_num.length() <= 3) {
+ std::string path_video = "/dev/video";
+ path_video += optarg;
+ setenv("V4L2_TRACER_OPTION_SET_VIDEO_DEVICE", path_video.c_str(), 0);
+ } else {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot use device number\'%s\'\n", device_num.c_str());
+ return -1;
+ }
+ break;
+ }
+ case V4l2TracerOptDebug:
+ setenv("V4L2_TRACER_OPTION_VERBOSE", "true", 0);
+ setenv("V4L2_TRACER_OPTION_DEBUG", "true", 0);
+ break;
+ case V4l2TracerOptHelp:
+ print_usage();
+ return -1;
+ case V4l2TracerOptSetMediaDevice: {
+ std::string device_num = optarg;
+ try {
+ std::stoi(device_num, nullptr, 0);
+ } catch (std::exception& e) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "can't convert <dev> \'%s\' to integer\n", device_num.c_str());
+ return -1;
+ }
+ if (device_num[0] >= '0' && device_num[0] <= '9' && device_num.length() <= 3) {
+ std::string path_media = "/dev/media";
+ path_media += optarg;
+ setenv("V4L2_TRACER_OPTION_SET_MEDIA_DEVICE", path_media.c_str(), 0);
+ } else {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot use device number\'%s\'\n", device_num.c_str());
+ return -1;
+ }
+ break;
+ }
+ case V4l2TracerOptWriteDecodedToJson:
+ setenv("V4L2_TRACER_OPTION_WRITE_DECODED_TO_JSON_FILE", "true", 0);
+ break;
+ case V4l2TracerOptVerbose:
+ setenv("V4L2_TRACER_OPTION_VERBOSE", "true", 0);
+ break;
+ case V4l2TracerOptWriteDecodedToYUVFile:
+ setenv("V4L2_TRACER_OPTION_WRITE_DECODED_TO_YUV_FILE", "true", 0);
+ break;
+ default:
+ break;
+ }
+
+ /* invalid option */
+ if (optopt > 0) {
+ print_usage();
+ return -1;
+ }
+
+ } while (option != -1);
+
+ return 0;
+}
+
+int clean(std::string trace_filename)
+{
+ FILE *trace_file = fopen(trace_filename.c_str(), "r");
+ if (trace_file == nullptr) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot open \'%s\'\n", trace_filename.c_str());
+ return 1;
+ }
+
+ fprintf(stderr, "Cleaning: %s\n", trace_filename.c_str());
+
+ std::string clean_filename = "clean_" + trace_filename;
+ FILE *clean_file = fopen(clean_filename.c_str(), "w");
+ if (clean_file == nullptr) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "cannot open \'%s\'\n", clean_filename.c_str());
+ return 1;
+ }
+
+ std::string line;
+ char buf[SHRT_MAX];
+ int count_total = 0;
+ int count_lines_removed = 0;
+
+ while (fgets(buf, SHRT_MAX, trace_file) != nullptr) {
+ line = buf;
+ count_total++;
+ if (line.find("fd") != std::string::npos) {
+ count_lines_removed++;
+ continue;
+ }
+ if (line.find("address") != std::string::npos) {
+ count_lines_removed++;
+ continue;
+ }
+ if (line.find("fildes") != std::string::npos) {
+ count_lines_removed++;
+ continue;
+ }
+ if (line.find("\"start\"") != std::string::npos) {
+ count_lines_removed++;
+ continue;
+ }
+ if (line.find("\"name\"") != std::string::npos) {
+ count_lines_removed++;
+ continue;
+ }
+
+ fputs(buf, clean_file);
+ }
+
+ fclose(trace_file);
+ fclose(clean_file);
+ fprintf(stderr, "Removed %d lines of %d total lines: %s\n",
+ count_lines_removed, count_total, clean_filename.c_str());
+
+ return 0;
+}
+
+int tracer(int argc, char *argv[], bool retrace)
+{
+ char *exec[argc];
+ int exec_index = 0;
+
+ char retrace_command[] = "__retrace";
+
+ if (retrace) {
+ std::string trace_file = argv[optind];
+ if (trace_file.find(".json") == std::string::npos) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "Trace file \'%s\' must have .json file extension\n",
+ trace_file.c_str());
+ print_usage();
+ return -1;
+ }
+ }
+
+ /* Get the application to be traced. */
+ if (retrace) {
+ exec[exec_index++] = argv[0]; /* tracee is v4l2-tracer, local or installed */
+ exec[exec_index++] = retrace_command;
+ exec[exec_index++] = argv[optind]; /* json file to be retraced */
+ } else {
+ while (optind < argc)
+ exec[exec_index++] = argv[optind++];
+ }
+ exec[exec_index] = nullptr;
+
+ /* Create a unique trace filename and open a file. */
+ std::string trace_id;
+ if (retrace) {
+ std::string json_file_name = argv[optind];
+ trace_id = json_file_name.substr(0, json_file_name.find(".json"));
+ trace_id += "_retrace";
+ } else {
+ const int timestamp_start_pos = 5;
+ trace_id = std::to_string(time(nullptr));
+ // trace_id = trace_id.substr(timestamp_start_pos, std::string::npos) + "_trace";
+ trace_id = trace_id.substr(timestamp_start_pos) + "_trace";
+
+ }
+ setenv("TRACE_ID", trace_id.c_str(), 0);
+ std::string trace_filename = trace_id + ".json";
+ FILE *trace_file = fopen(trace_filename.c_str(), "w");
+ if (trace_file == nullptr) {
+ fprintf(stderr, "Could not open trace file: %s\n", trace_filename.c_str());
+ perror("");
+ return errno;
+ }
+
+ /* Open the json array.*/
+ fputs("[\n", trace_file);
+
+ /* Add v4l-utils package and git info to the top of the trace file. */
+ std::string json_str;
+ json_object *v4l2_tracer_info_obj = json_object_new_object();
+ json_object_object_add(v4l2_tracer_info_obj, "package_version",
+ json_object_new_string(PACKAGE_VERSION));
+ std::string git_commit_cnt = STRING(GIT_COMMIT_CNT);
+ git_commit_cnt = git_commit_cnt.erase(0, 1); /* remove the hyphen in front of git_commit_cnt */
+ json_object_object_add(v4l2_tracer_info_obj, "git_commit_cnt",
+ json_object_new_string(git_commit_cnt.c_str()));
+ json_object_object_add(v4l2_tracer_info_obj, "git_sha",
+ json_object_new_string(STRING(GIT_SHA)));
+ json_object_object_add(v4l2_tracer_info_obj, "git_commit_date",
+ json_object_new_string(STRING(GIT_COMMIT_DATE)));
+ json_str = json_object_to_json_string(v4l2_tracer_info_obj);
+ fwrite(json_str.c_str(), sizeof(char), json_str.length(), trace_file);
+ fputs(",\n", trace_file);
+ json_object_put(v4l2_tracer_info_obj);
+
+ /* Add v4l2-tracer command line to the top of the trace file. */
+ json_object *tracee_obj = json_object_new_object();
+ std::string tracee;
+ for (int i = 0; i < argc; i++) {
+ tracee += argv[i];
+ tracee += " ";
+ }
+ json_object_object_add(tracee_obj, "Trace", json_object_new_string(tracee.c_str()));
+ const time_t current_time = time(nullptr);
+ json_object_object_add(tracee_obj, "Timestamp", json_object_new_string(ctime(&current_time)));
+
+ json_str = json_object_to_json_string(tracee_obj);
+ fwrite(json_str.c_str(), sizeof(char), json_str.length(), trace_file);
+ fputs(",\n", trace_file);
+ json_object_put(tracee_obj);
+ fclose(trace_file);
+
+ /*
+ * Preload the libv4l2tracer library. If the program is installed, load the library
+ * from its installed location, otherwise load it locally. If it's loaded locally,
+ * use ./configure --disable-dyn-libv4l.
+ */
+ std::string libv4l2tracer_path;
+ std::string program = argv[0];
+ std::size_t idx = program.rfind("/v4l2-tracer");
+ if (idx != std::string::npos) {
+ libv4l2tracer_path = program.replace(program.begin() + idx + 1, program.end(), ".libs");
+ DIR *directory_pointer = opendir(libv4l2tracer_path.c_str());
+ if (directory_pointer == nullptr)
+ libv4l2tracer_path = program.replace(program.begin() + idx, program.end(), "./.libs");
+ else
+ closedir(directory_pointer);
+ } else {
+ libv4l2tracer_path = STRING(LIBTRACER_PATH);
+ }
+ libv4l2tracer_path += "/libv4l2tracer.so";
+ if (is_verbose())
+ fprintf(stderr, "Loading libv4l2tracer: %s\n", libv4l2tracer_path.c_str());
+ setenv("LD_PRELOAD", libv4l2tracer_path.c_str(), 0);
+
+ if (fork() == 0) {
+
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "tracee: ");
+ for (int i = 0; i < exec_index; i++)
+ fprintf(stderr,"%s ", exec[i]);
+ fprintf(stderr, "\n");
+ }
+
+ execvpe(exec[0], (char* const*) exec, environ);
+
+ fprintf(stderr, "%s:%s:%d: ", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "could not execute application \'%s\'", exec[0]);
+ perror(" ");
+ return errno;
+ }
+
+ int exec_result = 0;
+ wait(&exec_result);
+
+ if (WEXITSTATUS(exec_result)) {
+ fprintf(stderr, "Trace error: %s\n", trace_filename.c_str());
+
+ trace_file = fopen(trace_filename.c_str(), "a");
+ fseek(trace_file, 0L, SEEK_END);
+ fputs("\n]\n", trace_file);
+ fclose(trace_file);
+
+ exit(EXIT_FAILURE);
+ }
+
+ /* Close the json-array and the trace file. */
+ trace_file = fopen(trace_filename.c_str(), "a");
+ fseek(trace_file, 0L, SEEK_END);
+ fputs("\n]\n", trace_file);
+ fclose(trace_file);
+
+ if (retrace)
+ fprintf(stderr, "Retrace complete: ");
+ else
+ fprintf(stderr, "Trace complete: ");
+ fprintf(stderr, "%s", trace_filename.c_str());
+ fprintf(stderr, "\n");
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret = -1;
+
+ if (argc <= 1) {
+ print_usage();
+ return ret;
+ }
+
+ ret = get_options(argc, argv);
+
+ if (ret < 0) {
+ if (is_debug())
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ return ret;
+ }
+
+ if (optind == argc) {
+ if (is_debug())
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_usage();
+ return ret;
+ }
+
+ std::string command = argv[optind++];
+
+ if (optind == argc) {
+ if (is_debug())
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ print_usage();
+ return ret;
+ }
+
+ if (command == "trace") {
+ ret = tracer(argc, argv);
+ } else if (command == "retrace") {
+ ret = tracer(argc, argv, true);
+ } else if (command == "__retrace") {
+ /*
+ * This command is meant to be used only internally to allow
+ * v4l2-tracer to recursively trace itself during a retrace.
+ */
+ ret = retrace(argv[optind]);
+ } else if (command == "clean") {
+ ret = clean (argv[optind]);
+ } else {
+ if (is_debug()) {
+ fprintf(stderr, "%s:%s:%d\n", __FILE__, __func__, __LINE__);
+ fprintf(stderr, "tracee: ");
+ for (int i = 0; i < argc; i++)
+ fprintf(stderr,"%s ", argv[i]);
+ fprintf(stderr, "\n");
+ }
+ print_usage();
+ }
+
+ return ret;
+}

Privacy Policy