• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4  */
5 
6 #include <cerrno>
7 #include <ctime>
8 #include <string>
9 #include <vector>
10 
11 #include <sys/ioctl.h>
12 #include <unistd.h>
13 
14 #include "cec-compliance.h"
15 
16 
get_power_status(struct node * node,unsigned me,unsigned la,__u8 & power_status)17 static bool get_power_status(struct node *node, unsigned me, unsigned la, __u8 &power_status)
18 {
19 	struct cec_msg msg;
20 
21 	cec_msg_init(&msg, me, la);
22 	cec_msg_give_device_power_status(&msg, true);
23 	msg.timeout = 2000;
24 	int res = doioctl(node, CEC_TRANSMIT, &msg);
25 	if (res == ENONET) {
26 		power_status = CEC_OP_POWER_STATUS_STANDBY;
27 		return true;
28 	}
29 	if (res || !(msg.tx_status & CEC_TX_STATUS_OK) || !(msg.rx_status & CEC_RX_STATUS_OK) ||
30 	    cec_msg_status_is_abort(&msg))
31 		return false;
32 	cec_ops_report_power_status(&msg, &power_status);
33 	return true;
34 }
35 
util_interactive_ensure_power_state(struct node * node,unsigned me,unsigned la,bool interactive,__u8 target_pwr)36 bool util_interactive_ensure_power_state(struct node *node, unsigned me, unsigned la, bool interactive,
37 						__u8 target_pwr)
38 {
39 	interactive_info(true, "Please ensure that the device is in state %s.",
40 			 power_status2s(target_pwr));
41 
42 	if (!node->remote[la].has_power_status)
43 		return true;
44 
45 	while (interactive) {
46 		__u8 pwr;
47 
48 		if (!get_power_status(node, me, la, pwr))
49 			announce("Failed to retrieve power status.");
50 		else if (pwr == target_pwr)
51 			return true;
52 		else
53 			announce("The device reported power status %s.", power_status2s(pwr));
54 		if (!question("Retry?"))
55 			return false;
56 	}
57 
58 	return true;
59 }
60 
61 
62 /* Give Device Power Status */
63 
power_status_give(struct node * node,unsigned me,unsigned la,bool interactive)64 static int power_status_give(struct node *node, unsigned me, unsigned la, bool interactive)
65 {
66 	struct cec_msg msg;
67 
68 	cec_msg_init(&msg, me, la);
69 	cec_msg_give_device_power_status(&msg, true);
70 	fail_on_test(!transmit_timeout(node, &msg));
71 	fail_on_test(timed_out(&msg));
72 	fail_on_test(unrecognized_op(&msg));
73 	if (refused(&msg))
74 		return OK_REFUSED;
75 	if (cec_msg_status_is_abort(&msg))
76 		return OK_PRESUMED;
77 
78 	__u8 power_status;
79 	cec_ops_report_power_status(&msg, &power_status);
80 	fail_on_test(power_status >= 4);
81 
82 	return 0;
83 }
84 
power_status_report(struct node * node,unsigned me,unsigned la,bool interactive)85 static int power_status_report(struct node *node, unsigned me, unsigned la, bool interactive)
86 {
87 	struct cec_msg msg;
88 
89 	cec_msg_init(&msg, me, la);
90 	cec_msg_report_power_status(&msg, CEC_OP_POWER_STATUS_ON);
91 	fail_on_test(!transmit_timeout(node, &msg));
92 	if (unrecognized_op(&msg))
93 		return OK_NOT_SUPPORTED;
94 	if (refused(&msg))
95 		return OK_REFUSED;
96 
97 	return OK_PRESUMED;
98 }
99 
100 const vec_remote_subtests power_status_subtests{
101 	{ "Give Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_give },
102 	{ "Report Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_report },
103 };
104 
105 /* One Touch Play */
106 
one_touch_play_view_on(struct node * node,unsigned me,unsigned la,bool interactive,__u8 opcode)107 static int one_touch_play_view_on(struct node *node, unsigned me, unsigned la, bool interactive,
108 				  __u8 opcode)
109 {
110 	struct cec_msg msg;
111 
112 	cec_msg_init(&msg, me, la);
113 	if (opcode == CEC_MSG_IMAGE_VIEW_ON)
114 		cec_msg_image_view_on(&msg);
115 	else if (opcode == CEC_MSG_TEXT_VIEW_ON)
116 		cec_msg_text_view_on(&msg);
117 
118 	int res = doioctl(node, CEC_TRANSMIT, &msg);
119 
120 	if (res == ENONET && la == CEC_LOG_ADDR_TV) {
121 		msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
122 		res = doioctl(node, CEC_TRANSMIT, &msg);
123 	}
124 	fail_on_test(res || !(msg.tx_status & CEC_TX_STATUS_OK));
125 
126 	fail_on_test(is_tv(la, node->remote[la].prim_type) && unrecognized_op(&msg));
127 	if (refused(&msg))
128 		return OK_REFUSED;
129 	if (cec_msg_status_is_abort(&msg))
130 		return OK_PRESUMED;
131 	if (opcode == CEC_MSG_IMAGE_VIEW_ON)
132 		node->remote[la].has_image_view_on = true;
133 	else if (opcode == CEC_MSG_TEXT_VIEW_ON)
134 		node->remote[la].has_text_view_on = true;
135 
136 	return 0;
137 }
138 
one_touch_play_image_view_on(struct node * node,unsigned me,unsigned la,bool interactive)139 static int one_touch_play_image_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
140 {
141 	return one_touch_play_view_on(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
142 }
143 
one_touch_play_text_view_on(struct node * node,unsigned me,unsigned la,bool interactive)144 static int one_touch_play_text_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
145 {
146 	return one_touch_play_view_on(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
147 }
148 
one_touch_play_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive,__u8 opcode)149 static int one_touch_play_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive,
150 					 __u8 opcode)
151 {
152 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_STANDBY));
153 
154 	int ret = one_touch_play_view_on(node, me, la, interactive, opcode);
155 
156 	if (ret && ret != OK_PRESUMED)
157 		return ret;
158 	fail_on_test(interactive && !question("Did the TV turn on?"));
159 
160 	if (interactive)
161 		return 0;
162 
163 	return OK_PRESUMED;
164 }
165 
one_touch_play_image_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)166 static int one_touch_play_image_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
167 {
168 	if (!interactive || !node->remote[la].has_image_view_on)
169 		return NOTAPPLICABLE;
170 	return one_touch_play_view_on_wakeup(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
171 }
172 
one_touch_play_text_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)173 static int one_touch_play_text_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
174 {
175 	if (!interactive || !node->remote[la].has_text_view_on)
176 		return NOTAPPLICABLE;
177 	return one_touch_play_view_on_wakeup(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
178 }
179 
one_touch_play_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive,__u8 opcode)180 static int one_touch_play_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive,
181 					 __u8 opcode)
182 {
183 	struct cec_msg msg;
184 	int ret;
185 
186 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
187 
188 	interactive_info(true, "Please switch the TV to another source.");
189 	ret = one_touch_play_view_on(node, me, la, interactive, opcode);
190 	if (ret && ret != OK_PRESUMED)
191 		return ret;
192 	cec_msg_init(&msg, me, la);
193 	cec_msg_active_source(&msg, node->phys_addr);
194 	fail_on_test(!transmit_timeout(node, &msg));
195 	fail_on_test(interactive && !question("Did the TV switch to this source?"));
196 
197 	if (interactive)
198 		return 0;
199 
200 	return OK_PRESUMED;
201 }
202 
one_touch_play_image_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive)203 static int one_touch_play_image_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive)
204 {
205 	if (!interactive || !node->remote[la].has_text_view_on)
206 		return NOTAPPLICABLE;
207 	return one_touch_play_view_on_change(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
208 }
209 
one_touch_play_text_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive)210 static int one_touch_play_text_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive)
211 {
212 	if (!interactive || !node->remote[la].has_text_view_on)
213 		return NOTAPPLICABLE;
214 	return one_touch_play_view_on_change(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
215 }
216 
one_touch_play_req_active_source(struct node * node,unsigned me,unsigned la,bool interactive)217 static int one_touch_play_req_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
218 {
219 	struct cec_msg msg;
220 
221 	cec_msg_init(&msg, me, la);
222 	cec_msg_active_source(&msg, node->phys_addr);
223 	fail_on_test(!transmit_timeout(node, &msg));
224 
225 	/* We have now said that we are active source, so receiving a reply to
226 	   Request Active Source should fail the test. */
227 	cec_msg_init(&msg, me, la);
228 	cec_msg_request_active_source(&msg, true);
229 	fail_on_test(!transmit_timeout(node, &msg));
230 	fail_on_test(!timed_out(&msg));
231 
232 	return 0;
233 }
234 
235 const vec_remote_subtests one_touch_play_subtests{
236 	{ "Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on },
237 	{ "Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on },
238 	{ "Wakeup on Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on_wakeup },
239 	{ "Wakeup Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on_wakeup },
240 	{ "Input change on Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on_change },
241 	{ "Input change on Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on_change },
242 	{ "Active Source and Request Active Source", CEC_LOG_ADDR_MASK_ALL, one_touch_play_req_active_source },
243 };
244 
245 /* Standby / Resume */
246 
247 /* The default sleep time between power status requests. */
248 #define SLEEP_POLL_POWER_STATUS 2
249 
wait_changing_power_status(struct node * node,unsigned me,unsigned la,__u8 & new_status,unsigned & unresponsive_cnt)250 static bool wait_changing_power_status(struct node *node, unsigned me, unsigned la, __u8 &new_status,
251 				       unsigned &unresponsive_cnt)
252 {
253 	__u8 old_status;
254 	time_t t = time(nullptr);
255 
256 	announce("Checking for power status change. This may take up to %llu s.", (long long)long_timeout);
257 	if (!get_power_status(node, me, la, old_status))
258 		return false;
259 	while (time(nullptr) - t < long_timeout) {
260 		__u8 power_status;
261 
262 		if (!get_power_status(node, me, la, power_status)) {
263 			/* Some TVs become completely unresponsive when transitioning
264 			   between power modes. Register that this happens, but continue
265 			   the test. */
266 			unresponsive_cnt++;
267 		} else if (old_status != power_status) {
268 			new_status = power_status;
269 			return true;
270 		}
271 		sleep(SLEEP_POLL_POWER_STATUS);
272 	}
273 	new_status = old_status;
274 	return false;
275 }
276 
poll_stable_power_status(struct node * node,unsigned me,unsigned la,__u8 expected_status,unsigned & unresponsive_cnt)277 static bool poll_stable_power_status(struct node *node, unsigned me, unsigned la,
278 				     __u8 expected_status, unsigned &unresponsive_cnt)
279 {
280 	bool transient = false;
281 	unsigned time_to_transient = 0;
282 	time_t t = time(nullptr);
283 
284 	/* Some devices can use several seconds to transition from one power
285 	   state to another, so the power state must be repeatedly polled */
286 	announce("Waiting for new stable power status. This may take up to %llu s.", (long long)long_timeout);
287 	while (time(nullptr) - t < long_timeout) {
288 		__u8 power_status;
289 
290 		if (!get_power_status(node, me, la, power_status)) {
291 			/* Some TVs become completely unresponsive when transitioning
292 			   between power modes. Register that this happens, but continue
293 			   the test. */
294 			unresponsive_cnt++;
295 			sleep(SLEEP_POLL_POWER_STATUS);
296 			continue;
297 		}
298 		if (!transient && (power_status == CEC_OP_POWER_STATUS_TO_ON ||
299 				   power_status == CEC_OP_POWER_STATUS_TO_STANDBY)) {
300 			time_to_transient = time(nullptr) - t;
301 			transient = true;
302 			warn_once_on_test(expected_status == CEC_OP_POWER_STATUS_ON &&
303 					  power_status == CEC_OP_POWER_STATUS_TO_STANDBY);
304 			warn_once_on_test(expected_status == CEC_OP_POWER_STATUS_STANDBY &&
305 					  power_status == CEC_OP_POWER_STATUS_TO_ON);
306 		}
307 		if (power_status == expected_status) {
308 			if (transient)
309 				announce("Transient state after %d s, stable state %s after %d s",
310 					 time_to_transient, power_status2s(power_status), (int)(time(NULL) - t));
311 			else
312 				announce("No transient state reported, stable state %s after %d s",
313 					 power_status2s(power_status), (int)(time(NULL) - t));
314 			return true;
315 		}
316 		sleep(SLEEP_POLL_POWER_STATUS);
317 	}
318 	return false;
319 }
320 
standby_resume_standby(struct node * node,unsigned me,unsigned la,bool interactive)321 static int standby_resume_standby(struct node *node, unsigned me, unsigned la, bool interactive)
322 {
323 	if (!node->remote[la].has_power_status)
324 		return NOTAPPLICABLE;
325 
326 	struct cec_msg msg;
327 	unsigned unresponsive_cnt = 0;
328 
329 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
330 
331 	/*
332 	 * Some displays only accept Standby from the Active Source.
333 	 * So make us the Active Source before sending Standby.
334 	 */
335 	if (is_tv(la, node->remote[la].prim_type)) {
336 		announce("Sending Active Source message.");
337 		cec_msg_init(&msg, me, la);
338 		cec_msg_active_source(&msg, node->phys_addr);
339 		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
340 	}
341 	announce("Sending Standby message.");
342 
343 	cec_msg_init(&msg, me, la);
344 	cec_msg_standby(&msg);
345 	fail_on_test(!transmit_timeout(node, &msg));
346 	fail_on_test(cec_msg_status_is_abort(&msg));
347 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_STANDBY, unresponsive_cnt));
348 	fail_on_test(interactive && !question("Is the device in standby?"));
349 	node->remote[la].in_standby = true;
350 
351 	if (unresponsive_cnt > 0)
352 		announce("The device went correctly into standby, but was unresponsive %d time%s during the transition.\n",
353 			 unresponsive_cnt, unresponsive_cnt == 1 ? "" : "s");
354 
355 	return 0;
356 }
357 
standby_resume_standby_toggle(struct node * node,unsigned me,unsigned la,bool interactive)358 static int standby_resume_standby_toggle(struct node *node, unsigned me, unsigned la, bool interactive)
359 {
360 	if (!node->remote[la].in_standby)
361 		return NOTAPPLICABLE;
362 
363 	struct cec_msg msg;
364 	unsigned unresponsive_cnt = 0;
365 	__u8 new_status;
366 
367 	node->remote[la].in_standby = false;
368 
369 	/* Send Standby again to test that it is not acting like a toggle */
370 	announce("Sending Standby message.");
371 	cec_msg_init(&msg, me, la);
372 	cec_msg_standby(&msg);
373 	int res = doioctl(node, CEC_TRANSMIT, &msg);
374 	fail_on_test(res && res != ENONET);
375 	fail_on_test(cec_msg_status_is_abort(&msg));
376 	fail_on_test(wait_changing_power_status(node, me, la, new_status, unresponsive_cnt));
377 	fail_on_test(new_status != CEC_OP_POWER_STATUS_STANDBY);
378 
379 	if (res == ENONET) {
380 		struct cec_caps caps = { };
381 		doioctl(node, CEC_ADAP_G_CAPS, &caps);
382 		unsigned major = caps.version >> 16;
383 		unsigned minor = (caps.version >> 8) & 0xff;
384 		if (!strcmp(caps.driver, "pulse8-cec") &&
385 		    !((major == 4 && minor == 19) || major > 5 ||
386 		      (major == 5 && minor >= 4))) {
387 			// The cec framework had a bug that prevented it from reliably
388 			// working with displays that pull down the HPD. This was fixed
389 			// in commit ac479b51f3f4 for kernel 5.5 and backported to kernels
390 			// 4.19.94 and 5.4.9. We only warn when the pulse8-cec driver is used,
391 			// for other CEC devices you hopefully know what you are doing...
392 			warn("This display appears to pull down the HPD when in Standby. For such\n");
393 			warn("displays kernel 4.19 or kernel 5.4 or higher is required.\n");
394 		}
395 	}
396 
397 	fail_on_test(interactive && !question("Is the device still in standby?"));
398 	node->remote[la].in_standby = true;
399 	if (unresponsive_cnt > 0)
400 		announce("The device went correctly into standby, but was unresponsive %d time%s during the transition.\n",
401 			 unresponsive_cnt, unresponsive_cnt == 1 ? "" : "s");
402 
403 	return 0;
404 }
405 
standby_resume_active_source_nowake(struct node * node,unsigned me,unsigned la,bool interactive)406 static int standby_resume_active_source_nowake(struct node *node, unsigned me, unsigned la, bool interactive)
407 {
408 	if (!node->remote[la].in_standby)
409 		return NOTAPPLICABLE;
410 
411 	struct cec_msg msg;
412 	unsigned unresponsive_cnt = 0;
413 	__u8 new_status;
414 
415 	node->remote[la].in_standby = false;
416 
417 	/*
418 	 * In CEC 2.0 it is specified that a device shall not go out of standby
419 	 * if an Active Source message is received. The CEC 1.4 implies this as
420 	 * well, even though it is not as clear about this as the 2.0 spec.
421 	 */
422 	announce("Sending Active Source message.");
423 	cec_msg_init(&msg, me, la);
424 	cec_msg_active_source(&msg, node->phys_addr);
425 	int res = doioctl(node, CEC_TRANSMIT, &msg);
426 	fail_on_test(res && res != ENONET);
427 	fail_on_test_v2_warn(node->remote[la].cec_version,
428 			     wait_changing_power_status(node, me, la, new_status, unresponsive_cnt));
429 	fail_on_test_v2_warn(node->remote[la].cec_version,
430 			     new_status != CEC_OP_POWER_STATUS_STANDBY);
431 	if (new_status != CEC_OP_POWER_STATUS_STANDBY)
432 		return standby_resume_standby(node, me, la, interactive);
433 
434 	node->remote[la].in_standby = true;
435 	if (unresponsive_cnt > 0)
436 		warn("The device stayed correctly in standby, but was unresponsive %d time%s.\n",
437 		     unresponsive_cnt, unresponsive_cnt == 1 ? "" : "s");
438 	return 0;
439 }
440 
wakeup_rc(struct node * node,unsigned me,unsigned la)441 static int wakeup_rc(struct node *node, unsigned me, unsigned la)
442 {
443 	struct cec_msg msg;
444 	struct cec_op_ui_command rc_press = {};
445 
446 	/* Todo: A release should be sent after this */
447 	cec_msg_init(&msg, me, la);
448 	rc_press.ui_cmd = CEC_OP_UI_CMD_POWER_ON_FUNCTION;
449 	cec_msg_user_control_pressed(&msg, &rc_press);
450 	fail_on_test(!transmit_timeout(node, &msg));
451 	fail_on_test(cec_msg_status_is_abort(&msg));
452 
453 	return 0;
454 }
455 
wakeup_tv(struct node * node,unsigned me,unsigned la)456 static int wakeup_tv(struct node *node, unsigned me, unsigned la)
457 {
458 	struct cec_msg msg;
459 
460 	cec_msg_init(&msg, me, la);
461 	cec_msg_image_view_on(&msg);
462 	msg.timeout = 2000;
463 	int res = doioctl(node, CEC_TRANSMIT, &msg);
464 	if (res == ENONET && la == CEC_LOG_ADDR_TV) {
465 		msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
466 		res = doioctl(node, CEC_TRANSMIT, &msg);
467 	}
468 	fail_on_test(res || !(msg.tx_status & CEC_TX_STATUS_OK));
469 	if (!cec_msg_status_is_abort(&msg))
470 		return 0;
471 
472 	cec_msg_init(&msg, me, la);
473 	cec_msg_text_view_on(&msg);
474 	fail_on_test(!transmit_timeout(node, &msg));
475 	if (!cec_msg_status_is_abort(&msg))
476 		return 0;
477 
478 	return wakeup_rc(node, me, la);
479 }
480 
wakeup_source(struct node * node,unsigned me,unsigned la)481 static int wakeup_source(struct node *node, unsigned me, unsigned la)
482 {
483 	struct cec_msg msg;
484 
485 	cec_msg_init(&msg, me, la);
486 	cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
487 	fail_on_test(!transmit_timeout(node, &msg));
488 	if (!cec_msg_status_is_abort(&msg))
489 		return 0;
490 
491 	return wakeup_rc(node, me, la);
492 }
493 
standby_resume_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)494 int standby_resume_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
495 {
496 	if (!node->remote[la].in_standby)
497 		return NOTAPPLICABLE;
498 
499 	int ret;
500 
501 	if (is_tv(la, node->remote[la].prim_type))
502 		ret = wakeup_tv(node, me, la);
503 	else
504 		ret = wakeup_source(node, me, la);
505 	if (ret)
506 		return ret;
507 
508 	unsigned unresponsive_cnt = 0;
509 
510 	announce("Wait for device to wake up");
511 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
512 	fail_on_test(interactive && !question("Is the device in On state?"));
513 
514 	if (unresponsive_cnt > 0)
515 		announce("The device went correctly out of standby, but was unresponsive %d time%s during the transition.\n",
516 			 unresponsive_cnt, unresponsive_cnt == 1 ? "" : "s");
517 
518 	return 0;
519 }
520 
standby_resume_wakeup_view_on(struct node * node,unsigned me,unsigned la,bool interactive,__u8 opcode)521 static int standby_resume_wakeup_view_on(struct node *node, unsigned me, unsigned la, bool interactive, __u8 opcode)
522 {
523 	if (!is_tv(la, node->remote[la].prim_type))
524 		return NOTAPPLICABLE;
525 
526 	unsigned unresponsive_cnt = 0;
527 
528 	sleep(5);
529 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
530 
531 	int ret = standby_resume_standby(node, me, la, interactive);
532 
533 	if (ret && opcode == CEC_MSG_TEXT_VIEW_ON) {
534 		ret = standby_resume_standby(node, me, la, interactive);
535 		if (!ret)
536 			warn("A STANDBY was sent right after the display reports it was powered on, but it was ignored.\n");
537 	}
538 
539 	if (ret)
540 		return ret;
541 
542 	sleep(6);
543 
544 	ret = one_touch_play_view_on(node, me, la, interactive, opcode);
545 
546 	if (ret)
547 		return ret;
548 
549 	announce("Wait for device to wake up");
550 	unresponsive_cnt = 0;
551 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
552 	fail_on_test(interactive && !question("Is the device in On state?"));
553 
554 	struct cec_msg msg;
555 
556 	cec_msg_init(&msg, me, la);
557 	cec_msg_active_source(&msg, node->phys_addr);
558 	fail_on_test(!transmit_timeout(node, &msg));
559 
560 	if (unresponsive_cnt > 0)
561 		announce("The device went correctly out of standby, but was unresponsive %d time%s during the transition.\n",
562 			 unresponsive_cnt, unresponsive_cnt == 1 ? "" : "s");
563 
564 	return 0;
565 }
566 
standby_resume_wakeup_image_view_on(struct node * node,unsigned me,unsigned la,bool interactive)567 static int standby_resume_wakeup_image_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
568 {
569 	return standby_resume_wakeup_view_on(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
570 }
571 
standby_resume_wakeup_text_view_on(struct node * node,unsigned me,unsigned la,bool interactive)572 static int standby_resume_wakeup_text_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
573 {
574 	return standby_resume_wakeup_view_on(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
575 }
576 
577 /* Test CEC 2.0 Power State Transitions (see HDMI 2.1, 11.5.5) */
power_state_transitions(struct node * node,unsigned me,unsigned la,bool interactive)578 static int power_state_transitions(struct node *node, unsigned me, unsigned la, bool interactive)
579 {
580 	struct cec_msg msg = {};
581 
582 	mode_set_follower(node);
583 	msg.timeout = 1000;
584 	doioctl(node, CEC_RECEIVE, &msg);
585 	cec_msg_init(&msg, me, la);
586 	cec_msg_standby(&msg);
587 	fail_on_test(!transmit_timeout(node, &msg));
588 	time_t start = time(nullptr);
589 	int res = util_receive(node, la, long_timeout * 1000, &msg, CEC_MSG_STANDBY,
590 			       CEC_MSG_REPORT_POWER_STATUS);
591 	fail_on_test(!res);
592 	if (res < 0) {
593 		warn("No Report Power Status seen when going to standby.\n");
594 		info("This might be due to the bug fix in commit cec935ce69fc\n");
595 		info("However, this was fixed in 5.5 and has been backported to LTS kernels,\n");
596 		info("so any kernel released after January 2020 should have this fix.\n");
597 		return OK_PRESUMED;
598 	}
599 	if (time(nullptr) - start > 3)
600 		warn("The first Report Power Status broadcast arrived > 3s after sending <Standby>\n");
601 	if (msg.msg[2] == CEC_OP_POWER_STATUS_STANDBY)
602 		return 0;
603 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_TO_STANDBY);
604 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, CEC_MSG_STANDBY,
605 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
606 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_STANDBY);
607 
608 	cec_msg_init(&msg, me, la);
609 	__u8 opcode;
610 	if (is_tv(la, node->remote[la].prim_type)) {
611 		cec_msg_image_view_on(&msg);
612 		opcode = msg.msg[2];
613 
614 		int res = doioctl(node, CEC_TRANSMIT, &msg);
615 
616 		if (res == ENONET && la == CEC_LOG_ADDR_TV) {
617 			msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
618 			fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
619 		}
620 	} else {
621 		cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
622 		opcode = msg.msg[2];
623 		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
624 	}
625 	fail_on_test(!(msg.tx_status & CEC_TX_STATUS_OK));
626 	start = time(nullptr);
627 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, opcode,
628 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
629 	if (time(nullptr) - start > 3)
630 		warn("The first Report Power Status broadcast arrived > 3s after sending <%s>\n",
631 		     opcode == CEC_MSG_IMAGE_VIEW_ON ? "Image View On" : "Set Stream Path");
632 	if (msg.msg[2] == CEC_OP_POWER_STATUS_ON)
633 		return 0;
634 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_TO_ON);
635 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, opcode,
636 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
637 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_ON);
638 
639 	return 0;
640 }
641 
standby_resume_wakeup_deck(struct node * node,unsigned me,unsigned la,bool interactive,__u8 opcode)642 static int standby_resume_wakeup_deck(struct node *node, unsigned me, unsigned la, bool interactive, __u8 opcode)
643 {
644 	struct cec_msg msg;
645 
646 	cec_msg_init(&msg, me, la);
647 	cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
648 	fail_on_test(!transmit_timeout(node, &msg));
649 	if (timed_out_or_abort(&msg))
650 		return OK_NOT_SUPPORTED;
651 
652 	unsigned unresponsive_cnt = 0;
653 
654 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
655 
656 	int ret = standby_resume_standby(node, me, la, interactive);
657 
658 	if (ret)
659 		return ret;
660 
661 	cec_msg_init(&msg, me, la);
662 	if (opcode == CEC_OP_PLAY_MODE_PLAY_FWD)
663 		cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_FWD);
664 	else
665 		cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_EJECT);
666 	fail_on_test(!transmit_timeout(node, &msg));
667 	fail_on_test(cec_msg_status_is_abort(&msg));
668 
669 	unresponsive_cnt = 0;
670 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
671 	fail_on_test(interactive && !question("Is the device in On state?"));
672 
673 	return OK;
674 }
675 
standby_resume_wakeup_deck_eject(struct node * node,unsigned me,unsigned la,bool interactive)676 static int standby_resume_wakeup_deck_eject(struct node *node, unsigned me, unsigned la, bool interactive)
677 {
678 	return standby_resume_wakeup_deck(node, me, la, interactive, CEC_OP_DECK_CTL_MODE_EJECT);
679 }
680 
standby_resume_wakeup_deck_play(struct node * node,unsigned me,unsigned la,bool interactive)681 static int standby_resume_wakeup_deck_play(struct node *node, unsigned me, unsigned la, bool interactive)
682 {
683 	return standby_resume_wakeup_deck(node, me, la, interactive, CEC_OP_PLAY_MODE_PLAY_FWD);
684 }
685 
standby_record(struct node * node,unsigned me,unsigned la,bool interactive,bool active_source)686 static int standby_record(struct node *node, unsigned me, unsigned la, bool interactive, bool active_source)
687 {
688 	struct cec_msg msg;
689 	__u8 rec_status;
690 	unsigned unresponsive_cnt = 0;
691 
692 	cec_msg_init(&msg, me, la);
693 	cec_msg_record_on_own(&msg);
694 	msg.reply = CEC_MSG_RECORD_STATUS;
695 	fail_on_test(!transmit_timeout(node, &msg, 10000));
696 	if (timed_out_or_abort(&msg))
697 		return OK_NOT_SUPPORTED;
698 	cec_ops_record_status(&msg, &rec_status);
699 	fail_on_test(rec_status != CEC_OP_RECORD_STATUS_CUR_SRC &&
700 	             rec_status != CEC_OP_RECORD_STATUS_ALREADY_RECORDING);
701 
702 	cec_msg_init(&msg, me, la);
703 	if (active_source)
704 		cec_msg_active_source(&msg, node->remote[la].phys_addr);
705 	else
706 		cec_msg_active_source(&msg, me);
707 	fail_on_test(!transmit_timeout(node, &msg));
708 
709 	cec_msg_init(&msg, me, la);
710 	cec_msg_standby(&msg);
711 	fail_on_test(!transmit_timeout(node, &msg));
712 	/* Standby should not interrupt the recording. */
713 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
714 
715 	cec_msg_init(&msg, me, la);
716 	cec_msg_record_off(&msg, false);
717 	fail_on_test(!transmit_timeout(node, &msg));
718 
719 	/* When the recording stops, recorder should standby unless it is the active source. */
720 	if (active_source) {
721 		fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_cnt));
722 	} else {
723 		fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_STANDBY, unresponsive_cnt));
724 		fail_on_test(interactive && !question("Is the device in standby?"));
725 		node->remote[la].in_standby = true;
726 
727 		int ret = standby_resume_wakeup(node, me, la, interactive);
728 		if (ret)
729 			return ret;
730 		node->remote[la].in_standby = false;
731 	}
732 
733 	return OK;
734 }
735 
standby_record_active_source(struct node * node,unsigned me,unsigned la,bool interactive)736 static int standby_record_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
737 {
738 	return standby_record(node, me, la, interactive, true);
739 }
740 
standby_record_inactive_source(struct node * node,unsigned me,unsigned la,bool interactive)741 static int standby_record_inactive_source(struct node *node, unsigned me, unsigned la, bool interactive)
742 {
743 	return standby_record(node, me, la, interactive, false);
744 }
745 
746 const vec_remote_subtests standby_resume_subtests{
747 	{ "Standby", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby },
748 	{ "Repeated Standby message does not wake up", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby_toggle },
749 	{ "Standby: Feature aborts unknown messages", CEC_LOG_ADDR_MASK_ALL, core_unknown, true },
750 	{ "Standby: Feature aborts Abort message", CEC_LOG_ADDR_MASK_ALL, core_abort, true },
751 	{ "Standby: Polling Message", CEC_LOG_ADDR_MASK_ALL, system_info_polling, true },
752 	{ "Standby: Give Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_give, true },
753 	{ "Standby: Give Physical Address", CEC_LOG_ADDR_MASK_ALL, system_info_phys_addr, true },
754 	{ "Standby: Give CEC Version", CEC_LOG_ADDR_MASK_ALL, system_info_version, true },
755 	{ "Standby: Give Device Vendor ID", CEC_LOG_ADDR_MASK_ALL, vendor_specific_commands_id, true },
756 	{ "Standby: Give OSD Name", CEC_LOG_ADDR_MASK_ALL, device_osd_transfer_give, true },
757 	{ "Standby: Get Menu Language", CEC_LOG_ADDR_MASK_ALL, system_info_get_menu_lang, true },
758 	{ "Standby: Give Device Features", CEC_LOG_ADDR_MASK_ALL, system_info_give_features, true },
759 	{ "No wakeup on Active Source", CEC_LOG_ADDR_MASK_ALL, standby_resume_active_source_nowake },
760 	{ "Wake up", CEC_LOG_ADDR_MASK_ALL, standby_resume_wakeup },
761 	{ "Wake up TV on Image View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_image_view_on },
762 	{ "Wake up TV on Text View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_text_view_on },
763 	{ "Power State Transitions", CEC_LOG_ADDR_MASK_TV, power_state_transitions, false, true },
764 	{ "Deck Eject Standby Resume", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, standby_resume_wakeup_deck_eject },
765 	{ "Deck Play Standby Resume", CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD, standby_resume_wakeup_deck_play },
766 	{ "Record Standby Active Source", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, standby_record_active_source },
767 	{ "Record Standby Inactive Source", CEC_LOG_ADDR_MASK_RECORD | CEC_LOG_ADDR_MASK_BACKUP, standby_record_inactive_source },
768 };
769