linux-media.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 0/3] cec: Deck Control tests
@ 2021-06-11 16:45 Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 1/3] cec: add tests for Deck Control message Deborah Brouwer
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Deborah Brouwer @ 2021-06-11 16:45 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, jaffe1, Deborah Brouwer

This is part of an Outreachy project to expand the testing of
Deck Control messages as handled by CEC adapters.

Changes since v6:
	cec-compliance:
	- reorder handling of Feature Abort
	- add short comments to explain wait for Skip Forward/Reverse
	- change invalid Play operand from 8 to 0
	cec-follower:
	- do not store the initiator in deck_report_changes_to; instead
		pass "me" (initiator) as an argument to update_deck_state
	- use cec_msg_init to initialize message in update_deck_state
	- remove redundant reset of clear deck_report_changes_to
	- refactor the No Media check in MSG_PLAY

Changes since v5:
	cec-compliance:
	- check for Feature Abort before getting deck status.
	cec-follower:
	- send updates to the logical address of the device that
		requested ongoing status updates which may be
		different from the device that requests deck-status
		on a one-time basis.

Changes since v4:
	cec-compliance:
	- Change deck_status to a reference and initialize to 0.
	- Revise test results after receiving Feature Abort with
		Incorrect Mode.
	- Simplify for loop for Skip Forward and Reverse.
	- Remove Play as a possible response to Eject.
	- Rename "match" and its argument,  add fail on abort.
	- Rearrange play tests to see clearly all three options
		(MIN/MED/MAX).
	cec-follower:
	- Set deck_skip_start to 0 if a new command or standby occurs
		in the interval between Skip Forward/Reverse and Play.
	- Move skip timer to the end of while loop in testProcessing.
	- Add helper function update_deck_state.

Changes since v3:
	cec-compliance:
		- Stop using REQ_On for monitoring deck status changes; 
			instead add a helper function to get and return deck status.
		- Allow Stop or Eject to return Feature Abort, Incorrect Mode.
		- Replace passive util_receive with an active deck query to see
			if the deck status changes to Play after Skip Forward/Reverse.
		- Add helper function to match play mode and expected deck status.
		- Remove the Deck Status test.
	cec-follower:
		- Track the elasped time since Skip Forward/Reverse and Play after 2s.
		- Remove tray open/close toggle from Eject

Changes since v2:
	cec-compliance:
		- If a deck returns Feature Abort, Incorrect Mode, just provide info
			unless the deck actually has media, then issue a warning.
		- If a deck reports Skip Forward/Reverse status, wait until the status
			changes again before resuming testing to avoid prematurely
			failing the test.
		- Rearrange/change the tests to trigger deck status changes.
	cec-follower:
		- Only report actual status changes, not just every Deck Control
			message that is processed.
		- Send Skip Forward/Reverse and then sleep 2 seconds before
			sending Play.
		- Remove the toggle between Play Forward/Play Still.

Changes since v1:
	- Remove unnecessary functions and node states.
	- Assume that media is present and use the "No Media"
		deck state solely to indicate whether the tray is open.
	- Change and add invalid operands so operands just
		outside of the valid range are tested.
	- Remove restriction to playback/record device.

Deborah Brouwer (3):
  cec: add tests for Deck Control message
  cec: add tests for Deck Play message
  cec-compliance: remove Deck Status test

 utils/cec-compliance/cec-compliance.h |   5 +
 utils/cec-compliance/cec-test.cpp     | 213 ++++++++++++++++++++++----
 utils/cec-follower/cec-follower.cpp   |   2 +
 utils/cec-follower/cec-follower.h     |   2 +
 utils/cec-follower/cec-processing.cpp | 121 ++++++++++++++-
 5 files changed, 308 insertions(+), 35 deletions(-)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v7 1/3] cec: add tests for Deck Control message
  2021-06-11 16:45 [PATCH v7 0/3] cec: Deck Control tests Deborah Brouwer
@ 2021-06-11 16:45 ` Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 2/3] cec: add tests for Deck Play message Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 3/3] cec-compliance: remove Deck Status test Deborah Brouwer
  2 siblings, 0 replies; 4+ messages in thread
From: Deborah Brouwer @ 2021-06-11 16:45 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, jaffe1, Deborah Brouwer

Send all Deck Control commands Skip Forward, Skip Reverse, Stop and Eject
and check the corresponding deck status. Test that the follower returns
Feature Abort for invalid Deck Control operands.

Signed-off-by: Deborah Brouwer <deborahbrouwer3563@gmail.com>
---
 utils/cec-compliance/cec-compliance.h |   5 ++
 utils/cec-compliance/cec-test.cpp     | 112 +++++++++++++++++++++++---
 utils/cec-follower/cec-follower.cpp   |   2 +
 utils/cec-follower/cec-follower.h     |   2 +
 utils/cec-follower/cec-processing.cpp |  68 +++++++++++++++-
 5 files changed, 177 insertions(+), 12 deletions(-)

diff --git a/utils/cec-compliance/cec-compliance.h b/utils/cec-compliance/cec-compliance.h
index fc50e6d9..818181ab 100644
--- a/utils/cec-compliance/cec-compliance.h
+++ b/utils/cec-compliance/cec-compliance.h
@@ -359,6 +359,11 @@ static inline bool refused(const struct cec_msg *msg)
 	return cec_msg_status_is_abort(msg) && abort_reason(msg) == CEC_OP_ABORT_REFUSED;
 }
 
+static inline bool incorrect_mode(const struct cec_msg *msg)
+{
+	return cec_msg_status_is_abort(msg) && abort_reason(msg) == CEC_OP_ABORT_INCORRECT_MODE;
+}
+
 static inline bool timed_out(const struct cec_msg *msg)
 {
 	return msg->rx_status & CEC_RX_STATUS_TIMEOUT;
diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
index 65a3943a..44f09b40 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -19,6 +19,20 @@ struct remote_test {
 	const vec_remote_subtests &subtests;
 };
 
+static int deck_status_get(struct node *node, unsigned me, unsigned la, __u8 &deck_status)
+{
+	struct cec_msg msg = {};
+	deck_status = 0;
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(timed_out_or_abort(&msg));
+	cec_ops_deck_status(&msg, &deck_status);
+
+	return OK;
+}
+
 
 /* System Information */
 
@@ -688,24 +702,93 @@ static int deck_ctl_deck_status(struct node *node, unsigned me, unsigned la, boo
 static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool interactive)
 {
 	struct cec_msg msg = {};
+	__u8 deck_status;
 
 	cec_msg_init(&msg, me, la);
 	cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
 	fail_on_test(!transmit_timeout(node, &msg));
-	if (is_playback_or_rec(la)) {
-		fail_on_test_v2(node->remote[la].cec_version,
-				node->remote[la].has_deck_ctl && unrecognized_op(&msg));
-		fail_on_test_v2(node->remote[la].cec_version,
-				!node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
-	}
+	fail_on_test_v2(node->remote[la].cec_version,
+	                node->remote[la].has_deck_ctl && unrecognized_op(&msg));
+	fail_on_test_v2(node->remote[la].cec_version,
+	                !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
 	if (unrecognized_op(&msg))
 		return OK_NOT_SUPPORTED;
 	if (refused(&msg))
 		return OK_REFUSED;
-	if (cec_msg_status_is_abort(&msg))
-		return OK_PRESUMED;
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	if (cec_msg_status_is_abort(&msg)) {
+		if (!incorrect_mode(&msg))
+			return FAIL;
+		if (deck_status == CEC_OP_DECK_INFO_NO_MEDIA)
+			info("Stop: no media.\n");
+		else
+			warn("Deck has media but returned Feature Abort with Incorrect Mode.");
+		return OK;
+	}
+	fail_on_test(deck_status != CEC_OP_DECK_INFO_STOP && deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
 
-	return OK_PRESUMED;
+	cec_msg_init(&msg, me, la);
+	cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_SKIP_FWD);
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	/*
+	 * If there is no media, Skip Forward should Feature Abort with Incorrect Mode
+	 * even if Stop did not.  If Skip Forward does not Feature Abort, the deck
+	 * is assumed to have media.
+	 */
+	if (incorrect_mode(&msg)) {
+		fail_on_test(deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
+		return OK;
+	}
+	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++) {
+		sleep(1);
+		fail_on_test(deck_status_get(node, me, la, deck_status));
+	}
+	fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_SKIP_REV);
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(cec_msg_status_is_abort(&msg)); /* Assumes deck has media. */
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	/* Wait for Deck to finish Skip Reverse. */
+	for (unsigned i = 0; deck_status == CEC_OP_DECK_INFO_SKIP_REV && i < long_timeout; i++) {
+		sleep(1);
+		fail_on_test(deck_status_get(node, me, la, deck_status));
+	}
+	fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_EJECT);
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(cec_msg_status_is_abort(&msg));
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	fail_on_test(deck_status != CEC_OP_DECK_INFO_NO_MEDIA);
+
+	return OK;
+}
+
+static int deck_ctl_deck_ctl_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+	struct cec_msg msg = {};
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_deck_control(&msg, 0); /* Invalid Deck Control 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_deck_control(&msg, 5); /* Invalid Deck Control operand */
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(!cec_msg_status_is_abort(&msg));
+	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+	return OK;
 }
 
 static int deck_ctl_play(struct node *node, unsigned me, unsigned la, bool interactive)
@@ -743,8 +826,17 @@ static const vec_remote_subtests deck_ctl_subtests{
 		deck_ctl_give_status_invalid,
 	},
 	{ "Deck Status", CEC_LOG_ADDR_MASK_ALL, deck_ctl_deck_status },
-	{ "Deck Control", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_deck_ctl },
 	{ "Play", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_play },
+	{
+		"Deck Control",
+		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+		deck_ctl_deck_ctl,
+	},
+	{
+		"Deck Control Invalid Operand",
+		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+		deck_ctl_deck_ctl_invalid,
+	},
 };
 
 /* Tuner Control */
diff --git a/utils/cec-follower/cec-follower.cpp b/utils/cec-follower/cec-follower.cpp
index 047d7a04..ff47d698 100644
--- a/utils/cec-follower/cec-follower.cpp
+++ b/utils/cec-follower/cec-follower.cpp
@@ -314,7 +314,9 @@ void state_init(struct node &node)
 	node.state.volume = 50;
 	node.state.mute = false;
 	node.state.deck_report_changes = false;
+	node.state.deck_report_changes_to = 0;
 	node.state.deck_state = CEC_OP_DECK_INFO_STOP;
+	node.state.deck_skip_start = 0;
 	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 0492faa9..68ef222a 100644
--- a/utils/cec-follower/cec-follower.h
+++ b/utils/cec-follower/cec-follower.h
@@ -50,7 +50,9 @@ struct state {
 	bool service_by_dig_id;
 	bool tuner_report_changes;
 	bool deck_report_changes;
+	__u8 deck_report_changes_to;
 	__u8 deck_state;
+	__u64 deck_skip_start;
 	unsigned toggle_power_status;
 	__u64 last_aud_rate_rx_ts;
 };
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index 3d2e4a2c..f88f0dcc 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -32,6 +32,9 @@
 /* The maximum interval in nanoseconds between audio rate messages as defined in the spec */
 #define MAX_AUD_RATE_MSG_INTERVAL_NS (2 * 1000000000ULL)
 
+/* The maximum interval in nanoseconds to allow a deck to skip forward/reverse */
+#define MAX_DECK_SKIP_NS (2 * 1000000000ULL)
+
 struct cec_enum_values {
 	const char *type_name;
 	__u8 value;
@@ -161,6 +164,7 @@ static bool enter_standby(struct node *node)
 		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;
 		dev_info("Changing state to standby\n");
 		return true;
 	}
@@ -252,6 +256,21 @@ static void aud_rate_msg_interval_check(struct node *node, __u64 ts_new)
 	}
 }
 
+static void update_deck_state(struct node *node, unsigned me, __u8 deck_state_new)
+{
+	if (node->state.deck_state != deck_state_new) {
+		node->state.deck_state = deck_state_new;
+
+		if (node->state.deck_report_changes) {
+			struct cec_msg msg = {};
+
+			cec_msg_init(&msg, me, node->state.deck_report_changes_to);
+			cec_msg_deck_status(&msg, node->state.deck_state);
+			transmit(node, &msg);
+		}
+	}
+}
+
 static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
 {
 	__u8 to = cec_msg_destination(&msg);
@@ -517,6 +536,7 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
 		switch (status_req) {
 		case CEC_OP_STATUS_REQ_ON:
 			node->state.deck_report_changes = true;
+			node->state.deck_report_changes_to = cec_msg_initiator(&msg);
 			fallthrough;
 		case CEC_OP_STATUS_REQ_ONCE:
 			cec_msg_set_reply_to(&msg, &msg);
@@ -535,9 +555,48 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
 			return;
 		break;
 	case CEC_MSG_DECK_CONTROL:
-		if (node->has_deck_ctl)
+		if (!node->has_deck_ctl)
+			break;
+
+		__u8 deck_state;
+		__u8 deck_control_mode;
+
+		cec_ops_deck_control(&msg, &deck_control_mode);
+
+		switch (deck_control_mode) {
+		case CEC_OP_DECK_CTL_MODE_STOP:
+			deck_state = CEC_OP_DECK_INFO_STOP;
+			node->state.deck_skip_start = 0;
+			break;
+		case CEC_OP_DECK_CTL_MODE_SKIP_FWD:
+			/* Skip Forward will not retract the deck tray. */
+			if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+				reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+				return;
+			}
+			deck_state = CEC_OP_DECK_INFO_SKIP_FWD;
+			node->state.deck_skip_start = msg.rx_ts;
+			break;
+		case CEC_OP_DECK_CTL_MODE_SKIP_REV:
+			/* Skip Reverse will not retract the deck tray. */
+			if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+				reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+				return;
+			}
+			deck_state = CEC_OP_DECK_INFO_SKIP_REV;
+			node->state.deck_skip_start = msg.rx_ts;
+			break;
+		case CEC_OP_DECK_CTL_MODE_EJECT:
+			deck_state = CEC_OP_DECK_INFO_NO_MEDIA;
+			node->state.deck_skip_start = 0;
+			break;
+		default:
+			cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
+			transmit(node, &msg);
 			return;
-		break;
+		}
+		update_deck_state(node, me, deck_state);
+		return;
 	case CEC_MSG_DECK_STATUS:
 		return;
 
@@ -1034,6 +1093,11 @@ void testProcessing(struct node *node, bool wallclock)
 
 		if (node->has_aud_rate)
 			aud_rate_msg_interval_check(node, ts_now);
+
+		if (node->state.deck_skip_start && ts_now - node->state.deck_skip_start > MAX_DECK_SKIP_NS) {
+			node->state.deck_skip_start = 0;
+			update_deck_state(node, me, CEC_OP_DECK_INFO_PLAY);
+		}
 	}
 	mode = CEC_MODE_INITIATOR;
 	doioctl(node, CEC_S_MODE, &mode);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v7 2/3] cec: add tests for Deck Play message
  2021-06-11 16:45 [PATCH v7 0/3] cec: Deck Control tests Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 1/3] cec: add tests for Deck Control message Deborah Brouwer
@ 2021-06-11 16:45 ` Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 3/3] cec-compliance: remove Deck Status test Deborah Brouwer
  2 siblings, 0 replies; 4+ messages in thread
From: Deborah Brouwer @ 2021-06-11 16:45 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, jaffe1, Deborah Brouwer

Send all Deck Play commands and check that the follower implements them.
Test that the follower returns Feature Abort for invalid Play operands.

Signed-off-by: Deborah Brouwer <deborahbrouwer3563@gmail.com>
---
 utils/cec-compliance/cec-test.cpp     | 99 ++++++++++++++++++++++++---
 utils/cec-follower/cec-processing.cpp | 55 ++++++++++++++-
 2 files changed, 140 insertions(+), 14 deletions(-)

diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
index 44f09b40..d463ffa4 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -33,6 +33,20 @@ static int deck_status_get(struct node *node, unsigned me, unsigned la, __u8 &de
 	return OK;
 }
 
+static int test_play_mode(struct node *node, unsigned me, unsigned la, __u8 play_mode, __u8 expected)
+{
+	struct cec_msg msg = {};
+	__u8 deck_status;
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_play(&msg, play_mode);
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(cec_msg_status_is_abort(&msg)); /* Assumes deck has media. */
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	fail_on_test(deck_status != expected);
+
+	return OK;
+}
 
 /* System Information */
 
@@ -794,24 +808,78 @@ 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 = {};
+	__u8 deck_status;
 
 	cec_msg_init(&msg, me, la);
-	cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_STILL);
+	cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_FWD);
 	fail_on_test(!transmit_timeout(node, &msg));
-	if (is_playback_or_rec(la)) {
-		fail_on_test_v2(node->remote[la].cec_version,
-				node->remote[la].has_deck_ctl && unrecognized_op(&msg));
-		fail_on_test_v2(node->remote[la].cec_version,
-				!node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
-	}
+	fail_on_test_v2(node->remote[la].cec_version,
+	                node->remote[la].has_deck_ctl && unrecognized_op(&msg));
+	fail_on_test_v2(node->remote[la].cec_version,
+	                !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
 	if (unrecognized_op(&msg))
 		return OK_NOT_SUPPORTED;
 	if (refused(&msg))
 		return OK_REFUSED;
-	if (cec_msg_status_is_abort(&msg))
-		return OK_PRESUMED;
+	fail_on_test(deck_status_get(node, me, la, deck_status));
+	if (cec_msg_status_is_abort(&msg)) {
+		if (!incorrect_mode(&msg))
+			return FAIL;
+		if (deck_status == CEC_OP_DECK_INFO_NO_MEDIA)
+			info("Play Still: no media.\n");
+		else
+			warn("Deck has media but returned Feature Abort with Incorrect Mode.");
+		return OK;
+	}
+	fail_on_test(deck_status != CEC_OP_DECK_INFO_PLAY);
 
-	return OK_PRESUMED;
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_STILL, CEC_OP_DECK_INFO_STILL));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_REV, CEC_OP_DECK_INFO_PLAY_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN, CEC_OP_DECK_INFO_FAST_FWD));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN, CEC_OP_DECK_INFO_FAST_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED, CEC_OP_DECK_INFO_FAST_FWD));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED, CEC_OP_DECK_INFO_FAST_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX, CEC_OP_DECK_INFO_FAST_FWD));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX, CEC_OP_DECK_INFO_FAST_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN, CEC_OP_DECK_INFO_SLOW));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN, CEC_OP_DECK_INFO_SLOW_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED, CEC_OP_DECK_INFO_SLOW));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED, CEC_OP_DECK_INFO_SLOW_REV));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX, CEC_OP_DECK_INFO_SLOW));
+	fail_on_test(test_play_mode(node, me, la, CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX, CEC_OP_DECK_INFO_SLOW_REV));
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
+	fail_on_test(!transmit_timeout(node, &msg));
+
+	return OK;
+}
+
+static int deck_ctl_play_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
+{
+	struct cec_msg msg = {};
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_play(&msg, 0); /* Invalid 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_play(&msg, 4); /* Invalid Operand */
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(!cec_msg_status_is_abort(&msg));
+	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+	cec_msg_init(&msg, me, la);
+	cec_msg_play(&msg, 0x26); /* Invalid Operand */
+	fail_on_test(!transmit_timeout(node, &msg));
+	fail_on_test(!cec_msg_status_is_abort(&msg));
+	fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
+
+	return OK;
 }
 
 static const vec_remote_subtests deck_ctl_subtests{
@@ -826,7 +894,6 @@ static const vec_remote_subtests deck_ctl_subtests{
 		deck_ctl_give_status_invalid,
 	},
 	{ "Deck Status", CEC_LOG_ADDR_MASK_ALL, deck_ctl_deck_status },
-	{ "Play", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, deck_ctl_play },
 	{
 		"Deck Control",
 		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
@@ -837,6 +904,16 @@ static const vec_remote_subtests deck_ctl_subtests{
 		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
 		deck_ctl_deck_ctl_invalid,
 	},
+	{
+		"Play",
+		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+		deck_ctl_play,
+	},
+	{
+		"Play Invalid Operand",
+		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
+		deck_ctl_play_invalid,
+	},
 };
 
 /* Tuner Control */
diff --git a/utils/cec-follower/cec-processing.cpp b/utils/cec-follower/cec-processing.cpp
index f88f0dcc..8665bf5a 100644
--- a/utils/cec-follower/cec-processing.cpp
+++ b/utils/cec-follower/cec-processing.cpp
@@ -551,14 +551,63 @@ static void processMsg(struct node *node, struct cec_msg &msg, unsigned me)
 			return;
 		}
 	case CEC_MSG_PLAY:
-		if (node->has_deck_ctl)
+		if (!node->has_deck_ctl)
+			break;
+
+		__u8 deck_state;
+		__u8 play_mode;
+
+		cec_ops_play(&msg, &play_mode);
+
+		switch (play_mode) {
+		case CEC_OP_PLAY_MODE_PLAY_FWD:
+			deck_state = CEC_OP_DECK_INFO_PLAY;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_REV:
+			deck_state = CEC_OP_DECK_INFO_PLAY_REV;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_STILL:
+			deck_state = CEC_OP_DECK_INFO_STILL;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN:
+		case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED:
+		case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX:
+			deck_state = CEC_OP_DECK_INFO_FAST_FWD;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN:
+		case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED:
+		case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX:
+			deck_state = CEC_OP_DECK_INFO_FAST_REV;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN:
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED:
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX:
+			deck_state = CEC_OP_DECK_INFO_SLOW;
+			break;
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN:
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED:
+		case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX:
+			deck_state = CEC_OP_DECK_INFO_SLOW_REV;
+			break;
+		default:
+			cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
+			transmit(node, &msg);
 			return;
-		break;
+		}
+		/* Only Play Forward and Play Still will close tray if open. */
+		if (play_mode != CEC_OP_PLAY_MODE_PLAY_FWD &&
+		    play_mode != CEC_OP_PLAY_MODE_PLAY_STILL &&
+		    node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
+				reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
+				return;
+		}
+		node->state.deck_skip_start = 0;
+		update_deck_state(node, me, deck_state);
+		return;
 	case CEC_MSG_DECK_CONTROL:
 		if (!node->has_deck_ctl)
 			break;
 
-		__u8 deck_state;
 		__u8 deck_control_mode;
 
 		cec_ops_deck_control(&msg, &deck_control_mode);
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH v7 3/3] cec-compliance: remove Deck Status test
  2021-06-11 16:45 [PATCH v7 0/3] cec: Deck Control tests Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 1/3] cec: add tests for Deck Control message Deborah Brouwer
  2021-06-11 16:45 ` [PATCH v7 2/3] cec: add tests for Deck Play message Deborah Brouwer
@ 2021-06-11 16:45 ` Deborah Brouwer
  2 siblings, 0 replies; 4+ messages in thread
From: Deborah Brouwer @ 2021-06-11 16:45 UTC (permalink / raw)
  To: linux-media; +Cc: hverkuil, jaffe1, Deborah Brouwer

Remove the Deck Status message test because invalid Deck Status messages
are already captured by cec-compliance in other tests.

Signed-off-by: Deborah Brouwer <deborahbrouwer3563@gmail.com>
---
 utils/cec-compliance/cec-test.cpp | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/utils/cec-compliance/cec-test.cpp b/utils/cec-compliance/cec-test.cpp
index d463ffa4..dac188ba 100644
--- a/utils/cec-compliance/cec-test.cpp
+++ b/utils/cec-compliance/cec-test.cpp
@@ -696,23 +696,6 @@ static int deck_ctl_give_status_invalid(struct node *node, unsigned me, unsigned
 	return OK;
 }
 
-static int deck_ctl_deck_status(struct node *node, unsigned me, unsigned la, bool interactive)
-{
-	struct cec_msg msg = {};
-
-	cec_msg_init(&msg, me, la);
-	cec_msg_deck_status(&msg, CEC_OP_DECK_INFO_STOP);
-	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 int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool interactive)
 {
 	struct cec_msg msg = {};
@@ -893,7 +876,6 @@ static const vec_remote_subtests deck_ctl_subtests{
 		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
 		deck_ctl_give_status_invalid,
 	},
-	{ "Deck Status", CEC_LOG_ADDR_MASK_ALL, deck_ctl_deck_status },
 	{
 		"Deck Control",
 		CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-06-11 16:47 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-06-11 16:45 [PATCH v7 0/3] cec: Deck Control tests Deborah Brouwer
2021-06-11 16:45 ` [PATCH v7 1/3] cec: add tests for Deck Control message Deborah Brouwer
2021-06-11 16:45 ` [PATCH v7 2/3] cec: add tests for Deck Play message Deborah Brouwer
2021-06-11 16:45 ` [PATCH v7 3/3] cec-compliance: remove Deck Status test Deborah Brouwer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).