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.cpp10
-rw-r--r--utils/cec-compliance/cec-compliance.h7
-rw-r--r--utils/cec-compliance/cec-test-adapter.cpp2
-rw-r--r--utils/cec-compliance/cec-test-audio.cpp46
-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.cpp645
-rw-r--r--utils/cec-ctl/cec-ctl.cpp2
-rw-r--r--utils/cec-ctl/cec-pin.cpp83
-rw-r--r--utils/cec-follower/cec-follower.cpp61
-rw-r--r--utils/cec-follower/cec-follower.h54
-rw-r--r--utils/cec-follower/cec-processing.cpp109
-rw-r--r--utils/cec-follower/cec-tuner.cpp466
-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/libcecutil/cec-info.cpp4
25 files changed, 2033 insertions, 765 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 322bc772..72199762 100644
--- a/utils/cec-compliance/cec-compliance.cpp
+++ b/utils/cec-compliance/cec-compliance.cpp
@@ -741,7 +741,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",
@@ -1236,6 +1236,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)));
@@ -1264,9 +1265,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 818181ab..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 {
@@ -455,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 df7feb21..81eb4013 100644
--- a/utils/cec-compliance/cec-test-adapter.cpp
+++ b/utils/cec-compliance/cec-test-adapter.cpp
@@ -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 5937a0be..611f600f 100644
--- a/utils/cec-compliance/cec-test-audio.cpp
+++ b/utils/cec-compliance/cec-test-audio.cpp
@@ -213,7 +213,7 @@ static void sad_decode(struct short_audio_desc *sad, __u32 descriptor)
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);
@@ -276,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. */
@@ -334,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
@@ -377,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);
@@ -404,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);
@@ -452,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);
@@ -526,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;
@@ -550,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
@@ -572,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;
@@ -627,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];
@@ -648,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);
@@ -666,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);
@@ -677,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.
@@ -701,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);
@@ -712,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
@@ -741,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
@@ -769,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. */
@@ -795,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
@@ -914,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
@@ -941,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);
@@ -1059,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);
@@ -1086,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);
@@ -1106,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 dac188ba..fae6c304 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -13,6 +13,8 @@
#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;
@@ -21,7 +23,7 @@ struct remote_test {
static int deck_status_get(struct node *node, unsigned me, unsigned la, __u8 &deck_status)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
deck_status = 0;
cec_msg_init(&msg, me, la);
@@ -35,7 +37,7 @@ static int deck_status_get(struct node *node, unsigned me, unsigned la, __u8 &de
static int test_play_mode(struct node *node, unsigned me, unsigned la, __u8 play_mode, __u8 expected)
{
- struct cec_msg msg = {};
+ struct cec_msg msg;
__u8 deck_status;
cec_msg_init(&msg, me, la);
@@ -48,11 +50,12 @@ static int test_play_mode(struct node *node, unsigned me, unsigned la, __u8 play
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));
@@ -74,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);
@@ -89,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);
@@ -110,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);
@@ -139,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");
@@ -154,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);
@@ -215,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
@@ -248,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) */
@@ -269,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);
@@ -295,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");
@@ -314,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. */
@@ -347,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;
@@ -388,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;
@@ -423,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. */
@@ -448,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.");
@@ -481,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);
@@ -497,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. */
@@ -511,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
@@ -555,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);
@@ -575,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);
@@ -604,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);
@@ -631,7 +634,7 @@ static const vec_remote_subtests dev_menu_ctl_subtests{
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);
@@ -677,7 +680,7 @@ static int deck_ctl_give_status(struct node *node, unsigned me, unsigned la, boo
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_give_deck_status(&msg, true, 0); /* Invalid Operand */
@@ -698,7 +701,7 @@ static int deck_ctl_give_status_invalid(struct node *node, unsigned me, unsigned
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);
@@ -739,7 +742,7 @@ static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool i
}
fail_on_test(cec_msg_status_is_abort(&msg));
/* Wait for Deck to finish Skip Forward. */
- for (unsigned i = 0; deck_status == CEC_OP_DECK_INFO_SKIP_FWD && i < long_timeout; i++) {
+ 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));
}
@@ -751,7 +754,7 @@ static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool i
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 (unsigned i = 0; deck_status == CEC_OP_DECK_INFO_SKIP_REV && i < long_timeout; i++) {
+ 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));
}
@@ -769,7 +772,7 @@ static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool i
static int deck_ctl_deck_ctl_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, 0); /* Invalid Deck Control operand */
@@ -790,7 +793,7 @@ static int deck_ctl_deck_ctl_invalid(struct node *node, unsigned me, unsigned la
static int deck_ctl_play(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);
@@ -840,7 +843,7 @@ static int deck_ctl_play(struct node *node, unsigned me, unsigned la, bool inter
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_play(&msg, 0); /* Invalid Operand */
@@ -898,577 +901,6 @@ static const vec_remote_subtests deck_ctl_subtests{
},
};
-/* 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;
-}
-
-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)
-{
- /*
- 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 = {};
-
- 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;
-
- 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 = {};
-
- rec_src.type = CEC_OP_RECORD_SRC_OWN;
- cec_msg_init(&msg, me, la);
- cec_msg_record_on(&msg, true, &rec_src);
- 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;
-}
-
-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, false);
- 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 = {};
-
- cec_msg_init(&msg, me, la);
- cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_DIG_SERVICE);
- 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 = {};
-
- 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;
-
- return 0;
-}
-
-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 int timer_prog_set_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
-{
- /* 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;
- }
- 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)
-{
- /* 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;
- }
- 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)
-{
- /* 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;
- }
- 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)
-{
- /* 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;
- }
- 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 const char *hec_func_state2s(__u8 hfs)
{
switch (hfs) {
@@ -1533,7 +965,7 @@ static int cdc_hec_discover(struct node *node, unsigned me, unsigned la, bool pr
{
/* 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;
@@ -1737,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) {
diff --git a/utils/cec-ctl/cec-ctl.cpp b/utils/cec-ctl/cec-ctl.cpp
index 44754787..4bed81a9 100644
--- a/utils/cec-ctl/cec-ctl.cpp
+++ b/utils/cec-ctl/cec-ctl.cpp
@@ -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))
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 ff47d698..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 },
@@ -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])
@@ -317,6 +374,10 @@ void state_init(struct node &node)
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;
}
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 68ef222a..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;
@@ -53,7 +57,11 @@ struct state {
__u8 deck_report_changes_to;
__u8 deck_state;
__u64 deck_skip_start;
- unsigned toggle_power_status;
+ 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;
};
@@ -62,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];
@@ -79,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 {
@@ -222,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 876e0bc0..661cbf42 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -146,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;
@@ -157,14 +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;
}
@@ -262,7 +274,7 @@ static void update_deck_state(struct node *node, unsigned me, __u8 deck_state_ne
node->state.deck_state = deck_state_new;
if (node->state.deck_report_changes) {
- struct cec_msg msg = {};
+ struct cec_msg msg;
cec_msg_init(&msg, me, node->state.deck_report_changes_to);
cec_msg_deck_status(&msg, node->state.deck_state);
@@ -271,7 +283,7 @@ static void update_deck_state(struct node *node, unsigned me, __u8 deck_state_ne
}
}
-static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
+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);
@@ -377,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 */
@@ -396,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;
}
@@ -561,6 +575,7 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
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:
@@ -618,6 +633,7 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
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;
@@ -657,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:
@@ -670,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 */
@@ -989,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;
@@ -1007,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);
@@ -1086,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);
@@ -1116,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);
@@ -1147,6 +1235,9 @@ void testProcessing(struct node *node, bool wallclock)
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/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/libcecutil/cec-info.cpp b/utils/libcecutil/cec-info.cpp
index 3c768261..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:

Privacy Policy