aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/freebsd/include/linux/v4l2-controls.h378
-rw-r--r--contrib/freebsd/include/linux/videodev2.h30
-rwxr-xr-xcontrib/test/test-media163
-rw-r--r--include/linux/bpf.h851
-rw-r--r--include/linux/cec-funcs.h2
-rw-r--r--include/linux/cec.h3
-rw-r--r--include/linux/lirc.h1
-rw-r--r--include/linux/v4l2-controls.h378
-rw-r--r--include/linux/v4l2-subdev.h12
-rw-r--r--include/linux/videodev2.h30
-rwxr-xr-xsync-with-kernel.sh2
-rw-r--r--utils/cec-compliance/Makefile.am2
-rw-r--r--utils/cec-compliance/cec-compliance.cpp272
-rw-r--r--utils/cec-compliance/cec-compliance.h18
-rw-r--r--utils/cec-compliance/cec-test-adapter.cpp14
-rw-r--r--utils/cec-compliance/cec-test-audio.cpp260
-rw-r--r--utils/cec-compliance/cec-test-power.cpp134
-rw-r--r--utils/cec-compliance/cec-test-tuner-record-timer.cpp1088
-rw-r--r--utils/cec-compliance/cec-test.cpp864
-rw-r--r--utils/cec-ctl/cec-ctl.cpp34
-rw-r--r--utils/cec-ctl/cec-pin.cpp83
-rw-r--r--utils/cec-follower/cec-follower.cpp109
-rw-r--r--utils/cec-follower/cec-follower.h57
-rw-r--r--utils/cec-follower/cec-processing.cpp273
-rw-r--r--utils/cec-follower/cec-tuner.cpp466
-rw-r--r--utils/common/media-info.cpp12
-rw-r--r--utils/common/v4l2-controls.patch15
-rw-r--r--utils/common/v4l2-info.cpp28
-rw-r--r--utils/common/v4l2-pix-formats.h1
-rw-r--r--utils/keytable/bpf_load.c29
-rw-r--r--utils/keytable/bpf_protocols/grundig.c6
-rw-r--r--utils/keytable/bpf_protocols/imon_rsc.c6
-rw-r--r--utils/keytable/bpf_protocols/manchester.c6
-rw-r--r--utils/keytable/bpf_protocols/pulse_distance.c6
-rw-r--r--utils/keytable/bpf_protocols/pulse_length.c6
-rw-r--r--utils/keytable/bpf_protocols/raw.c8
-rw-r--r--utils/keytable/bpf_protocols/rc_mm.c6
-rw-r--r--utils/keytable/bpf_protocols/samsung36.c6
-rw-r--r--utils/keytable/bpf_protocols/xbox-dvd.c6
-rw-r--r--utils/keytable/rc_keymaps/tango.toml56
-rw-r--r--utils/keytable/rc_maps.cfg3
-rwxr-xr-xutils/libcecutil/cec-gen.pl2
-rw-r--r--utils/libcecutil/cec-info.cpp6
-rw-r--r--utils/rds-ctl/rds-ctl.cpp2
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.cpp3
-rw-r--r--utils/v4l2-compliance/v4l2-compliance.h25
-rw-r--r--utils/v4l2-compliance/v4l2-test-buffers.cpp177
-rw-r--r--utils/v4l2-compliance/v4l2-test-colors.cpp10
-rw-r--r--utils/v4l2-compliance/v4l2-test-controls.cpp3
-rw-r--r--utils/v4l2-compliance/v4l2-test-formats.cpp2
-rw-r--r--utils/v4l2-compliance/v4l2-test-input-output.cpp4
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-common.cpp18
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-edid.cpp28
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-meta.cpp2
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-misc.cpp6
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-overlay.cpp10
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-selection.cpp4
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-stds.cpp2
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-streaming.cpp2
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-subdev.cpp12
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-tuner.cpp2
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-vbi.cpp2
-rw-r--r--utils/v4l2-ctl/v4l2-ctl-vidcap.cpp4
-rw-r--r--utils/v4l2-ctl/v4l2-ctl.cpp6
-rw-r--r--utils/v4l2-dbg/v4l2-dbg.cpp2
65 files changed, 4732 insertions, 1326 deletions
diff --git a/contrib/freebsd/include/linux/v4l2-controls.h b/contrib/freebsd/include/linux/v4l2-controls.h
index 40cf8b41..9db08be6 100644
--- a/contrib/freebsd/include/linux/v4l2-controls.h
+++ b/contrib/freebsd/include/linux/v4l2-controls.h
@@ -50,8 +50,13 @@
#ifndef __LINUX_V4L2_CONTROLS_H
#define __LINUX_V4L2_CONTROLS_H
+#include <linux/const.h>
#include <linux/types.h>
+#ifndef _BITUL
+#define _BITUL(x) (1U << (x))
+#endif
+
/* Control classes */
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CTRL_CLASS_CODEC 0x00990000 /* Stateful codec controls */
@@ -66,6 +71,7 @@
#define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */
#define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */
#define V4L2_CTRL_CLASS_CODEC_STATELESS 0x00a40000 /* Stateless codecs controls */
+#define V4L2_CTRL_CLASS_COLORIMETRY 0x00a50000 /* Colorimetry controls */
/* User-class control IDs */
@@ -426,6 +432,11 @@ enum v4l2_mpeg_video_multi_slice_mode {
#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_CODEC_BASE+228)
#define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_CODEC_BASE+229)
#define V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID (V4L2_CID_CODEC_BASE+230)
+#define V4L2_CID_MPEG_VIDEO_AU_DELIMITER (V4L2_CID_CODEC_BASE+231)
+#define V4L2_CID_MPEG_VIDEO_LTR_COUNT (V4L2_CID_CODEC_BASE+232)
+#define V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX (V4L2_CID_CODEC_BASE+233)
+#define V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES (V4L2_CID_CODEC_BASE+234)
+#define V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR (V4L2_CID_CODEC_BASE+235)
/* CIDs for the MPEG-2 Part 2 (H.262) codec */
#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270)
@@ -795,6 +806,9 @@ enum v4l2_mpeg_video_frame_skip_mode {
#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP (V4L2_CID_CODEC_BASE + 651)
#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP (V4L2_CID_CODEC_BASE + 652)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY (V4L2_CID_CODEC_BASE + 653)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE (V4L2_CID_CODEC_BASE + 654)
+
/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
#define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000)
#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0)
@@ -1591,30 +1605,30 @@ struct v4l2_ctrl_h264_decode_params {
#define V4L2_FWHT_VERSION 3
/* Set if this is an interlaced format */
-#define V4L2_FWHT_FL_IS_INTERLACED BIT(0)
+#define V4L2_FWHT_FL_IS_INTERLACED _BITUL(0)
/* Set if this is a bottom-first (NTSC) interlaced format */
-#define V4L2_FWHT_FL_IS_BOTTOM_FIRST BIT(1)
+#define V4L2_FWHT_FL_IS_BOTTOM_FIRST _BITUL(1)
/* Set if each 'frame' contains just one field */
-#define V4L2_FWHT_FL_IS_ALTERNATE BIT(2)
+#define V4L2_FWHT_FL_IS_ALTERNATE _BITUL(2)
/*
* If V4L2_FWHT_FL_IS_ALTERNATE was set, then this is set if this
* 'frame' is the bottom field, else it is the top field.
*/
-#define V4L2_FWHT_FL_IS_BOTTOM_FIELD BIT(3)
+#define V4L2_FWHT_FL_IS_BOTTOM_FIELD _BITUL(3)
/* Set if the Y' plane is uncompressed */
-#define V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED BIT(4)
+#define V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED _BITUL(4)
/* Set if the Cb plane is uncompressed */
-#define V4L2_FWHT_FL_CB_IS_UNCOMPRESSED BIT(5)
+#define V4L2_FWHT_FL_CB_IS_UNCOMPRESSED _BITUL(5)
/* Set if the Cr plane is uncompressed */
-#define V4L2_FWHT_FL_CR_IS_UNCOMPRESSED BIT(6)
+#define V4L2_FWHT_FL_CR_IS_UNCOMPRESSED _BITUL(6)
/* Set if the chroma plane is full height, if cleared it is half height */
-#define V4L2_FWHT_FL_CHROMA_FULL_HEIGHT BIT(7)
+#define V4L2_FWHT_FL_CHROMA_FULL_HEIGHT _BITUL(7)
/* Set if the chroma plane is full width, if cleared it is half width */
-#define V4L2_FWHT_FL_CHROMA_FULL_WIDTH BIT(8)
+#define V4L2_FWHT_FL_CHROMA_FULL_WIDTH _BITUL(8)
/* Set if the alpha plane is uncompressed */
-#define V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9)
+#define V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED _BITUL(9)
/* Set if this is an I Frame */
-#define V4L2_FWHT_FL_I_FRAME BIT(10)
+#define V4L2_FWHT_FL_I_FRAME _BITUL(10)
/* A 4-values flag - the number of components - 1 */
#define V4L2_FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16)
@@ -1655,6 +1669,348 @@ struct v4l2_ctrl_fwht_params {
__u32 quantization;
};
+/* Stateless VP8 control */
+
+#define V4L2_VP8_SEGMENT_FLAG_ENABLED 0x01
+#define V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP 0x02
+#define V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA 0x04
+#define V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE 0x08
+
+/**
+ * struct v4l2_vp8_segment - VP8 segment-based adjustments parameters
+ *
+ * @quant_update: update values for the segment quantizer.
+ * @lf_update: update values for the loop filter level.
+ * @segment_probs: branch probabilities of the segment_id decoding tree.
+ * @padding: padding field. Should be zeroed by applications.
+ * @flags: see V4L2_VP8_SEGMENT_FLAG_{}.
+ *
+ * This structure contains segment-based adjustments related parameters.
+ * See the 'update_segmentation()' part of the frame header syntax,
+ * and section '9.3. Segment-Based Adjustments' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_segment {
+ __s8 quant_update[4];
+ __s8 lf_update[4];
+ __u8 segment_probs[3];
+ __u8 padding;
+ __u32 flags;
+};
+
+#define V4L2_VP8_LF_ADJ_ENABLE 0x01
+#define V4L2_VP8_LF_DELTA_UPDATE 0x02
+#define V4L2_VP8_LF_FILTER_TYPE_SIMPLE 0x04
+
+/**
+ * struct v4l2_vp8_loop_filter - VP8 loop filter parameters
+ *
+ * @ref_frm_delta: Reference frame signed delta values.
+ * @mb_mode_delta: MB prediction mode signed delta values.
+ * @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_{}.
+ *
+ * This structure contains loop filter related parameters.
+ * See the 'mb_lf_adjustments()' part of the frame header syntax,
+ * and section '9.4. Loop Filter Type and Levels' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_loop_filter {
+ __s8 ref_frm_delta[4];
+ __s8 mb_mode_delta[4];
+ __u8 sharpness_level;
+ __u8 level;
+ __u16 padding;
+ __u32 flags;
+};
+
+/**
+ * struct v4l2_vp8_quantization - VP8 quantizattion indices
+ *
+ * @y_ac_qi: luma AC coefficient table index.
+ * @y_dc_delta: luma DC delta vaue.
+ * @y2_dc_delta: y2 block DC delta value.
+ * @y2_ac_delta: y2 block AC delta value.
+ * @uv_dc_delta: chroma DC delta value.
+ * @uv_ac_delta: chroma AC delta value.
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the quantization indices present
+ * in 'quant_indices()' part of the frame header syntax.
+ * See section '9.6. Dequantization Indices' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_quantization {
+ __u8 y_ac_qi;
+ __s8 y_dc_delta;
+ __s8 y2_dc_delta;
+ __s8 y2_ac_delta;
+ __s8 uv_dc_delta;
+ __s8 uv_ac_delta;
+ __u16 padding;
+};
+
+#define V4L2_VP8_COEFF_PROB_CNT 11
+#define V4L2_VP8_MV_PROB_CNT 19
+
+/**
+ * struct v4l2_vp8_entropy - VP8 update probabilities
+ *
+ * @coeff_probs: coefficient probability update values.
+ * @y_mode_probs: luma intra-prediction probabilities.
+ * @uv_mode_probs: chroma intra-prediction probabilities.
+ * @mv_probs: mv decoding probability.
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the update probabilities present in
+ * 'token_prob_update()' and 'mv_prob_update()' part of the frame header.
+ * See section '17.2. Probability Updates' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_entropy {
+ __u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT];
+ __u8 y_mode_probs[4];
+ __u8 uv_mode_probs[3];
+ __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT];
+ __u8 padding[3];
+};
+
+/**
+ * struct v4l2_vp8_entropy_coder_state - VP8 boolean coder state
+ *
+ * @range: coder state value for "Range"
+ * @value: coder state value for "Value"
+ * @bit_count: number of bits left in range "Value".
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the state for the boolean coder, as
+ * explained in section '7. Boolean Entropy Decoder' of the VP8 specification.
+ */
+struct v4l2_vp8_entropy_coder_state {
+ __u8 range;
+ __u8 value;
+ __u8 bit_count;
+ __u8 padding;
+};
+
+#define V4L2_VP8_FRAME_FLAG_KEY_FRAME 0x01
+#define V4L2_VP8_FRAME_FLAG_EXPERIMENTAL 0x02
+#define V4L2_VP8_FRAME_FLAG_SHOW_FRAME 0x04
+#define V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF 0x08
+#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN 0x10
+#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT 0x20
+
+#define V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) \
+ (!!((hdr)->flags & V4L2_VP8_FRAME_FLAG_KEY_FRAME))
+
+#define V4L2_CID_STATELESS_VP8_FRAME (V4L2_CID_CODEC_STATELESS_BASE + 200)
+/**
+ * struct v4l2_ctrl_vp8_frame - VP8 frame parameters
+ *
+ * @segment: segmentation parameters. See &v4l2_vp8_segment for more details
+ * @lf: loop filter parameters. See &v4l2_vp8_loop_filter for more details
+ * @quant: quantization parameters. See &v4l2_vp8_quantization for more details
+ * @entropy: update probabilities. See &v4l2_vp8_entropy for more details
+ * @coder_state: boolean coder state. See &v4l2_vp8_entropy_coder_state for more details
+ * @width: frame width.
+ * @height: frame height.
+ * @horizontal_scale: horizontal scaling factor.
+ * @vertical_scale: vertical scaling factor.
+ * @version: bitstream version.
+ * @prob_skip_false: frame header syntax element.
+ * @prob_intra: frame header syntax element.
+ * @prob_last: frame header syntax element.
+ * @prob_gf: frame header syntax element.
+ * @num_dct_parts: number of DCT coefficients partitions.
+ * @first_part_size: size of the first partition, i.e. the control partition.
+ * @first_part_header_bits: size in bits of the first partition header portion.
+ * @dct_part_sizes: DCT coefficients sizes.
+ * @last_frame_ts: "last" reference buffer timestamp.
+ * The timestamp refers to the timestamp field in struct v4l2_buffer.
+ * Use v4l2_timeval_to_ns() to convert the struct timeval to a __u64.
+ * @golden_frame_ts: "golden" reference buffer timestamp.
+ * @alt_frame_ts: "alt" reference buffer timestamp.
+ * @flags: see V4L2_VP8_FRAME_FLAG_{}.
+ */
+struct v4l2_ctrl_vp8_frame {
+ struct v4l2_vp8_segment segment;
+ struct v4l2_vp8_loop_filter lf;
+ struct v4l2_vp8_quantization quant;
+ struct v4l2_vp8_entropy entropy;
+ struct v4l2_vp8_entropy_coder_state coder_state;
+
+ __u16 width;
+ __u16 height;
+
+ __u8 horizontal_scale;
+ __u8 vertical_scale;
+
+ __u8 version;
+ __u8 prob_skip_false;
+ __u8 prob_intra;
+ __u8 prob_last;
+ __u8 prob_gf;
+ __u8 num_dct_parts;
+
+ __u32 first_part_size;
+ __u32 first_part_header_bits;
+ __u32 dct_part_sizes[8];
+
+ __u64 last_frame_ts;
+ __u64 golden_frame_ts;
+ __u64 alt_frame_ts;
+
+ __u64 flags;
+};
+
+/* Stateless MPEG-2 controls */
+
+#define V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE 0x01
+
+#define V4L2_CID_STATELESS_MPEG2_SEQUENCE (V4L2_CID_CODEC_STATELESS_BASE+220)
+/**
+ * struct v4l2_ctrl_mpeg2_sequence - MPEG-2 sequence header
+ *
+ * All the members on this structure match the sequence header and sequence
+ * extension syntaxes as specified by the MPEG-2 specification.
+ *
+ * Fields horizontal_size, vertical_size and vbv_buffer_size are a
+ * combination of respective _value and extension syntax elements,
+ * as described in section 6.3.3 "Sequence header".
+ *
+ * @horizontal_size: combination of elements horizontal_size_value and
+ * horizontal_size_extension.
+ * @vertical_size: combination of elements vertical_size_value and
+ * vertical_size_extension.
+ * @vbv_buffer_size: combination of elements vbv_buffer_size_value and
+ * vbv_buffer_size_extension.
+ * @profile_and_level_indication: see MPEG-2 specification.
+ * @chroma_format: see MPEG-2 specification.
+ * @flags: see V4L2_MPEG2_SEQ_FLAG_{}.
+ */
+struct v4l2_ctrl_mpeg2_sequence {
+ __u16 horizontal_size;
+ __u16 vertical_size;
+ __u32 vbv_buffer_size;
+ __u16 profile_and_level_indication;
+ __u8 chroma_format;
+ __u8 flags;
+};
+
+#define V4L2_MPEG2_PIC_CODING_TYPE_I 1
+#define V4L2_MPEG2_PIC_CODING_TYPE_P 2
+#define V4L2_MPEG2_PIC_CODING_TYPE_B 3
+#define V4L2_MPEG2_PIC_CODING_TYPE_D 4
+
+#define V4L2_MPEG2_PIC_TOP_FIELD 0x1
+#define V4L2_MPEG2_PIC_BOTTOM_FIELD 0x2
+#define V4L2_MPEG2_PIC_FRAME 0x3
+
+#define V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST 0x0001
+#define V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT 0x0002
+#define V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV 0x0004
+#define V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE 0x0008
+#define V4L2_MPEG2_PIC_FLAG_INTRA_VLC 0x0010
+#define V4L2_MPEG2_PIC_FLAG_ALT_SCAN 0x0020
+#define V4L2_MPEG2_PIC_FLAG_REPEAT_FIRST 0x0040
+#define V4L2_MPEG2_PIC_FLAG_PROGRESSIVE 0x0080
+
+#define V4L2_CID_STATELESS_MPEG2_PICTURE (V4L2_CID_CODEC_STATELESS_BASE+221)
+/**
+ * struct v4l2_ctrl_mpeg2_picture - MPEG-2 picture header
+ *
+ * All the members on this structure match the picture header and picture
+ * coding extension syntaxes as specified by the MPEG-2 specification.
+ *
+ * @backward_ref_ts: timestamp of the V4L2 capture buffer to use as
+ * reference for backward prediction.
+ * @forward_ref_ts: timestamp of the V4L2 capture buffer to use as
+ * reference for forward prediction. These timestamp refers to the
+ * timestamp field in struct v4l2_buffer. Use v4l2_timeval_to_ns()
+ * to convert the struct timeval to a __u64.
+ * @flags: see V4L2_MPEG2_PIC_FLAG_{}.
+ * @f_code: see MPEG-2 specification.
+ * @picture_coding_type: see MPEG-2 specification.
+ * @picture_structure: see V4L2_MPEG2_PIC_{}_FIELD.
+ * @intra_dc_precision: see MPEG-2 specification.
+ * @reserved: padding field. Should be zeroed by applications.
+ */
+struct v4l2_ctrl_mpeg2_picture {
+ __u64 backward_ref_ts;
+ __u64 forward_ref_ts;
+ __u32 flags;
+ __u8 f_code[2][2];
+ __u8 picture_coding_type;
+ __u8 picture_structure;
+ __u8 intra_dc_precision;
+ __u8 reserved[5];
+};
+
+#define V4L2_CID_STATELESS_MPEG2_QUANTISATION (V4L2_CID_CODEC_STATELESS_BASE+222)
+/**
+ * struct v4l2_ctrl_mpeg2_quantisation - MPEG-2 quantisation
+ *
+ * Quantisation matrices as specified by section 6.3.7
+ * "Quant matrix extension".
+ *
+ * @intra_quantiser_matrix: The quantisation matrix coefficients
+ * for intra-coded frames, in zigzag scanning order. It is relevant
+ * for both luma and chroma components, although it can be superseded
+ * by the chroma-specific matrix for non-4:2:0 YUV formats.
+ * @non_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for non-intra-coded frames, in zigzag scanning order. It is relevant
+ * for both luma and chroma components, although it can be superseded
+ * by the chroma-specific matrix for non-4:2:0 YUV formats.
+ * @chroma_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for the chominance component of intra-coded frames, in zigzag scanning
+ * order. Only relevant for 4:2:2 and 4:4:4 YUV formats.
+ * @chroma_non_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for the chrominance component of non-intra-coded frames, in zigzag scanning
+ * order. Only relevant for 4:2:2 and 4:4:4 YUV formats.
+ */
+struct v4l2_ctrl_mpeg2_quantisation {
+ __u8 intra_quantiser_matrix[64];
+ __u8 non_intra_quantiser_matrix[64];
+ __u8 chroma_intra_quantiser_matrix[64];
+ __u8 chroma_non_intra_quantiser_matrix[64];
+};
+
+#define V4L2_CID_COLORIMETRY_CLASS_BASE (V4L2_CTRL_CLASS_COLORIMETRY | 0x900)
+#define V4L2_CID_COLORIMETRY_CLASS (V4L2_CTRL_CLASS_COLORIMETRY | 1)
+
+#define V4L2_CID_COLORIMETRY_HDR10_CLL_INFO (V4L2_CID_COLORIMETRY_CLASS_BASE + 0)
+
+struct v4l2_ctrl_hdr10_cll_info {
+ __u16 max_content_light_level;
+ __u16 max_pic_average_light_level;
+};
+
+#define V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY (V4L2_CID_COLORIMETRY_CLASS_BASE + 1)
+
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_LOW 5
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_HIGH 37000
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_LOW 5
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_HIGH 42000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW 5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH 37000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW 5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH 42000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_LOW 50000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_HIGH 100000000
+#define V4L2_HDR10_MASTERING_MIN_LUMA_LOW 1
+#define V4L2_HDR10_MASTERING_MIN_LUMA_HIGH 50000
+
+struct v4l2_ctrl_hdr10_mastering_display {
+ __u16 display_primaries_x[3];
+ __u16 display_primaries_y[3];
+ __u16 white_point_x;
+ __u16 white_point_y;
+ __u32 max_display_mastering_luminance;
+ __u32 min_display_mastering_luminance;
+};
+
/* MPEG-compression definitions kept for backwards compatibility */
#define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC
#define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS
diff --git a/contrib/freebsd/include/linux/videodev2.h b/contrib/freebsd/include/linux/videodev2.h
index a4f6d627..c8a61dfd 100644
--- a/contrib/freebsd/include/linux/videodev2.h
+++ b/contrib/freebsd/include/linux/videodev2.h
@@ -614,6 +614,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */
#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */
#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */
+#define V4L2_PIX_FMT_YUV24 v4l2_fourcc('Y', 'U', 'V', '3') /* 24 YUV-8-8-8 */
#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */
#define V4L2_PIX_FMT_AYUV32 v4l2_fourcc('A', 'Y', 'U', 'V') /* 32 AYUV-8-8-8-8 */
#define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */
@@ -722,6 +723,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
#define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_VP8_FRAME v4l2_fourcc('V', 'P', '8', 'F') /* VP8 parsed frame */
#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */
#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */
#define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */
@@ -986,8 +988,10 @@ struct v4l2_requestbuffers {
* pointing to this plane
* @fd: when memory is V4L2_MEMORY_DMABUF, a userspace file
* descriptor associated with this plane
+ * @m: union of @mem_offset, @userptr and @fd
* @data_offset: offset in the plane to the start of data; usually 0,
* unless there is a header in front of the data
+ * @reserved: drivers and applications must zero this array
*
* Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
* with two planes can have one plane for Y, and another for interleaved CbCr
@@ -1029,10 +1033,14 @@ struct v4l2_plane {
* a userspace file descriptor associated with this buffer
* @planes: for multiplanar buffers; userspace pointer to the array of plane
* info structs for this buffer
+ * @m: union of @offset, @userptr, @planes and @fd
* @length: size in bytes of the buffer (NOT its payload) for single-plane
* buffers (when type != *_MPLANE); number of elements in the
* planes array for multi-plane buffers
+ * @reserved2: drivers and applications must zero this field
* @request_fd: fd of the request that this buffer should use
+ * @reserved: for backwards compatibility with applications that do not know
+ * about @request_fd
*
* Contains data exchanged by application and driver using one of the Streaming
* I/O methods.
@@ -1065,7 +1073,7 @@ struct v4l2_buffer {
/**
* v4l2_timeval_to_ns - Convert timeval to nanoseconds
- * @ts: pointer to the timeval variable to be converted
+ * @tv: pointer to the timeval variable to be converted
*
* Returns the scalar nanosecond representation of the timeval
* parameter.
@@ -1125,6 +1133,7 @@ static __inline__ uint64_t v4l2_timeval_to_ns(const struct timeval *tv)
* @flags: flags for newly created file, currently only O_CLOEXEC is
* supported, refer to manual of open syscall for more details
* @fd: file descriptor associated with DMABUF (set by driver)
+ * @reserved: drivers and applications must zero this array
*
* Contains data used for exporting a video buffer as DMABUF file descriptor.
* The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
@@ -1742,6 +1751,10 @@ struct v4l2_ext_control {
struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
struct v4l2_ctrl_fwht_params *p_fwht_params;
+ struct v4l2_ctrl_vp8_frame *p_vp8_frame;
+ struct v4l2_ctrl_mpeg2_sequence *p_mpeg2_sequence;
+ struct v4l2_ctrl_mpeg2_picture *p_mpeg2_picture;
+ struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quantisation;
void *ptr;
};
} __attribute__ ((packed));
@@ -1785,6 +1798,9 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_U32 = 0x0102,
V4L2_CTRL_TYPE_AREA = 0x0106,
+ V4L2_CTRL_TYPE_HDR10_CLL_INFO = 0x0110,
+ V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY = 0x0111,
+
V4L2_CTRL_TYPE_H264_SPS = 0x0200,
V4L2_CTRL_TYPE_H264_PPS = 0x0201,
V4L2_CTRL_TYPE_H264_SCALING_MATRIX = 0x0202,
@@ -1793,6 +1809,12 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205,
V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220,
+
+ V4L2_CTRL_TYPE_VP8_FRAME = 0x0240,
+
+ V4L2_CTRL_TYPE_MPEG2_QUANTISATION = 0x0250,
+ V4L2_CTRL_TYPE_MPEG2_SEQUENCE = 0x0251,
+ V4L2_CTRL_TYPE_MPEG2_PICTURE = 0x0252,
};
/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
@@ -2230,6 +2252,7 @@ struct v4l2_mpeg_vbi_fmt_ivtv {
* this plane will be used
* @bytesperline: distance in bytes between the leftmost pixels in two
* adjacent lines
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_plane_pix_format {
uint32_t sizeimage;
@@ -2248,8 +2271,10 @@ struct v4l2_plane_pix_format {
* @num_planes: number of planes for this format
* @flags: format flags (V4L2_PIX_FMT_FLAG_*)
* @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @hsv_enc: enum v4l2_hsv_encoding, HSV encoding
* @quantization: enum v4l2_quantization, colorspace quantization
* @xfer_func: enum v4l2_xfer_func, colorspace transfer function
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_pix_format_mplane {
uint32_t width;
@@ -2274,6 +2299,7 @@ struct v4l2_pix_format_mplane {
* struct v4l2_sdr_format - SDR format definition
* @pixelformat: little endian four character code (fourcc)
* @buffersize: maximum size in bytes required for data
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_sdr_format {
uint32_t pixelformat;
@@ -2300,6 +2326,8 @@ struct v4l2_meta_format {
* @vbi: raw VBI capture or output parameters
* @sliced: sliced VBI capture or output parameters
* @raw_data: placeholder for future extensions and custom formats
+ * @fmt: union of @pix, @pix_mp, @win, @vbi, @sliced, @sdr, @meta
+ * and @raw_data
*/
struct v4l2_format {
uint32_t type;
diff --git a/contrib/test/test-media b/contrib/test/test-media
index 10b7e89d..4b6276c8 100755
--- a/contrib/test/test-media
+++ b/contrib/test/test-media
@@ -13,6 +13,7 @@ cec=0
cecpwr=--skip-test-standby-resume
kmemleak=0
dmesg=0
+setup=0
unload=0
unbind_time=1
reunbind_time=9
@@ -50,15 +51,16 @@ if [ -z "$1" ]; then
echo "-kmemleak: enable memory leak scan"
echo "-dmesg: run dmesg at the end"
echo "-32: use v4l2-ctl-32 and v4l2-compliance-32 to test the 32 bit compat layer"
+ echo "-setup: load and configure all the drivers, but do not run tests"
echo
echo Test Targets:
- echo "vidtv: test the vidtv driver"
echo "vivid: test the vivid driver"
echo "vim2m: test the vim2m driver"
echo "vimc: test the vimc driver"
echo "vicodec: test the vicodec driver"
+ echo "vidtv: test the vidtv driver"
echo "cec: adds the vivid CEC compliance tests, 'cec-pwr' adds the CEC standby/wakeup tests."
- echo "all: equals 'vivid vim2m vimc vicodec cec cec-pwr'"
+ echo "all: equals 'vivid vim2m vimc vicodec vidtv cec cec-pwr'"
echo "mc: equals 'vivid vim2m vimc vicodec'"
exit 0
fi
@@ -78,6 +80,9 @@ while [ ! -z "$1" ]; do
-dmesg)
dmesg=1
;;
+ -setup)
+ setup=1
+ ;;
-kmemleak)
if [ -f /sys/kernel/debug/kmemleak ]; then
kmemleak=1
@@ -137,6 +142,20 @@ while [ ! -z "$1" ]; do
shift
done
+if [ $vidtv -eq 1 ]; then
+ if [ ! -f /proc/config.gz ]; then
+ if cat /proc/config.gz | gunzip | grep -q CONFIG_MEDIA_CONTROLLER_DVB=y ; then
+ echo vidtv cannot be tested since CONFIG_MEDIA_CONTROLLER_DVB is not enabled
+ exit 0
+ fi
+ elif [ -f .config ]; then
+ if ! grep -q CONFIG_MEDIA_CONTROLLER_DVB=y .config ; then
+ echo vidtv cannot be tested since CONFIG_MEDIA_CONTROLLER_DVB is not enabled
+ exit 0
+ fi
+ fi
+fi
+
if [ $unload -eq 1 ]; then
test-media-unload.pl
dmesg -n notice
@@ -168,7 +187,7 @@ $v4l2_ctl -z platform:vivid-002 -d vivid-002-vid-out -o1 -x width=3840,height=21
echo
-if [ $vivid -eq 1 ]; then
+if [ $vivid -eq 1 -a $setup -eq 0 ]; then
dmesg -n notice
echo
echo vivid compliance tests, contiguous planes | tee /dev/kmsg
@@ -192,9 +211,6 @@ fi
if [ $cec -eq 1 ]; then
dmesg -n notice
- echo
- echo vivid cec compliance tests | tee /dev/kmsg
- echo
if ! cec-ctl -D vivid -a vivid-000-vid-cap0 >/dev/null ; then
echo "FAIL: the vivid module had no cec support" | tee -a $tmp
echo "Grand Total for vivid cec: Succeeded: 0, Failed: 1, Warnings: 0" | tee -a $tmp
@@ -205,6 +221,12 @@ if [ $cec -eq 1 ]; then
sleep 1
cec-ctl -D vivid -a vivid-000-vid-out0 --playback
sleep 1
+fi
+
+if [ $cec -eq 1 -a $setup -eq 0 ]; then
+ echo
+ echo vivid cec compliance tests | tee /dev/kmsg
+ echo
trap 'kill $f0 $f1 2>/dev/null' INT
cec-follower -s -D vivid -a vivid-000-vid-cap0 2>&1 >/dev/null &
f0=$!
@@ -230,7 +252,7 @@ if [ $cec -eq 1 ]; then
vivid=1
fi
-if [ $vivid -eq 1 ]; then
+if [ $vivid -eq 1 -a $setup -eq 0 ]; then
echo
echo unbind vivid | tee /dev/kmsg
echo
@@ -294,7 +316,6 @@ if [ $vim2m -eq 1 ]; then
modprobe vim2m
sleep 1
dmesg -n notice
- echo
if ! $v4l2_ctl -z platform:vim2m ; then
echo "FAIL: the vim2m module failed to load" | tee -a $tmp
@@ -303,7 +324,10 @@ if [ $vim2m -eq 1 ]; then
rmmod vivid
exit 0
fi
+fi
+if [ $vim2m -eq 1 -a $setup -eq 0 ]; then
+ echo
echo vim2m compliance tests | tee /dev/kmsg
echo
date
@@ -384,6 +408,9 @@ if [ $vimc -eq 1 ]; then
$v4l2_ctl -z platform:vimc -d "RGB/YUV Capture" -v width=1920,height=1440
$v4l2_ctl -z platform:vimc -d "Raw Capture 0" -v pixelformat=BA81
$v4l2_ctl -z platform:vimc -d "Raw Capture 1" -v pixelformat=BA81
+fi
+
+if [ $vimc -eq 1 -a $setup -eq 0 ]; then
dmesg -n notice
echo
echo vimc compliance tests | tee /dev/kmsg
@@ -455,13 +482,18 @@ if [ $vimc -eq 1 ]; then
fi
if [ $vicodec -eq 1 ]; then
- tmpdir=`mktemp -d`
+ if [ $setup -eq 0 ]; then
+ tmpdir=`mktemp -d`
+ else
+ tmpdir=/tmp/vicodec-test
+ rm -rf $tmpdir
+ mkdir $tmpdir
+ fi
rmmod vicodec 2&>/dev/null
modprobe vicodec
sleep 2
dmesg -n notice
- echo
if ! $v4l2_ctl -z platform:vicodec ; then
echo "FAIL: the vicodec module failed to load" | tee -a $tmp
@@ -471,18 +503,8 @@ if [ $vicodec -eq 1 ]; then
exit 0
fi
- echo vicodec media controller compliance tests | tee /dev/kmsg
echo
- date
- stdbuf -oL $v4l2_compliance -M platform:vicodec 2>&1 | tee -a $tmp
-
- echo
- echo vicodec encoder compliance tests | tee /dev/kmsg
- echo
- stdbuf -oL $v4l2_compliance -z platform:vicodec -d stateful-encoder-source -z platform:vivid-002 -e vivid-002-vid-cap -s10 -P -a 2>&1 | tee -a $tmp
-
- echo
- echo vicodec create test files | tee /dev/kmsg
+ echo vicodec create test files in $tmpdir | tee /dev/kmsg
echo
encful_opts='-z platform:vicodec -d stateful-encoder-source --stream-mmap --stream-out-mmap --stream-out-hor-speed=1'
@@ -507,6 +529,19 @@ if [ $vicodec -eq 1 ]; then
cat $tmpdir/comp.yu12.1280.2 $tmpdir/comp.yu12.1920.2 >$tmpdir/comp.yu12.mix.4
cat $tmpdir/comp.hdr.yu12.mix.4 $tmpdir/comp.hdr.yu12.mix.4 $tmpdir/comp.hdr.yu12.mix.4 $tmpdir/comp.hdr.yu12.mix.4 $tmpdir/comp.hdr.yu12.mix.4 $tmpdir/comp.hdr.yu12.mix.4 >$tmpdir/comp.hdr.yu12.mix.4.6
cat $tmpdir/comp.yu12.mix.4 $tmpdir/comp.yu12.mix.4 $tmpdir/comp.yu12.mix.4 $tmpdir/comp.yu12.mix.4 $tmpdir/comp.yu12.mix.4 $tmpdir/comp.yu12.mix.4 >$tmpdir/comp.yu12.mix.4.6
+fi
+
+if [ $vicodec -eq 1 -a $setup -eq 0 ]; then
+ echo
+ echo vicodec media controller compliance tests | tee /dev/kmsg
+ echo
+ date
+ stdbuf -oL $v4l2_compliance -M platform:vicodec 2>&1 | tee -a $tmp
+
+ echo
+ echo vicodec encoder compliance tests | tee /dev/kmsg
+ echo
+ stdbuf -oL $v4l2_compliance -z platform:vicodec -d stateful-encoder-source -z platform:vivid-002 -e vivid-002-vid-cap -s10 -P -a 2>&1 | tee -a $tmp
echo
echo vicodec decoder compliance tests | tee /dev/kmsg
@@ -603,24 +638,11 @@ if [ $vicodec -eq 1 ]; then
echo
fi
-date
-echo
-echo unbind vivid | tee /dev/kmsg
-echo
-echo -n vivid.0 >/sys/bus/platform/drivers/vivid/unbind
-sleep $unbind_time
-echo
-echo rmmod vivid | tee /dev/kmsg
-echo
-rmmod vivid
-sleep $rmmod_time
-
if [ $vidtv -eq 1 ]; then
rmmod dvb_vidtv_bridge dvb_vidtv_tuner dvb_vidtv_demod 2&>/dev/null
modprobe vidtv
sleep 2
dmesg -n notice
- echo
if ! media-ctl -d platform:vidtv ; then
echo "FAIL: the vidtv module failed to load" | tee -a $tmp
@@ -629,7 +651,10 @@ if [ $vidtv -eq 1 ]; then
rmmod dvb_vidtv_bridge dvb_vidtv_tuner dvb_vidtv_demod 2&>/dev/null
exit 0
fi
+fi
+if [ $vidtv -eq 1 -a $setup -eq 0 ]; then
+ echo
echo vidtv media controller compliance tests | tee /dev/kmsg
echo
date
@@ -686,38 +711,54 @@ if [ $vidtv -eq 1 ]; then
fi
-date
-echo
-echo Summary:
-echo
+if [ $setup -eq 0 ]; then
+ date
+ echo
+ echo unbind vivid | tee /dev/kmsg
+ echo
+ echo -n vivid.0 >/sys/bus/platform/drivers/vivid/unbind
+ sleep $unbind_time
+ echo
+ echo rmmod vivid | tee /dev/kmsg
+ echo
+ rmmod vivid
+ sleep $rmmod_time
+fi
-grep 'Total' $tmp | perl -e '
-while (<>) {
- if (/Total/) {
- print;
- $added_nl = /^Grand/ || /\/dev\/cec/;
- printf("\n") if $added_nl;
+if [ $setup -eq 0 ]; then
+ date
+ echo
+ echo Summary:
+ echo
+
+ grep 'Total' $tmp | perl -e '
+ while (<>) {
+ if (/Total/) {
+ print;
+ $added_nl = /^Grand/ || /\/dev\/cec/;
+ printf("\n") if $added_nl;
+ }
+ next unless /^Total/;
+ ($succeeded, $failed, $warnings) = /Succeeded: (\d+), Failed: (\d+), Warnings: (\d+)/;
+ $tot_succeeded += $succeeded;
+ $tot_failed += $failed;
+ $tot_warnings += $warnings;
}
- next unless /^Total/;
- ($succeeded, $failed, $warnings) = /Succeeded: (\d+), Failed: (\d+), Warnings: (\d+)/;
- $tot_succeeded += $succeeded;
- $tot_failed += $failed;
- $tot_warnings += $warnings;
-}
-printf("\n") unless $added_nl;
-printf("Final Summary: %d, Succeeded: %d, Failed: %d, Warnings: %d\n",
- $tot_succeeded + $tot_failed, $tot_succeeded, $tot_failed, $tot_warnings);
-'
+ printf("\n") unless $added_nl;
+ printf("Final Summary: %d, Succeeded: %d, Failed: %d, Warnings: %d\n",
+ $tot_succeeded + $tot_failed, $tot_succeeded, $tot_failed, $tot_warnings);
+ '
-date
-rm -f $tmp
+ date
+ rm -f $tmp
-if [ $dmesg -eq 1 ]; then
- echo
- echo -----------------------------------------------------------
- echo
- dmesg
+ if [ $dmesg -eq 1 ]; then
+ echo
+ echo -----------------------------------------------------------
+ echo
+ dmesg
+ fi
fi
dmesg -n $cur_lvl
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 9c135426..33abd855 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -93,7 +93,738 @@ union bpf_iter_link_info {
} map;
};
-/* BPF syscall commands, see bpf(2) man-page for details. */
+/* BPF syscall commands, see bpf(2) man-page for more details. */
+/**
+ * DOC: eBPF Syscall Preamble
+ *
+ * The operation to be performed by the **bpf**\ () system call is determined
+ * by the *cmd* argument. Each operation takes an accompanying argument,
+ * provided via *attr*, which is a pointer to a union of type *bpf_attr* (see
+ * below). The size argument is the size of the union pointed to by *attr*.
+ */
+/**
+ * DOC: eBPF Syscall Commands
+ *
+ * BPF_MAP_CREATE
+ * Description
+ * Create a map and return a file descriptor that refers to the
+ * map. The close-on-exec file descriptor flag (see **fcntl**\ (2))
+ * is automatically enabled for the new file descriptor.
+ *
+ * Applying **close**\ (2) to the file descriptor returned by
+ * **BPF_MAP_CREATE** will delete the map (but see NOTES).
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_MAP_LOOKUP_ELEM
+ * Description
+ * Look up an element with a given *key* in the map referred to
+ * by the file descriptor *map_fd*.
+ *
+ * The *flags* argument may be specified as one of the
+ * following:
+ *
+ * **BPF_F_LOCK**
+ * Look up the value of a spin-locked map without
+ * returning the lock. This must be specified if the
+ * elements contain a spinlock.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_MAP_UPDATE_ELEM
+ * Description
+ * Create or update an element (key/value pair) in a specified map.
+ *
+ * The *flags* argument should be specified as one of the
+ * following:
+ *
+ * **BPF_ANY**
+ * Create a new element or update an existing element.
+ * **BPF_NOEXIST**
+ * Create a new element only if it did not exist.
+ * **BPF_EXIST**
+ * Update an existing element.
+ * **BPF_F_LOCK**
+ * Update a spin_lock-ed map element.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**,
+ * **E2BIG**, **EEXIST**, or **ENOENT**.
+ *
+ * **E2BIG**
+ * The number of elements in the map reached the
+ * *max_entries* limit specified at map creation time.
+ * **EEXIST**
+ * If *flags* specifies **BPF_NOEXIST** and the element
+ * with *key* already exists in the map.
+ * **ENOENT**
+ * If *flags* specifies **BPF_EXIST** and the element with
+ * *key* does not exist in the map.
+ *
+ * BPF_MAP_DELETE_ELEM
+ * Description
+ * Look up and delete an element by key in a specified map.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_MAP_GET_NEXT_KEY
+ * Description
+ * Look up an element by key in a specified map and return the key
+ * of the next element. Can be used to iterate over all elements
+ * in the map.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * The following cases can be used to iterate over all elements of
+ * the map:
+ *
+ * * If *key* is not found, the operation returns zero and sets
+ * the *next_key* pointer to the key of the first element.
+ * * If *key* is found, the operation returns zero and sets the
+ * *next_key* pointer to the key of the next element.
+ * * If *key* is the last element, returns -1 and *errno* is set
+ * to **ENOENT**.
+ *
+ * May set *errno* to **ENOMEM**, **EFAULT**, **EPERM**, or
+ * **EINVAL** on error.
+ *
+ * BPF_PROG_LOAD
+ * Description
+ * Verify and load an eBPF program, returning a new file
+ * descriptor associated with the program.
+ *
+ * Applying **close**\ (2) to the file descriptor returned by
+ * **BPF_PROG_LOAD** will unload the eBPF program (but see NOTES).
+ *
+ * The close-on-exec file descriptor flag (see **fcntl**\ (2)) is
+ * automatically enabled for the new file descriptor.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_OBJ_PIN
+ * Description
+ * Pin an eBPF program or map referred by the specified *bpf_fd*
+ * to the provided *pathname* on the filesystem.
+ *
+ * The *pathname* argument must not contain a dot (".").
+ *
+ * On success, *pathname* retains a reference to the eBPF object,
+ * preventing deallocation of the object when the original
+ * *bpf_fd* is closed. This allow the eBPF object to live beyond
+ * **close**\ (\ *bpf_fd*\ ), and hence the lifetime of the parent
+ * process.
+ *
+ * Applying **unlink**\ (2) or similar calls to the *pathname*
+ * unpins the object from the filesystem, removing the reference.
+ * If no other file descriptors or filesystem nodes refer to the
+ * same object, it will be deallocated (see NOTES).
+ *
+ * The filesystem type for the parent directory of *pathname* must
+ * be **BPF_FS_MAGIC**.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_OBJ_GET
+ * Description
+ * Open a file descriptor for the eBPF object pinned to the
+ * specified *pathname*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_PROG_ATTACH
+ * Description
+ * Attach an eBPF program to a *target_fd* at the specified
+ * *attach_type* hook.
+ *
+ * The *attach_type* specifies the eBPF attachment point to
+ * attach the program to, and must be one of *bpf_attach_type*
+ * (see below).
+ *
+ * The *attach_bpf_fd* must be a valid file descriptor for a
+ * loaded eBPF program of a cgroup, flow dissector, LIRC, sockmap
+ * or sock_ops type corresponding to the specified *attach_type*.
+ *
+ * The *target_fd* must be a valid file descriptor for a kernel
+ * object which depends on the attach type of *attach_bpf_fd*:
+ *
+ * **BPF_PROG_TYPE_CGROUP_DEVICE**,
+ * **BPF_PROG_TYPE_CGROUP_SKB**,
+ * **BPF_PROG_TYPE_CGROUP_SOCK**,
+ * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**,
+ * **BPF_PROG_TYPE_CGROUP_SOCKOPT**,
+ * **BPF_PROG_TYPE_CGROUP_SYSCTL**,
+ * **BPF_PROG_TYPE_SOCK_OPS**
+ *
+ * Control Group v2 hierarchy with the eBPF controller
+ * enabled. Requires the kernel to be compiled with
+ * **CONFIG_CGROUP_BPF**.
+ *
+ * **BPF_PROG_TYPE_FLOW_DISSECTOR**
+ *
+ * Network namespace (eg /proc/self/ns/net).
+ *
+ * **BPF_PROG_TYPE_LIRC_MODE2**
+ *
+ * LIRC device path (eg /dev/lircN). Requires the kernel
+ * to be compiled with **CONFIG_BPF_LIRC_MODE2**.
+ *
+ * **BPF_PROG_TYPE_SK_SKB**,
+ * **BPF_PROG_TYPE_SK_MSG**
+ *
+ * eBPF map of socket type (eg **BPF_MAP_TYPE_SOCKHASH**).
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_PROG_DETACH
+ * Description
+ * Detach the eBPF program associated with the *target_fd* at the
+ * hook specified by *attach_type*. The program must have been
+ * previously attached using **BPF_PROG_ATTACH**.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_PROG_TEST_RUN
+ * Description
+ * Run the eBPF program associated with the *prog_fd* a *repeat*
+ * number of times against a provided program context *ctx_in* and
+ * data *data_in*, and return the modified program context
+ * *ctx_out*, *data_out* (for example, packet data), result of the
+ * execution *retval*, and *duration* of the test run.
+ *
+ * The sizes of the buffers provided as input and output
+ * parameters *ctx_in*, *ctx_out*, *data_in*, and *data_out* must
+ * be provided in the corresponding variables *ctx_size_in*,
+ * *ctx_size_out*, *data_size_in*, and/or *data_size_out*. If any
+ * of these parameters are not provided (ie set to NULL), the
+ * corresponding size field must be zero.
+ *
+ * Some program types have particular requirements:
+ *
+ * **BPF_PROG_TYPE_SK_LOOKUP**
+ * *data_in* and *data_out* must be NULL.
+ *
+ * **BPF_PROG_TYPE_XDP**
+ * *ctx_in* and *ctx_out* must be NULL.
+ *
+ * **BPF_PROG_TYPE_RAW_TRACEPOINT**,
+ * **BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE**
+ *
+ * *ctx_out*, *data_in* and *data_out* must be NULL.
+ * *repeat* must be zero.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * **ENOSPC**
+ * Either *data_size_out* or *ctx_size_out* is too small.
+ * **ENOTSUPP**
+ * This command is not supported by the program type of
+ * the program referred to by *prog_fd*.
+ *
+ * BPF_PROG_GET_NEXT_ID
+ * Description
+ * Fetch the next eBPF program currently loaded into the kernel.
+ *
+ * Looks for the eBPF program with an id greater than *start_id*
+ * and updates *next_id* on success. If no other eBPF programs
+ * remain with ids higher than *start_id*, returns -1 and sets
+ * *errno* to **ENOENT**.
+ *
+ * Return
+ * Returns zero on success. On error, or when no id remains, -1
+ * is returned and *errno* is set appropriately.
+ *
+ * BPF_MAP_GET_NEXT_ID
+ * Description
+ * Fetch the next eBPF map currently loaded into the kernel.
+ *
+ * Looks for the eBPF map with an id greater than *start_id*
+ * and updates *next_id* on success. If no other eBPF maps
+ * remain with ids higher than *start_id*, returns -1 and sets
+ * *errno* to **ENOENT**.
+ *
+ * Return
+ * Returns zero on success. On error, or when no id remains, -1
+ * is returned and *errno* is set appropriately.
+ *
+ * BPF_PROG_GET_FD_BY_ID
+ * Description
+ * Open a file descriptor for the eBPF program corresponding to
+ * *prog_id*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_MAP_GET_FD_BY_ID
+ * Description
+ * Open a file descriptor for the eBPF map corresponding to
+ * *map_id*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_OBJ_GET_INFO_BY_FD
+ * Description
+ * Obtain information about the eBPF object corresponding to
+ * *bpf_fd*.
+ *
+ * Populates up to *info_len* bytes of *info*, which will be in
+ * one of the following formats depending on the eBPF object type
+ * of *bpf_fd*:
+ *
+ * * **struct bpf_prog_info**
+ * * **struct bpf_map_info**
+ * * **struct bpf_btf_info**
+ * * **struct bpf_link_info**
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_PROG_QUERY
+ * Description
+ * Obtain information about eBPF programs associated with the
+ * specified *attach_type* hook.
+ *
+ * The *target_fd* must be a valid file descriptor for a kernel
+ * object which depends on the attach type of *attach_bpf_fd*:
+ *
+ * **BPF_PROG_TYPE_CGROUP_DEVICE**,
+ * **BPF_PROG_TYPE_CGROUP_SKB**,
+ * **BPF_PROG_TYPE_CGROUP_SOCK**,
+ * **BPF_PROG_TYPE_CGROUP_SOCK_ADDR**,
+ * **BPF_PROG_TYPE_CGROUP_SOCKOPT**,
+ * **BPF_PROG_TYPE_CGROUP_SYSCTL**,
+ * **BPF_PROG_TYPE_SOCK_OPS**
+ *
+ * Control Group v2 hierarchy with the eBPF controller
+ * enabled. Requires the kernel to be compiled with
+ * **CONFIG_CGROUP_BPF**.
+ *
+ * **BPF_PROG_TYPE_FLOW_DISSECTOR**
+ *
+ * Network namespace (eg /proc/self/ns/net).
+ *
+ * **BPF_PROG_TYPE_LIRC_MODE2**
+ *
+ * LIRC device path (eg /dev/lircN). Requires the kernel
+ * to be compiled with **CONFIG_BPF_LIRC_MODE2**.
+ *
+ * **BPF_PROG_QUERY** always fetches the number of programs
+ * attached and the *attach_flags* which were used to attach those
+ * programs. Additionally, if *prog_ids* is nonzero and the number
+ * of attached programs is less than *prog_cnt*, populates
+ * *prog_ids* with the eBPF program ids of the programs attached
+ * at *target_fd*.
+ *
+ * The following flags may alter the result:
+ *
+ * **BPF_F_QUERY_EFFECTIVE**
+ * Only return information regarding programs which are
+ * currently effective at the specified *target_fd*.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_RAW_TRACEPOINT_OPEN
+ * Description
+ * Attach an eBPF program to a tracepoint *name* to access kernel
+ * internal arguments of the tracepoint in their raw form.
+ *
+ * The *prog_fd* must be a valid file descriptor associated with
+ * a loaded eBPF program of type **BPF_PROG_TYPE_RAW_TRACEPOINT**.
+ *
+ * No ABI guarantees are made about the content of tracepoint
+ * arguments exposed to the corresponding eBPF program.
+ *
+ * Applying **close**\ (2) to the file descriptor returned by
+ * **BPF_RAW_TRACEPOINT_OPEN** will delete the map (but see NOTES).
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_BTF_LOAD
+ * Description
+ * Verify and load BPF Type Format (BTF) metadata into the kernel,
+ * returning a new file descriptor associated with the metadata.
+ * BTF is described in more detail at
+ * https://www.kernel.org/doc/html/latest/bpf/btf.html.
+ *
+ * The *btf* parameter must point to valid memory providing
+ * *btf_size* bytes of BTF binary metadata.
+ *
+ * The returned file descriptor can be passed to other **bpf**\ ()
+ * subcommands such as **BPF_PROG_LOAD** or **BPF_MAP_CREATE** to
+ * associate the BTF with those objects.
+ *
+ * Similar to **BPF_PROG_LOAD**, **BPF_BTF_LOAD** has optional
+ * parameters to specify a *btf_log_buf*, *btf_log_size* and
+ * *btf_log_level* which allow the kernel to return freeform log
+ * output regarding the BTF verification process.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_BTF_GET_FD_BY_ID
+ * Description
+ * Open a file descriptor for the BPF Type Format (BTF)
+ * corresponding to *btf_id*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_TASK_FD_QUERY
+ * Description
+ * Obtain information about eBPF programs associated with the
+ * target process identified by *pid* and *fd*.
+ *
+ * If the *pid* and *fd* are associated with a tracepoint, kprobe
+ * or uprobe perf event, then the *prog_id* and *fd_type* will
+ * be populated with the eBPF program id and file descriptor type
+ * of type **bpf_task_fd_type**. If associated with a kprobe or
+ * uprobe, the *probe_offset* and *probe_addr* will also be
+ * populated. Optionally, if *buf* is provided, then up to
+ * *buf_len* bytes of *buf* will be populated with the name of
+ * the tracepoint, kprobe or uprobe.
+ *
+ * The resulting *prog_id* may be introspected in deeper detail
+ * using **BPF_PROG_GET_FD_BY_ID** and **BPF_OBJ_GET_INFO_BY_FD**.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_MAP_LOOKUP_AND_DELETE_ELEM
+ * Description
+ * Look up an element with the given *key* in the map referred to
+ * by the file descriptor *fd*, and if found, delete the element.
+ *
+ * The **BPF_MAP_TYPE_QUEUE** and **BPF_MAP_TYPE_STACK** map types
+ * implement this command as a "pop" operation, deleting the top
+ * element rather than one corresponding to *key*.
+ * The *key* and *key_len* parameters should be zeroed when
+ * issuing this operation for these map types.
+ *
+ * This command is only valid for the following map types:
+ * * **BPF_MAP_TYPE_QUEUE**
+ * * **BPF_MAP_TYPE_STACK**
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_MAP_FREEZE
+ * Description
+ * Freeze the permissions of the specified map.
+ *
+ * Write permissions may be frozen by passing zero *flags*.
+ * Upon success, no future syscall invocations may alter the
+ * map state of *map_fd*. Write operations from eBPF programs
+ * are still possible for a frozen map.
+ *
+ * Not supported for maps of type **BPF_MAP_TYPE_STRUCT_OPS**.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_BTF_GET_NEXT_ID
+ * Description
+ * Fetch the next BPF Type Format (BTF) object currently loaded
+ * into the kernel.
+ *
+ * Looks for the BTF object with an id greater than *start_id*
+ * and updates *next_id* on success. If no other BTF objects
+ * remain with ids higher than *start_id*, returns -1 and sets
+ * *errno* to **ENOENT**.
+ *
+ * Return
+ * Returns zero on success. On error, or when no id remains, -1
+ * is returned and *errno* is set appropriately.
+ *
+ * BPF_MAP_LOOKUP_BATCH
+ * Description
+ * Iterate and fetch multiple elements in a map.
+ *
+ * Two opaque values are used to manage batch operations,
+ * *in_batch* and *out_batch*. Initially, *in_batch* must be set
+ * to NULL to begin the batched operation. After each subsequent
+ * **BPF_MAP_LOOKUP_BATCH**, the caller should pass the resultant
+ * *out_batch* as the *in_batch* for the next operation to
+ * continue iteration from the current point.
+ *
+ * The *keys* and *values* are output parameters which must point
+ * to memory large enough to hold *count* items based on the key
+ * and value size of the map *map_fd*. The *keys* buffer must be
+ * of *key_size* * *count*. The *values* buffer must be of
+ * *value_size* * *count*.
+ *
+ * The *elem_flags* argument may be specified as one of the
+ * following:
+ *
+ * **BPF_F_LOCK**
+ * Look up the value of a spin-locked map without
+ * returning the lock. This must be specified if the
+ * elements contain a spinlock.
+ *
+ * On success, *count* elements from the map are copied into the
+ * user buffer, with the keys copied into *keys* and the values
+ * copied into the corresponding indices in *values*.
+ *
+ * If an error is returned and *errno* is not **EFAULT**, *count*
+ * is set to the number of successfully processed elements.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * May set *errno* to **ENOSPC** to indicate that *keys* or
+ * *values* is too small to dump an entire bucket during
+ * iteration of a hash-based map type.
+ *
+ * BPF_MAP_LOOKUP_AND_DELETE_BATCH
+ * Description
+ * Iterate and delete all elements in a map.
+ *
+ * This operation has the same behavior as
+ * **BPF_MAP_LOOKUP_BATCH** with two exceptions:
+ *
+ * * Every element that is successfully returned is also deleted
+ * from the map. This is at least *count* elements. Note that
+ * *count* is both an input and an output parameter.
+ * * Upon returning with *errno* set to **EFAULT**, up to
+ * *count* elements may be deleted without returning the keys
+ * and values of the deleted elements.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_MAP_UPDATE_BATCH
+ * Description
+ * Update multiple elements in a map by *key*.
+ *
+ * The *keys* and *values* are input parameters which must point
+ * to memory large enough to hold *count* items based on the key
+ * and value size of the map *map_fd*. The *keys* buffer must be
+ * of *key_size* * *count*. The *values* buffer must be of
+ * *value_size* * *count*.
+ *
+ * Each element specified in *keys* is sequentially updated to the
+ * value in the corresponding index in *values*. The *in_batch*
+ * and *out_batch* parameters are ignored and should be zeroed.
+ *
+ * The *elem_flags* argument should be specified as one of the
+ * following:
+ *
+ * **BPF_ANY**
+ * Create new elements or update a existing elements.
+ * **BPF_NOEXIST**
+ * Create new elements only if they do not exist.
+ * **BPF_EXIST**
+ * Update existing elements.
+ * **BPF_F_LOCK**
+ * Update spin_lock-ed map elements. This must be
+ * specified if the map value contains a spinlock.
+ *
+ * On success, *count* elements from the map are updated.
+ *
+ * If an error is returned and *errno* is not **EFAULT**, *count*
+ * is set to the number of successfully processed elements.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * May set *errno* to **EINVAL**, **EPERM**, **ENOMEM**, or
+ * **E2BIG**. **E2BIG** indicates that the number of elements in
+ * the map reached the *max_entries* limit specified at map
+ * creation time.
+ *
+ * May set *errno* to one of the following error codes under
+ * specific circumstances:
+ *
+ * **EEXIST**
+ * If *flags* specifies **BPF_NOEXIST** and the element
+ * with *key* already exists in the map.
+ * **ENOENT**
+ * If *flags* specifies **BPF_EXIST** and the element with
+ * *key* does not exist in the map.
+ *
+ * BPF_MAP_DELETE_BATCH
+ * Description
+ * Delete multiple elements in a map by *key*.
+ *
+ * The *keys* parameter is an input parameter which must point
+ * to memory large enough to hold *count* items based on the key
+ * size of the map *map_fd*, that is, *key_size* * *count*.
+ *
+ * Each element specified in *keys* is sequentially deleted. The
+ * *in_batch*, *out_batch*, and *values* parameters are ignored
+ * and should be zeroed.
+ *
+ * The *elem_flags* argument may be specified as one of the
+ * following:
+ *
+ * **BPF_F_LOCK**
+ * Look up the value of a spin-locked map without
+ * returning the lock. This must be specified if the
+ * elements contain a spinlock.
+ *
+ * On success, *count* elements from the map are updated.
+ *
+ * If an error is returned and *errno* is not **EFAULT**, *count*
+ * is set to the number of successfully processed elements. If
+ * *errno* is **EFAULT**, up to *count* elements may be been
+ * deleted.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_LINK_CREATE
+ * Description
+ * Attach an eBPF program to a *target_fd* at the specified
+ * *attach_type* hook and return a file descriptor handle for
+ * managing the link.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_LINK_UPDATE
+ * Description
+ * Update the eBPF program in the specified *link_fd* to
+ * *new_prog_fd*.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_LINK_GET_FD_BY_ID
+ * Description
+ * Open a file descriptor for the eBPF Link corresponding to
+ * *link_id*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_LINK_GET_NEXT_ID
+ * Description
+ * Fetch the next eBPF link currently loaded into the kernel.
+ *
+ * Looks for the eBPF link with an id greater than *start_id*
+ * and updates *next_id* on success. If no other eBPF links
+ * remain with ids higher than *start_id*, returns -1 and sets
+ * *errno* to **ENOENT**.
+ *
+ * Return
+ * Returns zero on success. On error, or when no id remains, -1
+ * is returned and *errno* is set appropriately.
+ *
+ * BPF_ENABLE_STATS
+ * Description
+ * Enable eBPF runtime statistics gathering.
+ *
+ * Runtime statistics gathering for the eBPF runtime is disabled
+ * by default to minimize the corresponding performance overhead.
+ * This command enables statistics globally.
+ *
+ * Multiple programs may independently enable statistics.
+ * After gathering the desired statistics, eBPF runtime statistics
+ * may be disabled again by calling **close**\ (2) for the file
+ * descriptor returned by this function. Statistics will only be
+ * disabled system-wide when all outstanding file descriptors
+ * returned by prior calls for this subcommand are closed.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_ITER_CREATE
+ * Description
+ * Create an iterator on top of the specified *link_fd* (as
+ * previously created using **BPF_LINK_CREATE**) and return a
+ * file descriptor that can be used to trigger the iteration.
+ *
+ * If the resulting file descriptor is pinned to the filesystem
+ * using **BPF_OBJ_PIN**, then subsequent **read**\ (2) syscalls
+ * for that path will trigger the iterator to read kernel state
+ * using the eBPF program attached to *link_fd*.
+ *
+ * Return
+ * A new file descriptor (a nonnegative integer), or -1 if an
+ * error occurred (in which case, *errno* is set appropriately).
+ *
+ * BPF_LINK_DETACH
+ * Description
+ * Forcefully detach the specified *link_fd* from its
+ * corresponding attachment point.
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * BPF_PROG_BIND_MAP
+ * Description
+ * Bind a map to the lifetime of an eBPF program.
+ *
+ * The map identified by *map_fd* is bound to the program
+ * identified by *prog_fd* and only released when *prog_fd* is
+ * released. This may be used in cases where metadata should be
+ * associated with a program which otherwise does not contain any
+ * references to the map (for example, embedded in the eBPF
+ * program instructions).
+ *
+ * Return
+ * Returns zero on success. On error, -1 is returned and *errno*
+ * is set appropriately.
+ *
+ * NOTES
+ * eBPF objects (maps and programs) can be shared between processes.
+ *
+ * * After **fork**\ (2), the child inherits file descriptors
+ * referring to the same eBPF objects.
+ * * File descriptors referring to eBPF objects can be transferred over
+ * **unix**\ (7) domain sockets.
+ * * File descriptors referring to eBPF objects can be duplicated in the
+ * usual way, using **dup**\ (2) and similar calls.
+ * * File descriptors referring to eBPF objects can be pinned to the
+ * filesystem using the **BPF_OBJ_PIN** command of **bpf**\ (2).
+ *
+ * An eBPF object is deallocated only after all file descriptors referring
+ * to the object have been closed and no references remain pinned to the
+ * filesystem or attached (for example, bound to a program or device).
+ */
enum bpf_cmd {
BPF_MAP_CREATE,
BPF_MAP_LOOKUP_ELEM,
@@ -247,6 +978,7 @@ enum bpf_attach_type {
BPF_XDP_CPUMAP,
BPF_SK_LOOKUP,
BPF_XDP,
+ BPF_SK_SKB_VERDICT,
__MAX_BPF_ATTACH_TYPE
};
@@ -393,11 +1125,24 @@ enum bpf_link_type {
* is struct/union.
*/
#define BPF_PSEUDO_BTF_ID 3
+/* insn[0].src_reg: BPF_PSEUDO_FUNC
+ * insn[0].imm: insn offset to the func
+ * insn[1].imm: 0
+ * insn[0].off: 0
+ * insn[1].off: 0
+ * ldimm64 rewrite: address of the function
+ * verifier type: PTR_TO_FUNC.
+ */
+#define BPF_PSEUDO_FUNC 4
/* when bpf_call->src_reg == BPF_PSEUDO_CALL, bpf_call->imm == pc-relative
* offset to another bpf function
*/
#define BPF_PSEUDO_CALL 1
+/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL,
+ * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel
+ */
+#define BPF_PSEUDO_KFUNC_CALL 2
/* flags for BPF_MAP_UPDATE_ELEM command */
enum {
@@ -720,7 +1465,7 @@ union bpf_attr {
* parsed and used to produce a manual page. The workflow is the following,
* and requires the rst2man utility:
*
- * $ ./scripts/bpf_helpers_doc.py \
+ * $ ./scripts/bpf_doc.py \
* --filename include/uapi/linux/bpf.h > /tmp/bpf-helpers.rst
* $ rst2man /tmp/bpf-helpers.rst > /tmp/bpf-helpers.7
* $ man /tmp/bpf-helpers.7
@@ -1765,6 +2510,10 @@ union bpf_attr {
* Use with ENCAP_L3/L4 flags to further specify the tunnel
* type; *len* is the length of the inner MAC header.
*
+ * * **BPF_F_ADJ_ROOM_ENCAP_L2_ETH**:
+ * Use with BPF_F_ADJ_ROOM_ENCAP_L2 flag to further specify the
+ * L2 type as Ethernet.
+ *
* A call to this helper is susceptible to change the underlying
* packet buffer. Therefore, at load time, all checks on pointers
* previously done by the verifier are invalidated and must be
@@ -3333,12 +4082,20 @@ union bpf_attr {
* of new data availability is sent.
* If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification
* of new data availability is sent unconditionally.
+ * If **0** is specified in *flags*, an adaptive notification
+ * of new data availability is sent.
+ *
+ * An adaptive notification is a notification sent whenever the user-space
+ * process has caught up and consumed all available payloads. In case the user-space
+ * process is still processing a previous payload, then no notification is needed
+ * as it will process the newly added payload automatically.
* Return
* 0 on success, or a negative error in case of failure.
*
* void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags)
* Description
* Reserve *size* bytes of payload in a ring buffer *ringbuf*.
+ * *flags* must be 0.
* Return
* Valid pointer with *size* bytes of memory available; NULL,
* otherwise.
@@ -3350,6 +4107,10 @@ union bpf_attr {
* of new data availability is sent.
* If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification
* of new data availability is sent unconditionally.
+ * If **0** is specified in *flags*, an adaptive notification
+ * of new data availability is sent.
+ *
+ * See 'bpf_ringbuf_output()' for the definition of adaptive notification.
* Return
* Nothing. Always succeeds.
*
@@ -3360,6 +4121,10 @@ union bpf_attr {
* of new data availability is sent.
* If **BPF_RB_FORCE_WAKEUP** is specified in *flags*, notification
* of new data availability is sent unconditionally.
+ * If **0** is specified in *flags*, an adaptive notification
+ * of new data availability is sent.
+ *
+ * See 'bpf_ringbuf_output()' for the definition of adaptive notification.
* Return
* Nothing. Always succeeds.
*
@@ -3850,8 +4615,7 @@ union bpf_attr {
*
* long bpf_check_mtu(void *ctx, u32 ifindex, u32 *mtu_len, s32 len_diff, u64 flags)
* Description
-
- * Check ctx packet size against exceeding MTU of net device (based
+ * Check packet size against exceeding MTU of net device (based
* on *ifindex*). This helper will likely be used in combination
* with helpers that adjust/change the packet size.
*
@@ -3868,6 +4632,14 @@ union bpf_attr {
* against the current net device. This is practical if this isn't
* used prior to redirect.
*
+ * On input *mtu_len* must be a valid pointer, else verifier will
+ * reject BPF program. If the value *mtu_len* is initialized to
+ * zero then the ctx packet size is use. When value *mtu_len* is
+ * provided as input this specify the L3 length that the MTU check
+ * is done against. Remember XDP and TC length operate at L2, but
+ * this value is L3 as this correlate to MTU and IP-header tot_len
+ * values which are L3 (similar behavior as bpf_fib_lookup).
+ *
* The Linux kernel route table can configure MTUs on a more
* specific per route level, which is not provided by this helper.
* For route level MTU checks use the **bpf_fib_lookup**\ ()
@@ -3892,11 +4664,9 @@ union bpf_attr {
*
* On return *mtu_len* pointer contains the MTU value of the net
* device. Remember the net device configured MTU is the L3 size,
- * which is returned here and XDP and TX length operate at L2.
+ * which is returned here and XDP and TC length operate at L2.
* Helper take this into account for you, but remember when using
- * MTU value in your BPF-code. On input *mtu_len* must be a valid
- * pointer and be initialized (to zero), else verifier will reject
- * BPF program.
+ * MTU value in your BPF-code.
*
* Return
* * 0 on success, and populate MTU value in *mtu_len* pointer.
@@ -3910,6 +4680,61 @@ union bpf_attr {
* * **BPF_MTU_CHK_RET_FRAG_NEEDED**
* * **BPF_MTU_CHK_RET_SEGS_TOOBIG**
*
+ * long bpf_for_each_map_elem(struct bpf_map *map, void *callback_fn, void *callback_ctx, u64 flags)
+ * Description
+ * For each element in **map**, call **callback_fn** function with
+ * **map**, **callback_ctx** and other map-specific parameters.
+ * The **callback_fn** should be a static function and
+ * the **callback_ctx** should be a pointer to the stack.
+ * The **flags** is used to control certain aspects of the helper.
+ * Currently, the **flags** must be 0.
+ *
+ * The following are a list of supported map types and their
+ * respective expected callback signatures:
+ *
+ * BPF_MAP_TYPE_HASH, BPF_MAP_TYPE_PERCPU_HASH,
+ * BPF_MAP_TYPE_LRU_HASH, BPF_MAP_TYPE_LRU_PERCPU_HASH,
+ * BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_PERCPU_ARRAY
+ *
+ * long (\*callback_fn)(struct bpf_map \*map, const void \*key, void \*value, void \*ctx);
+ *
+ * For per_cpu maps, the map_value is the value on the cpu where the
+ * bpf_prog is running.
+ *
+ * If **callback_fn** return 0, the helper will continue to the next
+ * element. If return value is 1, the helper will skip the rest of
+ * elements and return. Other return values are not used now.
+ *
+ * Return
+ * The number of traversed map elements for success, **-EINVAL** for
+ * invalid **flags**.
+ *
+ * long bpf_snprintf(char *str, u32 str_size, const char *fmt, u64 *data, u32 data_len)
+ * Description
+ * Outputs a string into the **str** buffer of size **str_size**
+ * based on a format string stored in a read-only map pointed by
+ * **fmt**.
+ *
+ * Each format specifier in **fmt** corresponds to one u64 element
+ * in the **data** array. For strings and pointers where pointees
+ * are accessed, only the pointer values are stored in the *data*
+ * array. The *data_len* is the size of *data* in bytes.
+ *
+ * Formats **%s** and **%p{i,I}{4,6}** require to read kernel
+ * memory. Reading kernel memory may fail due to either invalid
+ * address or valid address but requiring a major memory fault. If
+ * reading kernel memory fails, the string for **%s** will be an
+ * empty string, and the ip address for **%p{i,I}{4,6}** will be 0.
+ * Not returning error to bpf program is consistent with what
+ * **bpf_trace_printk**\ () does for now.
+ *
+ * Return
+ * The strictly positive length of the formatted string, including
+ * the trailing zero character. If the return value is greater than
+ * **str_size**, **str** contains a truncated string, guaranteed to
+ * be zero-terminated except when **str_size** is 0.
+ *
+ * Or **-EBUSY** if the per-CPU memory copy buffer is busy.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -4076,6 +4901,8 @@ union bpf_attr {
FN(ima_inode_hash), \
FN(sock_from_file), \
FN(check_mtu), \
+ FN(for_each_map_elem), \
+ FN(snprintf), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
@@ -4169,6 +4996,7 @@ enum {
BPF_F_ADJ_ROOM_ENCAP_L4_GRE = (1ULL << 3),
BPF_F_ADJ_ROOM_ENCAP_L4_UDP = (1ULL << 4),
BPF_F_ADJ_ROOM_NO_CSUM_RESET = (1ULL << 5),
+ BPF_F_ADJ_ROOM_ENCAP_L2_ETH = (1ULL << 6),
};
enum {
@@ -4616,6 +5444,8 @@ struct bpf_link_info {
} raw_tracepoint;
struct {
__u32 attach_type;
+ __u32 target_obj_id; /* prog_id for PROG_EXT, otherwise btf object id */
+ __u32 target_btf_id; /* BTF type id inside the object */
} tracing;
struct {
__u64 cgroup_id;
@@ -5206,7 +6036,10 @@ struct bpf_pidns_info {
/* User accessible data for SK_LOOKUP programs. Add new fields at the end. */
struct bpf_sk_lookup {
- __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */
+ union {
+ __bpf_md_ptr(struct bpf_sock *, sk); /* Selected socket */
+ __u64 cookie; /* Non-zero if socket was selected in PROG_TEST_RUN */
+ };
__u32 family; /* Protocol family (AF_INET, AF_INET6) */
__u32 protocol; /* IP protocol (IPPROTO_TCP, IPPROTO_UDP) */
diff --git a/include/linux/cec-funcs.h b/include/linux/cec-funcs.h
index 9932239b..0385d0e7 100644
--- a/include/linux/cec-funcs.h
+++ b/include/linux/cec-funcs.h
@@ -1665,7 +1665,7 @@ static __inline__ void cec_ops_report_current_latency(const struct cec_msg *msg,
if (*audio_out_compensated == 3 && msg->len >= 7)
*audio_out_delay = msg->msg[6];
else
- *audio_out_delay = 0;
+ *audio_out_delay = 1;
}
static __inline__ void cec_msg_request_current_latency(struct cec_msg *msg,
diff --git a/include/linux/cec.h b/include/linux/cec.h
index 16d96133..eaa2555a 100644
--- a/include/linux/cec.h
+++ b/include/linux/cec.h
@@ -396,6 +396,7 @@ struct cec_drm_connector_info {
* associated with the CEC adapter.
* @type: connector type (if any)
* @drm: drm connector info
+ * @raw: array to pad the union
*/
struct cec_connector_info {
__u32 type;
@@ -453,7 +454,7 @@ struct cec_event_lost_msgs {
* struct cec_event - CEC event structure
* @ts: the timestamp of when the event was sent.
* @event: the event.
- * array.
+ * @flags: event flags.
* @state_change: the event payload for CEC_EVENT_STATE_CHANGE.
* @lost_msgs: the event payload for CEC_EVENT_LOST_MSGS.
* @raw: array to pad the union.
diff --git a/include/linux/lirc.h b/include/linux/lirc.h
index c45a4eae..9919f206 100644
--- a/include/linux/lirc.h
+++ b/include/linux/lirc.h
@@ -1,7 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* lirc.h - linux infrared remote control header file
- * last modified 2010/07/13 by Jarod Wilson
*/
#ifndef _LINUX_LIRC_H
diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h
index 40cf8b41..9db08be6 100644
--- a/include/linux/v4l2-controls.h
+++ b/include/linux/v4l2-controls.h
@@ -50,8 +50,13 @@
#ifndef __LINUX_V4L2_CONTROLS_H
#define __LINUX_V4L2_CONTROLS_H
+#include <linux/const.h>
#include <linux/types.h>
+#ifndef _BITUL
+#define _BITUL(x) (1U << (x))
+#endif
+
/* Control classes */
#define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
#define V4L2_CTRL_CLASS_CODEC 0x00990000 /* Stateful codec controls */
@@ -66,6 +71,7 @@
#define V4L2_CTRL_CLASS_RF_TUNER 0x00a20000 /* RF tuner controls */
#define V4L2_CTRL_CLASS_DETECT 0x00a30000 /* Detection controls */
#define V4L2_CTRL_CLASS_CODEC_STATELESS 0x00a40000 /* Stateless codecs controls */
+#define V4L2_CTRL_CLASS_COLORIMETRY 0x00a50000 /* Colorimetry controls */
/* User-class control IDs */
@@ -426,6 +432,11 @@ enum v4l2_mpeg_video_multi_slice_mode {
#define V4L2_CID_MPEG_VIDEO_MV_V_SEARCH_RANGE (V4L2_CID_CODEC_BASE+228)
#define V4L2_CID_MPEG_VIDEO_FORCE_KEY_FRAME (V4L2_CID_CODEC_BASE+229)
#define V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID (V4L2_CID_CODEC_BASE+230)
+#define V4L2_CID_MPEG_VIDEO_AU_DELIMITER (V4L2_CID_CODEC_BASE+231)
+#define V4L2_CID_MPEG_VIDEO_LTR_COUNT (V4L2_CID_CODEC_BASE+232)
+#define V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX (V4L2_CID_CODEC_BASE+233)
+#define V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES (V4L2_CID_CODEC_BASE+234)
+#define V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR (V4L2_CID_CODEC_BASE+235)
/* CIDs for the MPEG-2 Part 2 (H.262) codec */
#define V4L2_CID_MPEG_VIDEO_MPEG2_LEVEL (V4L2_CID_CODEC_BASE+270)
@@ -795,6 +806,9 @@ enum v4l2_mpeg_video_frame_skip_mode {
#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MIN_QP (V4L2_CID_CODEC_BASE + 651)
#define V4L2_CID_MPEG_VIDEO_HEVC_B_FRAME_MAX_QP (V4L2_CID_CODEC_BASE + 652)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY (V4L2_CID_CODEC_BASE + 653)
+#define V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE (V4L2_CID_CODEC_BASE + 654)
+
/* MPEG-class control IDs specific to the CX2341x driver as defined by V4L2 */
#define V4L2_CID_CODEC_CX2341X_BASE (V4L2_CTRL_CLASS_CODEC | 0x1000)
#define V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE (V4L2_CID_CODEC_CX2341X_BASE+0)
@@ -1591,30 +1605,30 @@ struct v4l2_ctrl_h264_decode_params {
#define V4L2_FWHT_VERSION 3
/* Set if this is an interlaced format */
-#define V4L2_FWHT_FL_IS_INTERLACED BIT(0)
+#define V4L2_FWHT_FL_IS_INTERLACED _BITUL(0)
/* Set if this is a bottom-first (NTSC) interlaced format */
-#define V4L2_FWHT_FL_IS_BOTTOM_FIRST BIT(1)
+#define V4L2_FWHT_FL_IS_BOTTOM_FIRST _BITUL(1)
/* Set if each 'frame' contains just one field */
-#define V4L2_FWHT_FL_IS_ALTERNATE BIT(2)
+#define V4L2_FWHT_FL_IS_ALTERNATE _BITUL(2)
/*
* If V4L2_FWHT_FL_IS_ALTERNATE was set, then this is set if this
* 'frame' is the bottom field, else it is the top field.
*/
-#define V4L2_FWHT_FL_IS_BOTTOM_FIELD BIT(3)
+#define V4L2_FWHT_FL_IS_BOTTOM_FIELD _BITUL(3)
/* Set if the Y' plane is uncompressed */
-#define V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED BIT(4)
+#define V4L2_FWHT_FL_LUMA_IS_UNCOMPRESSED _BITUL(4)
/* Set if the Cb plane is uncompressed */
-#define V4L2_FWHT_FL_CB_IS_UNCOMPRESSED BIT(5)
+#define V4L2_FWHT_FL_CB_IS_UNCOMPRESSED _BITUL(5)
/* Set if the Cr plane is uncompressed */
-#define V4L2_FWHT_FL_CR_IS_UNCOMPRESSED BIT(6)
+#define V4L2_FWHT_FL_CR_IS_UNCOMPRESSED _BITUL(6)
/* Set if the chroma plane is full height, if cleared it is half height */
-#define V4L2_FWHT_FL_CHROMA_FULL_HEIGHT BIT(7)
+#define V4L2_FWHT_FL_CHROMA_FULL_HEIGHT _BITUL(7)
/* Set if the chroma plane is full width, if cleared it is half width */
-#define V4L2_FWHT_FL_CHROMA_FULL_WIDTH BIT(8)
+#define V4L2_FWHT_FL_CHROMA_FULL_WIDTH _BITUL(8)
/* Set if the alpha plane is uncompressed */
-#define V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED BIT(9)
+#define V4L2_FWHT_FL_ALPHA_IS_UNCOMPRESSED _BITUL(9)
/* Set if this is an I Frame */
-#define V4L2_FWHT_FL_I_FRAME BIT(10)
+#define V4L2_FWHT_FL_I_FRAME _BITUL(10)
/* A 4-values flag - the number of components - 1 */
#define V4L2_FWHT_FL_COMPONENTS_NUM_MSK GENMASK(18, 16)
@@ -1655,6 +1669,348 @@ struct v4l2_ctrl_fwht_params {
__u32 quantization;
};
+/* Stateless VP8 control */
+
+#define V4L2_VP8_SEGMENT_FLAG_ENABLED 0x01
+#define V4L2_VP8_SEGMENT_FLAG_UPDATE_MAP 0x02
+#define V4L2_VP8_SEGMENT_FLAG_UPDATE_FEATURE_DATA 0x04
+#define V4L2_VP8_SEGMENT_FLAG_DELTA_VALUE_MODE 0x08
+
+/**
+ * struct v4l2_vp8_segment - VP8 segment-based adjustments parameters
+ *
+ * @quant_update: update values for the segment quantizer.
+ * @lf_update: update values for the loop filter level.
+ * @segment_probs: branch probabilities of the segment_id decoding tree.
+ * @padding: padding field. Should be zeroed by applications.
+ * @flags: see V4L2_VP8_SEGMENT_FLAG_{}.
+ *
+ * This structure contains segment-based adjustments related parameters.
+ * See the 'update_segmentation()' part of the frame header syntax,
+ * and section '9.3. Segment-Based Adjustments' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_segment {
+ __s8 quant_update[4];
+ __s8 lf_update[4];
+ __u8 segment_probs[3];
+ __u8 padding;
+ __u32 flags;
+};
+
+#define V4L2_VP8_LF_ADJ_ENABLE 0x01
+#define V4L2_VP8_LF_DELTA_UPDATE 0x02
+#define V4L2_VP8_LF_FILTER_TYPE_SIMPLE 0x04
+
+/**
+ * struct v4l2_vp8_loop_filter - VP8 loop filter parameters
+ *
+ * @ref_frm_delta: Reference frame signed delta values.
+ * @mb_mode_delta: MB prediction mode signed delta values.
+ * @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_{}.
+ *
+ * This structure contains loop filter related parameters.
+ * See the 'mb_lf_adjustments()' part of the frame header syntax,
+ * and section '9.4. Loop Filter Type and Levels' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_loop_filter {
+ __s8 ref_frm_delta[4];
+ __s8 mb_mode_delta[4];
+ __u8 sharpness_level;
+ __u8 level;
+ __u16 padding;
+ __u32 flags;
+};
+
+/**
+ * struct v4l2_vp8_quantization - VP8 quantizattion indices
+ *
+ * @y_ac_qi: luma AC coefficient table index.
+ * @y_dc_delta: luma DC delta vaue.
+ * @y2_dc_delta: y2 block DC delta value.
+ * @y2_ac_delta: y2 block AC delta value.
+ * @uv_dc_delta: chroma DC delta value.
+ * @uv_ac_delta: chroma AC delta value.
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the quantization indices present
+ * in 'quant_indices()' part of the frame header syntax.
+ * See section '9.6. Dequantization Indices' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_quantization {
+ __u8 y_ac_qi;
+ __s8 y_dc_delta;
+ __s8 y2_dc_delta;
+ __s8 y2_ac_delta;
+ __s8 uv_dc_delta;
+ __s8 uv_ac_delta;
+ __u16 padding;
+};
+
+#define V4L2_VP8_COEFF_PROB_CNT 11
+#define V4L2_VP8_MV_PROB_CNT 19
+
+/**
+ * struct v4l2_vp8_entropy - VP8 update probabilities
+ *
+ * @coeff_probs: coefficient probability update values.
+ * @y_mode_probs: luma intra-prediction probabilities.
+ * @uv_mode_probs: chroma intra-prediction probabilities.
+ * @mv_probs: mv decoding probability.
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the update probabilities present in
+ * 'token_prob_update()' and 'mv_prob_update()' part of the frame header.
+ * See section '17.2. Probability Updates' of the VP8 specification
+ * for more details.
+ */
+struct v4l2_vp8_entropy {
+ __u8 coeff_probs[4][8][3][V4L2_VP8_COEFF_PROB_CNT];
+ __u8 y_mode_probs[4];
+ __u8 uv_mode_probs[3];
+ __u8 mv_probs[2][V4L2_VP8_MV_PROB_CNT];
+ __u8 padding[3];
+};
+
+/**
+ * struct v4l2_vp8_entropy_coder_state - VP8 boolean coder state
+ *
+ * @range: coder state value for "Range"
+ * @value: coder state value for "Value"
+ * @bit_count: number of bits left in range "Value".
+ * @padding: padding field. Should be zeroed by applications.
+ *
+ * This structure contains the state for the boolean coder, as
+ * explained in section '7. Boolean Entropy Decoder' of the VP8 specification.
+ */
+struct v4l2_vp8_entropy_coder_state {
+ __u8 range;
+ __u8 value;
+ __u8 bit_count;
+ __u8 padding;
+};
+
+#define V4L2_VP8_FRAME_FLAG_KEY_FRAME 0x01
+#define V4L2_VP8_FRAME_FLAG_EXPERIMENTAL 0x02
+#define V4L2_VP8_FRAME_FLAG_SHOW_FRAME 0x04
+#define V4L2_VP8_FRAME_FLAG_MB_NO_SKIP_COEFF 0x08
+#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_GOLDEN 0x10
+#define V4L2_VP8_FRAME_FLAG_SIGN_BIAS_ALT 0x20
+
+#define V4L2_VP8_FRAME_IS_KEY_FRAME(hdr) \
+ (!!((hdr)->flags & V4L2_VP8_FRAME_FLAG_KEY_FRAME))
+
+#define V4L2_CID_STATELESS_VP8_FRAME (V4L2_CID_CODEC_STATELESS_BASE + 200)
+/**
+ * struct v4l2_ctrl_vp8_frame - VP8 frame parameters
+ *
+ * @segment: segmentation parameters. See &v4l2_vp8_segment for more details
+ * @lf: loop filter parameters. See &v4l2_vp8_loop_filter for more details
+ * @quant: quantization parameters. See &v4l2_vp8_quantization for more details
+ * @entropy: update probabilities. See &v4l2_vp8_entropy for more details
+ * @coder_state: boolean coder state. See &v4l2_vp8_entropy_coder_state for more details
+ * @width: frame width.
+ * @height: frame height.
+ * @horizontal_scale: horizontal scaling factor.
+ * @vertical_scale: vertical scaling factor.
+ * @version: bitstream version.
+ * @prob_skip_false: frame header syntax element.
+ * @prob_intra: frame header syntax element.
+ * @prob_last: frame header syntax element.
+ * @prob_gf: frame header syntax element.
+ * @num_dct_parts: number of DCT coefficients partitions.
+ * @first_part_size: size of the first partition, i.e. the control partition.
+ * @first_part_header_bits: size in bits of the first partition header portion.
+ * @dct_part_sizes: DCT coefficients sizes.
+ * @last_frame_ts: "last" reference buffer timestamp.
+ * The timestamp refers to the timestamp field in struct v4l2_buffer.
+ * Use v4l2_timeval_to_ns() to convert the struct timeval to a __u64.
+ * @golden_frame_ts: "golden" reference buffer timestamp.
+ * @alt_frame_ts: "alt" reference buffer timestamp.
+ * @flags: see V4L2_VP8_FRAME_FLAG_{}.
+ */
+struct v4l2_ctrl_vp8_frame {
+ struct v4l2_vp8_segment segment;
+ struct v4l2_vp8_loop_filter lf;
+ struct v4l2_vp8_quantization quant;
+ struct v4l2_vp8_entropy entropy;
+ struct v4l2_vp8_entropy_coder_state coder_state;
+
+ __u16 width;
+ __u16 height;
+
+ __u8 horizontal_scale;
+ __u8 vertical_scale;
+
+ __u8 version;
+ __u8 prob_skip_false;
+ __u8 prob_intra;
+ __u8 prob_last;
+ __u8 prob_gf;
+ __u8 num_dct_parts;
+
+ __u32 first_part_size;
+ __u32 first_part_header_bits;
+ __u32 dct_part_sizes[8];
+
+ __u64 last_frame_ts;
+ __u64 golden_frame_ts;
+ __u64 alt_frame_ts;
+
+ __u64 flags;
+};
+
+/* Stateless MPEG-2 controls */
+
+#define V4L2_MPEG2_SEQ_FLAG_PROGRESSIVE 0x01
+
+#define V4L2_CID_STATELESS_MPEG2_SEQUENCE (V4L2_CID_CODEC_STATELESS_BASE+220)
+/**
+ * struct v4l2_ctrl_mpeg2_sequence - MPEG-2 sequence header
+ *
+ * All the members on this structure match the sequence header and sequence
+ * extension syntaxes as specified by the MPEG-2 specification.
+ *
+ * Fields horizontal_size, vertical_size and vbv_buffer_size are a
+ * combination of respective _value and extension syntax elements,
+ * as described in section 6.3.3 "Sequence header".
+ *
+ * @horizontal_size: combination of elements horizontal_size_value and
+ * horizontal_size_extension.
+ * @vertical_size: combination of elements vertical_size_value and
+ * vertical_size_extension.
+ * @vbv_buffer_size: combination of elements vbv_buffer_size_value and
+ * vbv_buffer_size_extension.
+ * @profile_and_level_indication: see MPEG-2 specification.
+ * @chroma_format: see MPEG-2 specification.
+ * @flags: see V4L2_MPEG2_SEQ_FLAG_{}.
+ */
+struct v4l2_ctrl_mpeg2_sequence {
+ __u16 horizontal_size;
+ __u16 vertical_size;
+ __u32 vbv_buffer_size;
+ __u16 profile_and_level_indication;
+ __u8 chroma_format;
+ __u8 flags;
+};
+
+#define V4L2_MPEG2_PIC_CODING_TYPE_I 1
+#define V4L2_MPEG2_PIC_CODING_TYPE_P 2
+#define V4L2_MPEG2_PIC_CODING_TYPE_B 3
+#define V4L2_MPEG2_PIC_CODING_TYPE_D 4
+
+#define V4L2_MPEG2_PIC_TOP_FIELD 0x1
+#define V4L2_MPEG2_PIC_BOTTOM_FIELD 0x2
+#define V4L2_MPEG2_PIC_FRAME 0x3
+
+#define V4L2_MPEG2_PIC_FLAG_TOP_FIELD_FIRST 0x0001
+#define V4L2_MPEG2_PIC_FLAG_FRAME_PRED_DCT 0x0002
+#define V4L2_MPEG2_PIC_FLAG_CONCEALMENT_MV 0x0004
+#define V4L2_MPEG2_PIC_FLAG_Q_SCALE_TYPE 0x0008
+#define V4L2_MPEG2_PIC_FLAG_INTRA_VLC 0x0010
+#define V4L2_MPEG2_PIC_FLAG_ALT_SCAN 0x0020
+#define V4L2_MPEG2_PIC_FLAG_REPEAT_FIRST 0x0040
+#define V4L2_MPEG2_PIC_FLAG_PROGRESSIVE 0x0080
+
+#define V4L2_CID_STATELESS_MPEG2_PICTURE (V4L2_CID_CODEC_STATELESS_BASE+221)
+/**
+ * struct v4l2_ctrl_mpeg2_picture - MPEG-2 picture header
+ *
+ * All the members on this structure match the picture header and picture
+ * coding extension syntaxes as specified by the MPEG-2 specification.
+ *
+ * @backward_ref_ts: timestamp of the V4L2 capture buffer to use as
+ * reference for backward prediction.
+ * @forward_ref_ts: timestamp of the V4L2 capture buffer to use as
+ * reference for forward prediction. These timestamp refers to the
+ * timestamp field in struct v4l2_buffer. Use v4l2_timeval_to_ns()
+ * to convert the struct timeval to a __u64.
+ * @flags: see V4L2_MPEG2_PIC_FLAG_{}.
+ * @f_code: see MPEG-2 specification.
+ * @picture_coding_type: see MPEG-2 specification.
+ * @picture_structure: see V4L2_MPEG2_PIC_{}_FIELD.
+ * @intra_dc_precision: see MPEG-2 specification.
+ * @reserved: padding field. Should be zeroed by applications.
+ */
+struct v4l2_ctrl_mpeg2_picture {
+ __u64 backward_ref_ts;
+ __u64 forward_ref_ts;
+ __u32 flags;
+ __u8 f_code[2][2];
+ __u8 picture_coding_type;
+ __u8 picture_structure;
+ __u8 intra_dc_precision;
+ __u8 reserved[5];
+};
+
+#define V4L2_CID_STATELESS_MPEG2_QUANTISATION (V4L2_CID_CODEC_STATELESS_BASE+222)
+/**
+ * struct v4l2_ctrl_mpeg2_quantisation - MPEG-2 quantisation
+ *
+ * Quantisation matrices as specified by section 6.3.7
+ * "Quant matrix extension".
+ *
+ * @intra_quantiser_matrix: The quantisation matrix coefficients
+ * for intra-coded frames, in zigzag scanning order. It is relevant
+ * for both luma and chroma components, although it can be superseded
+ * by the chroma-specific matrix for non-4:2:0 YUV formats.
+ * @non_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for non-intra-coded frames, in zigzag scanning order. It is relevant
+ * for both luma and chroma components, although it can be superseded
+ * by the chroma-specific matrix for non-4:2:0 YUV formats.
+ * @chroma_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for the chominance component of intra-coded frames, in zigzag scanning
+ * order. Only relevant for 4:2:2 and 4:4:4 YUV formats.
+ * @chroma_non_intra_quantiser_matrix: The quantisation matrix coefficients
+ * for the chrominance component of non-intra-coded frames, in zigzag scanning
+ * order. Only relevant for 4:2:2 and 4:4:4 YUV formats.
+ */
+struct v4l2_ctrl_mpeg2_quantisation {
+ __u8 intra_quantiser_matrix[64];
+ __u8 non_intra_quantiser_matrix[64];
+ __u8 chroma_intra_quantiser_matrix[64];
+ __u8 chroma_non_intra_quantiser_matrix[64];
+};
+
+#define V4L2_CID_COLORIMETRY_CLASS_BASE (V4L2_CTRL_CLASS_COLORIMETRY | 0x900)
+#define V4L2_CID_COLORIMETRY_CLASS (V4L2_CTRL_CLASS_COLORIMETRY | 1)
+
+#define V4L2_CID_COLORIMETRY_HDR10_CLL_INFO (V4L2_CID_COLORIMETRY_CLASS_BASE + 0)
+
+struct v4l2_ctrl_hdr10_cll_info {
+ __u16 max_content_light_level;
+ __u16 max_pic_average_light_level;
+};
+
+#define V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY (V4L2_CID_COLORIMETRY_CLASS_BASE + 1)
+
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_LOW 5
+#define V4L2_HDR10_MASTERING_PRIMARIES_X_HIGH 37000
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_LOW 5
+#define V4L2_HDR10_MASTERING_PRIMARIES_Y_HIGH 42000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_LOW 5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_X_HIGH 37000
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_LOW 5
+#define V4L2_HDR10_MASTERING_WHITE_POINT_Y_HIGH 42000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_LOW 50000
+#define V4L2_HDR10_MASTERING_MAX_LUMA_HIGH 100000000
+#define V4L2_HDR10_MASTERING_MIN_LUMA_LOW 1
+#define V4L2_HDR10_MASTERING_MIN_LUMA_HIGH 50000
+
+struct v4l2_ctrl_hdr10_mastering_display {
+ __u16 display_primaries_x[3];
+ __u16 display_primaries_y[3];
+ __u16 white_point_x;
+ __u16 white_point_y;
+ __u32 max_display_mastering_luminance;
+ __u32 min_display_mastering_luminance;
+};
+
/* MPEG-compression definitions kept for backwards compatibility */
#define V4L2_CTRL_CLASS_MPEG V4L2_CTRL_CLASS_CODEC
#define V4L2_CID_MPEG_CLASS V4L2_CID_CODEC_CLASS
diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h
index a38454d9..658106f5 100644
--- a/include/linux/v4l2-subdev.h
+++ b/include/linux/v4l2-subdev.h
@@ -44,6 +44,7 @@ enum v4l2_subdev_format_whence {
* @which: format type (from enum v4l2_subdev_format_whence)
* @pad: pad number, as reported by the media API
* @format: media bus format (format code and frame size)
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_format {
__u32 which;
@@ -57,6 +58,7 @@ struct v4l2_subdev_format {
* @which: format type (from enum v4l2_subdev_format_whence)
* @pad: pad number, as reported by the media API
* @rect: pad crop rectangle boundaries
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_crop {
__u32 which;
@@ -78,6 +80,7 @@ struct v4l2_subdev_crop {
* @code: format code (MEDIA_BUS_FMT_ definitions)
* @which: format type (from enum v4l2_subdev_format_whence)
* @flags: flags set by the driver, (V4L2_SUBDEV_MBUS_CODE_*)
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_mbus_code_enum {
__u32 pad;
@@ -90,10 +93,15 @@ struct v4l2_subdev_mbus_code_enum {
/**
* struct v4l2_subdev_frame_size_enum - Media bus format enumeration
- * @pad: pad number, as reported by the media API
* @index: format index during enumeration
+ * @pad: pad number, as reported by the media API
* @code: format code (MEDIA_BUS_FMT_ definitions)
+ * @min_width: minimum frame width, in pixels
+ * @max_width: maximum frame width, in pixels
+ * @min_height: minimum frame height, in pixels
+ * @max_height: maximum frame height, in pixels
* @which: format type (from enum v4l2_subdev_format_whence)
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_frame_size_enum {
__u32 index;
@@ -111,6 +119,7 @@ struct v4l2_subdev_frame_size_enum {
* struct v4l2_subdev_frame_interval - Pad-level frame rate
* @pad: pad number, as reported by the media API
* @interval: frame interval in seconds
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_frame_interval {
__u32 pad;
@@ -127,6 +136,7 @@ struct v4l2_subdev_frame_interval {
* @height: frame height in pixels
* @interval: frame interval in seconds
* @which: format type (from enum v4l2_subdev_format_whence)
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_subdev_frame_interval_enum {
__u32 index;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 31d1747e..59c466df 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -580,6 +580,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_YUV444 v4l2_fourcc('Y', '4', '4', '4') /* 16 xxxxyyyy uuuuvvvv */
#define V4L2_PIX_FMT_YUV555 v4l2_fourcc('Y', 'U', 'V', 'O') /* 16 YUV-5-5-5 */
#define V4L2_PIX_FMT_YUV565 v4l2_fourcc('Y', 'U', 'V', 'P') /* 16 YUV-5-6-5 */
+#define V4L2_PIX_FMT_YUV24 v4l2_fourcc('Y', 'U', 'V', '3') /* 24 YUV-8-8-8 */
#define V4L2_PIX_FMT_YUV32 v4l2_fourcc('Y', 'U', 'V', '4') /* 32 YUV-8-8-8-8 */
#define V4L2_PIX_FMT_AYUV32 v4l2_fourcc('A', 'Y', 'U', 'V') /* 32 AYUV-8-8-8-8 */
#define V4L2_PIX_FMT_XYUV32 v4l2_fourcc('X', 'Y', 'U', 'V') /* 32 XYUV-8-8-8-8 */
@@ -688,6 +689,7 @@ struct v4l2_pix_format {
#define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
#define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
#define V4L2_PIX_FMT_VP8 v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_VP8_FRAME v4l2_fourcc('V', 'P', '8', 'F') /* VP8 parsed frame */
#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */
#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC aka H.265 */
#define V4L2_PIX_FMT_FWHT v4l2_fourcc('F', 'W', 'H', 'T') /* Fast Walsh Hadamard Transform (vicodec) */
@@ -952,8 +954,10 @@ struct v4l2_requestbuffers {
* pointing to this plane
* @fd: when memory is V4L2_MEMORY_DMABUF, a userspace file
* descriptor associated with this plane
+ * @m: union of @mem_offset, @userptr and @fd
* @data_offset: offset in the plane to the start of data; usually 0,
* unless there is a header in front of the data
+ * @reserved: drivers and applications must zero this array
*
* Multi-planar buffers consist of one or more planes, e.g. an YCbCr buffer
* with two planes can have one plane for Y, and another for interleaved CbCr
@@ -995,10 +999,14 @@ struct v4l2_plane {
* a userspace file descriptor associated with this buffer
* @planes: for multiplanar buffers; userspace pointer to the array of plane
* info structs for this buffer
+ * @m: union of @offset, @userptr, @planes and @fd
* @length: size in bytes of the buffer (NOT its payload) for single-plane
* buffers (when type != *_MPLANE); number of elements in the
* planes array for multi-plane buffers
+ * @reserved2: drivers and applications must zero this field
* @request_fd: fd of the request that this buffer should use
+ * @reserved: for backwards compatibility with applications that do not know
+ * about @request_fd
*
* Contains data exchanged by application and driver using one of the Streaming
* I/O methods.
@@ -1031,7 +1039,7 @@ struct v4l2_buffer {
/**
* v4l2_timeval_to_ns - Convert timeval to nanoseconds
- * @ts: pointer to the timeval variable to be converted
+ * @tv: pointer to the timeval variable to be converted
*
* Returns the scalar nanosecond representation of the timeval
* parameter.
@@ -1091,6 +1099,7 @@ static __inline__ __u64 v4l2_timeval_to_ns(const struct timeval *tv)
* @flags: flags for newly created file, currently only O_CLOEXEC is
* supported, refer to manual of open syscall for more details
* @fd: file descriptor associated with DMABUF (set by driver)
+ * @reserved: drivers and applications must zero this array
*
* Contains data used for exporting a video buffer as DMABUF file descriptor.
* The buffer is identified by a 'cookie' returned by VIDIOC_QUERYBUF
@@ -1708,6 +1717,10 @@ struct v4l2_ext_control {
struct v4l2_ctrl_h264_slice_params *p_h264_slice_params;
struct v4l2_ctrl_h264_decode_params *p_h264_decode_params;
struct v4l2_ctrl_fwht_params *p_fwht_params;
+ struct v4l2_ctrl_vp8_frame *p_vp8_frame;
+ struct v4l2_ctrl_mpeg2_sequence *p_mpeg2_sequence;
+ struct v4l2_ctrl_mpeg2_picture *p_mpeg2_picture;
+ struct v4l2_ctrl_mpeg2_quantisation *p_mpeg2_quantisation;
void *ptr;
};
} __attribute__ ((packed));
@@ -1751,6 +1764,9 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_U32 = 0x0102,
V4L2_CTRL_TYPE_AREA = 0x0106,
+ V4L2_CTRL_TYPE_HDR10_CLL_INFO = 0x0110,
+ V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY = 0x0111,
+
V4L2_CTRL_TYPE_H264_SPS = 0x0200,
V4L2_CTRL_TYPE_H264_PPS = 0x0201,
V4L2_CTRL_TYPE_H264_SCALING_MATRIX = 0x0202,
@@ -1759,6 +1775,12 @@ enum v4l2_ctrl_type {
V4L2_CTRL_TYPE_H264_PRED_WEIGHTS = 0x0205,
V4L2_CTRL_TYPE_FWHT_PARAMS = 0x0220,
+
+ V4L2_CTRL_TYPE_VP8_FRAME = 0x0240,
+
+ V4L2_CTRL_TYPE_MPEG2_QUANTISATION = 0x0250,
+ V4L2_CTRL_TYPE_MPEG2_SEQUENCE = 0x0251,
+ V4L2_CTRL_TYPE_MPEG2_PICTURE = 0x0252,
};
/* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
@@ -2196,6 +2218,7 @@ struct v4l2_mpeg_vbi_fmt_ivtv {
* this plane will be used
* @bytesperline: distance in bytes between the leftmost pixels in two
* adjacent lines
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_plane_pix_format {
__u32 sizeimage;
@@ -2214,8 +2237,10 @@ struct v4l2_plane_pix_format {
* @num_planes: number of planes for this format
* @flags: format flags (V4L2_PIX_FMT_FLAG_*)
* @ycbcr_enc: enum v4l2_ycbcr_encoding, Y'CbCr encoding
+ * @hsv_enc: enum v4l2_hsv_encoding, HSV encoding
* @quantization: enum v4l2_quantization, colorspace quantization
* @xfer_func: enum v4l2_xfer_func, colorspace transfer function
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_pix_format_mplane {
__u32 width;
@@ -2240,6 +2265,7 @@ struct v4l2_pix_format_mplane {
* struct v4l2_sdr_format - SDR format definition
* @pixelformat: little endian four character code (fourcc)
* @buffersize: maximum size in bytes required for data
+ * @reserved: drivers and applications must zero this array
*/
struct v4l2_sdr_format {
__u32 pixelformat;
@@ -2266,6 +2292,8 @@ struct v4l2_meta_format {
* @vbi: raw VBI capture or output parameters
* @sliced: sliced VBI capture or output parameters
* @raw_data: placeholder for future extensions and custom formats
+ * @fmt: union of @pix, @pix_mp, @win, @vbi, @sliced, @sdr, @meta
+ * and @raw_data
*/
struct v4l2_format {
__u32 type;
diff --git a/sync-with-kernel.sh b/sync-with-kernel.sh
index fa881949..e3a70a0b 100755
--- a/sync-with-kernel.sh
+++ b/sync-with-kernel.sh
@@ -29,6 +29,7 @@ fi
cp -a ${KERNEL_DIR}/usr/include/linux/videodev2.h ${TOPSRCDIR}/include/linux
cp -a ${KERNEL_DIR}/usr/include/linux/fb.h ${TOPSRCDIR}/include/linux
cp -a ${KERNEL_DIR}/usr/include/linux/v4l2-controls.h ${TOPSRCDIR}/include/linux
+patch -d ${TOPSRCDIR} --no-backup-if-mismatch -p1 <${TOPSRCDIR}/utils/common/v4l2-controls.patch
cp -a ${KERNEL_DIR}/usr/include/linux/v4l2-common.h ${TOPSRCDIR}/include/linux
cp -a ${KERNEL_DIR}/usr/include/linux/v4l2-subdev.h ${TOPSRCDIR}/include/linux
cp -a ${KERNEL_DIR}/usr/include/linux/v4l2-mediabus.h ${TOPSRCDIR}/include/linux
@@ -103,6 +104,7 @@ function freebsd {
mkdir -p include/linux/$(dirname $i)
cp ${KERNEL_DIR}/usr/include/linux/$i include/linux/$i
done
+ patch -d ${SRCDIR} --no-backup-if-mismatch -p1 <${TOPSRCDIR}/utils/common/v4l2-controls.patch
for i in ivtv.h uinput.h videodev2.h v4l2-common.h; do
sed -e 's/__u8/uint8_t/g' -e 's/__u16/uint16_t/g' -e 's/__u32/uint32_t/g' -e 's/__u64/uint64_t/g' -e 's/__s8/int8_t/g' -e 's/__s16/int16_t/g' -e 's/__s32/int32_t/g' -e 's/__s64/int64_t/g' -e 's/__le32/uint32_t/g' -e 's/__user//g' -i include/linux/$i
diff --git a/utils/cec-compliance/Makefile.am b/utils/cec-compliance/Makefile.am
index 93c1b568..81a68370 100644
--- a/utils/cec-compliance/Makefile.am
+++ b/utils/cec-compliance/Makefile.am
@@ -1,7 +1,7 @@
bin_PROGRAMS = cec-compliance
man_MANS = cec-compliance.1
-cec_compliance_SOURCES = cec-compliance.cpp cec-compliance.h cec-test.cpp cec-test-adapter.cpp cec-test-audio.cpp cec-test-power.cpp cec-test-fuzzing.cpp
+cec_compliance_SOURCES = cec-compliance.cpp cec-compliance.h cec-test.cpp cec-test-adapter.cpp cec-test-audio.cpp cec-test-power.cpp cec-test-fuzzing.cpp cec-test-tuner-record-timer.cpp
cec_compliance_CPPFLAGS = -I$(top_srcdir)/utils/libcecutil $(GIT_SHA) $(GIT_COMMIT_CNT) $(GIT_COMMIT_DATE)
cec_compliance_LDADD = -lrt ../libcecutil/libcecutil.la
diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp
index b292e541..ce8fde3d 100644
--- a/utils/cec-compliance/cec-compliance.cpp
+++ b/utils/cec-compliance/cec-compliance.cpp
@@ -296,202 +296,6 @@ const char *power_status2s(__u8 power_status)
}
}
-static std::string audio_format_code2s(__u8 format_code)
-{
- switch (format_code) {
- case 0:
- return "Reserved";
- case SAD_FMT_CODE_LPCM:
- return "L-PCM";
- case SAD_FMT_CODE_AC3:
- return "AC-3";
- case SAD_FMT_CODE_MPEG1:
- return "MPEG-1";
- case SAD_FMT_CODE_MP3:
- return "MP3";
- case SAD_FMT_CODE_MPEG2:
- return "MPEG2";
- case SAD_FMT_CODE_AAC_LC:
- return "AAC LC";
- case SAD_FMT_CODE_DTS:
- return "DTS";
- case SAD_FMT_CODE_ATRAC:
- return "ATRAC";
- case SAD_FMT_CODE_ONE_BIT_AUDIO:
- return "One Bit Audio";
- case SAD_FMT_CODE_ENHANCED_AC3:
- return "Enhanced AC-3";
- case SAD_FMT_CODE_DTS_HD:
- return "DTS-HD";
- case SAD_FMT_CODE_MAT:
- return "MAT";
- case SAD_FMT_CODE_DST:
- return "DST";
- case SAD_FMT_CODE_WMA_PRO:
- return "WMA Pro";
- case SAD_FMT_CODE_EXTENDED:
- return "Extended";
- default:
- return "Illegal";
- }
-}
-
-std::string extension_type_code2s(__u8 type_code)
-{
- switch (type_code) {
- case 0:
- case 1:
- case 2:
- case 3:
- return "Not in use";
- case SAD_EXT_TYPE_MPEG4_HE_AAC:
- return "MPEG-4 HE AAC";
- case SAD_EXT_TYPE_MPEG4_HE_AACv2:
- return "MPEG-4 HE AAC v2";
- case SAD_EXT_TYPE_MPEG4_AAC_LC:
- return "MPEG-4 AAC LC";
- case SAD_EXT_TYPE_DRA:
- return "DRA";
- case SAD_EXT_TYPE_MPEG4_HE_AAC_SURROUND:
- return "MPEG-4 HE AAC + MPEG Surround";
- case SAD_EXT_TYPE_MPEG4_AAC_LC_SURROUND:
- return "MPEG-4 AAC LC + MPEG Surround";
- case SAD_EXT_TYPE_MPEG_H_3D_AUDIO:
- return "MPEG-H 3D Audio";
- case SAD_EXT_TYPE_AC_4:
- return "AC-4";
- case SAD_EXT_TYPE_LPCM_3D_AUDIO:
- return "L-PCM 3D Audio";
- default:
- return "Reserved";
- }
-}
-
-std::string short_audio_desc2s(const struct short_audio_desc &sad)
-{
- std::stringstream oss;
-
- if (sad.format_code != SAD_FMT_CODE_EXTENDED)
- oss << audio_format_code2s(sad.format_code);
- else
- oss << extension_type_code2s(sad.extension_type_code);
- oss << ", " << static_cast<int>(sad.num_channels) << " channels";
-
- oss << ", sampling rates (kHz): ";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_32)
- oss << "32,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_44_1)
- oss << "44.1,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_48)
- oss << "48,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_88_2)
- oss << "88.2,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_96)
- oss << "96,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_176_4)
- oss << "176.4,";
- if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_192)
- oss << "192,";
- if (sad.sample_freq_mask & (1 << 7))
- oss << "Reserved,";
- oss << "\b \b";
-
- if (sad.format_code == SAD_FMT_CODE_LPCM ||
- (sad.format_code == SAD_FMT_CODE_EXTENDED &&
- sad.extension_type_code == SAD_EXT_TYPE_LPCM_3D_AUDIO)) {
- oss << ", bit depth: ";
- if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_16)
- oss << "16,";
- if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_20)
- oss << "20,";
- if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_24)
- oss << "24,";
- oss << "\b \b";
- } else if (sad.format_code >= 2 && sad.format_code <= 8)
- oss << " max bitrate (kbit/s): " << 8 * sad.max_bitrate;
-
- if (sad.format_code == SAD_FMT_CODE_EXTENDED) {
- switch (sad.extension_type_code) {
- case 4:
- case 5:
- case 6:
- case 8:
- case 10:
- oss << ", frame length: ";
- if (sad.frame_length_mask & SAD_FRAME_LENGTH_MASK_960)
- oss << "960,";
- if (sad.frame_length_mask & SAD_FRAME_LENGTH_MASK_1024)
- oss << "1024,";
- oss << "\b";
- break;
- }
-
- if (sad.extension_type_code == 8 || sad.extension_type_code == 10)
- oss << ", MPS";
- }
-
- return oss.str();
-}
-
-void sad_decode(struct short_audio_desc *sad, __u32 descriptor)
-{
- __u8 b1 = (descriptor >> 16) & 0xff;
- __u8 b2 = (descriptor >> 8) & 0xff;
- __u8 b3 = descriptor & 0xff;
-
- sad->num_channels = (b1 & 0x07) + 1;
- sad->format_code = (b1 >> 3) & 0x0f;
- sad->sample_freq_mask = b2;
-
- switch (sad->format_code) {
- case SAD_FMT_CODE_LPCM:
- sad->bit_depth_mask = b3 & 0x07;
- break;
- case 2:
- case 3:
- case 4:
- case 5:
- case 6:
- case 7:
- case 8:
- sad->max_bitrate = b3;
- break;
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- sad->format_dependent = b3;
- break;
- case SAD_FMT_CODE_WMA_PRO:
- sad->wma_profile = b3 & 0x03;
- break;
- case SAD_FMT_CODE_EXTENDED:
- sad->extension_type_code = (b3 >> 3) & 0x1f;
-
- switch (sad->extension_type_code) {
- case 4:
- case 5:
- case 6:
- sad->frame_length_mask = (b3 >> 1) & 0x03;
- break;
- case 8:
- case 10:
- sad->frame_length_mask = (b3 >> 1) & 0x03;
- sad->mps = b3 & 1;
- break;
- case SAD_EXT_TYPE_MPEG_H_3D_AUDIO:
- case SAD_EXT_TYPE_AC_4:
- sad->format_dependent = b3 & 0x07;
- fallthrough;
- case SAD_EXT_TYPE_LPCM_3D_AUDIO:
- sad->bit_depth_mask = b3 & 0x07;
- break;
- }
- break;
- }
-}
-
const char *bcast_system2s(__u8 bcast_system)
{
switch (bcast_system) {
@@ -554,66 +358,6 @@ const char *dig_bcast_system2s(__u8 bcast_system)
}
}
-const char *hec_func_state2s(__u8 hfs)
-{
- switch (hfs) {
- case CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED:
- return "HEC Not Supported";
- case CEC_OP_HEC_FUNC_STATE_INACTIVE:
- return "HEC Inactive";
- case CEC_OP_HEC_FUNC_STATE_ACTIVE:
- return "HEC Active";
- case CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD:
- return "HEC Activation Field";
- default:
- return "Unknown";
- }
-}
-
-const char *host_func_state2s(__u8 hfs)
-{
- switch (hfs) {
- case CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED:
- return "Host Not Supported";
- case CEC_OP_HOST_FUNC_STATE_INACTIVE:
- return "Host Inactive";
- case CEC_OP_HOST_FUNC_STATE_ACTIVE:
- return "Host Active";
- default:
- return "Unknown";
- }
-}
-
-const char *enc_func_state2s(__u8 efs)
-{
- switch (efs) {
- case CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED:
- return "Ext Con Not Supported";
- case CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE:
- return "Ext Con Inactive";
- case CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE:
- return "Ext Con Active";
- default:
- return "Unknown";
- }
-}
-
-const char *cdc_errcode2s(__u8 cdc_errcode)
-{
- switch (cdc_errcode) {
- case CEC_OP_CDC_ERROR_CODE_NONE:
- return "No error";
- case CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED:
- return "Initiator does not have requested capability";
- case CEC_OP_CDC_ERROR_CODE_WRONG_STATE:
- return "Initiator is in wrong state";
- case CEC_OP_CDC_ERROR_CODE_OTHER:
- return "Other error";
- default:
- return "Unknown";
- }
-}
-
std::string opcode2s(const struct cec_msg *msg)
{
std::stringstream oss;
@@ -865,7 +609,6 @@ static bool wait_for_hpd(struct node *node, bool send_image_view_on)
bool transmit_timeout(struct node *node, struct cec_msg *msg, unsigned timeout)
{
struct cec_msg original_msg = *msg;
- __u8 opcode = cec_msg_opcode(msg);
bool retried = false;
int res;
@@ -896,7 +639,10 @@ retry:
if (((msg->rx_status & CEC_RX_STATUS_OK) || (msg->rx_status & CEC_RX_STATUS_FEATURE_ABORT))
&& response_time_ms(msg) > reply_threshold)
- warn("Waited %4ums for reply to msg 0x%02x.\n", response_time_ms(msg), opcode);
+ warn("Waited %4ums for %s to msg %s.\n",
+ response_time_ms(msg),
+ (msg->rx_status & CEC_RX_STATUS_OK) ? "reply" : "Feature Abort",
+ opcode2s(&original_msg).c_str());
if (!cec_msg_status_is_abort(msg))
return true;
@@ -997,7 +743,7 @@ static int poll_remote_devs(struct node *node)
static void topology_probe_device(struct node *node, unsigned i, unsigned la)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
bool unknown;
printf("\tSystem Information for device %d (%s) from device %d (%s):\n",
@@ -1492,6 +1238,7 @@ int main(int argc, char **argv)
node.num_log_addrs = laddrs.num_log_addrs;
memcpy(node.log_addr, laddrs.log_addr, laddrs.num_log_addrs);
node.adap_la_mask = laddrs.log_addr_mask;
+ node.current_time = time(nullptr);
printf("Find remote devices:\n");
printf("\tPolling: %s\n", ok(poll_remote_devs(&node)));
@@ -1520,9 +1267,10 @@ int main(int argc, char **argv)
remote_la_mask = 1 << remote_la;
if (test_remote) {
- for (unsigned from = 0; from <= 15; from++) {
- if (!(node.adap_la_mask & (1 << from)))
- continue;
+ for (unsigned i = 0; i < node.num_log_addrs; i++) {
+ unsigned from = node.log_addr[i];
+ node.prim_devtype = laddrs.primary_device_type[i];
+
for (unsigned to = 0; to <= 15; to++)
if (!(node.adap_la_mask & (1 << to)) &&
(remote_la_mask & (1 << to)))
diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h
index c558f043..08365e07 100644
--- a/utils/cec-compliance/cec-compliance.h
+++ b/utils/cec-compliance/cec-compliance.h
@@ -166,6 +166,8 @@ struct node {
struct remote remote[16];
__u16 phys_addr;
bool in_standby;
+ __u8 prim_devtype;
+ time_t current_time;
};
struct remote_subtest {
@@ -359,6 +361,11 @@ static inline bool refused(const struct cec_msg *msg)
return cec_msg_status_is_abort(msg) && abort_reason(msg) == CEC_OP_ABORT_REFUSED;
}
+static inline bool incorrect_mode(const struct cec_msg *msg)
+{
+ return cec_msg_status_is_abort(msg) && abort_reason(msg) == CEC_OP_ABORT_INCORRECT_MODE;
+}
+
static inline bool timed_out(const struct cec_msg *msg)
{
return msg->rx_status & CEC_RX_STATUS_TIMEOUT;
@@ -413,14 +420,8 @@ static inline unsigned get_ts_ms()
const char *result_name(int res, bool show_colors);
const char *ok(int res);
const char *power_status2s(__u8 power_status);
-std::string short_audio_desc2s(const struct short_audio_desc &sad);
-void sad_decode(struct short_audio_desc *sad, __u32 descriptor);
const char *bcast_system2s(__u8 bcast_system);
const char *dig_bcast_system2s(__u8 bcast_system);
-const char *hec_func_state2s(__u8 hfs);
-const char *host_func_state2s(__u8 hfs);
-const char *enc_func_state2s(__u8 efs);
-const char *cdc_errcode2s(__u8 cdc_errcode);
int check_0(const void *p, int len);
int util_receive(struct node *node, unsigned la, unsigned timeout,
struct cec_msg *msg, __u8 sent_msg,
@@ -456,6 +457,11 @@ int setExpectedResult(char *optarg, bool no_warnings);
void testRemote(struct node *node, unsigned me, unsigned la, unsigned test_tags,
bool interactive);
+// cec-test-tuner-record-timer.cpp
+extern const vec_remote_subtests tuner_ctl_subtests;
+extern const vec_remote_subtests one_touch_rec_subtests;
+extern const vec_remote_subtests timer_prog_subtests;
+
// cec-test-audio.cpp
extern const vec_remote_subtests sac_subtests;
extern const vec_remote_subtests dal_subtests;
diff --git a/utils/cec-compliance/cec-test-adapter.cpp b/utils/cec-compliance/cec-test-adapter.cpp
index cf1b6191..81eb4013 100644
--- a/utils/cec-compliance/cec-test-adapter.cpp
+++ b/utils/cec-compliance/cec-test-adapter.cpp
@@ -14,8 +14,8 @@
#include "cec-compliance.h"
-static const __u8 tx_ok_retry_mask = CEC_TX_STATUS_OK | CEC_TX_STATUS_MAX_RETRIES;
-static const __u32 msg_fl_mask = CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW;
+static constexpr __u8 tx_ok_retry_mask = CEC_TX_STATUS_OK | CEC_TX_STATUS_MAX_RETRIES;
+static constexpr __u32 msg_fl_mask = CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW;
// Flush any pending messages
static int flush_pending_msgs(struct node *node)
@@ -107,24 +107,24 @@ static int testAdapPhysAddr(struct node *node)
static int testAdapLogAddrs(struct node *node)
{
- static const __u8 la_types[] = {
+ static constexpr __u8 la_types[] = {
CEC_LOG_ADDR_TYPE_TV,
CEC_LOG_ADDR_TYPE_RECORD,
CEC_LOG_ADDR_TYPE_TUNER,
CEC_LOG_ADDR_TYPE_AUDIOSYSTEM
};
- static const __u8 prim_dev_types[] = {
+ static constexpr __u8 prim_dev_types[] = {
CEC_OP_PRIM_DEVTYPE_TV,
CEC_OP_PRIM_DEVTYPE_RECORD,
CEC_OP_PRIM_DEVTYPE_TUNER,
CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM
};
- static const __u8 all_dev_types[2] = {
+ static constexpr __u8 all_dev_types[2] = {
CEC_OP_ALL_DEVTYPE_TV | CEC_OP_ALL_DEVTYPE_RECORD |
CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM,
CEC_OP_ALL_DEVTYPE_RECORD | CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM,
};
- static const __u8 features[12] = {
+ static constexpr __u8 features[12] = {
0x90, 0x00, 0x8e, 0x00,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff
@@ -261,7 +261,7 @@ static int testAdapLogAddrs(struct node *node)
static int testTransmit(struct node *node)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
unsigned i, la = node->log_addr[0];
unsigned valid_la = 15, invalid_la = 15;
bool tested_self = false;
diff --git a/utils/cec-compliance/cec-test-audio.cpp b/utils/cec-compliance/cec-test-audio.cpp
index bdbcd178..611f600f 100644
--- a/utils/cec-compliance/cec-test-audio.cpp
+++ b/utils/cec-compliance/cec-test-audio.cpp
@@ -4,18 +4,216 @@
*/
#include <ctime>
+#include <sstream>
#include <string>
#include <sys/ioctl.h>
#include <unistd.h>
#include "cec-compliance.h"
+#include "compiler.h"
+
+static std::string audio_format_code2s(__u8 format_code)
+{
+ switch (format_code) {
+ case 0:
+ return "Reserved";
+ case SAD_FMT_CODE_LPCM:
+ return "L-PCM";
+ case SAD_FMT_CODE_AC3:
+ return "AC-3";
+ case SAD_FMT_CODE_MPEG1:
+ return "MPEG-1";
+ case SAD_FMT_CODE_MP3:
+ return "MP3";
+ case SAD_FMT_CODE_MPEG2:
+ return "MPEG2";
+ case SAD_FMT_CODE_AAC_LC:
+ return "AAC LC";
+ case SAD_FMT_CODE_DTS:
+ return "DTS";
+ case SAD_FMT_CODE_ATRAC:
+ return "ATRAC";
+ case SAD_FMT_CODE_ONE_BIT_AUDIO:
+ return "One Bit Audio";
+ case SAD_FMT_CODE_ENHANCED_AC3:
+ return "Enhanced AC-3";
+ case SAD_FMT_CODE_DTS_HD:
+ return "DTS-HD";
+ case SAD_FMT_CODE_MAT:
+ return "MAT";
+ case SAD_FMT_CODE_DST:
+ return "DST";
+ case SAD_FMT_CODE_WMA_PRO:
+ return "WMA Pro";
+ case SAD_FMT_CODE_EXTENDED:
+ return "Extended";
+ default:
+ return "Illegal";
+ }
+}
+
+static std::string extension_type_code2s(__u8 type_code)
+{
+ switch (type_code) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return "Not in use";
+ case SAD_EXT_TYPE_MPEG4_HE_AAC:
+ return "MPEG-4 HE AAC";
+ case SAD_EXT_TYPE_MPEG4_HE_AACv2:
+ return "MPEG-4 HE AAC v2";
+ case SAD_EXT_TYPE_MPEG4_AAC_LC:
+ return "MPEG-4 AAC LC";
+ case SAD_EXT_TYPE_DRA:
+ return "DRA";
+ case SAD_EXT_TYPE_MPEG4_HE_AAC_SURROUND:
+ return "MPEG-4 HE AAC + MPEG Surround";
+ case SAD_EXT_TYPE_MPEG4_AAC_LC_SURROUND:
+ return "MPEG-4 AAC LC + MPEG Surround";
+ case SAD_EXT_TYPE_MPEG_H_3D_AUDIO:
+ return "MPEG-H 3D Audio";
+ case SAD_EXT_TYPE_AC_4:
+ return "AC-4";
+ case SAD_EXT_TYPE_LPCM_3D_AUDIO:
+ return "L-PCM 3D Audio";
+ default:
+ return "Reserved";
+ }
+}
+
+static std::string short_audio_desc2s(const struct short_audio_desc &sad)
+{
+ std::stringstream oss;
+
+ if (sad.format_code != SAD_FMT_CODE_EXTENDED)
+ oss << audio_format_code2s(sad.format_code);
+ else
+ oss << extension_type_code2s(sad.extension_type_code);
+ oss << ", " << static_cast<int>(sad.num_channels) << " channels";
+
+ oss << ", sampling rates (kHz): ";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_32)
+ oss << "32,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_44_1)
+ oss << "44.1,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_48)
+ oss << "48,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_88_2)
+ oss << "88.2,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_96)
+ oss << "96,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_176_4)
+ oss << "176.4,";
+ if (sad.sample_freq_mask & SAD_SAMPLE_FREQ_MASK_192)
+ oss << "192,";
+ if (sad.sample_freq_mask & (1 << 7))
+ oss << "Reserved,";
+ oss << "\b \b";
+
+ if (sad.format_code == SAD_FMT_CODE_LPCM ||
+ (sad.format_code == SAD_FMT_CODE_EXTENDED &&
+ sad.extension_type_code == SAD_EXT_TYPE_LPCM_3D_AUDIO)) {
+ oss << ", bit depth: ";
+ if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_16)
+ oss << "16,";
+ if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_20)
+ oss << "20,";
+ if (sad.bit_depth_mask & SAD_BIT_DEPTH_MASK_24)
+ oss << "24,";
+ oss << "\b \b";
+ } else if (sad.format_code >= 2 && sad.format_code <= 8)
+ oss << " max bitrate (kbit/s): " << 8 * sad.max_bitrate;
+
+ if (sad.format_code == SAD_FMT_CODE_EXTENDED) {
+ switch (sad.extension_type_code) {
+ case 4:
+ case 5:
+ case 6:
+ case 8:
+ case 10:
+ oss << ", frame length: ";
+ if (sad.frame_length_mask & SAD_FRAME_LENGTH_MASK_960)
+ oss << "960,";
+ if (sad.frame_length_mask & SAD_FRAME_LENGTH_MASK_1024)
+ oss << "1024,";
+ oss << "\b";
+ break;
+ }
+
+ if (sad.extension_type_code == 8 || sad.extension_type_code == 10)
+ oss << ", MPS";
+ }
+
+ return oss.str();
+}
+
+static void sad_decode(struct short_audio_desc *sad, __u32 descriptor)
+{
+ __u8 b1 = (descriptor >> 16) & 0xff;
+ __u8 b2 = (descriptor >> 8) & 0xff;
+ __u8 b3 = descriptor & 0xff;
+
+ sad->num_channels = (b1 & 0x07) + 1;
+ sad->format_code = (b1 >> 3) & 0x0f;
+ sad->sample_freq_mask = b2;
+
+ switch (sad->format_code) {
+ case SAD_FMT_CODE_LPCM:
+ sad->bit_depth_mask = b3 & 0x07;
+ break;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ sad->max_bitrate = b3;
+ break;
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ sad->format_dependent = b3;
+ break;
+ case SAD_FMT_CODE_WMA_PRO:
+ sad->wma_profile = b3 & 0x03;
+ break;
+ case SAD_FMT_CODE_EXTENDED:
+ sad->extension_type_code = (b3 >> 3) & 0x1f;
+
+ switch (sad->extension_type_code) {
+ case 4:
+ case 5:
+ case 6:
+ sad->frame_length_mask = (b3 >> 1) & 0x03;
+ break;
+ case 8:
+ case 10:
+ sad->frame_length_mask = (b3 >> 1) & 0x03;
+ sad->mps = b3 & 1;
+ break;
+ case SAD_EXT_TYPE_MPEG_H_3D_AUDIO:
+ case SAD_EXT_TYPE_AC_4:
+ sad->format_dependent = b3 & 0x07;
+ fallthrough;
+ case SAD_EXT_TYPE_LPCM_3D_AUDIO:
+ sad->bit_depth_mask = b3 & 0x07;
+ break;
+ }
+ break;
+ }
+}
/* Dynamic Auto Lipsync */
static int dal_request_current_latency(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_request_current_latency(&msg, true, node->remote[la].phys_addr);
@@ -39,6 +237,13 @@ static int dal_request_current_latency(struct node *node, unsigned me, unsigned
cec_ops_report_current_latency(&msg, &phys_addr, &video_latency, &low_latency_mode,
&audio_out_compensated, &audio_out_delay);
+ // cec_ops_report_current_latency will hardcode audio_out_delay
+ // if it is unused, but for this test we want the real value, so
+ // get it from the actual message.
+ if (msg.len >= 7)
+ audio_out_delay = msg.msg[6];
+ else
+ audio_out_delay = 1;
fail_on_test(phys_addr != node->remote[la].phys_addr);
info("Video latency: %d (%dms)\n", video_latency, (video_latency - 1) * 2);
info("Low latency mode: %d\n", low_latency_mode);
@@ -51,9 +256,10 @@ static int dal_request_current_latency(struct node *node, unsigned me, unsigned
} else {
// Although this value will be ignored, it shouldn't use
// reserved values.
- warn_on_test(audio_out_delay == 0 || audio_out_delay > 251);
- if (audio_out_delay > 1 && audio_out_delay <= 251)
- warn("Audio out delay is %d (%dms), but value 1 is recommended when this field is unused\n",
+ if (audio_out_delay == 0 || audio_out_delay > 251)
+ warn("Audio out delay is set to a reserved value (%d), set it to 1 instead (recommended value when this field is unused).\n", audio_out_delay);
+ else if (audio_out_delay != 1)
+ warn("Audio out delay is %d (%dms), but value 1 is recommended when this field is unused.\n",
audio_out_delay, (audio_out_delay - 1) * 2);
}
fail_on_test(video_latency == 0 || video_latency > 251);
@@ -70,7 +276,7 @@ static int dal_request_current_latency(struct node *node, unsigned me, unsigned
static int dal_req_current_latency_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* Test that there is no reply when the physical address operand is not the
physical address of the remote device. */
@@ -128,7 +334,7 @@ static int arc_initiate_tx(struct node *node, unsigned me, unsigned la, bool int
if (pa_is_upstream_from(node->phys_addr, node->remote[la].phys_addr))
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
/*
* Note that this is a special case: INITIATE_ARC can reply with two possible
@@ -171,7 +377,7 @@ static int arc_terminate_tx(struct node *node, unsigned me, unsigned la, bool in
if (!node->remote[la].arc_initiated)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_terminate_arc(&msg, true);
@@ -198,7 +404,7 @@ static int arc_initiate_rx(struct node *node, unsigned me, unsigned la, bool int
if (pa_is_upstream_from(node->remote[la].phys_addr, node->phys_addr))
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_request_arc_initiation(&msg, true);
@@ -246,7 +452,7 @@ static int arc_terminate_rx(struct node *node, unsigned me, unsigned la, bool in
if (!node->remote[la].arc_initiated)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_request_arc_termination(&msg, true);
@@ -320,7 +526,7 @@ const vec_remote_subtests arc_subtests{
static int sac_request_sad_probe(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 audio_format_id = 0;
__u8 audio_format_code = 1;
@@ -344,7 +550,7 @@ static int sac_request_sad_invalid(struct node *node, unsigned me, unsigned la,
if (!node->remote[la].has_sad)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 audio_format_id = CEC_OP_AUD_FMT_ID_CEA861;
__u8 audio_format_code = 63; // This is outside the range of CEA861-F
@@ -366,7 +572,7 @@ static int sac_sad_format_check(struct node *node, unsigned me, unsigned la, boo
if (!node->remote[la].has_sad)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 audio_format_id;
__u8 audio_format_code;
@@ -394,7 +600,7 @@ static int sac_sad_format_check(struct node *node, unsigned me, unsigned la, boo
warn("The device has CEC version < 2.0 but reports audio format(s) introduced in CEC 2.0.\n");
for (int j = 0; j < num_descriptors; j++) {
- struct short_audio_desc sad;
+ struct short_audio_desc sad = {};
sad_decode(&sad, descriptors[j]);
if ((id == 0 && sad.format_code != fmt_code) ||
@@ -421,7 +627,7 @@ static int sac_sad_req_multiple(struct node *node, unsigned me, unsigned la, boo
/* Check that if we got a response to a Request Short Audio Descriptor
with a single format, we also get a response when the same audio format
occurs in a request together with other formats. */
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 audio_format_id[4] = { };
__u8 audio_format_code[4];
@@ -442,7 +648,7 @@ static int sac_sad_req_multiple(struct node *node, unsigned me, unsigned la, boo
static int sac_set_system_audio_mode_direct(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_system_audio_mode(&msg, CEC_OP_SYS_AUD_STATUS_ON);
@@ -460,7 +666,7 @@ static int sac_set_system_audio_mode_direct(struct node *node, unsigned me, unsi
static int sac_set_system_audio_mode_broadcast_on(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
cec_msg_set_system_audio_mode(&msg, CEC_OP_SYS_AUD_STATUS_ON);
@@ -471,7 +677,7 @@ static int sac_set_system_audio_mode_broadcast_on(struct node *node, unsigned me
static int sac_system_audio_mode_status(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* The device shall not feature abort System Audio Status if it did not
feature abort Set System Audio Mode.
@@ -495,7 +701,7 @@ static int sac_system_audio_mode_status(struct node *node, unsigned me, unsigned
static int sac_set_system_audio_mode_broadcast_off(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
cec_msg_set_system_audio_mode(&msg, CEC_OP_SYS_AUD_STATUS_OFF);
@@ -506,7 +712,7 @@ static int sac_set_system_audio_mode_broadcast_off(struct node *node, unsigned m
static int sac_system_audio_mode_req_on(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 status;
/* Send a System Audio Mode Request to the audio system. This notifies the
@@ -535,7 +741,7 @@ static int sac_system_audio_mode_req_on(struct node *node, unsigned me, unsigned
static int sac_give_system_audio_mode_status(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 system_audio_status;
/* The device shall not feature abort Give System Audio Mode Status if it did not
@@ -563,7 +769,7 @@ static int sac_give_system_audio_mode_status(struct node *node, unsigned me, uns
static int sac_give_audio_status(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* Give Audio Status is mandatory for audio systems in CEC 2.0, except
for systems that lack external controls for volume/mute status. */
@@ -589,7 +795,7 @@ static int sac_give_audio_status(struct node *node, unsigned me, unsigned la, bo
static int sac_util_send_user_control_press(struct node *node, unsigned me, unsigned la, __u8 ui_cmd)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
struct cec_op_ui_command rc_press = {};
/* The device shall not feature abort
@@ -708,7 +914,7 @@ static int sac_user_control_press_restore_volume_function(struct node *node, uns
static int sac_user_control_release(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* The device shall not feature abort User Control Released if it did not
feature abort System Audio Mode Request
@@ -735,7 +941,7 @@ static int sac_system_audio_mode_req_off(struct node *node, unsigned me, unsigne
if (!node->remote[la].has_sys_audio_mode_req)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 status;
cec_msg_init(&msg, me, la);
@@ -853,7 +1059,7 @@ const vec_remote_subtests sac_subtests{
static int audio_rate_ctl_set_audio_rate(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_audio_rate(&msg, CEC_OP_AUD_RATE_WIDE_STD);
@@ -880,7 +1086,7 @@ static int audio_rate_ctl_active_sensing(struct node *node, unsigned me, unsigne
if (!node->remote[la].has_aud_rate)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
@@ -900,7 +1106,7 @@ static int audio_rate_ctl_invalid(struct node *node, unsigned me, unsigned la, b
if (!node->remote[la].has_aud_rate)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_audio_rate(&msg, 0xa); /* Invalid Audio Rate Control message operand */
diff --git a/utils/cec-compliance/cec-test-power.cpp b/utils/cec-compliance/cec-test-power.cpp
index bc88eca7..f23d0414 100644
--- a/utils/cec-compliance/cec-test-power.cpp
+++ b/utils/cec-compliance/cec-test-power.cpp
@@ -16,7 +16,7 @@
static bool get_power_status(struct node *node, unsigned me, unsigned la, __u8 &power_status)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_device_power_status(&msg, true);
@@ -62,7 +62,7 @@ bool util_interactive_ensure_power_state(struct node *node, unsigned me, unsigne
static int power_status_give(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_device_power_status(&msg, true);
@@ -83,7 +83,7 @@ static int power_status_give(struct node *node, unsigned me, unsigned la, bool i
static int power_status_report(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_report_power_status(&msg, CEC_OP_POWER_STATUS_ON);
@@ -106,7 +106,7 @@ const vec_remote_subtests power_status_subtests{
static int one_touch_play_view_on(struct node *node, unsigned me, unsigned la, bool interactive,
__u8 opcode)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
if (opcode == CEC_MSG_IMAGE_VIEW_ON)
@@ -179,7 +179,7 @@ static int one_touch_play_text_view_on_wakeup(struct node *node, unsigned me, un
static int one_touch_play_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive,
__u8 opcode)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
int ret;
fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
@@ -215,7 +215,7 @@ static int one_touch_play_text_view_on_change(struct node *node, unsigned me, un
static int one_touch_play_req_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_active_source(&msg, node->phys_addr);
@@ -318,7 +318,7 @@ static int standby_resume_standby(struct node *node, unsigned me, unsigned la, b
if (!node->remote[la].has_power_status)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
unsigned unresponsive_time = 0;
fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
@@ -355,7 +355,7 @@ static int standby_resume_standby_toggle(struct node *node, unsigned me, unsigne
if (!node->remote[la].in_standby)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
unsigned unresponsive_time = 0;
__u8 new_status;
@@ -403,7 +403,7 @@ static int standby_resume_active_source_nowake(struct node *node, unsigned me, u
if (!node->remote[la].in_standby)
return NOTAPPLICABLE;
- struct cec_msg msg = {};
+ struct cec_msg msg;
unsigned unresponsive_time = 0;
__u8 new_status;
@@ -435,7 +435,7 @@ static int standby_resume_active_source_nowake(struct node *node, unsigned me, u
static int wakeup_rc(struct node *node, unsigned me, unsigned la)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
struct cec_op_ui_command rc_press = {};
/* Todo: A release should be sent after this */
@@ -450,7 +450,7 @@ static int wakeup_rc(struct node *node, unsigned me, unsigned la)
static int wakeup_tv(struct node *node, unsigned me, unsigned la)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_image_view_on(&msg);
@@ -475,7 +475,7 @@ static int wakeup_tv(struct node *node, unsigned me, unsigned la)
static int wakeup_source(struct node *node, unsigned me, unsigned la)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
@@ -545,7 +545,7 @@ static int standby_resume_wakeup_view_on(struct node *node, unsigned me, unsigne
fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
fail_on_test(interactive && !question("Is the device in On state?"));
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_active_source(&msg, node->phys_addr);
@@ -633,6 +633,110 @@ static int power_state_transitions(struct node *node, unsigned me, unsigned la,
return 0;
}
+static int standby_resume_wakeup_deck(struct node *node, unsigned me, unsigned la, bool interactive, __u8 opcode)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ fail_on_test(!transmit_timeout(node, &msg));
+ if (timed_out_or_abort(&msg))
+ return OK_NOT_SUPPORTED;
+
+ unsigned unresponsive_time = 0;
+
+ fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
+
+ int ret = standby_resume_standby(node, me, la, interactive);
+
+ if (ret)
+ return ret;
+
+ cec_msg_init(&msg, me, la);
+ if (opcode == CEC_OP_PLAY_MODE_PLAY_FWD)
+ cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_FWD);
+ else
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_EJECT);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg));
+
+ unresponsive_time = 0;
+ fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
+ fail_on_test(interactive && !question("Is the device in On state?"));
+
+ return OK;
+}
+
+static int standby_resume_wakeup_deck_eject(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ return standby_resume_wakeup_deck(node, me, la, interactive, CEC_OP_DECK_CTL_MODE_EJECT);
+}
+
+static int standby_resume_wakeup_deck_play(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ return standby_resume_wakeup_deck(node, me, la, interactive, CEC_OP_PLAY_MODE_PLAY_FWD);
+}
+
+static int standby_record(struct node *node, unsigned me, unsigned la, bool interactive, bool active_source)
+{
+ struct cec_msg msg;
+ __u8 rec_status;
+ unsigned unresponsive_time = 0;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on_own(&msg);
+ msg.reply = CEC_MSG_RECORD_STATUS;
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ if (timed_out_or_abort(&msg))
+ return OK_NOT_SUPPORTED;
+ cec_ops_record_status(&msg, &rec_status);
+ fail_on_test(rec_status != CEC_OP_RECORD_STATUS_CUR_SRC &&
+ rec_status != CEC_OP_RECORD_STATUS_ALREADY_RECORDING);
+
+ cec_msg_init(&msg, me, la);
+ if (active_source)
+ cec_msg_active_source(&msg, node->remote[la].phys_addr);
+ else
+ cec_msg_active_source(&msg, me);
+ fail_on_test(!transmit_timeout(node, &msg));
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_standby(&msg);
+ fail_on_test(!transmit_timeout(node, &msg));
+ /* Standby should not interrupt the recording. */
+ fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_off(&msg, false);
+ fail_on_test(!transmit_timeout(node, &msg));
+
+ /* When the recording stops, recorder should standby unless it is the active source. */
+ if (active_source) {
+ fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
+ } else {
+ fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_STANDBY, unresponsive_time));
+ fail_on_test(interactive && !question("Is the device in standby?"));
+ node->remote[la].in_standby = true;
+
+ int ret = standby_resume_wakeup(node, me, la, interactive);
+ if (ret)
+ return ret;
+ node->remote[la].in_standby = false;
+ }
+
+ return OK;
+}
+
+static int standby_record_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ return standby_record(node, me, la, interactive, true);
+}
+
+static int standby_record_inactive_source(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ return standby_record(node, me, la, interactive, false);
+}
+
const vec_remote_subtests standby_resume_subtests{
{ "Standby", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby },
{ "Repeated Standby message does not wake up", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby_toggle },
@@ -651,4 +755,8 @@ const vec_remote_subtests standby_resume_subtests{
{ "Wake up TV on Image View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_image_view_on },
{ "Wake up TV on Text View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_text_view_on },
{ "Power State Transitions", CEC_LOG_ADDR_MASK_TV, power_state_transitions, false, true },
+ { "Deck Eject Standby Resume", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, standby_resume_wakeup_deck_eject },
+ { "Deck Play Standby Resume", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, standby_resume_wakeup_deck_play },
+ { "Record Standby Active Source", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, standby_record_active_source },
+ { "Record Standby Inactive Source", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, standby_record_inactive_source },
};
diff --git a/utils/cec-compliance/cec-test-tuner-record-timer.cpp b/utils/cec-compliance/cec-test-tuner-record-timer.cpp
new file mode 100644
index 00000000..1ba5a135
--- /dev/null
+++ b/utils/cec-compliance/cec-test-tuner-record-timer.cpp
@@ -0,0 +1,1088 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
+ */
+
+#include <cstring>
+#include <map>
+#include <sstream>
+#include <vector>
+
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "cec-compliance.h"
+
+enum Months { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };
+
+/* Tuner Control */
+
+static const char *bcast_type2s(__u8 bcast_type)
+{
+ switch (bcast_type) {
+ case CEC_OP_ANA_BCAST_TYPE_CABLE:
+ return "Cable";
+ case CEC_OP_ANA_BCAST_TYPE_SATELLITE:
+ return "Satellite";
+ case CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL:
+ return "Terrestrial";
+ default:
+ return "Future use";
+ }
+}
+
+static int log_tuner_service(const struct cec_op_tuner_device_info &info,
+ const char *prefix = "")
+{
+ printf("\t\t%s", prefix);
+
+ if (info.is_analog) {
+ double freq_mhz = (info.analog.ana_freq * 625) / 10000.0;
+
+ printf("Analog Channel %.2f MHz (%s, %s)\n", freq_mhz,
+ bcast_system2s(info.analog.bcast_system),
+ bcast_type2s(info.analog.ana_bcast_type));
+
+ switch (info.analog.bcast_system) {
+ case CEC_OP_BCAST_SYSTEM_PAL_BG:
+ case CEC_OP_BCAST_SYSTEM_SECAM_LQ:
+ case CEC_OP_BCAST_SYSTEM_PAL_M:
+ case CEC_OP_BCAST_SYSTEM_NTSC_M:
+ case CEC_OP_BCAST_SYSTEM_PAL_I:
+ case CEC_OP_BCAST_SYSTEM_SECAM_DK:
+ case CEC_OP_BCAST_SYSTEM_SECAM_BG:
+ case CEC_OP_BCAST_SYSTEM_SECAM_L:
+ case CEC_OP_BCAST_SYSTEM_PAL_DK:
+ break;
+ default:
+ return fail("invalid analog bcast_system %u", info.analog.bcast_system);
+ }
+ if (info.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL)
+ return fail("invalid analog bcast_type %u\n", info.analog.ana_bcast_type);
+ fail_on_test(!info.analog.ana_freq);
+ return 0;
+ }
+
+ __u8 system = info.digital.dig_bcast_system;
+
+ printf("%s Channel ", dig_bcast_system2s(system));
+ if (info.digital.service_id_method) {
+ __u16 major = info.digital.channel.major;
+ __u16 minor = info.digital.channel.minor;
+
+ switch (info.digital.channel.channel_number_fmt) {
+ case CEC_OP_CHANNEL_NUMBER_FMT_2_PART:
+ printf("%u.%u\n", major, minor);
+ break;
+ case CEC_OP_CHANNEL_NUMBER_FMT_1_PART:
+ printf("%u\n", minor);
+ break;
+ default:
+ printf("%u.%u\n", major, minor);
+ return fail("invalid service ID method\n");
+ }
+ return 0;
+ }
+
+
+ switch (system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T: {
+ __u16 tsid = info.digital.arib.transport_id;
+ __u16 sid = info.digital.arib.service_id;
+ __u16 onid = info.digital.arib.orig_network_id;
+
+ printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
+ break;
+ }
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: {
+ __u16 tsid = info.digital.atsc.transport_id;
+ __u16 pn = info.digital.atsc.program_number;
+
+ printf("TSID: %u, Program Number: %u\n", tsid, pn);
+ break;
+ }
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T: {
+ __u16 tsid = info.digital.dvb.transport_id;
+ __u16 sid = info.digital.dvb.service_id;
+ __u16 onid = info.digital.dvb.orig_network_id;
+
+ printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
+ break;
+ }
+ default:
+ break;
+ }
+
+ switch (system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
+ warn_once("generic digital broadcast systems should not be used");
+ break;
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
+ break;
+ default:
+ return fail("invalid digital broadcast system %u", system);
+ }
+
+ if (info.digital.service_id_method > CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL)
+ return fail("invalid service ID method %u\n", info.digital.service_id_method);
+
+ return 0;
+}
+
+static int tuner_ctl_test(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+ struct cec_op_tuner_device_info info = {};
+ std::vector<struct cec_op_tuner_device_info> info_vec;
+ bool has_tuner = (1 << la) & (CEC_LOG_ADDR_MASK_TV | CEC_LOG_ADDR_MASK_TUNER);
+ int ret;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!has_tuner && !timed_out_or_abort(&msg));
+ if (!has_tuner)
+ return OK_NOT_SUPPORTED;
+ if (timed_out(&msg) || unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_REFUSED;
+
+ printf("\t Start Channel Scan\n");
+ cec_ops_tuner_device_status(&msg, &info);
+ info_vec.push_back(info);
+ ret = log_tuner_service(info);
+ if (ret)
+ return ret;
+
+ while (true) {
+ cec_msg_init(&msg, me, la);
+ cec_msg_tuner_step_increment(&msg);
+ fail_on_test(!transmit(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg));
+ if (cec_msg_status_is_abort(&msg)) {
+ fail_on_test(abort_reason(&msg) == CEC_OP_ABORT_UNRECOGNIZED_OP);
+ if (abort_reason(&msg) == CEC_OP_ABORT_REFUSED) {
+ warn("Tuner step increment does not wrap.\n");
+ break;
+ }
+
+ warn("Tuner at end of service list did not receive feature abort refused.\n");
+ break;
+ }
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(timed_out_or_abort(&msg));
+ memset(&info, 0, sizeof(info));
+ cec_ops_tuner_device_status(&msg, &info);
+ if (!memcmp(&info, &info_vec[0], sizeof(info)))
+ break;
+ ret = log_tuner_service(info);
+ if (ret)
+ return ret;
+ info_vec.push_back(info);
+ }
+ printf("\t Finished Channel Scan\n");
+
+ printf("\t Start Channel Test\n");
+ for (const auto &iter : info_vec) {
+ cec_msg_init(&msg, me, la);
+ log_tuner_service(iter, "Select ");
+ if (iter.is_analog)
+ cec_msg_select_analogue_service(&msg, iter.analog.ana_bcast_type,
+ iter.analog.ana_freq, iter.analog.bcast_system);
+ else
+ cec_msg_select_digital_service(&msg, &iter.digital);
+ fail_on_test(!transmit(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg));
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(timed_out_or_abort(&msg));
+ memset(&info, 0, sizeof(info));
+ cec_ops_tuner_device_status(&msg, &info);
+ if (memcmp(&info, &iter, sizeof(info))) {
+ log_tuner_service(info);
+ log_tuner_service(iter);
+ }
+ fail_on_test(memcmp(&info, &iter, sizeof(info)));
+ }
+ printf("\t Finished Channel Test\n");
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_select_analogue_service(&msg, 3, 16000, 9);
+ printf("\t\tSelect invalid analog channel\n");
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+ cec_msg_init(&msg, me, la);
+ info.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
+ info.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2;
+ info.digital.dvb.transport_id = 0;
+ info.digital.dvb.service_id = 0;
+ info.digital.dvb.orig_network_id = 0;
+ cec_msg_select_digital_service(&msg, &info.digital);
+ printf("\t\tSelect invalid digital channel\n");
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+ return 0;
+}
+
+const vec_remote_subtests tuner_ctl_subtests{
+ { "Tuner Control", CEC_LOG_ADDR_MASK_TUNER | CEC_LOG_ADDR_MASK_TV, tuner_ctl_test },
+};
+
+/* One Touch Record */
+
+static int one_touch_rec_on_send(struct node *node, unsigned me, unsigned la,
+ const struct cec_op_record_src &rec_src, __u8 &rec_status)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_off(&msg, false);
+ fail_on_test(!transmit_timeout(node, &msg));
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on(&msg, true, &rec_src);
+ /* Allow 10s for reply because the spec says it may take several seconds to accurately respond. */
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ cec_ops_record_status(&msg, &rec_status);
+
+ return OK;
+}
+
+static int one_touch_rec_on_send_invalid(struct node *node, unsigned me, unsigned la,
+ const struct cec_op_record_src &rec_src)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on(&msg, true, &rec_src);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+ return OK;
+}
+
+/*
+ * Returns true if the Record Status is an error indicating that the
+ * request to start recording has failed.
+ */
+static bool rec_status_is_a_valid_error_status(__u8 rec_status)
+{
+ switch (rec_status) {
+ case CEC_OP_RECORD_STATUS_NO_DIG_SERVICE:
+ case CEC_OP_RECORD_STATUS_NO_ANA_SERVICE:
+ case CEC_OP_RECORD_STATUS_NO_SERVICE:
+ case CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG:
+ case CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR:
+ case CEC_OP_RECORD_STATUS_UNSUP_CA:
+ case CEC_OP_RECORD_STATUS_NO_CA_ENTITLEMENTS:
+ case CEC_OP_RECORD_STATUS_CANT_COPY_SRC:
+ case CEC_OP_RECORD_STATUS_NO_MORE_COPIES:
+ case CEC_OP_RECORD_STATUS_NO_MEDIA:
+ case CEC_OP_RECORD_STATUS_PLAYING:
+ case CEC_OP_RECORD_STATUS_ALREADY_RECORDING:
+ case CEC_OP_RECORD_STATUS_MEDIA_PROT:
+ case CEC_OP_RECORD_STATUS_NO_SIGNAL:
+ case CEC_OP_RECORD_STATUS_MEDIA_PROBLEM:
+ case CEC_OP_RECORD_STATUS_NO_SPACE:
+ case CEC_OP_RECORD_STATUS_PARENTAL_LOCK:
+ case CEC_OP_RECORD_STATUS_OTHER:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int one_touch_rec_tv_screen(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_tv_screen(&msg, true);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test_v2(node->remote[la].cec_version,
+ node->remote[la].has_rec_tv && unrecognized_op(&msg));
+ fail_on_test_v2(node->remote[la].cec_version,
+ !node->remote[la].has_rec_tv && !unrecognized_op(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ /* Follower should ignore this message if it is not sent by a recording device */
+ if (node->prim_devtype != CEC_OP_PRIM_DEVTYPE_RECORD) {
+ fail_on_test(!timed_out(&msg));
+ return OK;
+ }
+ fail_on_test(timed_out(&msg));
+
+ struct cec_op_record_src rec_src = {};
+
+ cec_ops_record_on(&msg, &rec_src);
+
+ fail_on_test(rec_src.type < CEC_OP_RECORD_SRC_OWN ||
+ rec_src.type > CEC_OP_RECORD_SRC_EXT_PHYS_ADDR);
+
+ if (rec_src.type == CEC_OP_RECORD_SRC_DIGITAL) {
+ switch (rec_src.digital.dig_bcast_system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
+ break;
+ default:
+ return fail("Invalid digital service broadcast system operand.\n");
+ }
+
+ if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL)
+ fail_on_test(rec_src.digital.channel.channel_number_fmt < CEC_OP_CHANNEL_NUMBER_FMT_1_PART ||
+ rec_src.digital.channel.channel_number_fmt > CEC_OP_CHANNEL_NUMBER_FMT_2_PART);
+ }
+
+ if (rec_src.type == CEC_OP_RECORD_SRC_ANALOG) {
+ fail_on_test(rec_src.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL);
+ fail_on_test(rec_src.analog.bcast_system > CEC_OP_BCAST_SYSTEM_PAL_DK &&
+ rec_src.analog.bcast_system != CEC_OP_BCAST_SYSTEM_OTHER);
+ fail_on_test(rec_src.analog.ana_freq == 0 || rec_src.analog.ana_freq == 0xffff);
+ }
+
+ if (rec_src.type == CEC_OP_RECORD_SRC_EXT_PLUG)
+ fail_on_test(rec_src.ext_plug.plug == 0);
+
+ return OK;
+}
+
+static int one_touch_rec_on(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+ struct cec_op_record_src rec_src = {};
+
+ rec_src.type = CEC_OP_RECORD_SRC_OWN;
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on(&msg, true, &rec_src);
+ /* Allow 10s for reply because the spec says it may take several seconds to accurately respond. */
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg)) {
+ fail_on_test(node->remote[la].prim_type == CEC_OP_PRIM_DEVTYPE_RECORD);
+ return OK_NOT_SUPPORTED;
+ }
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+
+ __u8 rec_status;
+
+ cec_ops_record_status(&msg, &rec_status);
+ if (rec_status != CEC_OP_RECORD_STATUS_CUR_SRC)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ /* In the following tests, these digital services are taken from the cec-follower tuner emulation. */
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
+ rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS;
+ rec_src.digital.arib.transport_id = 1032;
+ rec_src.digital.arib.service_id = 30203;
+ rec_src.digital.arib.orig_network_id = 1;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+ rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T;
+ rec_src.digital.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_2_PART;
+ rec_src.digital.channel.major = 4;
+ rec_src.digital.channel.minor = 1;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
+ rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T;
+ rec_src.digital.dvb.transport_id = 1004;
+ rec_src.digital.dvb.service_id = 1040;
+ rec_src.digital.dvb.orig_network_id = 8945;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_DIG_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ /* In the following tests, these channels taken from the cec-follower tuner emulation. */
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_CABLE;
+ rec_src.analog.ana_freq = (471250 * 10) / 625;
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_BG;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_SATELLITE;
+ rec_src.analog.ana_freq = (551250 * 10) / 625;
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_SECAM_BG;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_STATUS_ANA_SERVICE;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL;
+ rec_src.analog.ana_freq = (185250 * 10) / 625;
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_DK;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_ANA_SERVICE)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_EXT_PLUG;
+ rec_src.ext_plug.plug = 1;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_EXT_INPUT)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
+ fail_on_test(one_touch_rec_on_send(node, me, la, rec_src, rec_status));
+ if (rec_status != CEC_OP_RECORD_STATUS_EXT_INPUT)
+ fail_on_test(!rec_status_is_a_valid_error_status(rec_status));
+
+ return OK;
+}
+
+static int one_touch_rec_on_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on_own(&msg);
+ msg.msg[2] = 0; /* Invalid source operand */
+ fail_on_test(!transmit_timeout(node, &msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_on_own(&msg);
+ msg.msg[2] = 6; /* Invalid source operand */
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+ struct cec_op_record_src rec_src = {};
+
+ rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+ rec_src.digital.dig_bcast_system = 0x7f; /* Invalid digital service broadcast system operand */
+ rec_src.digital.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
+ rec_src.digital.channel.major = 0;
+ rec_src.digital.channel.minor = 30203;
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ rec_src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ rec_src.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+ rec_src.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS;
+ rec_src.digital.channel.channel_number_fmt = 0; /* Invalid channel number format operand */
+ rec_src.digital.channel.major = 0;
+ rec_src.digital.channel.minor = 30609;
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
+ rec_src.analog.ana_bcast_type = 0xff; /* Invalid analog broadcast type */
+ rec_src.analog.ana_freq = (519250 * 10) / 625;
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_PAL_BG;
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_SATELLITE;
+ rec_src.analog.ana_freq = (703250 * 10) / 625;
+ rec_src.analog.bcast_system = 0xff; /* Invalid analog broadcast system */
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL;
+ rec_src.analog.ana_freq = 0; /* Invalid frequency */
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_NTSC_M;
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_ANALOG;
+ rec_src.analog.ana_bcast_type = CEC_OP_ANA_BCAST_TYPE_CABLE;
+ rec_src.analog.ana_freq = 0xffff; /* Invalid frequency */
+ rec_src.analog.bcast_system = CEC_OP_BCAST_SYSTEM_SECAM_L;
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ memset(&rec_src, 0, sizeof(rec_src));
+ rec_src.type = CEC_OP_RECORD_SRC_EXT_PLUG;
+ rec_src.ext_plug.plug = 0; /* Invalid plug */
+ fail_on_test(one_touch_rec_on_send_invalid(node, me, la, rec_src));
+
+ return OK;
+}
+
+static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_record_off(&msg, true);
+ /* Allow 10s for reply because the spec says it may take several seconds to accurately respond. */
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ if (unrecognized_op(&msg)) {
+ fail_on_test(node->remote[la].prim_type == CEC_OP_PRIM_DEVTYPE_RECORD);
+ return OK_NOT_SUPPORTED;
+ }
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ if (timed_out(&msg))
+ return OK_PRESUMED;
+
+ __u8 rec_status;
+
+ cec_ops_record_status(&msg, &rec_status);
+
+ fail_on_test(rec_status != CEC_OP_RECORD_STATUS_TERMINATED_OK &&
+ rec_status != CEC_OP_RECORD_STATUS_ALREADY_TERM);
+
+ return OK;
+}
+
+const vec_remote_subtests one_touch_rec_subtests{
+ { "Record TV Screen", CEC_LOG_ADDR_MASK_TV, one_touch_rec_tv_screen },
+ {
+ "Record On",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ one_touch_rec_on,
+ },
+ {
+ "Record On Invalid Operand",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ one_touch_rec_on_invalid,
+ },
+ { "Record Off", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, one_touch_rec_off },
+
+};
+
+/* Timer Programming */
+
+static int timer_status_is_valid(const struct cec_msg &msg)
+{
+ __u8 timer_overlap_warning;
+ __u8 media_info;
+ __u8 prog_info;
+ __u8 prog_error;
+ __u8 duration_hr;
+ __u8 duration_min;
+
+ cec_ops_timer_status(&msg, &timer_overlap_warning, &media_info, &prog_info,
+ &prog_error, &duration_hr, &duration_min);
+ fail_on_test(media_info > CEC_OP_MEDIA_INFO_NO_MEDIA);
+ if (prog_info)
+ fail_on_test(prog_info < CEC_OP_PROG_INFO_ENOUGH_SPACE ||
+ prog_info > CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE);
+ else
+ fail_on_test(prog_error < CEC_OP_PROG_ERROR_NO_FREE_TIMER ||
+ (prog_error > CEC_OP_PROG_ERROR_CLOCK_FAILURE &&
+ prog_error != CEC_OP_PROG_ERROR_DUPLICATE));
+
+ return OK;
+}
+
+static int timer_cleared_status_is_valid(const struct cec_msg &msg)
+{
+ __u8 timer_cleared_status;
+
+ cec_ops_timer_cleared_status(&msg, &timer_cleared_status);
+ fail_on_test(timer_cleared_status != CEC_OP_TIMER_CLR_STAT_RECORDING &&
+ timer_cleared_status != CEC_OP_TIMER_CLR_STAT_NO_MATCHING &&
+ timer_cleared_status != CEC_OP_TIMER_CLR_STAT_NO_INFO &&
+ timer_cleared_status != CEC_OP_TIMER_CLR_STAT_CLEARED);
+
+ return OK;
+}
+
+static bool timer_has_error(const struct cec_msg &msg)
+{
+ __u8 timer_overlap_warning;
+ __u8 media_info;
+ __u8 prog_info;
+ __u8 prog_error;
+ __u8 duration_hr;
+ __u8 duration_min;
+
+ cec_ops_timer_status(&msg, &timer_overlap_warning, &media_info, &prog_info,
+ &prog_error, &duration_hr, &duration_min);
+ if (prog_error)
+ return true;
+
+ return false;
+}
+
+static int send_timer_error(struct node *node, unsigned me, unsigned la, __u8 day, __u8 month,
+ __u8 start_hr, __u8 start_min, __u8 dur_hr, __u8 dur_min, __u8 rec_seq)
+{
+ struct cec_msg msg;
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, day, month, start_hr, start_min, dur_hr, dur_min,
+ rec_seq, CEC_OP_ANA_BCAST_TYPE_CABLE, 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (cec_msg_status_is_abort(&msg))
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+ else
+ fail_on_test(!timer_has_error(msg));
+
+ return OK;
+}
+
+static bool timer_overlap_warning_is_set(const struct cec_msg &msg)
+{
+ __u8 timer_overlap_warning;
+ __u8 media_info;
+ __u8 prog_info;
+ __u8 prog_error;
+ __u8 duration_hr;
+ __u8 duration_min;
+
+ cec_ops_timer_status(&msg, &timer_overlap_warning, &media_info, &prog_info,
+ &prog_error, &duration_hr, &duration_min);
+
+ if (timer_overlap_warning)
+ return true;
+
+ return false;
+}
+
+static int send_timer_overlap(struct node *node, unsigned me, unsigned la, __u8 day, __u8 month,
+ __u8 start_hr, __u8 start_min, __u8 dur_hr, __u8 dur_min, __u8 rec_seq)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, day, month, start_hr, start_min, dur_hr, dur_min,
+ rec_seq, CEC_OP_ANA_BCAST_TYPE_CABLE, 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg));
+ fail_on_test(!timer_overlap_warning_is_set(msg));
+
+ return OK;
+}
+
+static int clear_timer(struct node *node, unsigned me, unsigned la, __u8 day, __u8 month,
+ __u8 start_hr, __u8 start_min, __u8 dur_hr, __u8 dur_min, __u8 rec_seq)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_clear_analogue_timer(&msg, true, day, month, start_hr, start_min, dur_hr, dur_min,
+ rec_seq, CEC_OP_ANA_BCAST_TYPE_CABLE, 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg));
+ fail_on_test(timer_cleared_status_is_valid(msg));
+
+ return OK;
+}
+
+static int timer_prog_set_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ /* Set timer to start tomorrow, at current time, for 2 hr, 30 min. */
+ time_t tomorrow = node->current_time + (24 * 60 * 60);
+ struct tm *t = localtime(&tomorrow);
+ cec_msg_set_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 2, 30,
+ 0x7f, CEC_OP_ANA_BCAST_TYPE_CABLE, 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_status_is_valid(msg));
+
+ return OK;
+}
+
+static int timer_prog_set_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+ struct cec_op_digital_service_id digital_service_id = {};
+
+ digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+ digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
+ digital_service_id.channel.minor = 1;
+ digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
+ cec_msg_init(&msg, me, la);
+ /* Set timer to start 2 days from now, at current time, for 4 hr, 30 min. */
+ time_t two_days_ahead = node->current_time + (2 * 24 * 60 * 60);
+ struct tm *t = localtime(&two_days_ahead);
+ cec_msg_set_digital_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour,
+ t->tm_min, 4, 30, CEC_OP_REC_SEQ_ONCE_ONLY, &digital_service_id);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_status_is_valid(msg));
+
+ return 0;
+}
+
+static int timer_prog_set_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ /* Set timer to start 3 days from now, at current time, for 6 hr, 30 min. */
+ time_t three_days_ahead = node->current_time + (3 * 24 * 60 * 60);
+ struct tm *t = localtime(&three_days_ahead);
+ cec_msg_set_ext_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 6, 30,
+ CEC_OP_REC_SEQ_ONCE_ONLY, CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_status_is_valid(msg));
+
+ return 0;
+}
+
+static int timer_prog_clear_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ /* Clear timer set to start tomorrow, at current time, for 2 hr, 30 min. */
+ time_t tomorrow = node->current_time + (24 * 60 * 60);
+ struct tm *t = localtime(&tomorrow);
+ cec_msg_clear_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 2, 30,
+ 0x7f, CEC_OP_ANA_BCAST_TYPE_CABLE,7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_cleared_status_is_valid(msg));
+
+ return 0;
+}
+
+static int timer_prog_clear_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+ struct cec_op_digital_service_id digital_service_id = {};
+
+ digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
+ digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
+ digital_service_id.channel.minor = 1;
+ digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
+ cec_msg_init(&msg, me, la);
+ /* Clear timer set to start 2 days from now, at current time, for 4 hr, 30 min. */
+ time_t two_days_ahead = node->current_time + (2 * 24 * 60 * 60);
+ struct tm *t = localtime(&two_days_ahead);
+ cec_msg_clear_digital_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour,
+ t->tm_min, 4, 30, CEC_OP_REC_SEQ_ONCE_ONLY, &digital_service_id);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_cleared_status_is_valid(msg));
+
+ return 0;
+}
+
+static int timer_prog_clear_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ /* Clear timer set to start 3 days from now, at current time, for 6 hr, 30 min. */
+ time_t three_days_ahead = node->current_time + (3 * 24 * 60 * 60);
+ struct tm *t = localtime(&three_days_ahead);
+ cec_msg_clear_ext_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 6, 30,
+ CEC_OP_REC_SEQ_ONCE_ONLY, CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out(&msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+ if (cec_msg_status_is_abort(&msg))
+ return OK_PRESUMED;
+ fail_on_test(timer_cleared_status_is_valid(msg));
+
+ return 0;
+}
+
+static int timer_prog_set_prog_title(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_timer_program_title(&msg, "Super-Hans II");
+ fail_on_test(!transmit_timeout(node, &msg));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ if (refused(&msg))
+ return OK_REFUSED;
+
+ return OK_PRESUMED;
+}
+
+static int timer_errors(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ /* Day error: November 31, at 6:00 am, for 1 hr. */
+ fail_on_test(send_timer_error(node, me, la, 31, Nov, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Day error: December 32, at 6:00 am, for 1 hr. */
+ fail_on_test(send_timer_error(node, me, la, 32, Dec, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Day error: 0, in January, at 6:00 am, for 1 hr. Day range begins at 1. */
+ fail_on_test(send_timer_error(node, me, la, 0, Jan, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Month error: 0, on day 5, at 6:00 am, for 1 hr. CEC month range is 1-12. */
+ fail_on_test(send_timer_error(node, me, la, 5, 0, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Month error: 13, on day 5, at 6:00 am, for 1 hr. */
+ fail_on_test(send_timer_error(node, me, la, 5, 13, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Start hour error: 24 hr, on August 5, for 1 hr. Start hour range is 0-23. */
+ fail_on_test(send_timer_error(node, me, la, 5, Aug, 24, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Start min error: 60 min, on August 5, for 1 hr. Start min range is 0-59. */
+ fail_on_test(send_timer_error(node, me, la, 5, Aug, 0, 60, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Recording duration error: 0 hr, 0 min on August 5, at 6:00am. */
+ fail_on_test(send_timer_error(node, me, la, 5, Aug, 6, 0, 0, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Duplicate timer error: start 2 hrs from now, for 1 hr. */
+ time_t two_hours_ahead = node->current_time + (2 * 60 * 60);
+ struct tm *t = localtime(&two_hours_ahead);
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 1, 0,
+ CEC_OP_REC_SEQ_ONCE_ONLY,CEC_OP_ANA_BCAST_TYPE_CABLE,
+ 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg)); /* The first timer should be set. */
+ fail_on_test(send_timer_error(node, me, la, t->tm_mday, t->tm_mon + 1, t->tm_hour,
+ t->tm_min, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Clear the timer that was set to test duplicate timers. */
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, t->tm_hour, t->tm_min, 1, 0,
+ CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ /* Recording sequence error: 0xff, on August 5, at 6:00 am, for 1 hr. */
+ fail_on_test(send_timer_error(node, me, la, 5, Aug, 6, 0, 1, 0, 0xff));
+
+ /* Error in last day of February, at 6:00 am, for 1 hr. */
+ time_t current_time = node->current_time;
+ t = localtime(&current_time);
+ if ((t->tm_mon + 1) > Feb)
+ t->tm_year++; /* The timer will be for next year. */
+ if (!(t->tm_year % 4) && ((t->tm_year % 100) || !(t->tm_year % 400)))
+ fail_on_test(send_timer_error(node, me, la, 30, Feb, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+ else
+ fail_on_test(send_timer_error(node, me, la, 29, Feb, 6, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY));
+
+ return OK;
+}
+
+static int timer_overlap_warning(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+ struct cec_msg msg;
+
+ time_t tomorrow = node->current_time + (24 * 60 * 60);
+ struct tm *t = localtime(&tomorrow);
+
+ /* No overlap: set timer for tomorrow at 8:00 am for 2 hr. */
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, 8, 0, 2, 0,
+ CEC_OP_REC_SEQ_ONCE_ONLY, CEC_OP_ANA_BCAST_TYPE_CABLE,
+ 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ if (unrecognized_op(&msg))
+ return OK_NOT_SUPPORTED;
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg));
+ fail_on_test(timer_overlap_warning_is_set(msg));
+
+ /* No overlap, just adjacent: set timer for tomorrow at 10:00 am for 15 min. */
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, 10, 0, 0, 15,
+ CEC_OP_REC_SEQ_ONCE_ONLY, CEC_OP_ANA_BCAST_TYPE_CABLE,
+ 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg));
+ fail_on_test(timer_overlap_warning_is_set(msg));
+
+ /* No overlap, just adjacent: set timer for tomorrow at 7:45 am for 15 min. */
+ cec_msg_init(&msg, me, la);
+ cec_msg_set_analogue_timer(&msg, true, t->tm_mday, t->tm_mon + 1, 7, 45, 0, 15,
+ CEC_OP_REC_SEQ_ONCE_ONLY, CEC_OP_ANA_BCAST_TYPE_CABLE,
+ 7668, // 479.25 MHz
+ node->remote[la].bcast_sys);
+ fail_on_test(!transmit_timeout(node, &msg, 10000));
+ fail_on_test(timed_out_or_abort(&msg));
+ fail_on_test(timer_has_error(msg));
+ fail_on_test(timer_overlap_warning_is_set(msg));
+
+ /* Overlap tail end: set timer for tomorrow at 9:00 am for 2 hr, repeats on Sun. */
+ fail_on_test(send_timer_overlap(node, me, la, t->tm_mday, t->tm_mon + 1, 9, 0, 2, 0, 0x1));
+
+ /* Overlap front end: set timer for tomorrow at 7:00 am for 1 hr, 30 min. */
+ fail_on_test(send_timer_overlap(node, me, la, t->tm_mday, t->tm_mon + 1, 7, 0, 1, 30, 0x1));
+
+ /* Overlap same start time: set timer for tomorrow at 8:00 am for 30 min. */
+ fail_on_test(send_timer_overlap(node, me, la, t->tm_mday, t->tm_mon + 1, 8, 0, 0, 30, 0x1));
+
+ /* Overlap same end time: set timer for tomorrow at 9:30 am for 30 min. */
+ fail_on_test(send_timer_overlap(node, me, la, t->tm_mday, t->tm_mon + 1, 9, 30, 0, 30, 0x1));
+
+ /* Overlap all timers: set timer for tomorrow at 6:00 am for 6 hr. */
+ fail_on_test(send_timer_overlap(node, me, la, t->tm_mday, t->tm_mon + 1, 6, 0, 6, 0, 0x1));
+
+ /* Clear all the timers. */
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 8, 0, 2, 0,
+ CEC_OP_REC_SEQ_ONCE_ONLY));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 10, 0, 0, 15,
+ CEC_OP_REC_SEQ_ONCE_ONLY));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 7, 45, 0, 15,
+ CEC_OP_REC_SEQ_ONCE_ONLY));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 9, 0, 2, 0, 0x1));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 7, 0, 1, 30, 0x1));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 8, 0, 0, 30, 0x1));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 9, 30, 0, 30, 0x1));
+ fail_on_test(clear_timer(node, me, la, t->tm_mday, t->tm_mon + 1, 6, 0, 6, 0, 0x1));
+
+ return OK;
+}
+
+const vec_remote_subtests timer_prog_subtests{
+ {
+ "Set Analogue Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_set_analog_timer,
+ },
+ {
+ "Set Digital Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_set_digital_timer,
+ },
+ {
+ "Set Timer Program Title",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_set_prog_title,
+ },
+ {
+ "Set External Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_set_ext_timer,
+ },
+ {
+ "Clear Analogue Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_clear_analog_timer,
+ },
+ {
+ "Clear Digital Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_clear_digital_timer,
+ },
+ {
+ "Clear External Timer",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_prog_clear_ext_timer,
+ },
+ {
+ "Set Timers with Errors",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_errors,
+ },
+ {
+ "Set Overlapping Timers",
+ CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP,
+ timer_overlap_warning,
+ },
+};
diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
index 8da29f22..fae6c304 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -13,18 +13,49 @@
#include "cec-compliance.h"
+enum Months { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };
+
struct remote_test {
const char *name;
const unsigned tags;
const vec_remote_subtests &subtests;
};
+static int deck_status_get(struct node *node, unsigned me, unsigned la, __u8 &deck_status)
+{
+ struct cec_msg msg;
+ deck_status = 0;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(timed_out_or_abort(&msg));
+ cec_ops_deck_status(&msg, &deck_status);
+
+ return OK;
+}
+
+static int test_play_mode(struct node *node, unsigned me, unsigned la, __u8 play_mode, __u8 expected)
+{
+ struct cec_msg msg;
+ __u8 deck_status;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_play(&msg, play_mode);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg)); /* Assumes deck has media. */
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ fail_on_test(deck_status != expected);
+
+ return OK;
+}
+
/* System Information */
int system_info_polling(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
@@ -46,7 +77,7 @@ int system_info_polling(struct node *node, unsigned me, unsigned la, bool intera
int system_info_phys_addr(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_physical_addr(&msg, true);
@@ -61,7 +92,7 @@ int system_info_phys_addr(struct node *node, unsigned me, unsigned la, bool inte
int system_info_version(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_get_cec_version(&msg, true);
@@ -82,7 +113,7 @@ int system_info_version(struct node *node, unsigned me, unsigned la, bool intera
int system_info_get_menu_lang(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
char language[4];
cec_msg_init(&msg, me, la);
@@ -111,7 +142,7 @@ int system_info_get_menu_lang(struct node *node, unsigned me, unsigned la, bool
static int system_info_set_menu_lang(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_menu_language(&msg, "eng");
@@ -126,7 +157,7 @@ static int system_info_set_menu_lang(struct node *node, unsigned me, unsigned la
int system_info_give_features(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_features(&msg, true);
@@ -187,7 +218,7 @@ static const vec_remote_subtests system_info_subtests{
int core_unknown(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
const __u8 unknown_opcode = 0xfe;
/* Unknown opcodes should be responded to with Feature Abort, with abort
@@ -220,7 +251,7 @@ int core_unknown(struct node *node, unsigned me, unsigned la, bool interactive)
int core_abort(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* The Abort message should always be responded to with Feature Abort
(with any abort reason) */
@@ -241,7 +272,7 @@ static const vec_remote_subtests core_subtests{
int vendor_specific_commands_id(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_device_vendor_id(&msg, true);
@@ -267,7 +298,7 @@ static const vec_remote_subtests vendor_specific_subtests{
static int device_osd_transfer_set(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_set_osd_name(&msg, "Whatever");
@@ -286,7 +317,7 @@ static int device_osd_transfer_set(struct node *node, unsigned me, unsigned la,
int device_osd_transfer_give(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
/* Todo: CEC 2.0: devices with several logical addresses shall report
the same for each logical address. */
@@ -319,7 +350,7 @@ static const vec_remote_subtests device_osd_transfer_subtests{
static int osd_string_set_default(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
char osd[14];
bool unsuitable = false;
@@ -360,7 +391,7 @@ static int osd_string_set_until_clear(struct node *node, unsigned me, unsigned l
if (!node->remote[la].has_osd)
return NOTAPPLICABLE;
- struct cec_msg msg = { };
+ struct cec_msg msg;
char osd[14];
bool unsuitable = false;
@@ -395,7 +426,7 @@ static int osd_string_invalid(struct node *node, unsigned me, unsigned la, bool
if (!node->remote[la].has_osd)
return NOTAPPLICABLE;
- struct cec_msg msg = { };
+ struct cec_msg msg;
/* Send Set OSD String with an Display Control operand. A Feature Abort is
expected in reply. */
@@ -420,7 +451,7 @@ static const vec_remote_subtests osd_string_subtests{
static int routing_control_inactive_source(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
int response;
interactive_info(true, "Please make sure that the TV is currently viewing this source.");
@@ -453,7 +484,7 @@ static int routing_control_inactive_source(struct node *node, unsigned me, unsig
static int routing_control_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
interactive_info(true, "Please switch the TV to another source.");
cec_msg_init(&msg, me, la);
@@ -469,7 +500,7 @@ static int routing_control_active_source(struct node *node, unsigned me, unsigne
static int routing_control_req_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
/* We have now said that we are active source, so receiving a reply to
Request Active Source should fail the test. */
@@ -483,7 +514,7 @@ static int routing_control_req_active_source(struct node *node, unsigned me, uns
static int routing_control_set_stream_path(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u16 phys_addr;
/* Send Set Stream Path with the remote physical address. We expect the
@@ -527,7 +558,7 @@ static const vec_remote_subtests routing_control_subtests{
static int rc_passthrough_user_ctrl_pressed(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
struct cec_op_ui_command rc_press;
cec_msg_init(&msg, me, la);
@@ -547,7 +578,7 @@ static int rc_passthrough_user_ctrl_pressed(struct node *node, unsigned me, unsi
static int rc_passthrough_user_ctrl_released(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_user_control_released(&msg);
@@ -576,7 +607,7 @@ static const vec_remote_subtests rc_passthrough_subtests{
static int dev_menu_ctl_request(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_menu_request(&msg, true, CEC_OP_MENU_REQUEST_QUERY);
@@ -601,24 +632,19 @@ static const vec_remote_subtests dev_menu_ctl_subtests{
/* Deck Control */
-/*
- TODO: These are very rudimentary tests which should be expanded.
- */
-
static int deck_ctl_give_status(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
fail_on_test(!transmit_timeout(node, &msg));
fail_on_test(timed_out(&msg));
- if (is_playback_or_rec(la)) {
- fail_on_test_v2(node->remote[la].cec_version,
- node->remote[la].has_deck_ctl && cec_msg_status_is_abort(&msg));
- fail_on_test_v2(node->remote[la].cec_version,
- !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
- }
+
+ fail_on_test_v2(node->remote[la].cec_version,
+ node->remote[la].has_deck_ctl && cec_msg_status_is_abort(&msg));
+ fail_on_test_v2(node->remote[la].cec_version,
+ !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
if (refused(&msg))
@@ -626,655 +652,320 @@ static int deck_ctl_give_status(struct node *node, unsigned me, unsigned la, boo
if (cec_msg_status_is_abort(&msg))
return OK_PRESUMED;
- return 0;
-}
+ __u8 deck_info;
-static int deck_ctl_deck_status(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- struct cec_msg msg = {};
+ cec_ops_deck_status(&msg, &deck_info);
+ fail_on_test(deck_info < CEC_OP_DECK_INFO_PLAY || deck_info > CEC_OP_DECK_INFO_OTHER);
cec_msg_init(&msg, me, la);
- cec_msg_deck_status(&msg, CEC_OP_DECK_INFO_STOP);
+ cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ON);
fail_on_test(!transmit_timeout(node, &msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
+ fail_on_test(timed_out(&msg));
+ cec_ops_deck_status(&msg, &deck_info);
+ fail_on_test(deck_info < CEC_OP_DECK_INFO_PLAY || deck_info > CEC_OP_DECK_INFO_OTHER);
- return 0;
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_OFF);
+ /*
+ * Reply would not normally be expected for CEC_OP_STATUS_REQ_OFF.
+ * If a reply is received, then the follower failed to turn off
+ * status reporting as required.
+ */
+ msg.reply = CEC_MSG_DECK_STATUS;
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!timed_out(&msg));
+
+ return OK;
}
-static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool interactive)
+static int deck_ctl_give_status_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
- cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
+ cec_msg_give_deck_status(&msg, true, 0); /* Invalid Operand */
fail_on_test(!transmit_timeout(node, &msg));
- if (is_playback_or_rec(la)) {
- fail_on_test_v2(node->remote[la].cec_version,
- node->remote[la].has_deck_ctl && unrecognized_op(&msg));
- fail_on_test_v2(node->remote[la].cec_version,
- !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
- }
if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
- return OK_PRESUMED;
+ cec_msg_init(&msg, me, la);
+ cec_msg_give_deck_status(&msg, true, 4); /* Invalid Operand */
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+ return OK;
}
-static int deck_ctl_play(struct node *node, unsigned me, unsigned la, bool interactive)
+static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
+ __u8 deck_status;
cec_msg_init(&msg, me, la);
- cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_STILL);
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
fail_on_test(!transmit_timeout(node, &msg));
- if (is_playback_or_rec(la)) {
- fail_on_test_v2(node->remote[la].cec_version,
- node->remote[la].has_deck_ctl && unrecognized_op(&msg));
- fail_on_test_v2(node->remote[la].cec_version,
- !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
- }
+ fail_on_test_v2(node->remote[la].cec_version,
+ node->remote[la].has_deck_ctl && unrecognized_op(&msg));
+ fail_on_test_v2(node->remote[la].cec_version,
+ !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
if (refused(&msg))
return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return OK_PRESUMED;
-}
-
-static const vec_remote_subtests deck_ctl_subtests{
- { "Give Deck Status", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_give_status },
- { "Deck Status", CEC_LOG_ADDR_MASK_ALL, deck_ctl_deck_status },
- { "Deck Control", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_deck_ctl },
- { "Play", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_play },
-};
-
-/* Tuner Control */
-
-static const char *bcast_type2s(__u8 bcast_type)
-{
- switch (bcast_type) {
- case CEC_OP_ANA_BCAST_TYPE_CABLE:
- return "Cable";
- case CEC_OP_ANA_BCAST_TYPE_SATELLITE:
- return "Satellite";
- case CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL:
- return "Terrestrial";
- default:
- return "Future use";
- }
-}
-
-static int log_tuner_service(const struct cec_op_tuner_device_info &info,
- const char *prefix = "")
-{
- printf("\t\t%s", prefix);
-
- if (info.is_analog) {
- double freq_mhz = (info.analog.ana_freq * 625) / 10000.0;
-
- printf("Analog Channel %.2f MHz (%s, %s)\n", freq_mhz,
- bcast_system2s(info.analog.bcast_system),
- bcast_type2s(info.analog.ana_bcast_type));
-
- switch (info.analog.bcast_system) {
- case CEC_OP_BCAST_SYSTEM_PAL_BG:
- case CEC_OP_BCAST_SYSTEM_SECAM_LQ:
- case CEC_OP_BCAST_SYSTEM_PAL_M:
- case CEC_OP_BCAST_SYSTEM_NTSC_M:
- case CEC_OP_BCAST_SYSTEM_PAL_I:
- case CEC_OP_BCAST_SYSTEM_SECAM_DK:
- case CEC_OP_BCAST_SYSTEM_SECAM_BG:
- case CEC_OP_BCAST_SYSTEM_SECAM_L:
- case CEC_OP_BCAST_SYSTEM_PAL_DK:
- break;
- default:
- return fail("invalid analog bcast_system %u", info.analog.bcast_system);
- }
- if (info.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL)
- return fail("invalid analog bcast_type %u\n", info.analog.ana_bcast_type);
- fail_on_test(!info.analog.ana_freq);
- return 0;
- }
-
- __u8 system = info.digital.dig_bcast_system;
-
- printf("%s Channel ", dig_bcast_system2s(system));
- if (info.digital.service_id_method) {
- __u16 major = info.digital.channel.major;
- __u16 minor = info.digital.channel.minor;
-
- switch (info.digital.channel.channel_number_fmt) {
- case CEC_OP_CHANNEL_NUMBER_FMT_2_PART:
- printf("%u.%u\n", major, minor);
- break;
- case CEC_OP_CHANNEL_NUMBER_FMT_1_PART:
- printf("%u\n", minor);
- break;
- default:
- printf("%u.%u\n", major, minor);
- return fail("invalid service ID method\n");
- }
- return 0;
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ if (cec_msg_status_is_abort(&msg)) {
+ if (!incorrect_mode(&msg))
+ return FAIL;
+ if (deck_status == CEC_OP_DECK_INFO_NO_MEDIA)
+ info("Stop: no media.\n");
+ else
+ warn("Deck has media but returned Feature Abort with Incorrect Mode.");
+ return OK;
}
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_STOP && deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
-
- switch (system) {
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T: {
- __u16 tsid = info.digital.arib.transport_id;
- __u16 sid = info.digital.arib.service_id;
- __u16 onid = info.digital.arib.orig_network_id;
-
- printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
- break;
- }
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: {
- __u16 tsid = info.digital.atsc.transport_id;
- __u16 pn = info.digital.atsc.program_number;
-
- printf("TSID: %u, Program Number: %u\n", tsid, pn);
- break;
- }
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T: {
- __u16 tsid = info.digital.dvb.transport_id;
- __u16 sid = info.digital.dvb.service_id;
- __u16 onid = info.digital.dvb.orig_network_id;
-
- printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
- break;
+ cec_msg_init(&msg, me, la);
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_SKIP_FWD);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ /*
+ * If there is no media, Skip Forward should Feature Abort with Incorrect Mode
+ * even if Stop did not. If Skip Forward does not Feature Abort, the deck
+ * is assumed to have media.
+ */
+ if (incorrect_mode(&msg)) {
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
+ return OK;
}
- default:
- break;
+ fail_on_test(cec_msg_status_is_abort(&msg));
+ /* Wait for Deck to finish Skip Forward. */
+ for (int i = 0; deck_status == CEC_OP_DECK_INFO_SKIP_FWD && i < long_timeout; i++) {
+ sleep(1);
+ fail_on_test(deck_status_get(node, me, la, deck_status));
}
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
- switch (system) {
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
- warn_once("generic digital broadcast systems should not be used");
- break;
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
- case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
- break;
- default:
- return fail("invalid digital broadcast system %u", system);
+ cec_msg_init(&msg, me, la);
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_SKIP_REV);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg)); /* Assumes deck has media. */
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ /* Wait for Deck to finish Skip Reverse. */
+ for (int i = 0; deck_status == CEC_OP_DECK_INFO_SKIP_REV && i < long_timeout; i++) {
+ sleep(1);
+ fail_on_test(deck_status_get(node, me, la, deck_status));
}
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
- if (info.digital.service_id_method > CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL)
- return fail("invalid service ID method %u\n", info.digital.service_id_method);
+ cec_msg_init(&msg, me, la);
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_EJECT);
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(cec_msg_status_is_abort(&msg));
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
- return 0;
+ return OK;
}
-static int tuner_ctl_test(struct node *node, unsigned me, unsigned la, bool interactive)
+static int deck_ctl_deck_ctl_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
- struct cec_op_tuner_device_info info = {};
- std::vector<struct cec_op_tuner_device_info> info_vec;
- bool has_tuner = (1 << la) & (CEC_LOG_ADDR_MASK_TV | CEC_LOG_ADDR_MASK_TUNER);
- int ret;
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
- cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+ cec_msg_deck_control(&msg, 0); /* Invalid Deck Control operand */
fail_on_test(!transmit_timeout(node, &msg));
- fail_on_test(!has_tuner && !timed_out_or_abort(&msg));
- if (!has_tuner)
- return OK_NOT_SUPPORTED;
- if (timed_out(&msg) || unrecognized_op(&msg))
+ if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
- if (cec_msg_status_is_abort(&msg))
- return OK_REFUSED;
-
- printf("\t Start Channel Scan\n");
- cec_ops_tuner_device_status(&msg, &info);
- info_vec.push_back(info);
- ret = log_tuner_service(info);
- if (ret)
- return ret;
-
- while (true) {
- cec_msg_init(&msg, me, la);
- cec_msg_tuner_step_increment(&msg);
- fail_on_test(!transmit(node, &msg));
- fail_on_test(cec_msg_status_is_abort(&msg));
- if (cec_msg_status_is_abort(&msg)) {
- fail_on_test(abort_reason(&msg) == CEC_OP_ABORT_UNRECOGNIZED_OP);
- if (abort_reason(&msg) == CEC_OP_ABORT_REFUSED) {
- warn("Tuner step increment does not wrap.\n");
- break;
- }
-
- warn("Tuner at end of service list did not receive feature abort refused.\n");
- break;
- }
- cec_msg_init(&msg, me, la);
- cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
- fail_on_test(!transmit_timeout(node, &msg));
- fail_on_test(timed_out_or_abort(&msg));
- memset(&info, 0, sizeof(info));
- cec_ops_tuner_device_status(&msg, &info);
- if (!memcmp(&info, &info_vec[0], sizeof(info)))
- break;
- ret = log_tuner_service(info);
- if (ret)
- return ret;
- info_vec.push_back(info);
- }
- printf("\t Finished Channel Scan\n");
-
- printf("\t Start Channel Test\n");
- for (const auto &iter : info_vec) {
- cec_msg_init(&msg, me, la);
- log_tuner_service(iter, "Select ");
- if (iter.is_analog)
- cec_msg_select_analogue_service(&msg, iter.analog.ana_bcast_type,
- iter.analog.ana_freq, iter.analog.bcast_system);
- else
- cec_msg_select_digital_service(&msg, &iter.digital);
- fail_on_test(!transmit(node, &msg));
- fail_on_test(cec_msg_status_is_abort(&msg));
- cec_msg_init(&msg, me, la);
- cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
- fail_on_test(!transmit_timeout(node, &msg));
- fail_on_test(timed_out_or_abort(&msg));
- memset(&info, 0, sizeof(info));
- cec_ops_tuner_device_status(&msg, &info);
- if (memcmp(&info, &iter, sizeof(info))) {
- log_tuner_service(info);
- log_tuner_service(iter);
- }
- fail_on_test(memcmp(&info, &iter, sizeof(info)));
- }
- printf("\t Finished Channel Test\n");
-
- cec_msg_init(&msg, me, la);
- cec_msg_select_analogue_service(&msg, 3, 16000, 9);
- printf("\t\tSelect invalid analog channel\n");
- fail_on_test(!transmit_timeout(node, &msg));
fail_on_test(!cec_msg_status_is_abort(&msg));
fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
cec_msg_init(&msg, me, la);
- info.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
- info.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2;
- info.digital.dvb.transport_id = 0;
- info.digital.dvb.service_id = 0;
- info.digital.dvb.orig_network_id = 0;
- cec_msg_select_digital_service(&msg, &info.digital);
- printf("\t\tSelect invalid digital channel\n");
+ cec_msg_deck_control(&msg, 5); /* Invalid Deck Control operand */
fail_on_test(!transmit_timeout(node, &msg));
fail_on_test(!cec_msg_status_is_abort(&msg));
fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
- return 0;
+ return OK;
}
-static const vec_remote_subtests tuner_ctl_subtests{
- { "Tuner Control", CEC_LOG_ADDR_MASK_TUNER | CEC_LOG_ADDR_MASK_TV, tuner_ctl_test },
-};
-
-/* One Touch Record */
-
-/*
- TODO: These are very rudimentary tests which should be expanded.
-
- - The HDMI CEC 1.4b spec details that Standby shall not be acted upon while the
- device is recording, but it should remember that it received Standby.
- */
-
-static int one_touch_rec_tv_screen(struct node *node, unsigned me, unsigned la, bool interactive)
+static int deck_ctl_play(struct node *node, unsigned me, unsigned la, bool interactive)
{
- /*
- TODO:
- - Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
- checked for.
- - The TV should ignore this message when received from other LA than Recording or
- Reserved.
- */
- struct cec_msg msg = {};
+ struct cec_msg msg;
+ __u8 deck_status;
cec_msg_init(&msg, me, la);
- cec_msg_record_tv_screen(&msg, true);
+ cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_FWD);
fail_on_test(!transmit_timeout(node, &msg));
fail_on_test_v2(node->remote[la].cec_version,
- node->remote[la].has_rec_tv && unrecognized_op(&msg));
+ node->remote[la].has_deck_ctl && unrecognized_op(&msg));
fail_on_test_v2(node->remote[la].cec_version,
- !node->remote[la].has_rec_tv && !unrecognized_op(&msg));
+ !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
if (refused(&msg))
return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
-}
-
-static int one_touch_rec_on(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- /*
- TODO: Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
- checked for.
- */
- struct cec_msg msg = {};
- struct cec_op_record_src rec_src = {};
+ fail_on_test(deck_status_get(node, me, la, deck_status));
+ if (cec_msg_status_is_abort(&msg)) {
+ if (!incorrect_mode(&msg))
+ return FAIL;
+ if (deck_status == CEC_OP_DECK_INFO_NO_MEDIA)
+ info("Play Still: no media.\n");
+ else
+ warn("Deck has media but returned Feature Abort with Incorrect Mode.");
+ return OK;
+ }
+ fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
+
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_STILL, CEC_OP_DECK_INFO_STILL));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_REV, CEC_OP_DECK_INFO_PLAY_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN, CEC_OP_DECK_INFO_FAST_FWD));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN, CEC_OP_DECK_INFO_FAST_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED, CEC_OP_DECK_INFO_FAST_FWD));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED, CEC_OP_DECK_INFO_FAST_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX, CEC_OP_DECK_INFO_FAST_FWD));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX, CEC_OP_DECK_INFO_FAST_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN, CEC_OP_DECK_INFO_SLOW));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN, CEC_OP_DECK_INFO_SLOW_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED, CEC_OP_DECK_INFO_SLOW));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED, CEC_OP_DECK_INFO_SLOW_REV));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX, CEC_OP_DECK_INFO_SLOW));
+ fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX, CEC_OP_DECK_INFO_SLOW_REV));
- rec_src.type = CEC_OP_RECORD_SRC_OWN;
cec_msg_init(&msg, me, la);
- cec_msg_record_on(&msg, true, &rec_src);
+ cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
fail_on_test(!transmit_timeout(node, &msg));
- fail_on_test(timed_out(&msg));
- fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
- return 0;
+ return OK;
}
-static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool interactive)
+static int deck_ctl_play_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, la);
- cec_msg_record_off(&msg, false);
+ cec_msg_play(&msg, 0); /* Invalid Operand */
fail_on_test(!transmit_timeout(node, &msg));
- fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
if (unrecognized_op(&msg))
return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
- if (timed_out(&msg))
- return OK_PRESUMED;
-
- return 0;
-}
-
-static int one_touch_rec_status(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- struct cec_msg msg = {};
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
cec_msg_init(&msg, me, la);
- cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_DIG_SERVICE);
+ cec_msg_play(&msg, 4); /* Invalid Operand */
fail_on_test(!transmit_timeout(node, &msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
-}
-
-static const vec_remote_subtests one_touch_rec_subtests{
- { "Record TV Screen", CEC_LOG_ADDR_MASK_TV, one_touch_rec_tv_screen },
- { "Record On", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_on },
- { "Record Off", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_off },
- { "Record Status", CEC_LOG_ADDR_MASK_ALL, one_touch_rec_status },
-};
-
-/* Timer Programming */
-
-/*
- TODO: These are very rudimentary tests which should be expanded.
- */
-
-static int timer_prog_set_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- /* TODO: Check the timer status for possible errors, etc. */
-
- struct cec_msg msg = {};
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
cec_msg_init(&msg, me, la);
- cec_msg_set_analogue_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- CEC_OP_ANA_BCAST_TYPE_CABLE,
- 7668, // 479.25 MHz
- node->remote[la].bcast_sys);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
- return OK_PRESUMED;
- }
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
+ cec_msg_play(&msg, 0x26); /* Invalid Operand */
+ fail_on_test(!transmit_timeout(node, &msg));
+ fail_on_test(!cec_msg_status_is_abort(&msg));
+ fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
- return 0;
+ return OK;
}
-static int timer_prog_set_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- /* TODO: Check the timer status for possible errors, etc. */
-
- struct cec_msg msg = {};
- struct cec_op_digital_service_id digital_service_id = {};
-
- digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
- digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
- digital_service_id.channel.minor = 1;
- digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
- cec_msg_init(&msg, me, la);
- cec_msg_set_digital_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- &digital_service_id);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
- return OK_PRESUMED;
- }
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
-}
+static const vec_remote_subtests deck_ctl_subtests{
+ {
+ "Give Deck Status",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_give_status,
+ },
+ {
+ "Give Deck Status Invalid Operand",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_give_status_invalid,
+ },
+ {
+ "Deck Control",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_deck_ctl,
+ },
+ {
+ "Deck Control Invalid Operand",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_deck_ctl_invalid,
+ },
+ {
+ "Play",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_play,
+ },
+ {
+ "Play Invalid Operand",
+ CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+ deck_ctl_play_invalid,
+ },
+};
-static int timer_prog_set_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+static const char *hec_func_state2s(__u8 hfs)
{
- /* TODO: Check the timer status. */
-
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_set_ext_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
- return OK_PRESUMED;
+ switch (hfs) {
+ case CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED:
+ return "HEC Not Supported";
+ case CEC_OP_HEC_FUNC_STATE_INACTIVE:
+ return "HEC Inactive";
+ case CEC_OP_HEC_FUNC_STATE_ACTIVE:
+ return "HEC Active";
+ case CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD:
+ return "HEC Activation Field";
+ default:
+ return "Unknown";
}
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
}
-static int timer_prog_clear_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+static const char *host_func_state2s(__u8 hfs)
{
- /* TODO: Check the timer cleared status. */
-
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_clear_analogue_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- CEC_OP_ANA_BCAST_TYPE_CABLE,
- 7668, // 479.25 MHz
- node->remote[la].bcast_sys);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
- return OK_PRESUMED;
+ switch (hfs) {
+ case CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED:
+ return "Host Not Supported";
+ case CEC_OP_HOST_FUNC_STATE_INACTIVE:
+ return "Host Inactive";
+ case CEC_OP_HOST_FUNC_STATE_ACTIVE:
+ return "Host Active";
+ default:
+ return "Unknown";
}
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
}
-static int timer_prog_clear_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+static const char *enc_func_state2s(__u8 efs)
{
- /* TODO: Check the timer cleared status. */
-
- struct cec_msg msg = {};
- struct cec_op_digital_service_id digital_service_id = {};
-
- digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
- digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
- digital_service_id.channel.minor = 1;
- digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
- cec_msg_init(&msg, me, la);
- cec_msg_clear_digital_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- &digital_service_id);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
- return OK_PRESUMED;
+ switch (efs) {
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED:
+ return "Ext Con Not Supported";
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE:
+ return "Ext Con Inactive";
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE:
+ return "Ext Con Active";
+ default:
+ return "Unknown";
}
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
}
-static int timer_prog_clear_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
+static const char *cdc_errcode2s(__u8 cdc_errcode)
{
- /* TODO: Check the timer cleared status. */
-
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_clear_ext_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
- CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
- fail_on_test(!transmit_timeout(node, &msg, 10000));
- if (timed_out(&msg)) {
- warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
- return OK_PRESUMED;
+ switch (cdc_errcode) {
+ case CEC_OP_CDC_ERROR_CODE_NONE:
+ return "No error";
+ case CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED:
+ return "Initiator does not have requested capability";
+ case CEC_OP_CDC_ERROR_CODE_WRONG_STATE:
+ return "Initiator is in wrong state";
+ case CEC_OP_CDC_ERROR_CODE_OTHER:
+ return "Other error";
+ default:
+ return "Unknown";
}
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
- if (cec_msg_status_is_abort(&msg))
- return OK_PRESUMED;
-
- return 0;
-}
-
-static int timer_prog_set_prog_title(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_set_timer_program_title(&msg, "Super-Hans II");
- fail_on_test(!transmit_timeout(node, &msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
-
- return OK_PRESUMED;
}
-static int timer_prog_timer_status(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_timer_status(&msg, CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP,
- CEC_OP_MEDIA_INFO_NO_MEDIA,
- CEC_OP_PROG_INFO_ENOUGH_SPACE,
- 0, 0, 0);
- fail_on_test(!transmit_timeout(node, &msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
-
- return OK_PRESUMED;
-}
-
-static int timer_prog_timer_clear_status(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- struct cec_msg msg = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_timer_cleared_status(&msg, CEC_OP_TIMER_CLR_STAT_CLEARED);
- fail_on_test(!transmit_timeout(node, &msg));
- if (unrecognized_op(&msg))
- return OK_NOT_SUPPORTED;
- if (refused(&msg))
- return OK_REFUSED;
-
- return OK_PRESUMED;
-}
-
-static const vec_remote_subtests timer_prog_subtests{
- { "Set Analogue Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_analog_timer },
- { "Set Digital Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_digital_timer },
- { "Set Timer Program Title", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_prog_title },
- { "Set External Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_ext_timer },
- { "Clear Analogue Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_analog_timer },
- { "Clear Digital Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_digital_timer },
- { "Clear External Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_ext_timer },
- { "Timer Status", CEC_LOG_ADDR_MASK_RECORD, timer_prog_timer_status },
- { "Timer Cleared Status", CEC_LOG_ADDR_MASK_RECORD, timer_prog_timer_clear_status },
-};
-
static int cdc_hec_discover(struct node *node, unsigned me, unsigned la, bool print)
{
/* TODO: For future use cases, it might be necessary to store the results
from the HEC discovery to know which HECs are possible to form, etc. */
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u32 mode = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER;
bool has_cdc = false;
@@ -1478,6 +1169,13 @@ void testRemote(struct node *node, unsigned me, unsigned la, unsigned test_tags,
announce("Assuming that the device is powered on.");
}
+ /* Ensure that the remote device knows the initiator's primary device type.*/
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, la);
+ cec_msg_report_physical_addr(&msg, node->phys_addr, node->prim_devtype);
+ transmit_timeout(node, &msg);
+
int ret = 0;
for (const auto &test : tests) {
@@ -1514,7 +1212,7 @@ void testRemote(struct node *node, unsigned me, unsigned la, unsigned test_tags,
result_name(mapTests[safename(name)], false),
result_name(ret, false));
else if (has_warnings && mapTestsNoWarnings[safename(name)])
- printf("\t %s: %s (Expected no warnings, got %d warnings)\n",
+ printf("\t %s: %s (Expected no warnings, but got %d)\n",
name, ok(FAIL), warnings - old_warnings);
else if (ret == FAIL)
printf("\t %s: %s\n", name, ok(OK_EXPECTED_FAIL));
diff --git a/utils/cec-ctl/cec-ctl.cpp b/utils/cec-ctl/cec-ctl.cpp
index b1db4aac..4bed81a9 100644
--- a/utils/cec-ctl/cec-ctl.cpp
+++ b/utils/cec-ctl/cec-ctl.cpp
@@ -409,7 +409,7 @@ static __u64 current_ts()
return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
}
-int cec_named_ioctl(int fd, const char *name,
+static int cec_named_ioctl(int fd, const char *name,
unsigned long int request, void *parm)
{
int retval = ioctl(fd, request, parm);
@@ -673,7 +673,7 @@ static __u16 calc_mask(__u16 pa)
static int showTopology(struct node *node)
{
- struct cec_msg msg = { };
+ struct cec_msg msg;
struct cec_log_addrs laddrs = { };
if (!(node->caps & CEC_CAP_TRANSMIT))
@@ -1508,7 +1508,7 @@ static void test_power_cycle(const struct node &node, unsigned int max_tries,
}
static void stress_test_power_cycle(const struct node &node, unsigned cnt,
- unsigned min_sleep, unsigned max_sleep, unsigned max_tries,
+ double min_sleep, double max_sleep, unsigned max_tries,
bool has_seed, unsigned seed, unsigned repeats,
double sleep_before_on, double sleep_before_off)
{
@@ -1516,7 +1516,7 @@ static void stress_test_power_cycle(const struct node &node, unsigned cnt,
struct cec_msg msg;
unsigned tries = 0;
unsigned iter = 0;
- unsigned min_usleep = 1000000 * (max_sleep ? min_sleep : 0);
+ unsigned min_usleep = 1000000.0 * (max_sleep ? min_sleep : 0);
unsigned mod_usleep = 0;
unsigned wakeup_la;
__u16 pa, prev_pa;
@@ -1524,7 +1524,7 @@ static void stress_test_power_cycle(const struct node &node, unsigned cnt,
int ret;
if (max_sleep)
- mod_usleep = 1000000 * (max_sleep - min_sleep) + 1;
+ mod_usleep = 1000000.0 * (max_sleep - min_sleep) + 1;
if (!has_seed)
seed = time(nullptr);
@@ -1557,7 +1557,7 @@ static void stress_test_power_cycle(const struct node &node, unsigned cnt,
iter++;
if (usecs1)
- printf("%s: Sleep %.2fs\n", ts2s(current_ts()).c_str(),
+ printf("%s: Sleep %.2fs before Image View On\n", ts2s(current_ts()).c_str(),
usecs1 / 1000000.0);
fflush(stdout);
usleep(usecs1);
@@ -1647,7 +1647,7 @@ static void stress_test_power_cycle(const struct node &node, unsigned cnt,
break;
if (usecs2)
- printf("%s: Sleep %.2fs\n", ts2s(current_ts()).c_str(),
+ printf("%s: Sleep %.2fs before Standby\n", ts2s(current_ts()).c_str(),
usecs2 / 1000000.0);
fflush(stdout);
usleep(usecs2);
@@ -1958,8 +1958,8 @@ int main(int argc, char **argv)
__u32 monitor_time = 0;
__u32 vendor_id = 0x000c03; /* HDMI LLC vendor ID */
unsigned int stress_test_pwr_cycle_cnt = 0;
- unsigned int stress_test_pwr_cycle_min_sleep = 0;
- unsigned int stress_test_pwr_cycle_max_sleep = 0;
+ double stress_test_pwr_cycle_min_sleep = 0;
+ double stress_test_pwr_cycle_max_sleep = 0;
unsigned int stress_test_pwr_cycle_polls = 30;
bool stress_test_pwr_cycle_has_seed = false;
unsigned int stress_test_pwr_cycle_seed = 0;
@@ -2179,7 +2179,7 @@ int main(int argc, char **argv)
usage();
return 1;
case OptVendorCommand: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"payload",
nullptr
};
@@ -2213,7 +2213,7 @@ int main(int argc, char **argv)
break;
}
case OptCustomCommand: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"cmd",
"payload",
nullptr
@@ -2256,7 +2256,7 @@ int main(int argc, char **argv)
break;
}
case OptVendorCommandWithID: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"vendor-id",
"cmd",
nullptr
@@ -2295,7 +2295,7 @@ int main(int argc, char **argv)
break;
}
case OptVendorRemoteButtonDown: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"rc-code",
nullptr
};
@@ -2344,7 +2344,7 @@ int main(int argc, char **argv)
break;
case OptTestPowerCycle: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"polls",
"sleep",
nullptr
@@ -2371,7 +2371,7 @@ int main(int argc, char **argv)
}
case OptStressTestPowerCycle: {
- static const char *arg_names[] = {
+ static constexpr const char *arg_names[] = {
"cnt",
"min-sleep",
"max-sleep",
@@ -2390,10 +2390,10 @@ int main(int argc, char **argv)
stress_test_pwr_cycle_cnt = strtoul(value, nullptr, 0);
break;
case 1:
- stress_test_pwr_cycle_min_sleep = strtoul(value, nullptr, 0);
+ stress_test_pwr_cycle_min_sleep = strtod(value, nullptr);
break;
case 2:
- stress_test_pwr_cycle_max_sleep = strtoul(value, nullptr, 0);
+ stress_test_pwr_cycle_max_sleep = strtod(value, nullptr);
break;
case 3:
stress_test_pwr_cycle_has_seed = true;
diff --git a/utils/cec-ctl/cec-pin.cpp b/utils/cec-ctl/cec-pin.cpp
index 30e5accf..cf4b6d34 100644
--- a/utils/cec-ctl/cec-pin.cpp
+++ b/utils/cec-ctl/cec-pin.cpp
@@ -320,6 +320,12 @@ static void cec_pin_debug(__u64 ev_ts, __u64 usecs, bool was_high, bool is_high,
}
}
+#define verb_printf(fmt, args...) \
+ do { \
+ if (verbose) \
+ printf(fmt, ##args); \
+ } while (0)
+
void log_event_pin(bool is_high, __u64 ev_ts, bool show)
{
static __u64 last_ts;
@@ -336,49 +342,54 @@ void log_event_pin(bool is_high, __u64 ev_ts, bool show)
if (is_high)
return;
}
- if (verbose && show) {
+ if (show) {
double delta = (ev_ts - last_change_ts) / 1000000.0;
if (!was_high && last_change_ts && state == CEC_ST_RECEIVE_START_BIT &&
delta * 1000 >= CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_MARGIN)
- printf("\n");
- printf("%s: ", ts2s(ts).c_str());
+ verb_printf("\n");
+ verb_printf("%s: ", ts2s(ts).c_str());
if (last_change_ts && is_high && was_high &&
- (ev_ts - last_1_to_0_ts) / 1000000 <= 10)
- printf("1 -> 1 (was 1 for %.2f ms, period of previous %spulse %.2f ms)\n",
- delta, state == CEC_ST_RECEIVE_START_BIT ? "start " : "",
- (ev_ts - last_1_to_0_ts) / 1000000.0);
- else if (last_change_ts && is_high && was_high)
- printf("1 -> 1 (%.2f ms)\n", delta);
- else if (was_high && state == CEC_ST_IDLE) {
+ (ev_ts - last_1_to_0_ts) / 1000000 <= 10) {
+ verb_printf("1 -> 1 (was 1 for %.2f ms, period of previous %spulse %.2f ms)\n",
+ delta, state == CEC_ST_RECEIVE_START_BIT ? "start " : "",
+ (ev_ts - last_1_to_0_ts) / 1000000.0);
+ } else if (last_change_ts && is_high && was_high) {
+ verb_printf("1 -> 1 (%.2f ms)\n", delta);
+ } else if (was_high && state == CEC_ST_IDLE) {
if (bit_periods > 1 && bit_periods < 10)
- printf("1 -> 0 (was 1 for %.2f ms, free signal time = %.1f bit periods)\n",
- delta, bit_periods);
+ verb_printf("1 -> 0 (was 1 for %.2f ms, signal free time = %.1f bit periods)\n",
+ delta, bit_periods);
else
- printf("1 -> 0 (was 1 for %.2f ms)\n", delta);
- } else if (was_high && (ev_ts - last_1_to_0_ts) / 1000000 <= 10)
- printf("1 -> 0 (was 1 for %.2f ms, period of previous %spulse %.2f ms)\n",
- delta, state == CEC_ST_RECEIVE_START_BIT ? "start " : "",
- (ev_ts - last_1_to_0_ts) / 1000000.0);
- else if (was_high)
- printf("1 -> 0 (was 1 for %.2f ms)\n", delta);
- else if (last_change_ts && state == CEC_ST_RECEIVE_START_BIT &&
- delta * 1000 < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_MARGIN)
- printf("0 -> 1 (was 0 for %.2f ms, might indicate %d bit)\n", delta,
- delta * 1000 < CEC_TIM_DATA_BIT_1_LOW_MAX + CEC_TIM_MARGIN);
- else if (last_change_ts && state == CEC_ST_RECEIVE_START_BIT)
- printf("0 -> 1 (was 0 for %.2f ms)\n", delta);
- else if (last_change_ts &&
- delta * 1000 >= CEC_TIM_LOW_DRIVE_ERROR_MIN - CEC_TIM_MARGIN)
- printf("0 -> 1 (was 0 for %.2f ms, warn: indicates low drive)\n", delta);
- else if (last_change_ts)
- printf("0 -> 1 (was 0 for %.2f ms, indicates %d bit)\n", delta,
- delta * 1000 < CEC_TIM_DATA_BIT_1_LOW_MAX + CEC_TIM_MARGIN);
- else
- printf("0 -> 1\n");
- } else if (!is_high && bit_periods > 1 && bit_periods < 10 && show) {
- printf("%s: free signal time = %.1f bit periods\n",
- ts2s(ts).c_str(), bit_periods);
+ verb_printf("1 -> 0 (was 1 for %.2f ms)\n", delta);
+ } else if (was_high && (ev_ts - last_1_to_0_ts) / 1000000 <= 10) {
+ verb_printf("1 -> 0 (was 1 for %.2f ms, period of previous %spulse %.2f ms)\n",
+ delta, state == CEC_ST_RECEIVE_START_BIT ? "start " : "",
+ (ev_ts - last_1_to_0_ts) / 1000000.0);
+ } else if (was_high) {
+ verb_printf("1 -> 0 (was 1 for %.2f ms)\n", delta);
+ } else if (last_change_ts && state == CEC_ST_RECEIVE_START_BIT &&
+ delta * 1000 < CEC_TIM_START_BIT_LOW_MIN - CEC_TIM_MARGIN) {
+ verb_printf("0 -> 1 (was 0 for %.2f ms, might indicate %d bit)\n", delta,
+ delta * 1000 < CEC_TIM_DATA_BIT_1_LOW_MAX + CEC_TIM_MARGIN);
+ } else if (last_change_ts && state == CEC_ST_RECEIVE_START_BIT) {
+ verb_printf("0 -> 1 (was 0 for %.2f ms)\n", delta);
+ } else if (last_change_ts &&
+ delta * 1000 >= CEC_TIM_LOW_DRIVE_ERROR_MIN - CEC_TIM_MARGIN) {
+ if (verbose)
+ printf("0 -> 1 (was 0 for %.2f ms, warn: indicates low drive)\n", delta);
+ else
+ printf("\n%s: warn: low drive for %.2f ms\n", ts2s(ts).c_str(), delta);
+ } else if (last_change_ts) {
+ verb_printf("0 -> 1 (was 0 for %.2f ms, indicates %d bit)\n", delta,
+ delta * 1000 < CEC_TIM_DATA_BIT_1_LOW_MAX + CEC_TIM_MARGIN);
+ } else {
+ verb_printf("0 -> 1\n");
+ }
+
+ if (!verbose && !is_high && bit_periods > 1 && bit_periods < 10)
+ printf("%s: signal free time = %.1f bit periods\n",
+ ts2s(ts).c_str(), bit_periods);
}
cec_pin_debug(ev_ts, (ev_ts - last_ts) / 1000, was_high, is_high, show);
last_change_ts = ev_ts;
diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
index 184cf16a..0adf6ce8 100644
--- a/utils/cec-follower/cec-follower.cpp
+++ b/utils/cec-follower/cec-follower.cpp
@@ -47,6 +47,7 @@ bool show_msgs;
bool show_state;
bool show_warnings = true;
unsigned warnings;
+std::set<struct Timer> programmed_timers;
static struct option long_options[] = {
{ "device", required_argument, nullptr, OptSetDevice },
@@ -197,7 +198,7 @@ static std::string audio_format_code2s(__u8 format_code)
}
}
-std::string extension_type_code2s(__u8 type_code)
+static std::string extension_type_code2s(__u8 type_code)
{
switch (type_code) {
case 0:
@@ -296,6 +297,62 @@ int cec_named_ioctl(int fd, const char *name,
return retval == -1 ? e : (retval ? -1 : 0);
}
+void print_timers(struct node *node)
+{
+ if (show_info) {
+ printf("Timers Set:\n");
+ if (node->state.recording_controlled_by_timer)
+ printf("Deck is currently recording from the first timer.\n");
+ if (node->state.one_touch_record_on && !node->state.recording_controlled_by_timer)
+ printf("Deck is currently recording independent of timers.\n");
+ for (auto &t : programmed_timers) {
+ std::string start = ctime(&t.start_time);
+ time_t end_time = t.start_time + t.duration;
+ std::string end = ctime(&end_time);
+ /* Remove the seconds because timer is precise only to the minute. */
+ start.erase(16, 3);
+ end.erase(16, 3);
+ /* Remove the new line characters. */
+ start.erase(start.end() - 1);
+ end.erase(end.end() - 1);
+ /* Remove the start year if it is the same as the end year. */
+ if ((start.compare(start.size() - 4, 5, end, end.size() - 4, 5) == 0))
+ start.erase(start.size() - 5, 5);
+ printf("\t%s - %s, ", start.c_str(), end.c_str());
+ /* Find and print the source. */
+ std::string source;
+ switch (t.src.type) {
+ case CEC_OP_RECORD_SRC_OWN:
+ source = "own";
+ break;
+ case CEC_OP_RECORD_SRC_DIGITAL:
+ source = "digital";
+ break;
+ case CEC_OP_RECORD_SRC_ANALOG:
+ source = "analog";
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PLUG:
+ source = "ext plug";
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+ source = "ext phy addr";
+ break;
+ default:
+ break;
+ }
+ printf("source: %s, ", source.c_str());
+ if (t.recording_seq)
+ printf("rec-seq: 0x%x, ", t.recording_seq);
+ printf("needs: %ld %s\n", t.duration, "MB."); /* 1MB per second. */
+ }
+ printf("Total media space available for recording: ");
+ if (node->state.media_space_available >= 0)
+ printf("%d MB.\n\n", node->state.media_space_available);
+ else
+ printf("0 MB.\n\n");
+ }
+}
+
void state_init(struct node &node)
{
if (options[OptStandby])
@@ -313,6 +370,14 @@ void state_init(struct node &node)
node.state.sac_active = false;
node.state.volume = 50;
node.state.mute = false;
+ node.state.deck_report_changes = false;
+ node.state.deck_report_changes_to = 0;
+ node.state.deck_state = CEC_OP_DECK_INFO_STOP;
+ node.state.deck_skip_start = 0;
+ node.state.one_touch_record_on = false;
+ node.state.record_received_standby = false;
+ node.state.media_space_available = 36000; /* In MB; space for 10 hours @ 1MB/sec */
+ node.state.recording_controlled_by_timer = false;
tuner_dev_info_init(&node.state);
node.state.last_aud_rate_rx_ts = 0;
}
@@ -508,26 +573,30 @@ int main(int argc, char **argv)
cec_driver_info(caps, laddrs, node.phys_addr, conn_info);
- if (laddrs.cec_version >= CEC_OP_CEC_VERSION_2_0) {
- bool is_dev_feat = false;
-
- for (__u8 byte : laddrs.features[0]) {
- if (is_dev_feat) {
- node.source_has_arc_rx = (byte & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX) != 0;
- node.sink_has_arc_tx = (byte & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX) != 0;
- node.has_aud_rate = (byte & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE) != 0;
- node.has_deck_ctl = (byte & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL) != 0;
- node.has_rec_tv = (byte & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN) != 0;
- node.has_osd_string = (byte & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING) != 0;
- break;
- }
- if (byte & CEC_OP_FEAT_EXT)
- continue;
- if (!is_dev_feat)
- is_dev_feat = true;
- else
- break;
+ /*
+ * For CEC 1.4, features of a logical address may still be
+ * filled in according to the CEC 2.0 guidelines even though
+ * the CEC framework won’t use the features in the CEC 2.0
+ * CEC_MSG_REPORT_FEATURES.
+ */
+ bool is_dev_feat = false;
+
+ for (__u8 byte : laddrs.features[0]) {
+ if (is_dev_feat) {
+ node.source_has_arc_rx = (byte & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX) != 0;
+ node.sink_has_arc_tx = (byte & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX) != 0;
+ node.has_aud_rate = (byte & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE) != 0;
+ node.has_deck_ctl = (byte & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL) != 0;
+ node.has_rec_tv = (byte & CEC_OP_FEAT_DEV_HAS_RECORD_TV_SCREEN) != 0;
+ node.has_osd_string = (byte & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING) != 0;
+ break;
}
+ if (byte & CEC_OP_FEAT_EXT)
+ continue;
+ if (!is_dev_feat)
+ is_dev_feat = true;
+ else
+ break;
}
printf("\n");
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 391b9ab4..945e65ff 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -19,12 +19,16 @@
#include <cec-info.h>
#include <cec-log.h>
+#include <set>
+#include <ctime>
extern bool show_info;
extern bool show_msgs;
extern bool show_state;
extern bool show_warnings;
extern unsigned warnings;
+extern std::set<struct Timer> programmed_timers;
+extern void print_timers(struct node *node);
struct state {
__u16 active_source_pa;
@@ -50,7 +54,14 @@ struct state {
bool service_by_dig_id;
bool tuner_report_changes;
bool deck_report_changes;
- unsigned toggle_power_status;
+ __u8 deck_report_changes_to;
+ __u8 deck_state;
+ __u64 deck_skip_start;
+ bool one_touch_record_on;
+ bool record_received_standby;
+ int media_space_available;
+ bool recording_controlled_by_timer;
+ time_t toggle_power_status;
__u64 last_aud_rate_rx_ts;
};
@@ -59,6 +70,7 @@ struct node {
const char *device;
unsigned caps;
unsigned available_log_addrs;
+ __u8 remote_prim_devtype[15];
unsigned adap_la_mask;
unsigned remote_la_mask;
__u16 remote_phys_addr[15];
@@ -76,6 +88,44 @@ struct node {
unsigned short ignore_opcode[256];
};
+struct Timer {
+ time_t start_time;
+ time_t duration; /* In seconds. */
+ __u8 recording_seq;
+ struct cec_op_record_src src;
+
+ Timer()
+ {
+ start_time = 0;
+ duration = 0;
+ recording_seq = 0;
+ src = {};
+ }
+
+ Timer(const Timer& timer)
+ {
+ start_time = timer.start_time;
+ duration = timer.duration;
+ recording_seq = timer.recording_seq;
+ src = timer.src;
+ }
+
+ bool operator<(const Timer &r) const
+ {
+ return start_time < r.start_time ||
+ (start_time == r.start_time && duration < r.duration) ||
+ (start_time == r.start_time && duration == r.duration && src.type < r.src.type) ||
+ (start_time == r.start_time && duration == r.duration && src.type == r.src.type &&
+ recording_seq < r.recording_seq);
+ }
+
+ bool operator==(const Timer &right) const
+ {
+ return start_time == right.start_time && duration == right.duration &&
+ src.type == right.src.type && recording_seq == right.recording_seq;
+ }
+};
+
struct la_info {
__u64 ts;
struct {
@@ -219,11 +269,14 @@ void sad_encode(const struct short_audio_desc *sad, __u32 *descriptor);
// cec-tuner.cpp
void tuner_dev_info_init(struct state *state);
-void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, unsigned me);
+void process_tuner_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type);
+void process_record_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type);
+void process_timer_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type);
// CEC processing
void reply_feature_abort(struct node *node, struct cec_msg *msg,
__u8 reason = CEC_OP_ABORT_UNRECOGNIZED_OP);
void testProcessing(struct node *node, bool wallclock);
+bool enter_standby(struct node *node);
#endif
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index c76ec54e..661cbf42 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -32,6 +32,9 @@
/* The maximum interval in nanoseconds between audio rate messages as defined in the spec */
#define MAX_AUD_RATE_MSG_INTERVAL_NS (2 * 1000000000ULL)
+/* The maximum interval in nanoseconds to allow a deck to skip forward/reverse */
+#define MAX_DECK_SKIP_NS (2 * 1000000000ULL)
+
struct cec_enum_values {
const char *type_name;
__u8 value;
@@ -41,8 +44,8 @@ struct la_info la_info[15];
static struct timespec start_monotonic;
static struct timeval start_timeofday;
-static const time_t time_to_transient = 1;
-static const time_t time_to_stable = 8;
+static constexpr time_t time_to_transient = 1;
+static constexpr time_t time_to_stable = 8;
static const char *get_ui_cmd_string(__u8 ui_cmd)
{
@@ -143,6 +146,9 @@ void reply_feature_abort(struct node *node, struct cec_msg *msg, __u8 reason)
static bool exit_standby(struct node *node)
{
+ /* Cancel any standby request that was pending. */
+ node->state.record_received_standby = false;
+
if (node->state.power_status == CEC_OP_POWER_STATUS_STANDBY ||
node->state.power_status == CEC_OP_POWER_STATUS_TO_STANDBY) {
node->state.old_power_status = node->state.power_status;
@@ -154,13 +160,23 @@ static bool exit_standby(struct node *node)
return false;
}
-static bool enter_standby(struct node *node)
+bool enter_standby(struct node *node)
{
if (node->state.power_status == CEC_OP_POWER_STATUS_ON ||
node->state.power_status == CEC_OP_POWER_STATUS_TO_ON) {
+ /*
+ * Standby should not interrupt a recording in progress, but
+ * remember to go to standby once the recording is finished.
+ */
+ if (node->state.one_touch_record_on) {
+ node->state.record_received_standby = true;
+ return false;
+ }
node->state.old_power_status = node->state.power_status;
node->state.power_status = CEC_OP_POWER_STATUS_STANDBY;
node->state.power_status_changed_time = time(nullptr);
+ node->state.deck_skip_start = 0;
+ node->state.record_received_standby = false;
dev_info("Changing state to standby\n");
return true;
}
@@ -252,7 +268,22 @@ static void aud_rate_msg_interval_check(struct node *node, __u64 ts_new)
}
}
-static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
+static void update_deck_state(struct node *node, unsigned me, __u8 deck_state_new)
+{
+ if (node->state.deck_state != deck_state_new) {
+ node->state.deck_state = deck_state_new;
+
+ if (node->state.deck_report_changes) {
+ struct cec_msg msg;
+
+ cec_msg_init(&msg, me, node->state.deck_report_changes_to);
+ cec_msg_deck_status(&msg, node->state.deck_state);
+ transmit(node, &msg);
+ }
+ }
+}
+
+static void processMsg(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
{
__u8 to = cec_msg_destination(&msg);
__u8 from = cec_msg_initiator(&msg);
@@ -358,8 +389,8 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
if (cec_has_tv(1 << la) && la_info[la].phys_addr == 0)
warn("TV (0) at 0.0.0.0 sent Routing Information.");
+ return;
}
- fallthrough;
/* System Information */
@@ -377,8 +408,10 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
__u8 prim_dev_type;
cec_ops_report_physical_addr(&msg, &phys_addr, &prim_dev_type);
- if (from < 15)
+ if (from < 15) {
node->remote_phys_addr[from] = phys_addr;
+ node->remote_prim_devtype[from] = prim_dev_type;
+ }
return;
}
@@ -505,44 +538,130 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
break;
- /*
- Deck Control
-
- This is only a basic implementation.
-
- TODO: Device state should reflect whether we are playing,
- fast forwarding, etc.
- */
+ /* Deck Control */
case CEC_MSG_GIVE_DECK_STATUS:
- if (node->has_deck_ctl) {
- __u8 status_req;
+ if (!node->has_deck_ctl)
+ break;
- cec_ops_give_deck_status(&msg, &status_req);
- if (status_req < CEC_OP_STATUS_REQ_ON ||
- status_req > CEC_OP_STATUS_REQ_ONCE) {
- reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
- return;
- }
- if (status_req != CEC_OP_STATUS_REQ_ONCE)
- node->state.deck_report_changes =
- status_req == CEC_OP_STATUS_REQ_ON;
- if (status_req == CEC_OP_STATUS_REQ_OFF)
- return;
+ __u8 status_req;
+ cec_ops_give_deck_status(&msg, &status_req);
+
+ switch (status_req) {
+ case CEC_OP_STATUS_REQ_ON:
+ node->state.deck_report_changes = true;
+ node->state.deck_report_changes_to = cec_msg_initiator(&msg);
+ fallthrough;
+ case CEC_OP_STATUS_REQ_ONCE:
cec_msg_set_reply_to(&msg, &msg);
- cec_msg_deck_status(&msg, CEC_OP_DECK_INFO_STOP);
+ cec_msg_deck_status(&msg, node->state.deck_state);
transmit(node, &msg);
return;
+ case CEC_OP_STATUS_REQ_OFF:
+ node->state.deck_report_changes = false;
+ return;
+ default:
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
+ return;
}
- break;
case CEC_MSG_PLAY:
- if (node->has_deck_ctl)
+ if (!node->has_deck_ctl)
+ break;
+
+ __u8 deck_state;
+ __u8 play_mode;
+
+ cec_ops_play(&msg, &play_mode);
+
+ switch (play_mode) {
+ case CEC_OP_PLAY_MODE_PLAY_FWD:
+ exit_standby(node);
+ deck_state = CEC_OP_DECK_INFO_PLAY;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_REV:
+ deck_state = CEC_OP_DECK_INFO_PLAY_REV;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_STILL:
+ deck_state = CEC_OP_DECK_INFO_STILL;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN:
+ case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED:
+ case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX:
+ deck_state = CEC_OP_DECK_INFO_FAST_FWD;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN:
+ case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED:
+ case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX:
+ deck_state = CEC_OP_DECK_INFO_FAST_REV;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN:
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED:
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX:
+ deck_state = CEC_OP_DECK_INFO_SLOW;
+ break;
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN:
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED:
+ case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX:
+ deck_state = CEC_OP_DECK_INFO_SLOW_REV;
+ break;
+ default:
+ cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
+ transmit(node, &msg);
return;
- break;
+ }
+ /* Only Play Forward and Play Still will close tray if open. */
+ if (play_mode != CEC_OP_PLAY_MODE_PLAY_FWD &&
+ play_mode != CEC_OP_PLAY_MODE_PLAY_STILL &&
+ node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+ return;
+ }
+ node->state.deck_skip_start = 0;
+ update_deck_state(node, me, deck_state);
+ return;
case CEC_MSG_DECK_CONTROL:
- if (node->has_deck_ctl)
+ if (!node->has_deck_ctl)
+ break;
+
+ __u8 deck_control_mode;
+
+ cec_ops_deck_control(&msg, &deck_control_mode);
+
+ switch (deck_control_mode) {
+ case CEC_OP_DECK_CTL_MODE_STOP:
+ deck_state = CEC_OP_DECK_INFO_STOP;
+ node->state.deck_skip_start = 0;
+ break;
+ case CEC_OP_DECK_CTL_MODE_EJECT:
+ exit_standby(node);
+ deck_state = CEC_OP_DECK_INFO_NO_MEDIA;
+ node->state.deck_skip_start = 0;
+ break;
+ case CEC_OP_DECK_CTL_MODE_SKIP_FWD:
+ /* Skip Forward will not retract the deck tray. */
+ if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+ return;
+ }
+ deck_state = CEC_OP_DECK_INFO_SKIP_FWD;
+ node->state.deck_skip_start = msg.rx_ts;
+ break;
+ case CEC_OP_DECK_CTL_MODE_SKIP_REV:
+ /* Skip Reverse will not retract the deck tray. */
+ if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+ return;
+ }
+ deck_state = CEC_OP_DECK_INFO_SKIP_REV;
+ node->state.deck_skip_start = msg.rx_ts;
+ break;
+ default:
+ cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
+ transmit(node, &msg);
return;
- break;
+ }
+ update_deck_state(node, me, deck_state);
+ return;
case CEC_MSG_DECK_STATUS:
return;
@@ -554,10 +673,14 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
case CEC_MSG_SELECT_DIGITAL_SERVICE:
case CEC_MSG_TUNER_STEP_DECREMENT:
case CEC_MSG_TUNER_STEP_INCREMENT:
+ process_tuner_msgs(node, msg, me, type);
+ return;
case CEC_MSG_RECORD_TV_SCREEN:
case CEC_MSG_RECORD_ON:
case CEC_MSG_RECORD_OFF:
case CEC_MSG_RECORD_STATUS:
+ process_record_msgs(node, msg, me, type);
+ return;
case CEC_MSG_SET_ANALOGUE_TIMER:
case CEC_MSG_SET_DIGITAL_TIMER:
case CEC_MSG_SET_EXT_TIMER:
@@ -567,7 +690,7 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
case CEC_MSG_SET_TIMER_PROGRAM_TITLE:
case CEC_MSG_TIMER_CLEARED_STATUS:
case CEC_MSG_TIMER_STATUS:
- process_tuner_record_timer_msgs(node, msg, me);
+ process_timer_msgs(node, msg, me, type);
return;
/* Dynamic Auto Lipsync */
@@ -886,6 +1009,73 @@ static void poll_remote_devs(struct node *node, unsigned me)
}
}
+static void update_programmed_timers(struct node *node)
+{
+ std::set<struct Timer>::iterator it = programmed_timers.begin();
+ /* Use the current minute because timers do not have second precision. */
+ time_t current_minute = time(nullptr) / 60;
+ time_t timer_start_minute = it->start_time / 60;
+ time_t timer_end_minute = (it->start_time + it->duration) / 60;
+
+ /* Start the timed recording only if the deck is not already recording. */
+ if (timer_start_minute == current_minute && !node->state.one_touch_record_on) {
+ node->state.one_touch_record_on = true;
+ node->state.recording_controlled_by_timer = true;
+ print_timers(node);
+ }
+
+ /* Delete an overlapped timer. Recording will be at best incomplete. */
+ if (timer_start_minute < current_minute &&
+ (!node->state.recording_controlled_by_timer || !node->state.one_touch_record_on)) {
+ programmed_timers.erase(*it);
+ if (show_info)
+ printf("Deleted overlapped timer.\n");
+ print_timers(node);
+ }
+
+ if (timer_end_minute != current_minute || !node->state.recording_controlled_by_timer)
+ return;
+
+ /* Delete finished timers. */
+ node->state.one_touch_record_on = false;
+ node->state.recording_controlled_by_timer = false;
+ node->state.media_space_available -= it->duration; /* 1 MB per second */
+ /*
+ * TODO: We are only ever decreasing the amount of space available,
+ * there is no heuristic that reclaims the space.
+ */
+
+ if (it->recording_seq) {
+ struct tm *last_start_time = localtime(&(it->start_time));
+ int next_wday = (last_start_time->tm_wday + 1) % 7;
+ int days_to_move_ahead = 1;
+
+ while ((it->recording_seq & (1 << next_wday)) == 0) {
+ days_to_move_ahead++;
+ next_wday = (next_wday + 1) % 7;
+ }
+ struct Timer next_timer = {};
+ next_timer = *it;
+ last_start_time->tm_mday += days_to_move_ahead;
+ last_start_time->tm_isdst = -1;
+ next_timer.start_time = mktime(last_start_time);
+ programmed_timers.insert(next_timer);
+ }
+ programmed_timers.erase(*it);
+ if (show_info)
+ printf("Deleted finished timer.\n");
+ print_timers(node);
+ /*
+ * If the finished timer was recording, and standby was received during recording,
+ * enter standby when the recording stops unless recording device is the active source.
+ */
+ if (node->state.record_received_standby) {
+ if (node->phys_addr != node->state.active_source_pa)
+ enter_standby(node);
+ node->state.record_received_standby = false;
+ }
+}
+
void testProcessing(struct node *node, bool wallclock)
{
struct cec_log_addrs laddrs;
@@ -904,6 +1094,7 @@ void testProcessing(struct node *node, bool wallclock)
doioctl(node, CEC_S_MODE, &mode);
doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
me = laddrs.log_addr[0];
+ __u8 type = laddrs.log_addr_type[0];
poll_remote_devs(node, me);
@@ -983,14 +1174,14 @@ void testProcessing(struct node *node, bool wallclock)
msg.sequence, ts2s(msg.rx_ts, wallclock).c_str());
}
if (node->adap_la_mask)
- processMsg(node, msg, me);
+ processMsg(node, msg, me, type);
}
__u8 pwr_state = current_power_state(node);
if (node->cec_version >= CEC_OP_CEC_VERSION_2_0 &&
last_pwr_state != pwr_state &&
(time_to_stable > 2 || pwr_state < CEC_OP_POWER_STATUS_TO_ON)) {
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
cec_msg_report_power_status(&msg, pwr_state);
@@ -1013,7 +1204,7 @@ void testProcessing(struct node *node, bool wallclock)
if (poll_la != me &&
poll_la != last_poll_la && poll_la < 15 && la_info[poll_la].ts &&
ts_to_ms(ts_now - la_info[poll_la].ts) > POLL_PERIOD) {
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, poll_la);
transmit(node, &msg);
@@ -1039,6 +1230,14 @@ void testProcessing(struct node *node, bool wallclock)
if (node->has_aud_rate)
aud_rate_msg_interval_check(node, ts_now);
+
+ if (node->state.deck_skip_start && ts_now - node->state.deck_skip_start > MAX_DECK_SKIP_NS) {
+ node->state.deck_skip_start = 0;
+ update_deck_state(node, me, CEC_OP_DECK_INFO_PLAY);
+ }
+
+ if (!programmed_timers.empty())
+ update_programmed_timers(node);
}
mode = CEC_MODE_INITIATOR;
doioctl(node, CEC_S_MODE, &mode);
diff --git a/utils/cec-follower/cec-tuner.cpp b/utils/cec-follower/cec-tuner.cpp
index b9c21684..a2cb6cab 100644
--- a/utils/cec-follower/cec-tuner.cpp
+++ b/utils/cec-follower/cec-tuner.cpp
@@ -4,7 +4,6 @@
*/
#include <array>
-#include <ctime>
#include <string>
#include <sys/ioctl.h>
@@ -17,6 +16,8 @@
#define TOT_ANALOG_FREQS analog_freqs_khz[0][0].size()
#define TOT_DIGITAL_CHANS digital_arib_data[0].size() + digital_atsc_data[0].size() + digital_dvb_data[0].size()
+enum Months { Jan = 1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec };
+
struct service_info {
unsigned tsid;
unsigned onid;
@@ -482,14 +483,71 @@ static bool analog_set_tuner_dev_info(struct node *node, struct cec_msg *msg)
return false;
}
-void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, unsigned me)
+static bool digital_operand_invalid(const struct cec_op_record_src &rec_src)
{
- bool is_bcast = cec_msg_is_broadcast(&msg);
+ switch (rec_src.digital.dig_bcast_system) {
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
+ case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
+ break;
+ default:
+ return true;
+ }
- switch (msg.msg[1]) {
+ if (rec_src.digital.service_id_method == CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL) {
+ if (rec_src.digital.channel.channel_number_fmt < CEC_OP_CHANNEL_NUMBER_FMT_1_PART ||
+ rec_src.digital.channel.channel_number_fmt > CEC_OP_CHANNEL_NUMBER_FMT_2_PART)
+ return true;
+ }
+ return false;
+}
+
+static bool analog_operand_invalid(const cec_op_record_src &rec_src)
+{
+ if (rec_src.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL)
+ return true;
+
+ if (rec_src.analog.bcast_system > CEC_OP_BCAST_SYSTEM_PAL_DK &&
+ rec_src.analog.bcast_system != CEC_OP_BCAST_SYSTEM_OTHER)
+ return true;
+
+ if (rec_src.analog.ana_freq == 0 || rec_src.analog.ana_freq == 0xffff)
+ return true;
+
+ return false;
+}
+
+static bool analog_channel_is_available(const cec_op_record_src &rec_src)
+{
+ __u8 bcast_type = rec_src.analog.ana_bcast_type;
+ unsigned freq = (rec_src.analog.ana_freq * 625) / 10;
+ __u8 bcast_system = rec_src.analog.bcast_system;
+
+ for (unsigned i = 0; i < NUM_ANALOG_FREQS; i++) {
+ if (freq == analog_freqs_khz[bcast_type][bcast_system][i])
+ return true;
+ }
+
+ return false;
+}
+
+void process_tuner_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
+{
+ bool is_bcast = cec_msg_is_broadcast(&msg);
/* Tuner Control */
+ switch (msg.msg[1]) {
case CEC_MSG_GIVE_TUNER_DEVICE_STATUS: {
__u8 status_req;
@@ -576,24 +634,31 @@ void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
analog_update_tuner_dev_info(node, node->state.service_idx, &msg);
return;
}
+ default:
+ break;
+ }
- /*
- One Touch Record
-
- This is only a basic implementation.
+ if (is_bcast)
+ return;
- TODO:
- - If we are a TV, we should only send Record On if the
- remote end is a Recording device or Reserved. Otherwise ignore.
+ reply_feature_abort(node, &msg);
+}
- - Device state should reflect whether we are recording, etc. In
- recording mode we should ignore Standby messages.
- */
+void process_record_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
+{
+ __u8 from = cec_msg_initiator(&msg);
+ bool is_bcast = cec_msg_is_broadcast(&msg);
+ /* One Touch Record */
+ switch (msg.msg[1]) {
case CEC_MSG_RECORD_TV_SCREEN: {
if (!node->has_rec_tv)
break;
+ /* Ignore if initiator is not a recording device */
+ if (!cec_has_record(1 << from) && node->remote_prim_devtype[from] != CEC_OP_PRIM_DEVTYPE_RECORD)
+ return;
+
struct cec_op_record_src rec_src = {};
rec_src.type = CEC_OP_RECORD_SRC_OWN;
@@ -602,57 +667,388 @@ void process_tuner_record_timer_msgs(struct node *node, struct cec_msg &msg, uns
transmit(node, &msg);
return;
}
- case CEC_MSG_RECORD_ON:
- if (!cec_has_record(1 << me))
+ case CEC_MSG_RECORD_ON: {
+ if (type != CEC_LOG_ADDR_TYPE_RECORD)
+ break;
+
+ __u8 rec_status;
+ bool feature_abort = false;
+ struct cec_op_record_src rec_src = {};
+
+ cec_ops_record_on(&msg, &rec_src);
+ switch (rec_src.type) {
+ case CEC_OP_RECORD_SRC_OWN:
+ rec_status = CEC_OP_RECORD_STATUS_CUR_SRC;
+ break;
+ case CEC_OP_RECORD_SRC_DIGITAL:
+ if (digital_operand_invalid(rec_src)) {
+ feature_abort = true;
+ break;
+ }
+ if (digital_get_service_idx(&rec_src.digital) >= 0)
+ rec_status = CEC_OP_RECORD_STATUS_DIG_SERVICE;
+ else
+ rec_status = CEC_OP_RECORD_STATUS_NO_DIG_SERVICE;
+ break;
+ case CEC_OP_RECORD_SRC_ANALOG:
+ if (analog_operand_invalid(rec_src)) {
+ feature_abort = true;
+ break;
+ }
+ if (analog_channel_is_available(rec_src))
+ rec_status = CEC_OP_RECORD_STATUS_ANA_SERVICE;
+ else
+ rec_status = CEC_OP_RECORD_STATUS_NO_ANA_SERVICE;
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PLUG:
+ if (rec_src.ext_plug.plug == 0)
+ feature_abort = true;
+ /* Plug number range is 1-255 in spec, but a realistic range of connectors is 6. */
+ else if (rec_src.ext_plug.plug > 6)
+ rec_status = CEC_OP_RECORD_STATUS_INVALID_EXT_PLUG;
+ else
+ rec_status = CEC_OP_RECORD_STATUS_EXT_INPUT;
+ break;
+ case CEC_OP_RECORD_SRC_EXT_PHYS_ADDR:
+ rec_status = CEC_OP_RECORD_STATUS_INVALID_EXT_PHYS_ADDR;
+ break;
+ default:
+ feature_abort = true;
break;
+ }
+ if (feature_abort) {
+ reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
+ return;
+ }
+ if (node->state.one_touch_record_on)
+ rec_status = CEC_OP_RECORD_STATUS_ALREADY_RECORDING;
cec_msg_set_reply_to(&msg, &msg);
- cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_CUR_SRC);
+ cec_msg_record_status(&msg, rec_status);
transmit(node, &msg);
+ node->state.one_touch_record_on = true;
return;
+ }
case CEC_MSG_RECORD_OFF:
- if (!cec_has_record(1 << me))
+ if (type != CEC_LOG_ADDR_TYPE_RECORD)
break;
+
cec_msg_set_reply_to(&msg, &msg);
cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_TERMINATED_OK);
transmit(node, &msg);
+ node->state.one_touch_record_on = false;
+
+ /* Delete any currently active recording timer or it may restart itself in first minute. */
+ if (node->state.recording_controlled_by_timer) {
+ node->state.recording_controlled_by_timer = false;
+ programmed_timers.erase(programmed_timers.begin());
+ if (show_info)
+ printf("Deleted manually stopped timer.\n");
+ print_timers(node);
+ }
+ /*
+ * If standby was received during recording, enter standby when the
+ * recording is finished unless recording device is the active source.
+ */
+ if (node->state.record_received_standby) {
+ if (node->phys_addr != node->state.active_source_pa)
+ enter_standby(node);
+ node->state.record_received_standby = false;
+ }
return;
case CEC_MSG_RECORD_STATUS:
return;
+ default:
+ break;
+ }
+ if (is_bcast)
+ return;
- /*
- Timer Programming
+ reply_feature_abort(node, &msg);
+}
- This is only a basic implementation.
+static struct Timer get_timer_from_message(const struct cec_msg &msg)
+{
+ struct Timer timer = {};
- TODO/Ideas:
- - Act like an actual recording device; keep track of recording
- schedule and act correctly when colliding timers are set.
- - Emulate a finite storage space for recordings
- */
+ __u8 day = 0;
+ __u8 month = 0;
+ __u8 start_hr = 0;
+ __u8 start_min = 0;
+ __u8 duration_hr = 0;
+ __u8 duration_min = 0;
+ __u8 ext_src_spec = 0;
+ __u8 plug = 0;
+ __u16 phys_addr = 0;
+
+ switch (msg.msg[1]) {
+ case CEC_MSG_CLEAR_ANALOGUE_TIMER:
+ case CEC_MSG_SET_ANALOGUE_TIMER:
+ timer.src.type = CEC_OP_RECORD_SRC_ANALOG;
+ cec_ops_set_analogue_timer(&msg, &day, &month, &start_hr, &start_min,
+ &duration_hr, &duration_min, &timer.recording_seq,
+ &timer.src.analog.ana_bcast_type, &timer.src.analog.ana_freq,
+ &timer.src.analog.bcast_system);
+ break;
+ case CEC_MSG_CLEAR_DIGITAL_TIMER:
+ case CEC_MSG_SET_DIGITAL_TIMER: {
+ struct cec_op_digital_service_id digital = {};
+ timer.src.type = CEC_OP_RECORD_SRC_DIGITAL;
+ timer.src.digital = digital;
+ cec_ops_set_digital_timer(&msg, &day, &month, &start_hr, &start_min,
+ &duration_hr, &duration_min, &timer.recording_seq,
+ &timer.src.digital);
+ break;
+ }
+ case CEC_MSG_CLEAR_EXT_TIMER:
+ case CEC_MSG_SET_EXT_TIMER: {
+ cec_ops_set_ext_timer(&msg, &day, &month, &start_hr, &start_min,
+ &duration_hr, &duration_min, &timer.recording_seq, &ext_src_spec,
+ &plug, &phys_addr);
+ if (ext_src_spec == CEC_OP_EXT_SRC_PLUG) {
+ timer.src.type = CEC_OP_RECORD_SRC_EXT_PLUG;
+ timer.src.ext_plug.plug = plug;
+ }
+ if (ext_src_spec == CEC_OP_EXT_SRC_PHYS_ADDR) {
+ timer.src.type = CEC_OP_RECORD_SRC_EXT_PHYS_ADDR;
+ timer.src.ext_phys_addr.phys_addr = phys_addr;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ timer.duration = ((duration_hr * 60) + duration_min) * 60; /* In seconds. */
+
+ /* Use current time in the timer when it is not available from message e.g. year. */
+ time_t current_time = time(nullptr);
+ struct tm *temp = localtime(&current_time);
+ temp->tm_mday = day;
+ temp->tm_mon = month - 1; /* CEC months are 1-12 but struct tm range is 0-11. */
+ temp->tm_hour = start_hr;
+ temp->tm_min = start_min;
+ /*
+ * Timer precision is only to the minute. Set sec to 0 so that differences in seconds
+ * do not affect timer comparisons.
+ */
+ temp->tm_sec = 0;
+ temp->tm_isdst = -1;
+ timer.start_time = mktime(temp);
+
+ return timer;
+}
+
+static bool timer_date_out_of_range(const struct cec_msg &msg, const struct Timer &timer)
+{
+ __u8 day = msg.msg[2];
+ __u8 month = msg.msg[3];
+ /* Hours and minutes are in BCD format */
+ __u8 start_hr = (msg.msg[4] >> 4) * 10 + (msg.msg[4] & 0xf);
+ __u8 start_min = (msg.msg[5] >> 4) * 10 + (msg.msg[5] & 0xf);
+ __u8 duration_hr = (msg.msg[6] >> 4) * 10 + (msg.msg[6] & 0xf);
+ __u8 duration_min = (msg.msg[7] >> 4) * 10 + (msg.msg[7] & 0xf);
+
+ if (start_min > 59 || start_hr > 23 || month > 12 || month == 0 || day > 31 || day == 0 ||
+ duration_min > 59 || (duration_hr == 0 && duration_min == 0))
+ return true;
+
+ switch (month) {
+ case Apr: case Jun: case Sep: case Nov:
+ if (day > 30)
+ return true;
+ break;
+ case Feb: {
+ struct tm *tp = localtime(&timer.start_time);
+
+ if (!(tp->tm_year % 4) && ((tp->tm_year % 100) || !(tp->tm_year % 400))) {
+ if (day > 29)
+ return true;
+ } else {
+ if (day > 28)
+ return true;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static bool timer_overlap(const struct Timer &new_timer)
+{
+ if (programmed_timers.size() == 1)
+ return false;
+
+ time_t new_timer_end = new_timer.start_time + new_timer.duration;
+ for (auto &t : programmed_timers) {
+ if (new_timer == t)
+ continue; /* Timer doesn't overlap itself. */
+
+ time_t existing_timer_end = t.start_time + t.duration;
+
+ if ((t.start_time < new_timer.start_time && new_timer.start_time < existing_timer_end) ||
+ (t.start_time < new_timer_end && new_timer_end < existing_timer_end) ||
+ (t.start_time == new_timer.start_time || existing_timer_end == new_timer_end) ||
+ (new_timer.start_time < t.start_time && existing_timer_end < new_timer_end))
+ return true;
+ }
+
+ return false;
+}
+
+void process_timer_msgs(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
+{
+ bool is_bcast = cec_msg_is_broadcast(&msg);
+
+ /* Timer Programming */
+ switch (msg.msg[1]) {
case CEC_MSG_SET_ANALOGUE_TIMER:
case CEC_MSG_SET_DIGITAL_TIMER:
- case CEC_MSG_SET_EXT_TIMER:
- if (!cec_has_record(1 << me))
+ case CEC_MSG_SET_EXT_TIMER: {
+ if (type != CEC_LOG_ADDR_TYPE_RECORD)
break;
+
+ __u8 prog_error = 0;
+ __u8 prog_info = 0;
+ __u8 timer_overlap_warning = CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP;
+ __u8 available_space_hr = 0;
+ __u8 available_space_min = 0;
+ struct Timer timer = get_timer_from_message(msg);
+
+ /* If timer starts in the past, increment the year so that timers can be set across year-end. */
+ if (time(nullptr) > timer.start_time) {
+ struct tm *temp = localtime(&timer.start_time);
+ temp->tm_year++;
+ temp->tm_isdst = -1;
+ timer.start_time = mktime(temp);
+ }
+
+ if (timer_date_out_of_range(msg, timer))
+ prog_error = CEC_OP_PROG_ERROR_DATE_OUT_OF_RANGE;
+
+ if (timer.recording_seq > 0x7f)
+ prog_error = CEC_OP_PROG_ERROR_REC_SEQ_ERROR;
+
+ if (programmed_timers.find(timer) != programmed_timers.end())
+ prog_error = CEC_OP_PROG_ERROR_DUPLICATE;
+
+ if (!prog_error) {
+ programmed_timers.insert(timer);
+
+ if (timer_overlap(timer))
+ timer_overlap_warning = CEC_OP_TIMER_OVERLAP_WARNING_OVERLAP;
+
+ if (node->state.media_space_available <= 0 ||
+ timer.duration > node->state.media_space_available) {
+ prog_info = CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE;
+ } else {
+ int space_that_may_be_needed = 0;
+ for (auto &t : programmed_timers) {
+ space_that_may_be_needed += t.duration;
+ if (t == timer) /* Only count the space up to and including the new timer. */
+ break;
+ }
+ if ((node->state.media_space_available - space_that_may_be_needed) >= 0)
+ prog_info = CEC_OP_PROG_INFO_ENOUGH_SPACE;
+ else
+ prog_info = CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE;
+ }
+ print_timers(node);
+ }
+
+ if (prog_info == CEC_OP_PROG_INFO_NOT_ENOUGH_SPACE ||
+ prog_info == CEC_OP_PROG_INFO_MIGHT_NOT_BE_ENOUGH_SPACE ||
+ prog_error == CEC_OP_PROG_ERROR_DUPLICATE) {
+ available_space_hr = node->state.media_space_available / 3600; /* 3600 MB/hour */
+ available_space_min = (node->state.media_space_available % 3600) / 60; /* 60 MB/min */
+ }
cec_msg_set_reply_to(&msg, &msg);
- cec_msg_timer_status(&msg, CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP,
- CEC_OP_MEDIA_INFO_NO_MEDIA,
- CEC_OP_PROG_INFO_ENOUGH_SPACE, 0, 0, 0);
+ cec_msg_timer_status(&msg, timer_overlap_warning, CEC_OP_MEDIA_INFO_UNPROT_MEDIA,
+ prog_info, prog_error, available_space_hr, available_space_min);
transmit(node, &msg);
return;
+ }
case CEC_MSG_CLEAR_ANALOGUE_TIMER:
case CEC_MSG_CLEAR_DIGITAL_TIMER:
- case CEC_MSG_CLEAR_EXT_TIMER:
- if (!cec_has_record(1 << me))
+ case CEC_MSG_CLEAR_EXT_TIMER: {
+ if (type != CEC_LOG_ADDR_TYPE_RECORD)
break;
+
+ __u8 timer_cleared_status = CEC_OP_TIMER_CLR_STAT_NO_MATCHING;
+
+ /* Look for timer in the previous year which have persisted across year-end. */
+ struct Timer timer_in_previous_year = get_timer_from_message(msg);
+ struct tm *temp = localtime(&timer_in_previous_year.start_time);
+ temp->tm_year--;
+ temp->tm_isdst = -1;
+ timer_in_previous_year.start_time = mktime(temp);
+ auto it_previous_year = programmed_timers.find(timer_in_previous_year);
+
+ if (it_previous_year != programmed_timers.end()) {
+ if (node->state.recording_controlled_by_timer && it_previous_year == programmed_timers.begin()) {
+ timer_cleared_status = CEC_OP_TIMER_CLR_STAT_RECORDING;
+ node->state.one_touch_record_on = false;
+ node->state.recording_controlled_by_timer = false;
+ } else {
+ timer_cleared_status = CEC_OP_TIMER_CLR_STAT_CLEARED;
+ }
+ programmed_timers.erase(timer_in_previous_year);
+ print_timers(node);
+ }
+
+ /* Look for timer in the current year. */
+ struct Timer timer_in_current_year = get_timer_from_message(msg);
+ auto it_current_year = programmed_timers.find(timer_in_current_year);
+
+ if (it_current_year != programmed_timers.end()) {
+ if (node->state.recording_controlled_by_timer && it_current_year == programmed_timers.begin()) {
+ timer_cleared_status = CEC_OP_TIMER_CLR_STAT_RECORDING;
+ node->state.one_touch_record_on = false;
+ node->state.recording_controlled_by_timer = false;
+ } else {
+ /* Do not overwrite status if already set. */
+ if (timer_cleared_status == CEC_OP_TIMER_CLR_STAT_NO_MATCHING)
+ timer_cleared_status = CEC_OP_TIMER_CLR_STAT_CLEARED;
+ }
+ programmed_timers.erase(timer_in_current_year);
+ print_timers(node);
+ }
+
+ /* Look for timer in the next year. */
+ struct Timer timer_in_next_year = get_timer_from_message(msg);
+ temp = localtime(&timer_in_next_year.start_time);
+ temp->tm_year++;
+ temp->tm_isdst = -1;
+ timer_in_next_year.start_time = mktime(temp);
+ if (programmed_timers.find(timer_in_next_year) != programmed_timers.end()) {
+ /* Do not overwrite status if already set. */
+ if (timer_cleared_status == CEC_OP_TIMER_CLR_STAT_NO_MATCHING)
+ timer_cleared_status = CEC_OP_TIMER_CLR_STAT_CLEARED;
+ programmed_timers.erase(timer_in_next_year);
+ print_timers(node);
+ }
cec_msg_set_reply_to(&msg, &msg);
- cec_msg_timer_cleared_status(&msg, CEC_OP_TIMER_CLR_STAT_CLEARED);
+ cec_msg_timer_cleared_status(&msg, timer_cleared_status);
transmit(node, &msg);
+ /*
+ * If the cleared timer was recording, and standby was received during recording,
+ * enter standby when the recording stops unless recording device is the active source.
+ */
+ if (timer_cleared_status == CEC_OP_TIMER_CLR_STAT_RECORDING) {
+ if (node->state.record_received_standby) {
+ if (node->phys_addr != node->state.active_source_pa)
+ enter_standby(node);
+ node->state.record_received_standby = false;
+ }
+ }
return;
+ }
case CEC_MSG_SET_TIMER_PROGRAM_TITLE:
- if (!cec_has_record(1 << me))
+ if (type != CEC_LOG_ADDR_TYPE_RECORD)
break;
return;
case CEC_MSG_TIMER_CLEARED_STATUS:
diff --git a/utils/common/media-info.cpp b/utils/common/media-info.cpp
index 3a5477e8..410e18cd 100644
--- a/utils/common/media-info.cpp
+++ b/utils/common/media-info.cpp
@@ -33,7 +33,7 @@ static std::string num2s(unsigned num, bool is_hex = true)
return buf;
}
-static struct {
+static constexpr struct {
const char *devname;
enum media_type type;
} media_types[] = {
@@ -255,7 +255,7 @@ int mi_get_media_fd(int fd, const char *bus_info)
return media_fd;
}
-static const flag_def entity_flags_def[] = {
+static constexpr flag_def entity_flags_def[] = {
{ MEDIA_ENT_FL_DEFAULT, "default" },
{ MEDIA_ENT_FL_CONNECTOR, "connector" },
{ 0, nullptr }
@@ -266,7 +266,7 @@ std::string mi_entflags2s(__u32 flags)
return flags2s(flags, entity_flags_def);
}
-static const flag_def interface_types_def[] = {
+static constexpr flag_def interface_types_def[] = {
{ MEDIA_INTF_T_DVB_FE, "DVB Front End" },
{ MEDIA_INTF_T_DVB_DEMUX, "DVB Demuxer" },
{ MEDIA_INTF_T_DVB_DVR, "DVB DVR" },
@@ -299,7 +299,7 @@ std::string mi_ifacetype2s(__u32 type)
return "FAIL: Unknown (" + num2s(type) + ")";
}
-static const flag_def entity_functions_def[] = {
+static constexpr flag_def entity_functions_def[] = {
{ MEDIA_ENT_F_UNKNOWN, "FAIL: Uninitialized Function" },
{ MEDIA_ENT_F_V4L2_SUBDEV_UNKNOWN, "FAIL: Unknown V4L2 Sub-Device" },
{ MEDIA_ENT_T_DEVNODE_UNKNOWN, "FAIL: Unknown Device Node" },
@@ -392,7 +392,7 @@ bool mi_func_requires_intf(__u32 function)
}
}
-static const flag_def pad_flags_def[] = {
+static constexpr flag_def pad_flags_def[] = {
{ MEDIA_PAD_FL_SINK, "Sink" },
{ MEDIA_PAD_FL_SOURCE, "Source" },
{ MEDIA_PAD_FL_MUST_CONNECT, "Must Connect" },
@@ -404,7 +404,7 @@ std::string mi_padflags2s(__u32 flags)
return flags2s(flags, pad_flags_def);
}
-static const flag_def link_flags_def[] = {
+static constexpr flag_def link_flags_def[] = {
{ MEDIA_LNK_FL_ENABLED, "Enabled" },
{ MEDIA_LNK_FL_IMMUTABLE, "Immutable" },
{ MEDIA_LNK_FL_DYNAMIC, "Dynamic" },
diff --git a/utils/common/v4l2-controls.patch b/utils/common/v4l2-controls.patch
new file mode 100644
index 00000000..cf290e0b
--- /dev/null
+++ b/utils/common/v4l2-controls.patch
@@ -0,0 +1,15 @@
+diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h
+index e329974..9db08be 100644
+--- a/include/linux/v4l2-controls.h
++++ b/include/linux/v4l2-controls.h
+@@ -53,6 +53,10 @@
+ #include <linux/const.h>
+ #include <linux/types.h>
+
++#ifndef _BITUL
++#define _BITUL(x) (1U << (x))
++#endif
++
+ /* Control classes */
+ #define V4L2_CTRL_CLASS_USER 0x00980000 /* Old-style 'user' controls */
+ #define V4L2_CTRL_CLASS_CODEC 0x00990000 /* Stateful codec controls */
diff --git a/utils/common/v4l2-info.cpp b/utils/common/v4l2-info.cpp
index cb3cb91f..b8f2c865 100644
--- a/utils/common/v4l2-info.cpp
+++ b/utils/common/v4l2-info.cpp
@@ -201,7 +201,7 @@ std::string buftype2s(int type)
}
}
-static const flag_def bufcap_def[] = {
+static constexpr flag_def bufcap_def[] = {
{ V4L2_BUF_CAP_SUPPORTS_MMAP, "mmap" },
{ V4L2_BUF_CAP_SUPPORTS_USERPTR, "userptr" },
{ V4L2_BUF_CAP_SUPPORTS_DMABUF, "dmabuf" },
@@ -345,7 +345,7 @@ std::string quantization2s(int val)
}
}
-static const flag_def pixflags_def[] = {
+static constexpr flag_def pixflags_def[] = {
{ V4L2_PIX_FMT_FLAG_PREMUL_ALPHA, "premultiplied-alpha" },
{ 0, nullptr }
};
@@ -355,7 +355,7 @@ std::string pixflags2s(unsigned flags)
return flags2s(flags, pixflags_def);
}
-static const flag_def service_def[] = {
+static constexpr flag_def service_def[] = {
{ V4L2_SLICED_TELETEXT_B, "teletext" },
{ V4L2_SLICED_VPS, "vps" },
{ V4L2_SLICED_CAPTION_525, "cc" },
@@ -369,7 +369,7 @@ std::string service2s(unsigned service)
}
#define FMTDESC_DEF(enc_type) \
-static const flag_def fmtdesc_ ## enc_type ## _def[] = { \
+static constexpr flag_def fmtdesc_ ## enc_type ## _def[] = { \
{ V4L2_FMT_FLAG_COMPRESSED, "compressed" }, \
{ V4L2_FMT_FLAG_EMULATED, "emulated" }, \
{ V4L2_FMT_FLAG_CONTINUOUS_BYTESTREAM, "continuous-bytestream" }, \
@@ -393,7 +393,7 @@ std::string fmtdesc2s(unsigned flags, bool is_hsv)
}
#define MBUS_DEF(enc_type) \
-static const flag_def mbus_ ## enc_type ## _def[] = { \
+static constexpr flag_def mbus_ ## enc_type ## _def[] = { \
{ V4L2_SUBDEV_MBUS_CODE_CSC_COLORSPACE, "csc-colorspace" }, \
{ V4L2_SUBDEV_MBUS_CODE_CSC_YCBCR_ENC, "csc-"#enc_type }, \
{ V4L2_SUBDEV_MBUS_CODE_CSC_QUANTIZATION, "csc-quantization" }, \
@@ -411,7 +411,7 @@ std::string mbus2s(unsigned flags, bool is_hsv)
return flags2s(flags, mbus_ycbcr_def);
}
-static const flag_def selection_targets_def[] = {
+static constexpr flag_def selection_targets_def[] = {
{ V4L2_SEL_TGT_CROP_ACTIVE, "crop" },
{ V4L2_SEL_TGT_CROP_DEFAULT, "crop_default" },
{ V4L2_SEL_TGT_CROP_BOUNDS, "crop_bounds" },
@@ -519,7 +519,7 @@ std::string std2s(v4l2_std_id std, const char *sep)
std::string ctrlflags2s(__u32 flags)
{
- static const flag_def def[] = {
+ static constexpr flag_def def[] = {
{ V4L2_CTRL_FLAG_GRABBED, "grabbed" },
{ V4L2_CTRL_FLAG_DISABLED, "disabled" },
{ V4L2_CTRL_FLAG_READ_ONLY, "read-only" },
@@ -536,7 +536,7 @@ std::string ctrlflags2s(__u32 flags)
return flags2s(flags, def);
}
-static const flag_def in_status_def[] = {
+static constexpr flag_def in_status_def[] = {
{ V4L2_IN_ST_NO_POWER, "no power" },
{ V4L2_IN_ST_NO_SIGNAL, "no signal" },
{ V4L2_IN_ST_NO_COLOR, "no color" },
@@ -560,7 +560,7 @@ std::string in_status2s(__u32 status)
return status ? flags2s(status, in_status_def) : "ok";
}
-static const flag_def input_cap_def[] = {
+static constexpr flag_def input_cap_def[] = {
{ V4L2_IN_CAP_DV_TIMINGS, "DV timings" },
{ V4L2_IN_CAP_STD, "SDTV standards" },
{ V4L2_IN_CAP_NATIVE_SIZE, "Native Size" },
@@ -572,7 +572,7 @@ std::string input_cap2s(__u32 capabilities)
return capabilities ? flags2s(capabilities, input_cap_def) : "not defined";
}
-static const flag_def output_cap_def[] = {
+static constexpr flag_def output_cap_def[] = {
{ V4L2_OUT_CAP_DV_TIMINGS, "DV timings" },
{ V4L2_OUT_CAP_STD, "SDTV standards" },
{ V4L2_OUT_CAP_NATIVE_SIZE, "Native Size" },
@@ -630,7 +630,7 @@ std::string fbufflags2s(unsigned fl)
return s;
}
-static const flag_def dv_standards_def[] = {
+static constexpr flag_def dv_standards_def[] = {
{ V4L2_DV_BT_STD_CEA861, "CTA-861" },
{ V4L2_DV_BT_STD_DMT, "DMT" },
{ V4L2_DV_BT_STD_CVT, "CVT" },
@@ -675,7 +675,7 @@ std::string dvflags2s(unsigned vsync, int val)
return s;
}
-static const flag_def dv_caps_def[] = {
+static constexpr flag_def dv_caps_def[] = {
{ V4L2_DV_BT_CAP_INTERLACED, "Interlaced" },
{ V4L2_DV_BT_CAP_PROGRESSIVE, "Progressive" },
{ V4L2_DV_BT_CAP_REDUCED_BLANKING, "Reduced Blanking" },
@@ -688,7 +688,7 @@ std::string dv_caps2s(__u32 flags)
return flags2s(flags, dv_caps_def);
}
-static const flag_def tc_flags_def[] = {
+static constexpr flag_def tc_flags_def[] = {
{ V4L2_TC_FLAG_DROPFRAME, "dropframe" },
{ V4L2_TC_FLAG_COLORFRAME, "colorframe" },
{ V4L2_TC_USERBITS_field, "userbits-field" },
@@ -702,7 +702,7 @@ std::string tc_flags2s(__u32 flags)
return flags2s(flags, tc_flags_def);
}
-static const flag_def buffer_flags_def[] = {
+static constexpr flag_def buffer_flags_def[] = {
{ V4L2_BUF_FLAG_MAPPED, "mapped" },
{ V4L2_BUF_FLAG_QUEUED, "queued" },
{ V4L2_BUF_FLAG_DONE, "done" },
diff --git a/utils/common/v4l2-pix-formats.h b/utils/common/v4l2-pix-formats.h
index 4bf091ac..fe21314a 100644
--- a/utils/common/v4l2-pix-formats.h
+++ b/utils/common/v4l2-pix-formats.h
@@ -65,6 +65,7 @@
case V4L2_PIX_FMT_YUV444: return "16-bit A/XYUV 4-4-4-4";
case V4L2_PIX_FMT_YUV555: return "16-bit A/XYUV 1-5-5-5";
case V4L2_PIX_FMT_YUV565: return "16-bit YUV 5-6-5";
+ case V4L2_PIX_FMT_YUV24: return "24-bit YUV 4:4:4 8-8-8";
case V4L2_PIX_FMT_YUV32: return "32-bit A/XYUV 8-8-8-8";
case V4L2_PIX_FMT_AYUV32: return "32-bit AYUV 8-8-8-8";
case V4L2_PIX_FMT_XYUV32: return "32-bit XYUV 8-8-8-8";
diff --git a/utils/keytable/bpf_load.c b/utils/keytable/bpf_load.c
index ec6eb98e..7c633dac 100644
--- a/utils/keytable/bpf_load.c
+++ b/utils/keytable/bpf_load.c
@@ -58,16 +58,24 @@ struct bpf_file {
int strtabidx;
Elf_Data *symbols;
struct protocol_param *param;
+ char name[128];
};
static int load_and_attach(int lirc_fd, struct bpf_file *bpf_file, struct bpf_insn *prog, int size)
{
- size_t insns_cnt = size / sizeof(struct bpf_insn);
+ struct bpf_load_program_attr load_attr;
int fd, err;
- fd = bpf_load_program(BPF_PROG_TYPE_LIRC_MODE2, prog, insns_cnt,
- bpf_file->license, 0,
- bpf_log_buf, LOG_BUF_SIZE);
+ memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
+
+ 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;
+
+ fd = bpf_load_program_xattr(&load_attr, bpf_log_buf, LOG_BUF_SIZE);
if (fd < 0) {
printf("bpf_load_program() err=%m\n%s", bpf_log_buf);
return -1;
@@ -78,6 +86,7 @@ static int load_and_attach(int lirc_fd, struct bpf_file *bpf_file, struct bpf_in
printf("bpf_prog_attach: err=%m\n");
return -1;
}
+
return 0;
}
@@ -260,7 +269,7 @@ static int parse_relo_and_apply(struct bpf_file *bpf_file, GElf_Shdr *shdr,
}
if (match) {
- insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
+ insn[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
insn[insn_idx].imm = bpf_file->map_data[map_idx].fd;
continue;
}
@@ -427,7 +436,7 @@ static int load_elf_maps_section(struct bpf_file *bpf_file)
}
int load_bpf_file(const char *path, int lirc_fd, struct protocol_param *param,
- struct raw_entry *raw)
+ struct raw_entry *raw)
{
struct bpf_file bpf_file = { .param = param };
int fd, i, ret;
@@ -469,7 +478,8 @@ int load_bpf_file(const char *path, int lirc_fd, struct protocol_param *param,
if (strcmp(shname, "license") == 0) {
bpf_file.processed_sec[i] = true;
memcpy(bpf_file.license, data->d_buf, data->d_size);
- } else if (strcmp(shname, "maps") == 0) {
+ } else if (strcmp(shname, "lirc_mode2/maps") == 0 ||
+ strcmp(shname, "maps") == 0) {
int j;
bpf_file.maps_shidx = i;
@@ -530,6 +540,11 @@ int load_bpf_file(const char *path, int lirc_fd, struct protocol_param *param,
!(shdr_prog.sh_flags & SHF_EXECINSTR))
continue;
+ if (strncmp(shname_prog, "lirc_mode2/", 11))
+ strncpy(bpf_file.name, shname_prog, sizeof(bpf_file.name) - 1);
+ else
+ strncpy(bpf_file.name, shname_prog + 11, sizeof(bpf_file.name) - 1);
+
insns = (struct bpf_insn *) data_prog->d_buf;
bpf_file.processed_sec[i] = true; /* relo section */
diff --git a/utils/keytable/bpf_protocols/grundig.c b/utils/keytable/bpf_protocols/grundig.c
index 4d8cc4b9..cdd551f9 100644
--- a/utils/keytable/bpf_protocols/grundig.c
+++ b/utils/keytable/bpf_protocols/grundig.c
@@ -22,7 +22,7 @@ struct decoder_state {
unsigned int last_space;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -44,9 +44,9 @@ int header_pulse = 900;
int header_space = 2900;
int leader_pulse = 1300;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
-SEC("grundig")
+SEC("lirc_mode2/grundig")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/imon_rsc.c b/utils/keytable/bpf_protocols/imon_rsc.c
index 14c4ec37..e163f217 100644
--- a/utils/keytable/bpf_protocols/imon_rsc.c
+++ b/utils/keytable/bpf_protocols/imon_rsc.c
@@ -19,7 +19,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -34,14 +34,14 @@ struct bpf_map_def SEC("maps") decoder_state_map = {
// actual value (either overridden or taken from the data segment).
int margin = 325;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("imon_rsc")
+SEC("lirc_mode2/imon_rsc")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/manchester.c b/utils/keytable/bpf_protocols/manchester.c
index 94b53fd0..0310f37e 100644
--- a/utils/keytable/bpf_protocols/manchester.c
+++ b/utils/keytable/bpf_protocols/manchester.c
@@ -13,7 +13,7 @@ struct decoder_state {
unsigned long bits;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -41,7 +41,7 @@ int bits = 14;
int scancode_mask = 0;
int rc_protocol = 66;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
@@ -79,7 +79,7 @@ static int emitBit(unsigned int *sample, struct decoder_state *s, int bit, int s
return state;
}
-SEC("manchester")
+SEC("lirc_mode2/manchester")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/pulse_distance.c b/utils/keytable/bpf_protocols/pulse_distance.c
index 9e9ea4ad..f2de8d27 100644
--- a/utils/keytable/bpf_protocols/pulse_distance.c
+++ b/utils/keytable/bpf_protocols/pulse_distance.c
@@ -22,7 +22,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -49,14 +49,14 @@ int reverse = 0;
int header_optional = 0;
int rc_protocol = 64;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("pulse_distance")
+SEC("lirc_mode2/pulse_distance")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/pulse_length.c b/utils/keytable/bpf_protocols/pulse_length.c
index e33f0899..1c9e1948 100644
--- a/utils/keytable/bpf_protocols/pulse_length.c
+++ b/utils/keytable/bpf_protocols/pulse_length.c
@@ -22,7 +22,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -49,14 +49,14 @@ int reverse = 0;
int header_optional = 0;
int rc_protocol = 67;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("pulse_length")
+SEC("lirc_mode2/pulse_length")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/raw.c b/utils/keytable/bpf_protocols/raw.c
index a0ee78b4..5084264d 100644
--- a/utils/keytable/bpf_protocols/raw.c
+++ b/utils/keytable/bpf_protocols/raw.c
@@ -27,7 +27,7 @@ struct decoder_state {
DECLARE_BITMAP(nomatch, MAX_PATTERNS);
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -40,7 +40,7 @@ struct raw_pattern {
};
// ir-keytable will load the raw patterns here
-struct bpf_map_def SEC("maps") raw_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") raw_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct raw_pattern), // this is not used
@@ -60,14 +60,14 @@ int rc_protocol = 68;
int trail_space = 1000;
int max_length = 1;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("raw")
+SEC("lirc_mode2/raw")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/rc_mm.c b/utils/keytable/bpf_protocols/rc_mm.c
index 034d39b9..117f7d62 100644
--- a/utils/keytable/bpf_protocols/rc_mm.c
+++ b/utils/keytable/bpf_protocols/rc_mm.c
@@ -21,7 +21,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -37,7 +37,7 @@ struct bpf_map_def SEC("maps") decoder_state_map = {
//
// This is why they should be accessed through the BPF_PARAM() macro.
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
int margin = 100;
int header_pulse = 417;
@@ -56,7 +56,7 @@ static inline int eq_margin(unsigned d1, unsigned d2)
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("rc_mm")
+SEC("lirc_mode2/rc_mm")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/samsung36.c b/utils/keytable/bpf_protocols/samsung36.c
index 1b09365f..a83137e4 100644
--- a/utils/keytable/bpf_protocols/samsung36.c
+++ b/utils/keytable/bpf_protocols/samsung36.c
@@ -26,7 +26,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -42,14 +42,14 @@ struct bpf_map_def SEC("maps") decoder_state_map = {
int margin = 300;
int rc_protocol = 69;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("samsung36")
+SEC("lirc_mode2/samsung36")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/bpf_protocols/xbox-dvd.c b/utils/keytable/bpf_protocols/xbox-dvd.c
index 18225453..c0b57a7c 100644
--- a/utils/keytable/bpf_protocols/xbox-dvd.c
+++ b/utils/keytable/bpf_protocols/xbox-dvd.c
@@ -21,7 +21,7 @@ struct decoder_state {
unsigned int count;
};
-struct bpf_map_def SEC("maps") decoder_state_map = {
+struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
.type = BPF_MAP_TYPE_ARRAY,
.key_size = sizeof(unsigned int),
.value_size = sizeof(struct decoder_state),
@@ -44,14 +44,14 @@ int trailer_pulse = 550;
int bits = 24;
int rc_protocol = 68;
-#define BPF_PARAM(x) (int)(&(x))
+#define BPF_PARAM(x) (int)(long)(&(x))
static inline int eq_margin(unsigned d1, unsigned d2)
{
return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
}
-SEC("xbox_dvd")
+SEC("lirc_mode2/xbox_dvd")
int bpf_decoder(unsigned int *sample)
{
unsigned int key = 0;
diff --git a/utils/keytable/rc_keymaps/tango.toml b/utils/keytable/rc_keymaps/tango.toml
deleted file mode 100644
index 91bbd8a7..00000000
--- a/utils/keytable/rc_keymaps/tango.toml
+++ /dev/null
@@ -1,56 +0,0 @@
-# Generated with gen_keytables.pl from drivers/media/rc/keymaps/rc-tango.c
-[[protocols]]
-name = "tango"
-protocol = "nec"
-variant = "necx"
-[protocols.scancodes]
-0x4cb4a = "KEY_POWER"
-0x4cb48 = "KEY_FILE"
-0x4cb0f = "KEY_SETUP"
-0x4cb4d = "KEY_SUSPEND"
-0x4cb4e = "KEY_VOLUMEUP"
-0x4cb44 = "KEY_EJECTCD"
-0x4cb13 = "KEY_TV"
-0x4cb51 = "KEY_MUTE"
-0x4cb52 = "KEY_VOLUMEDOWN"
-0x4cb41 = "KEY_NUMERIC_1"
-0x4cb03 = "KEY_NUMERIC_2"
-0x4cb42 = "KEY_NUMERIC_3"
-0x4cb45 = "KEY_NUMERIC_4"
-0x4cb07 = "KEY_NUMERIC_5"
-0x4cb46 = "KEY_NUMERIC_6"
-0x4cb55 = "KEY_NUMERIC_7"
-0x4cb17 = "KEY_NUMERIC_8"
-0x4cb56 = "KEY_NUMERIC_9"
-0x4cb1b = "KEY_NUMERIC_0"
-0x4cb59 = "KEY_DELETE"
-0x4cb5a = "KEY_CAPSLOCK"
-0x4cb47 = "KEY_BACK"
-0x4cb05 = "KEY_SWITCHVIDEOMODE"
-0x4cb06 = "KEY_UP"
-0x4cb43 = "KEY_LEFT"
-0x4cb01 = "KEY_RIGHT"
-0x4cb0a = "KEY_DOWN"
-0x4cb02 = "KEY_ENTER"
-0x4cb4b = "KEY_INFO"
-0x4cb09 = "KEY_HOME"
-0x4cb53 = "KEY_MENU"
-0x4cb12 = "KEY_PREVIOUS"
-0x4cb50 = "KEY_PLAY"
-0x4cb11 = "KEY_NEXT"
-0x4cb4f = "KEY_TITLE"
-0x4cb0e = "KEY_REWIND"
-0x4cb4c = "KEY_STOP"
-0x4cb0d = "KEY_FORWARD"
-0x4cb57 = "KEY_MEDIA_REPEAT"
-0x4cb16 = "KEY_ANGLE"
-0x4cb54 = "KEY_PAUSE"
-0x4cb15 = "KEY_SLOW"
-0x4cb5b = "KEY_TIME"
-0x4cb1a = "KEY_AUDIO"
-0x4cb58 = "KEY_SUBTITLE"
-0x4cb19 = "KEY_ZOOM"
-0x4cb5f = "KEY_RED"
-0x4cb1e = "KEY_GREEN"
-0x4cb5c = "KEY_YELLOW"
-0x4cb1d = "KEY_BLUE"
diff --git a/utils/keytable/rc_maps.cfg b/utils/keytable/rc_maps.cfg
index da7a3ef1..c2357ad8 100644
--- a/utils/keytable/rc_maps.cfg
+++ b/utils/keytable/rc_maps.cfg
@@ -99,6 +99,8 @@
* rc-leadtek-y04g0051 leadtek_y04g0051.toml
* rc-lme2510 lme2510.toml
* rc-manli manli.toml
+* rc-mecool-kii-pro mecool_kii_pro.toml
+* rc-mecool-kiii-pro mecool_kiii_pro.toml
* rc-medion-x10-digitainer medion_x10_digitainer.toml
* rc-medion-x10-or2x medion_x10_or2x.toml
* rc-medion-x10 medion_x10.toml
@@ -131,7 +133,6 @@
* rc-snapstream-firefly snapstream_firefly.toml
* rc-streamzap streamzap.toml
* rc-su3000 su3000.toml
-* rc-tango tango.toml
* rc-tanix-tx3mini tanix_tx3mini.toml
* rc-tanix-tx5max tanix_tx5max.toml
* rc-tbs-nec tbs_nec.toml
diff --git a/utils/libcecutil/cec-gen.pl b/utils/libcecutil/cec-gen.pl
index 224d0ba2..726e74b3 100755
--- a/utils/libcecutil/cec-gen.pl
+++ b/utils/libcecutil/cec-gen.pl
@@ -503,7 +503,7 @@ status:
printf("\t%s\n", cec_status2s(*msg).c_str());
}
-void log_htng_msg(const struct cec_msg *msg)
+static void log_htng_msg(const struct cec_msg *msg)
{
if ((msg->tx_status && !(msg->tx_status & CEC_TX_STATUS_OK)) ||
(msg->rx_status && !(msg->rx_status & (CEC_RX_STATUS_OK | CEC_RX_STATUS_FEATURE_ABORT))))
diff --git a/utils/libcecutil/cec-info.cpp b/utils/libcecutil/cec-info.cpp
index 8b3c55e8..87aa00a2 100644
--- a/utils/libcecutil/cec-info.cpp
+++ b/utils/libcecutil/cec-info.cpp
@@ -244,9 +244,9 @@ const char *cec_la2s(unsigned la)
case 11:
return "Playback Device 3";
case 12:
- return "Reserved 1";
+ return "Backup 1";
case 13:
- return "Reserved 2";
+ return "Backup 2";
case 14:
return "Specific";
case 15:
@@ -448,8 +448,6 @@ void cec_driver_info(const struct cec_caps &caps,
cec_prim_type2s(laddrs.primary_device_type[i]));
printf("\t Logical Address Type : %s\n",
cec_la_type2s(laddrs.log_addr_type[i]));
- if (laddrs.cec_version < CEC_OP_CEC_VERSION_2_0)
- continue;
printf("\t All Device Types : %s\n",
cec_all_dev_types2s(laddrs.all_device_types[i]).c_str());
diff --git a/utils/rds-ctl/rds-ctl.cpp b/utils/rds-ctl/rds-ctl.cpp
index 8161aa45..80a9396a 100644
--- a/utils/rds-ctl/rds-ctl.cpp
+++ b/utils/rds-ctl/rds-ctl.cpp
@@ -382,7 +382,7 @@ static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
char *subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"dir",
"wrap",
"spacing",
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index 90a50361..a450d487 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -574,6 +574,7 @@ static void determine_codec_mask(struct node &node)
break;
case V4L2_PIX_FMT_MPEG2_SLICE:
case V4L2_PIX_FMT_H264_SLICE:
+ case V4L2_PIX_FMT_VP8_FRAME:
case V4L2_PIX_FMT_FWHT_STATELESS:
mask |= STATELESS_DECODER;
break;
@@ -1618,7 +1619,7 @@ int main(int argc, char **argv)
case OptStreamAllColorTest:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"color",
"skip",
"perc",
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index c2835d95..0b05fff7 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -80,6 +80,9 @@ enum poll_mode {
#define VIVID_CID_QUEUE_ERROR (VIVID_CID_VIVID_BASE + 70)
#define VIVID_CID_REQ_VALIDATE_ERROR (VIVID_CID_VIVID_BASE + 72)
+#define VIVID_CID_CUSTOM_BASE (V4L2_CID_USER_BASE | 0xf000)
+#define VIVID_CID_RO_INTEGER (VIVID_CID_CUSTOM_BASE + 12)
+
struct test_query_ext_ctrl: v4l2_query_ext_ctrl {
__u64 menu_mask;
};
@@ -220,14 +223,14 @@ private:
std::exit(EXIT_FAILURE); \
} while (0)
-#define warn_once(fmt, args...) \
- do { \
- static bool show; \
- \
- if (!show) { \
- show = true; \
- warn(fmt, ##args); \
- } \
+#define warn_once(fmt, args...) \
+ do { \
+ static bool show; \
+ \
+ if (!show) { \
+ show = true; \
+ warn(fmt, ##args); \
+ } \
} while (0)
#define warn_on_test(test) \
@@ -236,6 +239,12 @@ private:
warn("%s\n", #test); \
} while (0)
+#define warn_once_on_test(test) \
+ do { \
+ if (test) \
+ warn_once("%s\n", #test); \
+ } while (0)
+
#define warn_or_info(is_info, fmt, args...) \
do { \
if (is_info) \
diff --git a/utils/v4l2-compliance/v4l2-test-buffers.cpp b/utils/v4l2-compliance/v4l2-test-buffers.cpp
index 045f8d54..48718656 100644
--- a/utils/v4l2-compliance/v4l2-test-buffers.cpp
+++ b/utils/v4l2-compliance/v4l2-test-buffers.cpp
@@ -39,7 +39,7 @@ static bool stream_use_hdr;
static unsigned max_bytesused[VIDEO_MAX_PLANES];
static unsigned min_data_offset[VIDEO_MAX_PLANES];
-bool operator<(struct timeval const& n1, struct timeval const& n2)
+static bool operator<(struct timeval const& n1, struct timeval const& n2)
{
return n1.tv_sec < n2.tv_sec ||
(n1.tv_sec == n2.tv_sec && n1.tv_usec < n2.tv_usec);
@@ -845,7 +845,7 @@ static int captureBufs(struct node *node, struct node *node_m2m_cap, const cv4l_
cv4l_queue &m2m_q, unsigned frame_count, int pollmode,
unsigned &capture_count)
{
- static const char *pollmode_str[] = {
+ static constexpr const char *pollmode_str[] = {
"",
" (select)",
" (epoll)",
@@ -1950,12 +1950,26 @@ int testRequests(struct node *node, bool test_streaming)
struct test_query_ext_ctrl valid_qctrl;
v4l2_ext_controls ctrls;
v4l2_ext_control ctrl;
+ v4l2_ext_control vivid_ro_ctrl = {
+ .id = VIVID_CID_RO_INTEGER,
+ };
+ v4l2_ext_controls vivid_ro_ctrls = {};
bool have_controls;
int ret;
+ // Note: trying to initialize vivid_ro_ctrls as was done for
+ // vivid_ro_ctrl fails with gcc 7 with this error:
+ // sorry, unimplemented: non-trivial designated initializers not supported
+ // So just set this struct the old-fashioned way.
+ vivid_ro_ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
+ vivid_ro_ctrls.count = 1;
+ vivid_ro_ctrls.controls = &vivid_ro_ctrl;
+
+ // 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);
+ // Check if the driver has controls that can be used to test requests
memset(&valid_qctrl, 0, sizeof(valid_qctrl));
memset(&ctrls, 0, sizeof(ctrls));
memset(&ctrl, 0, sizeof(ctrl));
@@ -1982,42 +1996,57 @@ int testRequests(struct node *node, bool test_streaming)
0 : ENOTTY;
}
+ // Test if V4L2_CTRL_WHICH_REQUEST_VAL is supported
ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
ret = doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls);
fail_on_test(ret != EINVAL && ret != EBADR && ret != ENOTTY);
have_controls = ret != ENOTTY;
if (media_fd < 0 || ret == EBADR) {
+ // Should not happen if requests are supported
fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS);
return ENOTTY;
}
if (have_controls) {
ctrls.request_fd = 10;
+ // Test that querying controls with an invalid request_fd
+ // returns EINVAL
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EINVAL);
}
ret = doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd);
if (ret == ENOTTY) {
+ // Should not happen if requests are supported
fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS);
return ENOTTY;
}
+ // Check that a request was allocated with a valid fd
fail_on_test(ret);
- fhs.add(req_fd);
fail_on_test(req_fd < 0);
+ fhs.add(req_fd);
if (have_controls) {
ctrls.request_fd = req_fd;
+ // The request is in unqueued state, so this should return EACCES
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES);
}
+ // You cannot queue a request that has no buffer
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, nullptr) != ENOENT);
+ // REINIT must work for an unqueued request
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, nullptr));
+ // Close media_fd
fhs.del(media_fd);
+ // This should have no effect on req_fd
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, nullptr) != ENOENT);
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, nullptr));
+ // Close req_fd
fhs.del(req_fd);
+ // G_EXT_CTRLS must now return EINVAL for req_fd since it no longer exists
if (have_controls)
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EINVAL);
+ // And the media request ioctls now must return EBADF
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_QUEUE, nullptr) != EBADF);
fail_on_test(doioctl_fd(req_fd, MEDIA_REQUEST_IOC_REINIT, nullptr) != EBADF);
+ // Open media_fd and alloc a request again
media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info));
fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &req_fd));
fhs.add(req_fd);
@@ -2026,29 +2055,40 @@ int testRequests(struct node *node, bool test_streaming)
if (have_controls) {
ctrl.value = valid_qctrl.minimum;
ctrls.which = 0;
+ // Set control without requests
fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
ctrl.value = valid_qctrl.maximum;
ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
ctrls.request_fd = req_fd;
+ // Set control for a request
fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
ctrl.value = valid_qctrl.minimum;
ctrls.request_fd = req_fd;
+ // But you cannot get the value of an unqueued request
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES);
ctrls.which = 0;
+ // But you can without a request
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls));
fail_on_test(ctrl.value != valid_qctrl.minimum);
ctrls.request_fd = req_fd;
ctrls.which = V4L2_CTRL_WHICH_REQUEST_VAL;
ctrl.id = 1;
+ // Setting an invalid control in a request must fail
fail_on_test(!doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
+ // And also when trying to read an invalid control of a request
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls) != EACCES);
}
ctrl.id = valid_qctrl.id;
+ // Close req_fd and media_fd and reopen device node
fhs.del(req_fd);
fhs.del(media_fd);
node->reopen();
int type = node->g_type();
+ // For m2m devices g_type() will return the capture type, so
+ // we need to invert it to get the output type.
+ // At the moment only the output type of an m2m device can use
+ // requests.
if (node->is_m2m)
type = v4l_type_invert(type);
if (v4l_type_is_vbi(type)) {
@@ -2059,6 +2099,8 @@ int testRequests(struct node *node, bool test_streaming)
}
if (!(node->valid_buftypes & (1 << type))) {
+ // If the type is not supported, then check that requests
+ // are also not supported.
fail_on_test(node->buf_caps & V4L2_BUF_CAP_SUPPORTS_REQUESTS);
return ENOTTY;
}
@@ -2067,46 +2109,60 @@ int testRequests(struct node *node, bool test_streaming)
buffer_info.clear();
cv4l_queue q(type, V4L2_MEMORY_MMAP);
+ // For m2m devices q is the output queue and m2m_q is the capture queue
cv4l_queue m2m_q(v4l_type_invert(type));
q.init(type, V4L2_MEMORY_MMAP);
- fail_on_test(q.reqbufs(node, 2));
+ fail_on_test(q.reqbufs(node, 15));
unsigned min_bufs = q.g_buffers();
- fail_on_test(q.reqbufs(node, min_bufs + 4));
+ fail_on_test(q.reqbufs(node, min_bufs + 6));
unsigned num_bufs = q.g_buffers();
+ // Create twice as many requests as there are buffers
unsigned num_requests = 2 * num_bufs;
last_seq.init();
media_fd = fhs.add(mi_get_media_fd(node->g_fd(), node->bus_info));
+ // Allocate the requests
for (unsigned i = 0; i < num_requests; i++) {
fail_on_test(doioctl_fd(media_fd, MEDIA_IOC_REQUEST_ALLOC, &buf_req_fds[i]));
fhs.add(buf_req_fds[i]);
fail_on_test(buf_req_fds[i] < 0);
+ // Check that empty requests can't be queued
fail_on_test(!doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, nullptr));
}
+ // close the media fd, should not be needed anymore
fhs.del(media_fd);
buffer buf(q);
fail_on_test(buf.querybuf(node, 0));
+ // Queue a buffer without using requests
ret = buf.qbuf(node);
+ // If this fails, then that can only happen if the queue
+ // requires requests. In that case EBADR is returned.
fail_on_test(ret && ret != EBADR);
fail_on_test(buf.querybuf(node, 1));
+ // Now try to queue the buffer to the request
buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD);
buf.s_request_fd(buf_req_fds[1]);
+ // If requests are required, then this must now work
+ // If requests are optional, then this must now fail since the
+ // queue in is non-request mode.
if (ret == EBADR)
fail_on_test(buf.qbuf(node));
else
fail_on_test(!buf.qbuf(node));
+ // Reopen device node, clearing any pending requests
node->reopen();
q.init(type, V4L2_MEMORY_MMAP);
fail_on_test(q.reqbufs(node, num_bufs));
if (node->is_m2m) {
+ // Setup the capture queue
fail_on_test(m2m_q.reqbufs(node, 2));
fail_on_test(node->streamon(m2m_q.g_type()));
@@ -2115,6 +2171,9 @@ int testRequests(struct node *node, bool test_streaming)
fail_on_test(buf.querybuf(node, 0));
buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD);
buf.s_request_fd(buf_req_fds[0]);
+ // Only the output queue can support requests,
+ // so if the capture queue also supports requests,
+ // then something is wrong.
fail_on_test(!buf.qbuf(node));
fail_on_test(node->streamoff(m2m_q.g_type()));
fail_on_test(m2m_q.reqbufs(node, 0));
@@ -2128,10 +2187,12 @@ int testRequests(struct node *node, bool test_streaming)
buffer buf(q);
fail_on_test(buf.querybuf(node, i));
+ // No request was set, so this should return 0
fail_on_test(buf.g_request_fd());
buf.s_flags(V4L2_BUF_FLAG_REQUEST_FD);
if (i == 0) {
buf.s_request_fd(-1);
+ // Can't queue to an invalid request fd
fail_on_test(!buf.qbuf(node));
buf.s_request_fd(0xdead);
fail_on_test(!buf.qbuf(node));
@@ -2140,25 +2201,34 @@ int testRequests(struct node *node, bool test_streaming)
if (v4l_type_is_video(buf.g_type()))
buf.s_field(V4L2_FIELD_ANY);
if (!(i & 1)) {
+ // VIDIOC_PREPARE_BUF is incompatible with requests
fail_on_test(buf.prepare_buf(node) != EINVAL);
buf.s_flags(0);
+ // Test vivid error injection
if (node->inject_error(VIVID_CID_BUF_PREPARE_ERROR))
fail_on_test(buf.prepare_buf(node) != EINVAL);
fail_on_test(buf.prepare_buf(node));
fail_on_test(buf.querybuf(node, i));
+ // Check that the buffer was prepared
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_PREPARED));
buf.s_flags(buf.g_flags() | V4L2_BUF_FLAG_REQUEST_FD);
buf.s_request_fd(buf_req_fds[i]);
}
+ // Queue the buffer to the request
int err = buf.qbuf(node);
if (!err) {
+ // If requests are not supported, this should fail
fail_on_test(!supports_requests);
+ // You can't queue the same buffer again
fail_on_test(!buf.qbuf(node));
} else {
+ // Can only fail if requests are not supported
fail_on_test(supports_requests);
+ // and should fail with EBADR in that case
fail_on_test(err != EBADR);
}
if (err) {
+ // Requests are not supported, so clean up and return
fail_on_test(node->streamoff(q.g_type()));
fail_on_test(q.reqbufs(node, 0));
if (node->is_m2m) {
@@ -2169,11 +2239,14 @@ int testRequests(struct node *node, bool test_streaming)
node->reopen();
return ENOTTY;
}
+ // Check flags and request fd
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE);
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST));
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD));
fail_on_test(buf.g_request_fd() < 0);
+ // Query the buffer again
fail_on_test(buf.querybuf(node, i));
+ // Check returned flags and request fd
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_DONE);
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST));
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD));
@@ -2182,55 +2255,81 @@ int testRequests(struct node *node, bool test_streaming)
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_PREPARED);
else
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_PREPARED));
+ // Check that you can't queue it again
buf.s_request_fd(buf_req_fds[i]);
fail_on_test(!buf.qbuf(node));
+ // Set a control in the request, except for every third request.
ctrl.value = (i & 1) ? valid_qctrl.maximum : valid_qctrl.minimum;
ctrls.request_fd = buf_req_fds[i];
- fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
+ if (i % 3 < 2)
+ fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
+ // Re-init the unqueued request
fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, nullptr));
+ // Make sure that the buffer is no longer in a request
fail_on_test(buf.querybuf(node, i));
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST);
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD);
+ // Set the control again
ctrls.request_fd = buf_req_fds[i];
- fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
-
+ if (i % 3 < 2)
+ fail_on_test(doioctl(node, VIDIOC_S_EXT_CTRLS, &ctrls));
+
+ // After the re-init the buffer is no longer marked for
+ // a request. If a request has been queued before (hence
+ // the 'if (i)' check), then queuing the buffer without
+ // a request must fail since you can't mix the two streamining
+ // models.
if (i)
fail_on_test(!buf.qbuf(node));
buf.s_flags(buf.g_flags() | V4L2_BUF_FLAG_REQUEST_FD);
buf.s_request_fd(buf_req_fds[i]);
buf.s_field(V4L2_FIELD_ANY);
+ // Queue the buffer for the request
fail_on_test(buf.qbuf(node));
+ // Verify that drivers will replace FIELD_ANY for video output queues
if (v4l_type_is_video(buf.g_type()) && v4l_type_is_output(buf.g_type()))
fail_on_test(buf.g_field() == V4L2_FIELD_ANY);
+ // Query buffer and check that it is marked as being part of a request
fail_on_test(buf.querybuf(node, i));
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST));
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD));
+ // Use vivid to check buffer prepare or request validation error injection
if ((i & 1) && node->inject_error(i > num_bufs / 2 ?
VIVID_CID_BUF_PREPARE_ERROR :
VIVID_CID_REQ_VALIDATE_ERROR))
fail_on_test(doioctl_fd(buf_req_fds[i],
MEDIA_REQUEST_IOC_QUEUE, nullptr) != EINVAL);
+ // Queue the request
ret = doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, nullptr);
if (node->codec_mask & STATELESS_DECODER) {
+ // Stateless decoders might require that certain
+ // controls are present in the request. In that
+ // case they return ENOENT and we just stop testing
+ // since we don't know what those controls are.
fail_on_test(ret != ENOENT);
test_streaming = false;
break;
}
fail_on_test(ret);
fail_on_test(buf.querybuf(node, i));
+ // Check that the buffer is now queued up
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_IN_REQUEST);
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD));
fail_on_test(!(buf.g_flags() & V4L2_BUF_FLAG_QUEUED));
+ // Re-initing or requeuing the request is no longer possible
fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, nullptr) != EBUSY);
fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, nullptr) != EBUSY);
if (i >= min_bufs) {
+ // Close some of the request fds to check that this
+ // is safe to do
close(buf_req_fds[i]);
buf_req_fds[i] = -1;
}
if (i == min_bufs - 1) {
+ // Check vivid STREAMON error injection
if (node->inject_error(VIVID_CID_START_STR_ERROR))
fail_on_test(!node->streamon(q.g_type()));
fail_on_test(node->streamon(q.g_type()));
@@ -2242,45 +2341,97 @@ int testRequests(struct node *node, bool test_streaming)
if (test_streaming) {
unsigned capture_count;
- fail_on_test(captureBufs(node, node, q, m2m_q, num_bufs,
+ // Continue streaming
+ // For m2m devices captureBufs() behaves a bit odd: you pass
+ // in the total number of output buffers that you want to
+ // stream, but since there are already q.g_buffers() output
+ // buffers queued up (see previous loop), the captureBufs()
+ // function will subtract that from frame_count, so it will
+ // only queue frame_count - q.g_buffers() output buffers.
+ // In order to ensure we captured at least
+ // min_bufs buffers we need to add min_bufs to the frame_count.
+ fail_on_test(captureBufs(node, node, q, m2m_q,
+ num_bufs + (node->is_m2m ? min_bufs : 0),
POLL_MODE_SELECT, capture_count));
}
fail_on_test(node->streamoff(q.g_type()));
+ // Note that requests min_bufs...2*min_bufs-1 close their filehandles,
+ // so here we just go through the first half of the requests.
for (unsigned i = 0; test_streaming && i < min_bufs; i++) {
buffer buf(q);
+ // Get the control
ctrls.request_fd = buf_req_fds[i];
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls));
- fail_on_test(ctrl.value != ((i & 1) ? valid_qctrl.maximum :
+ bool is_max = i & 1;
+ // Since the control was not set for every third request,
+ // the value will actually be that of the previous request.
+ if (i % 3 == 2)
+ is_max = !is_max;
+ // Check that the value is as expected
+ fail_on_test(ctrl.value != (is_max ? valid_qctrl.maximum :
valid_qctrl.minimum));
+ if (is_vivid) {
+ // vivid specific: check that the read-only control
+ // of the completed request has the expected value
+ // (sequence number & 0xff).
+ vivid_ro_ctrls.request_fd = buf_req_fds[i];
+ fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &vivid_ro_ctrls));
+ if (node->is_video && !node->can_output)
+ warn_once_on_test(vivid_ro_ctrl.value != (int)i);
+ }
fail_on_test(buf.querybuf(node, i));
+ // Check that all the buffers of the stopped stream are
+ // no longer marked as belonging to a request.
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD);
fail_on_test(buf.g_request_fd());
struct pollfd pfd = {
buf_req_fds[i],
POLLPRI, 0
};
+ // Check that polling the request fd will immediately return,
+ // indicating that the request has completed.
fail_on_test(poll(&pfd, 1, 100) != 1);
+ // Requeuing the request must fail
fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_QUEUE, nullptr) != EBUSY);
+ // But reinit must succeed.
fail_on_test(doioctl_fd(buf_req_fds[i], MEDIA_REQUEST_IOC_REINIT, nullptr));
fail_on_test(buf.querybuf(node, i));
fail_on_test(buf.g_flags() & V4L2_BUF_FLAG_REQUEST_FD);
fail_on_test(buf.g_request_fd());
- fhs.del(buf_req_fds[i]);
ctrls.request_fd = buf_req_fds[i];
+ // Check that getting controls from a reinited request fails
fail_on_test(!doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls));
+ // Close the request fd
+ fhs.del(buf_req_fds[i]);
+ buf_req_fds[i] = -1;
}
+ // Close any remaining open request fds
for (unsigned i = 0; i < num_requests; i++)
if (buf_req_fds[i] >= 0)
fhs.del(buf_req_fds[i]);
+ // Getting the current control value must work
ctrls.which = 0;
fail_on_test(doioctl(node, VIDIOC_G_EXT_CTRLS, &ctrls));
- if (test_streaming)
- fail_on_test(ctrl.value != (((num_bufs - 1) & 1) ? valid_qctrl.maximum :
+ // Check the final control value
+ if (test_streaming) {
+ bool is_max = (num_bufs - 1) & 1;
+ if ((num_bufs - 1) % 3 == 2)
+ is_max = !is_max;
+ fail_on_test(ctrl.value != (is_max ? valid_qctrl.maximum :
valid_qctrl.minimum));
+ if (is_vivid) {
+ // 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)
+ warn_on_test(vivid_ro_ctrl.value != (int)(num_bufs - 1));
+ }
+ }
+ // Cleanup
fail_on_test(q.reqbufs(node, 0));
if (node->is_m2m) {
fail_on_test(node->streamoff(m2m_q.g_type()));
diff --git a/utils/v4l2-compliance/v4l2-test-colors.cpp b/utils/v4l2-compliance/v4l2-test-colors.cpp
index 55a81694..887b2fd4 100644
--- a/utils/v4l2-compliance/v4l2-test-colors.cpp
+++ b/utils/v4l2-compliance/v4l2-test-colors.cpp
@@ -59,22 +59,22 @@ struct color {
double r, g, b, a;
};
-static const double bt601[3][3] = {
+static constexpr double bt601[3][3] = {
{ 1, 0, 1.4020 },
{ 1, -0.3441, -0.7141 },
{ 1, 1.7720, 0 },
};
-static const double rec709[3][3] = {
+static constexpr double rec709[3][3] = {
{ 1, 0, 1.5748 },
{ 1, -0.1873, -0.4681 },
{ 1, 1.8556, 0 },
};
-static const double smpte240m[3][3] = {
+static constexpr double smpte240m[3][3] = {
{ 1, 0, 1.5756 },
{ 1, -0.2253, -0.4767 },
{ 1, 1.8270, 0 },
};
-static const double bt2020[3][3] = {
+static constexpr double bt2020[3][3] = {
{ 1, 0, 1.4746 },
{ 1, -0.1646, -0.5714 },
{ 1, 1.8814, 0 },
@@ -465,7 +465,7 @@ static void getColor(const cv4l_fmt &fmt, __u8 * const planes[3],
}
}
-static const char * const colors[] = {
+static constexpr const char *colors[] = {
"red",
"green",
"blue"
diff --git a/utils/v4l2-compliance/v4l2-test-controls.cpp b/utils/v4l2-compliance/v4l2-test-controls.cpp
index 4be2f61c..07685d72 100644
--- a/utils/v4l2-compliance/v4l2-test-controls.cpp
+++ b/utils/v4l2-compliance/v4l2-test-controls.cpp
@@ -738,7 +738,8 @@ int testExtendedControls(struct node *node)
if (checkExtendedCtrl(ctrl, qctrl))
return fail("s_ext_ctrls returned invalid control contents (%08x)\n", qctrl.id);
}
- if (qctrl.type == V4L2_CTRL_TYPE_STRING)
+
+ if (qctrl.flags & V4L2_CTRL_FLAG_HAS_PAYLOAD)
delete [] ctrl.string;
ctrl.string = nullptr;
}
diff --git a/utils/v4l2-compliance/v4l2-test-formats.cpp b/utils/v4l2-compliance/v4l2-test-formats.cpp
index 60ebf559..3761b1fa 100644
--- a/utils/v4l2-compliance/v4l2-test-formats.cpp
+++ b/utils/v4l2-compliance/v4l2-test-formats.cpp
@@ -27,7 +27,7 @@
#include "compiler.h"
#include "v4l2-compliance.h"
-static const __u32 buftype2cap[] = {
+static constexpr __u32 buftype2cap[] = {
0,
V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_M2M,
V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_M2M,
diff --git a/utils/v4l2-compliance/v4l2-test-input-output.cpp b/utils/v4l2-compliance/v4l2-test-input-output.cpp
index 7088819a..006e05ec 100644
--- a/utils/v4l2-compliance/v4l2-test-input-output.cpp
+++ b/utils/v4l2-compliance/v4l2-test-input-output.cpp
@@ -413,8 +413,8 @@ static int checkInput(struct node *node, const struct v4l2_input &descr, unsigne
if (check_ustring(descr.name, sizeof(descr.name)))
return fail("invalid name\n");
if (descr.type != V4L2_INPUT_TYPE_TUNER &&
- descr.type != V4L2_INPUT_TYPE_CAMERA &&
- descr.type != V4L2_INPUT_TYPE_TOUCH)
+ descr.type != V4L2_INPUT_TYPE_CAMERA &&
+ descr.type != V4L2_INPUT_TYPE_TOUCH)
return fail("invalid type\n");
if (descr.type == V4L2_INPUT_TYPE_CAMERA && descr.tuner)
return fail("invalid tuner\n");
diff --git a/utils/v4l2-ctl/v4l2-ctl-common.cpp b/utils/v4l2-ctl/v4l2-ctl-common.cpp
index 17ad488d..51919fa7 100644
--- a/utils/v4l2-ctl/v4l2-ctl-common.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-common.cpp
@@ -599,6 +599,12 @@ static void print_qctrl(int fd, const v4l2_query_ext_ctrl &qc,
case V4L2_CTRL_TYPE_AREA:
printf("%31s %#8.8x (area) :", s.c_str(), qc.id);
break;
+ case V4L2_CTRL_TYPE_HDR10_CLL_INFO:
+ printf("%31s %#8.8x (hdr10-cll-info):", s.c_str(), qc.id);
+ break;
+ case V4L2_CTRL_TYPE_HDR10_MASTERING_DISPLAY:
+ printf("%31s %#8.8x (hdr10-mastering-display):", s.c_str(), qc.id);
+ break;
case V4L2_CTRL_TYPE_H264_SPS:
printf("%31s %#8.8x (h264-sps):", s.c_str(), qc.id);
break;
@@ -617,6 +623,18 @@ static void print_qctrl(int fd, const v4l2_query_ext_ctrl &qc,
case V4L2_CTRL_TYPE_H264_PRED_WEIGHTS:
printf("%31s %#8.8x (h264-pred-weights):", s.c_str(), qc.id);
break;
+ case V4L2_CTRL_TYPE_VP8_FRAME:
+ printf("%31s %#8.8x (vp8-frame):", s.c_str(), qc.id);
+ break;
+ case V4L2_CTRL_TYPE_MPEG2_QUANTISATION:
+ printf("%31s %#8.8x (mpeg2-quantisation):", s.c_str(), qc.id);
+ break;
+ case V4L2_CTRL_TYPE_MPEG2_SEQUENCE:
+ printf("%31s %#8.8x (mpeg2-sequence):", s.c_str(), qc.id);
+ break;
+ case V4L2_CTRL_TYPE_MPEG2_PICTURE:
+ printf("%31s %#8.8x (mpeg2-picture):", s.c_str(), qc.id);
+ break;
case V4L2_CTRL_TYPE_FWHT_PARAMS:
printf("%31s %#8.8x (fwht-params):", s.c_str(), qc.id);
break;
diff --git a/utils/v4l2-ctl/v4l2-ctl-edid.cpp b/utils/v4l2-ctl/v4l2-ctl-edid.cpp
index 80ea151f..e803e41f 100644
--- a/utils/v4l2-ctl/v4l2-ctl-edid.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-edid.cpp
@@ -3,6 +3,7 @@
#include <linux/v4l2-subdev.h>
+#include "compiler.h"
#include "v4l2-ctl.h"
/*
@@ -121,6 +122,7 @@ void edid_usage()
" --set-edid pad=<pad>[,type=<type>|file=<file>][,format=<fmt>][modifiers]\n"
" <pad> is the input index for which to set the EDID.\n"
" <type> can be one of:\n"
+ " list: list all EDID types\n"
" vga: Base Block supporting VGA interface (1920x1200p60)\n"
" dvid: Base Block supporting DVI-D interface (1920x1200p60)\n"
" hdmi: CTA-861 with HDMI support up to 1080p60\n"
@@ -658,19 +660,19 @@ static void print_edid_mods(const struct v4l2_edid *e)
}
loc = get_edid_vid_cap_location(e->edid, e->blocks * 128);
if (loc >= 0) {
- static const char *pt_scan[] = {
+ static constexpr const char *pt_scan[] = {
"No Data",
"Always Overscanned",
"Always Underscanned",
"Supports both over- and underscan"
};
- static const char *it_scan[] = {
+ static constexpr const char *it_scan[] = {
"IT Formats not supported",
"Always Overscanned",
"Always Underscanned",
"Supports both over- and underscan"
};
- static const char *ce_scan[] = {
+ static constexpr const char *ce_scan[] = {
"CE Formats not supported",
"Always Overscanned",
"Always Underscanned",
@@ -1087,7 +1089,7 @@ void edid_cmd(int ch, char *optarg)
break;
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"type",
"edid",
@@ -1191,6 +1193,20 @@ void edid_cmd(int ch, char *optarg)
} else if (!strcmp(value, "displayport")) {
sedid.edid = displayport_edid;
sedid.blocks = sizeof(displayport_edid) / 128;
+ } else if (!strcmp(value, "list")) {
+ printf("EDID types:\n");
+ printf("vga: Base Block supporting VGA interface (1920x1200p60)\n");
+ printf("dvid: Base Block supporting DVI-D interface (1920x1200p60)\n");
+ printf("hdmi: CTA-861 with HDMI support up to 1080p60\n");
+ printf("hdmi-4k-170mhz: CTA-861 with HDMI support up to 1080p60 or 4kp30 4:2:0\n");
+ printf("hdmi-4k-300mhz: CTA-861 with HDMI support up to 4kp30\n");
+ printf("hdmi-4k-600mhz: CTA-861 with HDMI support up to 4kp60\n");
+ printf("hdmi-4k-600mhz-with-displayid: Block Map Extension Block, CTA-861 with\n");
+ printf("\tHDMI support up to 4kp60, DisplayID Extension Block\n");
+ printf("displayport: DisplayID supporting a DisplayPort interface (1920x1200)\n");
+ printf("displayport-with-cta861: DisplayID supporting a DisplayPort interface,\n");
+ printf("\tCTA-861 Extension Block (1080p60)\n");
+ std::exit(EXIT_FAILURE);
} else {
edid_usage();
std::exit(EXIT_FAILURE);
@@ -1285,7 +1301,7 @@ void edid_cmd(int ch, char *optarg)
sedid.pad = strtoul(value, nullptr, 0);
break;
}
- // fall through
+ fallthrough;
default:
edid_usage();
std::exit(EXIT_FAILURE);
@@ -1307,7 +1323,7 @@ void edid_cmd(int ch, char *optarg)
break;
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"startblock",
"blocks",
diff --git a/utils/v4l2-ctl/v4l2-ctl-meta.cpp b/utils/v4l2-ctl/v4l2-ctl-meta.cpp
index 33c6db46..1e4117cb 100644
--- a/utils/v4l2-ctl/v4l2-ctl-meta.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-meta.cpp
@@ -102,7 +102,7 @@ void meta_set(cv4l_fd &_fd)
options[OptTryMetaOutFormat], V4L2_BUF_TYPE_META_OUTPUT);
}
-void __meta_get(cv4l_fd &fd, __u32 type)
+static void __meta_get(cv4l_fd &fd, __u32 type)
{
vfmt.type = type;
if (doioctl(fd.g_fd(), VIDIOC_G_FMT, &vfmt) == 0)
diff --git a/utils/v4l2-ctl/v4l2-ctl-misc.cpp b/utils/v4l2-ctl/v4l2-ctl-misc.cpp
index 1853608a..dc587aeb 100644
--- a/utils/v4l2-ctl/v4l2-ctl-misc.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-misc.cpp
@@ -178,7 +178,7 @@ void misc_cmd(int ch, char *optarg)
case OptSetJpegComp:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"app0", "app1", "app2", "app3",
"app4", "app5", "app6", "app7",
"app8", "app9", "appa", "appb",
@@ -235,7 +235,7 @@ void misc_cmd(int ch, char *optarg)
case OptTryEncoderCmd:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"cmd",
"flags",
nullptr
@@ -258,7 +258,7 @@ void misc_cmd(int ch, char *optarg)
case OptTryDecoderCmd:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"cmd",
"flags",
"stop_pts",
diff --git a/utils/v4l2-ctl/v4l2-ctl-overlay.cpp b/utils/v4l2-ctl/v4l2-ctl-overlay.cpp
index 639a4175..5493222d 100644
--- a/utils/v4l2-ctl/v4l2-ctl-overlay.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-overlay.cpp
@@ -209,7 +209,7 @@ void overlay_cmd(int ch, char *optarg)
case OptTryOverlayFormat:
subs = optarg;
while (subs && *subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"chromakey",
"global_alpha",
"left",
@@ -260,7 +260,7 @@ void overlay_cmd(int ch, char *optarg)
subs = optarg;
memset(&r, 0, sizeof(r));
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"left",
"top",
"width",
@@ -303,12 +303,12 @@ void overlay_cmd(int ch, char *optarg)
case OptSetFBuf:
subs = optarg;
while (*subs != '\0') {
- const unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
+ constexpr unsigned chroma_flags = V4L2_FBUF_FLAG_CHROMAKEY |
V4L2_FBUF_FLAG_SRC_CHROMAKEY;
- const unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
+ constexpr unsigned alpha_flags = V4L2_FBUF_FLAG_GLOBAL_ALPHA |
V4L2_FBUF_FLAG_LOCAL_ALPHA |
V4L2_FBUF_FLAG_LOCAL_INV_ALPHA;
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"chromakey",
"src_chromakey",
"global_alpha",
diff --git a/utils/v4l2-ctl/v4l2-ctl-selection.cpp b/utils/v4l2-ctl/v4l2-ctl-selection.cpp
index 4633776f..68a39c14 100644
--- a/utils/v4l2-ctl/v4l2-ctl-selection.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-selection.cpp
@@ -93,7 +93,7 @@ static void parse_crop(char *optarg, unsigned int &set_crop, v4l2_rect &vcrop)
char *subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"left",
"top",
"width",
@@ -153,7 +153,7 @@ static int parse_selection(char *optarg, unsigned int &set_sel, v4l2_selection &
char *subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"target",
"flags",
"left",
diff --git a/utils/v4l2-ctl/v4l2-ctl-stds.cpp b/utils/v4l2-ctl/v4l2-ctl-stds.cpp
index 82571f93..08154df4 100644
--- a/utils/v4l2-ctl/v4l2-ctl-stds.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-stds.cpp
@@ -160,7 +160,7 @@ static int parse_timing_subopt(char **subopt_str, int *value)
int opt;
char *opt_str;
- static const char * const subopt_list[] = {
+ static constexpr const char *subopt_list[] = {
"width",
"height",
"interlaced",
diff --git a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
index b8dc30fb..62424e4c 100644
--- a/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-streaming.cpp
@@ -503,7 +503,7 @@ static void print_buffer(FILE *f, struct v4l2_buffer &buf)
static_cast<__u64>(buf.timestamp.tv_sec), static_cast<__u64>(buf.timestamp.tv_usec),
timestamp_type2s(buf.flags).c_str(), timestamp_src2s(buf.flags).c_str());
if (buf.flags & V4L2_BUF_FLAG_TIMECODE) {
- static const int fps_types[] = { 0, 24, 25, 30, 50, 60 };
+ static constexpr int fps_types[] = { 0, 24, 25, 30, 50, 60 };
int fps = buf.timecode.type;
if (fps > 5)
diff --git a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
index ecfd3244..33cc1342 100644
--- a/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-subdev.cpp
@@ -97,7 +97,7 @@ void subdev_cmd(int ch, char *optarg)
case OptListSubDevFrameSizes:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"code",
nullptr
@@ -119,7 +119,7 @@ void subdev_cmd(int ch, char *optarg)
case OptListSubDevFrameIntervals:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"code",
"width",
@@ -153,7 +153,7 @@ void subdev_cmd(int ch, char *optarg)
case OptGetSubDevSelection:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"target",
nullptr
@@ -187,7 +187,7 @@ void subdev_cmd(int ch, char *optarg)
ffmt.field = V4L2_FIELD_ANY;
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"width",
"height",
"code",
@@ -256,7 +256,7 @@ void subdev_cmd(int ch, char *optarg)
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"target",
"flags",
"left",
@@ -309,7 +309,7 @@ void subdev_cmd(int ch, char *optarg)
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"pad",
"fps",
nullptr
diff --git a/utils/v4l2-ctl/v4l2-ctl-tuner.cpp b/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
index 7d6f6f3e..177aa8e6 100644
--- a/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-tuner.cpp
@@ -157,7 +157,7 @@ static void parse_freq_seek(char *optarg, struct v4l2_hw_freq_seek &seek)
char *subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"dir",
"wrap",
"spacing",
diff --git a/utils/v4l2-ctl/v4l2-ctl-vbi.cpp b/utils/v4l2-ctl/v4l2-ctl-vbi.cpp
index 33191aa6..dc7b4282 100644
--- a/utils/v4l2-ctl/v4l2-ctl-vbi.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-vbi.cpp
@@ -123,7 +123,7 @@ void vbi_cmd(int ch, char *optarg)
subs = optarg;
memset(&raw->fmt.vbi, 0, sizeof(raw->fmt.vbi));
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"samplingrate",
"offset",
"samplesperline",
diff --git a/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp b/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp
index c66c248a..cca2d4b4 100644
--- a/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl-vidcap.cpp
@@ -133,7 +133,7 @@ void vidcap_cmd(int ch, char *optarg)
case OptListFrameIntervals:
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"width",
"height",
"pixelformat",
@@ -395,7 +395,7 @@ void vidcap_list(cv4l_fd &fd)
void print_touch_buffer(FILE *f, cv4l_buffer &buf, cv4l_fmt &fmt, cv4l_queue &q)
{
- static const char img[16] = {
+ static constexpr char img[16] = {
'.', ',', ':', ';', '!', '|', 'i', 'c',
'n', 'o', 'm', 'I', 'C', 'N', 'O', 'M',
};
diff --git a/utils/v4l2-ctl/v4l2-ctl.cpp b/utils/v4l2-ctl/v4l2-ctl.cpp
index d91577e1..95b8a2e7 100644
--- a/utils/v4l2-ctl/v4l2-ctl.cpp
+++ b/utils/v4l2-ctl/v4l2-ctl.cpp
@@ -523,7 +523,7 @@ void printfmt(int fd, const struct v4l2_format &vfmt)
static std::string frmtype2s(unsigned type)
{
- static const char *types[] = {
+ static constexpr const char *types[] = {
"Unknown",
"Discrete",
"Continuous",
@@ -764,7 +764,7 @@ int parse_fmt(char *optarg, __u32 &width, __u32 &height, __u32 &pixelformat,
flags = 0;
subs = optarg;
while (*subs != '\0') {
- static const char *const subopts[] = {
+ static constexpr const char *subopts[] = {
"width",
"height",
"pixelformat",
@@ -1033,7 +1033,7 @@ static int open_media_bus_info(const std::string &bus_info)
}
static const char *make_devname(const char *device, const char *devname,
- const std::string &media_bus_info)
+ const std::string &media_bus_info)
{
if (device[0] >= '0' && device[0] <= '9' && strlen(device) <= 3) {
static char newdev[32];
diff --git a/utils/v4l2-dbg/v4l2-dbg.cpp b/utils/v4l2-dbg/v4l2-dbg.cpp
index 47b1263c..d711c9e4 100644
--- a/utils/v4l2-dbg/v4l2-dbg.cpp
+++ b/utils/v4l2-dbg/v4l2-dbg.cpp
@@ -494,7 +494,7 @@ int main(int argc, char **argv)
break;
while (*subs != '\0') {
- static const char * const subopts[] = {
+ static constexpr const char *subopts[] = {
"min",
"max",
nullptr

Privacy Policy