aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Fjeldtvedt <jaffe1@gmail.com>2016-08-11 10:30:15 +0200
committerHans Verkuil <hans.verkuil@cisco.com>2016-08-12 13:25:09 +0200
commit25b9f9bf8e4f0a90f01499d3c5222febbd1b9969 (patch)
tree0b469775f9be64ecbee617ccb482774e765e2082
parent55daf8a417744269b08ff601176779f533655670 (diff)
add test for CDC discovery
This adds a test for basic CDC discovery. The CDC_HEC_Discover message is broadcasted, and the bus is then monitored for incoming state reports. The follower responds to Discover messages and reports back that it does not support HEC. Functions are also added for getting string descriptions of various HEC state parameters. Signed-off-by: Johan Fjeldtvedt <jaffe1@gmail.com> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
-rw-r--r--utils/cec-compliance/cec-compliance.cpp60
-rw-r--r--utils/cec-compliance/cec-compliance.h13
-rw-r--r--utils/cec-compliance/cec-test.cpp99
-rw-r--r--utils/cec-follower/cec-follower.h1
-rw-r--r--utils/cec-follower/cec-processing.cpp20
5 files changed, 193 insertions, 0 deletions
diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp
index 9de69795..c893a30b 100644
--- a/utils/cec-compliance/cec-compliance.cpp
+++ b/utils/cec-compliance/cec-compliance.cpp
@@ -671,6 +671,66 @@ const char *dig_bcast_system2s(__u8 bcast_system)
}
}
+const char *hec_func_state2s(__u8 hfs)
+{
+ switch (hfs) {
+ case CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED:
+ return "HEC Not Supported";
+ case CEC_OP_HEC_FUNC_STATE_INACTIVE:
+ return "HEC Inactive";
+ case CEC_OP_HEC_FUNC_STATE_ACTIVE:
+ return "HEC Active";
+ case CEC_OP_HEC_FUNC_STATE_ACTIVATION_FIELD:
+ return "HEC Activation Field";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *host_func_state2s(__u8 hfs)
+{
+ switch (hfs) {
+ case CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED:
+ return "Host Not Supported";
+ case CEC_OP_HOST_FUNC_STATE_INACTIVE:
+ return "Host Inactive";
+ case CEC_OP_HOST_FUNC_STATE_ACTIVE:
+ return "Host Active";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *enc_func_state2s(__u8 efs)
+{
+ switch (efs) {
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED:
+ return "Ext Con Not Supported";
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_INACTIVE:
+ return "Ext Con Inactive";
+ case CEC_OP_ENC_FUNC_STATE_EXT_CON_ACTIVE:
+ return "Ext Con Active";
+ default:
+ return "Unknown";
+ }
+}
+
+const char *cdc_errcode2s(__u8 cdc_errcode)
+{
+ switch (cdc_errcode) {
+ case CEC_OP_CDC_ERROR_CODE_NONE:
+ return "No error";
+ case CEC_OP_CDC_ERROR_CODE_CAP_UNSUPPORTED:
+ return "Initiator does not have requested capability";
+ case CEC_OP_CDC_ERROR_CODE_WRONG_STATE:
+ return "Initiator is in wrong state";
+ case CEC_OP_CDC_ERROR_CODE_OTHER:
+ return "Other error";
+ default:
+ return "Unknown";
+ }
+}
+
int cec_named_ioctl(struct node *node, const char *name,
unsigned long int request, void *parm)
{
diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h
index 7a848393..2403c3c7 100644
--- a/utils/cec-compliance/cec-compliance.h
+++ b/utils/cec-compliance/cec-compliance.h
@@ -151,6 +151,7 @@ struct remote {
__u8 bcast_sys;
__u8 dig_bcast_sys;
bool has_rec_tv;
+ bool has_cdc;
};
struct node {
@@ -376,6 +377,14 @@ static inline bool transmit(struct node *node, struct cec_msg *msg)
return transmit_timeout(node, msg, 0);
}
+static inline unsigned get_ts_ms()
+{
+ struct timespec timespec;
+
+ clock_gettime(CLOCK_MONOTONIC, &timespec);
+ return timespec.tv_sec * 1000ull + timespec.tv_nsec / 1000000;
+}
+
const char *ok(int res);
const char *la2s(unsigned la);
const char *la_type2s(unsigned type);
@@ -390,6 +399,10 @@ std::string short_audio_desc2s(const struct short_audio_desc &sad);
void sad_decode(struct short_audio_desc *sad, __u32 descriptor);
const char *bcast_system2s(__u8 bcast_system);
const char *dig_bcast_system2s(__u8 bcast_system);
+const char *hec_func_state2s(__u8 hfs);
+const char *host_func_state2s(__u8 hfs);
+const char *enc_func_state2s(__u8 efs);
+const char *cdc_errcode2s(__u8 cdc_errcode);
int check_0(const void *p, int len);
// CEC adapter tests
diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
index a6058882..7d203368 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -27,6 +27,7 @@
#include <errno.h>
#include <sys/ioctl.h>
#include <config.h>
+#include <sstream>
#include "cec-compliance.h"
@@ -1104,6 +1105,101 @@ static struct remote_subtest timer_prog_subtests[] = {
{ "Timer Cleared Status", CEC_LOG_ADDR_MASK_RECORD, timer_prog_timer_clear_status },
};
+static int cdc_hec_discover(struct node *node, unsigned me, unsigned la, bool print)
+{
+ /* TODO: For future use cases, it might be necessary to store the results
+ from the HEC discovery to know which HECs are possible to form, etc. */
+ struct cec_msg msg = {};
+ __u32 mode = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER;
+ bool has_cdc = false;
+
+ doioctl(node, CEC_S_MODE, &mode);
+ cec_msg_init(&msg, me, la);
+ cec_msg_cdc_hec_discover(&msg);
+ fail_on_test(!transmit(node, &msg));
+
+ /* The spec describes that we shall wait for messages
+ up to 1 second, and extend the deadline for every received
+ message. The maximum time to wait for incoming state reports
+ is 5 seconds. */
+ unsigned ts_start = get_ts_ms();
+ while (get_ts_ms() - ts_start < 5000) {
+ __u8 from;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.timeout = 1000;
+ if (doioctl(node, CEC_RECEIVE, &msg))
+ break;
+ from = cec_msg_initiator(&msg);
+ if (msg.msg[1] == CEC_MSG_FEATURE_ABORT) {
+ if (from == la)
+ return fail("Device replied Feature Abort to broadcast message\n");
+ else
+ warn("Device %d replied Feature Abort to broadcast message\n", cec_msg_initiator(&msg));
+ }
+ if (msg.msg[1] != CEC_MSG_CDC_MESSAGE)
+ continue;
+ if (msg.msg[4] != CEC_MSG_CDC_HEC_REPORT_STATE)
+ continue;
+
+ __u16 phys_addr, target_phys_addr, hec_field;
+ __u8 hec_func_state, host_func_state, enc_func_state, cdc_errcode, has_field;
+
+ cec_ops_cdc_hec_report_state(&msg, &phys_addr, &target_phys_addr,
+ &hec_func_state, &host_func_state,
+ &enc_func_state, &cdc_errcode,
+ &has_field, &hec_field);
+
+ if (target_phys_addr != node->phys_addr)
+ continue;
+ if (phys_addr == node->remote[la].phys_addr)
+ has_cdc = true;
+ if (!print)
+ continue;
+
+ from = cec_msg_initiator(&msg);
+ info("Received CDC HEC State report from device %d (%s):\n", from, la2s(from));
+ info("Physical address : %x.%x.%x.%x\n",
+ cec_phys_addr_exp(phys_addr));
+ info("Target physical address : %x.%x.%x.%x\n",
+ cec_phys_addr_exp(target_phys_addr));
+ info("HEC Functionality State : %s\n", hec_func_state2s(hec_func_state));
+ info("Host Functionality State : %s\n", host_func_state2s(host_func_state));
+ info("ENC Functionality State : %s\n", enc_func_state2s(enc_func_state));
+ info("CDC Error Code : %s\n", cdc_errcode2s(cdc_errcode));
+
+ if (has_field) {
+ std::ostringstream oss;
+
+ /* Bit 14 indicates whether or not the device's HDMI
+ output has HEC support/is active. */
+ if (!hec_field)
+ oss << "None";
+ else {
+ if (hec_field & (1 << 14))
+ oss << "out, ";
+ for (int i = 13; i >= 0; i--) {
+ if (hec_field & (1 << i))
+ oss << "in" << (14 - i) << ", ";
+ }
+ oss << "\b\b ";
+ }
+ info("HEC Suppport Field : %s\n", oss.str().c_str());
+ }
+ }
+
+ mode = CEC_MODE_INITIATOR;
+ doioctl(node, CEC_S_MODE, &mode);
+
+ if (has_cdc)
+ return 0;
+ return NOTSUPPORTED;
+}
+
+static struct remote_subtest cdc_subtests[] = {
+ { "CDC_HEC_Discover", CEC_LOG_ADDR_MASK_ALL, cdc_hec_discover },
+};
+
/* Post-test checks */
@@ -1165,6 +1261,9 @@ static struct remote_test tests[] = {
test_case("Timer Progrmaming feature",
TAG_TIMER_PROGRAMMING,
timer_prog_subtests),
+ test_case("Capability Discovery and Control feature",
+ TAG_CAP_DISCOVERY_CONTROL,
+ cdc_subtests),
test_case_ext("Dynamic Auto Lipsync feature",
TAG_DYNAMIC_AUTO_LIPSYNC,
dal_subtests),
diff --git a/utils/cec-follower/cec-follower.h b/utils/cec-follower/cec-follower.h
index 178fd093..7ca05a45 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -80,6 +80,7 @@ struct la_info {
unsigned count;
__u64 ts;
} feature_aborted[256];
+ __u16 phys_addr;
};
extern struct la_info la_info[15];
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index 5dde9606..42592b04 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -872,6 +872,26 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
break;
+ /* CDC */
+
+ case CEC_MSG_CDC_MESSAGE: {
+ switch (msg.msg[4]) {
+ case CEC_MSG_CDC_HEC_DISCOVER:
+ __u16 phys_addr;
+
+ cec_msg_set_reply_to(&msg, &msg);
+ cec_ops_cdc_hec_discover(&msg, &phys_addr);
+ cec_msg_cdc_hec_report_state(&msg, phys_addr,
+ CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED,
+ CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED,
+ CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED,
+ CEC_OP_CDC_ERROR_CODE_NONE,
+ 1, 0); // We do not support HEC on any HDMI connections
+ transmit(node, &msg);
+ return;
+ }
+ }
+
/* Core */
case CEC_MSG_FEATURE_ABORT:

Privacy Policy