aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
Diffstat (limited to 'utils')
-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
54 files changed, 2980 insertions, 1228 deletions
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