diff mbox series

[2/2] cec-compliance: add tests for CEC_MSG_FL_REPLY_VENDOR_ID

Message ID f670cb2142cb87816260e7fca60938d820660c2c.1721908330.git.hverkuil-cisco@xs4all.nl
State New
Headers show
Series v4l-utils: add support for CEC_MSG_FL_REPLY_VENDOR_ID | expand

Commit Message

Hans Verkuil July 25, 2024, 11:52 a.m. UTC
While some simple generic checks are possible, most of the
tests are only performed if the CEC driver is 'vivid', since
that has a well defined <Vendor Command With ID> implementation,
specifically created to regression test this flag.

Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
---
 utils/cec-compliance/cec-compliance.cpp   |  2 +
 utils/cec-compliance/cec-compliance.h     |  2 +
 utils/cec-compliance/cec-test-adapter.cpp | 90 ++++++++++++++++++++++-
 3 files changed, 93 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/utils/cec-compliance/cec-compliance.cpp b/utils/cec-compliance/cec-compliance.cpp
index bddd57cb..aa368026 100644
--- a/utils/cec-compliance/cec-compliance.cpp
+++ b/utils/cec-compliance/cec-compliance.cpp
@@ -1072,6 +1072,7 @@  int main(int argc, char **argv)
 	doioctl(&node, CEC_ADAP_G_CAPS, &caps);
 	node.caps = caps.capabilities;
 	node.available_log_addrs = caps.available_log_addrs;
+	node.is_vivid = !strcmp(caps.driver, "vivid");
 
 	if (options[OptTestAudioRateControl])
 		test_tags |= TAG_AUDIO_RATE_CONTROL;
@@ -1169,6 +1170,7 @@  int main(int argc, char **argv)
 
 	struct cec_log_addrs laddrs = { };
 	doioctl(&node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
+	node.vendor_id = laddrs.vendor_id;
 
 	if (node.phys_addr == CEC_PHYS_ADDR_INVALID &&
 	    !(node.caps & (CEC_CAP_PHYS_ADDR | CEC_CAP_NEEDS_HPD)) &&
diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h
index aae72842..c291e9ac 100644
--- a/utils/cec-compliance/cec-compliance.h
+++ b/utils/cec-compliance/cec-compliance.h
@@ -151,6 +151,7 @@  struct remote {
 struct node {
 	int fd;
 	const char *device;
+	bool is_vivid;
 	bool has_cec20;
 	unsigned caps;
 	unsigned available_log_addrs;
@@ -160,6 +161,7 @@  struct node {
 	unsigned remote_la_mask;
 	struct remote remote[16];
 	__u16 phys_addr;
+	__u32 vendor_id;
 	bool in_standby;
 	__u8 prim_devtype;
 	time_t current_time;
diff --git a/utils/cec-compliance/cec-test-adapter.cpp b/utils/cec-compliance/cec-test-adapter.cpp
index f96baaf3..3f818583 100644
--- a/utils/cec-compliance/cec-test-adapter.cpp
+++ b/utils/cec-compliance/cec-test-adapter.cpp
@@ -15,7 +15,8 @@ 
 #include "cec-compliance.h"
 
 static constexpr __u8 tx_ok_retry_mask = CEC_TX_STATUS_OK | CEC_TX_STATUS_MAX_RETRIES;
-static constexpr __u32 msg_fl_mask = CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW;
+static constexpr __u32 msg_fl_mask = CEC_MSG_FL_REPLY_TO_FOLLOWERS | CEC_MSG_FL_RAW |
+				     CEC_MSG_FL_REPLY_VENDOR_ID;
 
 // Flush any pending messages
 static int flush_pending_msgs(struct node *node)
@@ -267,6 +268,7 @@  static int testTransmit(struct node *node)
 	bool tested_self = false;
 	bool tested_valid_la = false;
 	bool tested_invalid_la = false;
+	bool has_reply_vendor_id = node->caps & CEC_CAP_REPLY_VENDOR_ID;
 
 	if (!(node->caps & CEC_CAP_TRANSMIT)) {
 		cec_msg_init(&msg, la, 0);
@@ -294,6 +296,19 @@  static int testTransmit(struct node *node)
 	msg.reply = CEC_MSG_CEC_VERSION;
 	fail_on_test(doioctl(node, CEC_TRANSMIT, &msg) != EINVAL);
 
+	if (has_reply_vendor_id) {
+		// Test that CEC_MSG_FL_REPLY_VENDOR_ID requires a message
+		// size of at least 6 by constructing a message of length 5
+		// and verifying that that fails with EINVAL.
+		cec_msg_init(&msg, la, 0);
+		__u8 cmd = 0;
+		cec_msg_vendor_command_with_id(&msg, node->vendor_id, 0, &cmd);
+		msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+		msg.reply = cmd + 1;
+		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg) != EINVAL);
+		fail_on_test(!(msg.flags & CEC_MSG_FL_REPLY_VENDOR_ID));
+	}
+
 	for (i = 0; i < 15; i++) {
 		if (tested_self && (node->adap_la_mask & (1 << i)))
 			continue;
@@ -391,6 +406,79 @@  static int testTransmit(struct node *node)
 			fail_on_test(msg.tx_arb_lost_cnt == 0xff);
 			fail_on_test(msg.tx_low_drive_cnt == 0xff);
 			fail_on_test(msg.tx_error_cnt == 0xff);
+
+			// CEC_MSG_FL_REPLY_VENDOR_ID tests, only valid for use with
+			// the vivid driver since that has support for this.
+			//
+			// The vivid driver will Feature Abort the vendor message if
+			// it has a payload size != 1.
+			//
+			// It will ignore messages with an even payload byte, and
+			// it will reply to messages with an odd payload byte with
+			// that payload byte incremented by 1.
+			if (node->is_vivid && has_reply_vendor_id) {
+				__u32 vendor_id;
+				__u8 size;
+				const __u8 *vendor_data;
+				__u8 vendor_cmd = 0x11;
+
+				// Test that an invalid vendor ID is ignored
+				cec_msg_init(&msg, la, i);
+				cec_msg_vendor_command_with_id(&msg, node->vendor_id + 1, 1, &vendor_cmd);
+				msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+				msg.reply = vendor_cmd + 2;
+				fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
+				fail_on_test(!(msg.rx_status & CEC_RX_STATUS_TIMEOUT));
+				fail_on_test(!(msg.flags & CEC_MSG_FL_REPLY_VENDOR_ID));
+
+				// The vivid driver will reply with value vendor_cmd + 1, so
+				// waiting for different reply must time out
+				cec_msg_init(&msg, la, i);
+				cec_msg_vendor_command_with_id(&msg, node->vendor_id, 1, &vendor_cmd);
+				msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+				msg.reply = vendor_cmd + 2;
+				fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
+				fail_on_test(!(msg.rx_status & CEC_RX_STATUS_TIMEOUT));
+				fail_on_test(!(msg.flags & CEC_MSG_FL_REPLY_VENDOR_ID));
+
+				// This should work
+				cec_msg_init(&msg, la, i);
+				cec_msg_vendor_command_with_id(&msg, node->vendor_id, 1, &vendor_cmd);
+				msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+				msg.reply = vendor_cmd + 1;
+				fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
+				fail_on_test(!(msg.rx_status & CEC_RX_STATUS_OK));
+				fail_on_test(!(msg.flags & CEC_MSG_FL_REPLY_VENDOR_ID));
+				cec_ops_vendor_command_with_id(&msg, &vendor_id, &size, &vendor_data);
+				fail_on_test(vendor_id != node->vendor_id);
+				fail_on_test(size != 1);
+				fail_on_test(vendor_data[0] != vendor_cmd + 1);
+
+				// This too: here the reply is 0 (0xff + 1 % 256)
+				cec_msg_init(&msg, la, i);
+				vendor_cmd = 0xff;
+				cec_msg_vendor_command_with_id(&msg, node->vendor_id, 1, &vendor_cmd);
+				msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+				msg.reply = 0;
+				fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
+				fail_on_test(!(msg.rx_status & CEC_RX_STATUS_OK));
+				fail_on_test(!(msg.flags & CEC_MSG_FL_REPLY_VENDOR_ID));
+				cec_ops_vendor_command_with_id(&msg, &vendor_id, &size, &vendor_data);
+				fail_on_test(vendor_id != node->vendor_id);
+				fail_on_test(size != 1);
+				fail_on_test(vendor_data[0]);
+
+				// A size != 1 should result in a feature abort
+				cec_msg_init(&msg, la, i);
+				vendor_cmd = 0xff;
+				cec_msg_vendor_command_with_id(&msg, node->vendor_id, 1, &vendor_cmd);
+				msg.len++;
+				msg.flags = CEC_MSG_FL_REPLY_VENDOR_ID;
+				msg.reply = 0;
+				fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
+				fail_on_test(!(msg.rx_status & CEC_RX_STATUS_FEATURE_ABORT));
+				fail_on_test(msg.msg[3] != CEC_OP_ABORT_INVALID_OP);
+			}
 		} else {
 			if (tested_invalid_la)
 				continue;