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 <unistd.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <inttypes.h>
11 #include <sys/types.h>
12 #include <sys/stat.h>
13 #include <fcntl.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <sys/ioctl.h>
17 #include <config.h>
18 
19 #include "cec-compliance.h"
20 
21 
get_power_status(struct node * node,unsigned me,unsigned la,uint8_t & power_status)22 static bool get_power_status(struct node *node, unsigned me, unsigned la, uint8_t &power_status)
23 {
24 	struct cec_msg msg = {};
25 
26 	cec_msg_init(&msg, me, la);
27 	cec_msg_give_device_power_status(&msg, true);
28 	msg.timeout = 2000;
29 	int res = doioctl(node, CEC_TRANSMIT, &msg);
30 	if (res == EHOSTDOWN) {
31 		power_status = CEC_OP_POWER_STATUS_STANDBY;
32 		return true;
33 	}
34 	if (res || !(msg.tx_status & CEC_TX_STATUS_OK) || timed_out_or_abort(&msg))
35 		return false;
36 	cec_ops_report_power_status(&msg, &power_status);
37 	return true;
38 }
39 
util_interactive_ensure_power_state(struct node * node,unsigned me,unsigned la,bool interactive,uint8_t target_pwr)40 bool util_interactive_ensure_power_state(struct node *node, unsigned me, unsigned la, bool interactive,
41 						uint8_t target_pwr)
42 {
43 	interactive_info(true, "Please ensure that the device is in state %s.",
44 			 power_status2s(target_pwr));
45 
46 	if (!node->remote[la].has_power_status)
47 		return true;
48 
49 	while (interactive) {
50 		uint8_t pwr;
51 
52 		if (!get_power_status(node, me, la, pwr))
53 			announce("Failed to retrieve power status.");
54 		else if (pwr == target_pwr)
55 			return true;
56 		else
57 			announce("The device reported power status %s.", power_status2s(pwr));
58 		if (!question("Retry?"))
59 			return false;
60 	}
61 
62 	return true;
63 }
64 
65 
66 /* Give Device Power Status */
67 
power_status_give(struct node * node,unsigned me,unsigned la,bool interactive)68 static int power_status_give(struct node *node, unsigned me, unsigned la, bool interactive)
69 {
70 	struct cec_msg msg = { };
71 
72 	cec_msg_init(&msg, me, la);
73 	cec_msg_give_device_power_status(&msg, true);
74 	fail_on_test(!transmit_timeout(node, &msg));
75 	fail_on_test(timed_out(&msg));
76 	fail_on_test(unrecognized_op(&msg));
77 	if (refused(&msg))
78 		return OK_REFUSED;
79 	if (cec_msg_status_is_abort(&msg))
80 		return OK_PRESUMED;
81 
82 	uint8_t power_status;
83 	cec_ops_report_power_status(&msg, &power_status);
84 	fail_on_test(power_status >= 4);
85 
86 	return 0;
87 }
88 
power_status_report(struct node * node,unsigned me,unsigned la,bool interactive)89 static int power_status_report(struct node *node, unsigned me, unsigned la, bool interactive)
90 {
91 	struct cec_msg msg = {};
92 
93 	cec_msg_init(&msg, me, la);
94 	cec_msg_report_power_status(&msg, CEC_OP_POWER_STATUS_ON);
95 	fail_on_test(!transmit_timeout(node, &msg));
96 	if (unrecognized_op(&msg))
97 		return OK_NOT_SUPPORTED;
98 	if (refused(&msg))
99 		return OK_REFUSED;
100 
101 	return OK_PRESUMED;
102 }
103 
104 struct remote_subtest power_status_subtests[] = {
105 	{ "Give Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_give },
106 	{ "Report Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_report },
107 };
108 
109 const unsigned power_status_subtests_size = ARRAY_SIZE(power_status_subtests);
110 
111 
112 /* One Touch Play */
113 
one_touch_play_view_on(struct node * node,unsigned me,unsigned la,bool interactive,uint8_t opcode)114 static int one_touch_play_view_on(struct node *node, unsigned me, unsigned la, bool interactive,
115 				  uint8_t opcode)
116 {
117 	struct cec_msg msg = {};
118 
119 	cec_msg_init(&msg, me, la);
120 	if (opcode == CEC_MSG_IMAGE_VIEW_ON)
121 		cec_msg_image_view_on(&msg);
122 	else if (opcode == CEC_MSG_TEXT_VIEW_ON)
123 		cec_msg_text_view_on(&msg);
124 
125 	int res = doioctl(node, CEC_TRANSMIT, &msg);
126 
127 	if (res == EHOSTDOWN && la == CEC_LOG_ADDR_TV) {
128 		msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
129 		res = doioctl(node, CEC_TRANSMIT, &msg);
130 	}
131 	fail_on_test(res || !(msg.tx_status & CEC_TX_STATUS_OK));
132 
133 	fail_on_test(is_tv(la, node->remote[la].prim_type) && unrecognized_op(&msg));
134 	if (refused(&msg))
135 		return OK_REFUSED;
136 	if (cec_msg_status_is_abort(&msg))
137 		return OK_PRESUMED;
138 	if (opcode == CEC_MSG_IMAGE_VIEW_ON)
139 		node->remote[la].has_image_view_on = true;
140 	else if (opcode == CEC_MSG_TEXT_VIEW_ON)
141 		node->remote[la].has_text_view_on = true;
142 
143 	return 0;
144 }
145 
one_touch_play_image_view_on(struct node * node,unsigned me,unsigned la,bool interactive)146 static int one_touch_play_image_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
147 {
148 	return one_touch_play_view_on(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
149 }
150 
one_touch_play_text_view_on(struct node * node,unsigned me,unsigned la,bool interactive)151 static int one_touch_play_text_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
152 {
153 	return one_touch_play_view_on(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
154 }
155 
one_touch_play_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive,uint8_t opcode)156 static int one_touch_play_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive,
157 					 uint8_t opcode)
158 {
159 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_STANDBY));
160 
161 	int ret = one_touch_play_view_on(node, me, la, interactive, opcode);
162 
163 	if (ret && ret != OK_PRESUMED)
164 		return ret;
165 	fail_on_test(interactive && !question("Did the TV turn on?"));
166 
167 	if (interactive)
168 		return 0;
169 
170 	return OK_PRESUMED;
171 }
172 
one_touch_play_image_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)173 static int one_touch_play_image_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
174 {
175 	if (!interactive || !node->remote[la].has_image_view_on)
176 		return NOTAPPLICABLE;
177 	return one_touch_play_view_on_wakeup(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
178 }
179 
one_touch_play_text_view_on_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)180 static int one_touch_play_text_view_on_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
181 {
182 	if (!interactive || !node->remote[la].has_text_view_on)
183 		return NOTAPPLICABLE;
184 	return one_touch_play_view_on_wakeup(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
185 }
186 
one_touch_play_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive,uint8_t opcode)187 static int one_touch_play_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive,
188 					 uint8_t opcode)
189 {
190 	struct cec_msg msg = {};
191 	int ret;
192 
193 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
194 
195 	interactive_info(true, "Please switch the TV to another source.");
196 	ret = one_touch_play_view_on(node, me, la, interactive, opcode);
197 	if (ret && ret != OK_PRESUMED)
198 		return ret;
199 	cec_msg_init(&msg, me, la);
200 	cec_msg_active_source(&msg, node->phys_addr);
201 	fail_on_test(!transmit_timeout(node, &msg));
202 	fail_on_test(interactive && !question("Did the TV switch to this source?"));
203 
204 	if (interactive)
205 		return 0;
206 
207 	return OK_PRESUMED;
208 }
209 
one_touch_play_image_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive)210 static int one_touch_play_image_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_IMAGE_VIEW_ON);
215 }
216 
one_touch_play_text_view_on_change(struct node * node,unsigned me,unsigned la,bool interactive)217 static int one_touch_play_text_view_on_change(struct node *node, unsigned me, unsigned la, bool interactive)
218 {
219 	if (!interactive || !node->remote[la].has_text_view_on)
220 		return NOTAPPLICABLE;
221 	return one_touch_play_view_on_change(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
222 }
223 
one_touch_play_req_active_source(struct node * node,unsigned me,unsigned la,bool interactive)224 static int one_touch_play_req_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
225 {
226 	struct cec_msg msg = {};
227 
228 	cec_msg_init(&msg, me, la);
229 	cec_msg_active_source(&msg, node->phys_addr);
230 	fail_on_test(!transmit_timeout(node, &msg));
231 
232 	/* We have now said that we are active source, so receiving a reply to
233 	   Request Active Source should fail the test. */
234 	cec_msg_init(&msg, me, la);
235 	cec_msg_request_active_source(&msg, true);
236 	fail_on_test(!transmit_timeout(node, &msg));
237 	fail_on_test(!timed_out(&msg));
238 
239 	return 0;
240 }
241 
242 struct remote_subtest one_touch_play_subtests[] = {
243 	{ "Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on },
244 	{ "Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on },
245 	{ "Wakeup on Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on_wakeup },
246 	{ "Wakeup Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on_wakeup },
247 	{ "Input change on Image View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_image_view_on_change },
248 	{ "Input change on Text View On", CEC_LOG_ADDR_MASK_TV, one_touch_play_text_view_on_change },
249 	{ "Active Source and Request Active Source", CEC_LOG_ADDR_MASK_ALL, one_touch_play_req_active_source },
250 };
251 
252 const unsigned one_touch_play_subtests_size = ARRAY_SIZE(one_touch_play_subtests);
253 
254 
255 /* Standby / Resume */
256 
257 /* The default sleep time between power status requests. */
258 #define SLEEP_POLL_POWER_STATUS 2
259 
wait_changing_power_status(struct node * node,unsigned me,unsigned la,uint8_t & new_status,unsigned & unresponsive_time)260 static bool wait_changing_power_status(struct node *node, unsigned me, unsigned la, uint8_t &new_status,
261 				       unsigned &unresponsive_time)
262 {
263 	uint8_t old_status;
264 	time_t t = time(NULL);
265 
266 	announce("Checking for power status change. This may take up to %llu s.", (long long)long_timeout);
267 	if (!get_power_status(node, me, la, old_status))
268 		return false;
269 	while (time(NULL) - t < long_timeout) {
270 		uint8_t power_status;
271 
272 		if (!get_power_status(node, me, la, power_status)) {
273 			/* Some TVs become completely unresponsive when transitioning
274 			   between power modes. Register that this happens, but continue
275 			   the test. */
276 			unresponsive_time = time(NULL) - t;
277 		} else if (old_status != power_status) {
278 			new_status = power_status;
279 			return true;
280 		}
281 		sleep(SLEEP_POLL_POWER_STATUS);
282 	}
283 	new_status = old_status;
284 	return false;
285 }
286 
poll_stable_power_status(struct node * node,unsigned me,unsigned la,uint8_t expected_status,unsigned & unresponsive_time)287 static bool poll_stable_power_status(struct node *node, unsigned me, unsigned la,
288 				     uint8_t expected_status, unsigned &unresponsive_time)
289 {
290 	bool transient = false;
291 	unsigned time_to_transient = 0;
292 	time_t t = time(NULL);
293 
294 	/* Some devices can use several seconds to transition from one power
295 	   state to another, so the power state must be repeatedly polled */
296 	announce("Waiting for new stable power status. This may take up to %llu s.", (long long)long_timeout);
297 	while (time(NULL) - t < long_timeout) {
298 		uint8_t power_status;
299 
300 		if (!get_power_status(node, me, la, power_status)) {
301 			/* Some TVs become completely unresponsive when transitioning
302 			   between power modes. Register that this happens, but continue
303 			   the test. */
304 			unresponsive_time = time(NULL) - t;
305 			sleep(SLEEP_POLL_POWER_STATUS);
306 			continue;
307 		}
308 		if (!transient && (power_status == CEC_OP_POWER_STATUS_TO_ON ||
309 				   power_status == CEC_OP_POWER_STATUS_TO_STANDBY)) {
310 			time_to_transient = time(NULL) - t;
311 			transient = true;
312 			warn_once_on_test(expected_status == CEC_OP_POWER_STATUS_ON &&
313 					  power_status == CEC_OP_POWER_STATUS_TO_STANDBY);
314 			warn_once_on_test(expected_status == CEC_OP_POWER_STATUS_STANDBY &&
315 					  power_status == CEC_OP_POWER_STATUS_TO_ON);
316 		}
317 		if (power_status == expected_status) {
318 			announce("Transient state after %d s, stable state %s after %d s",
319 			     time_to_transient, power_status2s(power_status), (int)(time(NULL) - t));
320 			return true;
321 		}
322 		sleep(SLEEP_POLL_POWER_STATUS);
323 	}
324 	return false;
325 }
326 
standby_resume_standby(struct node * node,unsigned me,unsigned la,bool interactive)327 static int standby_resume_standby(struct node *node, unsigned me, unsigned la, bool interactive)
328 {
329 	if (!node->remote[la].has_power_status)
330 		return NOTAPPLICABLE;
331 
332 	struct cec_msg msg = {};
333 	unsigned unresponsive_time = 0;
334 
335 	fail_on_test(!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON));
336 
337 	/*
338 	 * Some displays only accept Standby from the Active Source.
339 	 * So make us the Active Source before sending Standby.
340 	 */
341 	if (is_tv(la, node->remote[la].prim_type)) {
342 		announce("Sending Active Source message.");
343 		cec_msg_init(&msg, me, la);
344 		cec_msg_active_source(&msg, node->phys_addr);
345 		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
346 	}
347 	announce("Sending Standby message.");
348 
349 	cec_msg_init(&msg, me, la);
350 	cec_msg_standby(&msg);
351 	fail_on_test(!transmit_timeout(node, &msg));
352 	fail_on_test(cec_msg_status_is_abort(&msg));
353 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_STANDBY, unresponsive_time));
354 	fail_on_test(interactive && !question("Is the device in standby?"));
355 	node->remote[la].in_standby = true;
356 
357 	if (unresponsive_time > 0)
358 		warn("The device went correctly into standby, but became unresponsive for %d s during the transition.\n",
359 		     unresponsive_time);
360 
361 	return 0;
362 }
363 
standby_resume_standby_toggle(struct node * node,unsigned me,unsigned la,bool interactive)364 static int standby_resume_standby_toggle(struct node *node, unsigned me, unsigned la, bool interactive)
365 {
366 	if (!node->remote[la].in_standby)
367 		return NOTAPPLICABLE;
368 
369 	struct cec_msg msg = {};
370 	unsigned unresponsive_time = 0;
371 	uint8_t new_status;
372 
373 	node->remote[la].in_standby = false;
374 
375 	/* Send Standby again to test that it is not acting like a toggle */
376 	announce("Sending Standby message.");
377 	cec_msg_init(&msg, me, la);
378 	cec_msg_standby(&msg);
379 	int res = doioctl(node, CEC_TRANSMIT, &msg);
380 	fail_on_test(res && res != EHOSTDOWN);
381 	fail_on_test(cec_msg_status_is_abort(&msg));
382 	fail_on_test(wait_changing_power_status(node, me, la, new_status, unresponsive_time));
383 	fail_on_test(new_status != CEC_OP_POWER_STATUS_STANDBY);
384 	fail_on_test(interactive && !question("Is the device still in standby?"));
385 	node->remote[la].in_standby = true;
386 	if (unresponsive_time > 0)
387 		warn("The device went correctly into standby, but became unresponsive for %d s during the transition.\n",
388 		     unresponsive_time);
389 
390 	return 0;
391 }
392 
standby_resume_active_source_nowake(struct node * node,unsigned me,unsigned la,bool interactive)393 static int standby_resume_active_source_nowake(struct node *node, unsigned me, unsigned la, bool interactive)
394 {
395 	if (!node->remote[la].in_standby)
396 		return NOTAPPLICABLE;
397 
398 	struct cec_msg msg = {};
399 	unsigned unresponsive_time = 0;
400 	uint8_t new_status;
401 
402 	node->remote[la].in_standby = false;
403 
404 	/*
405 	 * In CEC 2.0 it is specified that a device shall not go out of standby
406 	 * if an Active Source message is received. The CEC 1.4 implies this as
407 	 * well, even though it is not as clear about this as the 2.0 spec.
408 	 */
409 	announce("Sending Active Source message.");
410 	cec_msg_init(&msg, me, la);
411 	cec_msg_active_source(&msg, node->phys_addr);
412 	int res = doioctl(node, CEC_TRANSMIT, &msg);
413 	fail_on_test(res && res != EHOSTDOWN);
414 	fail_on_test_v2_warn(node->remote[la].cec_version,
415 			     wait_changing_power_status(node, me, la, new_status, unresponsive_time));
416 	fail_on_test_v2_warn(node->remote[la].cec_version,
417 			     new_status != CEC_OP_POWER_STATUS_STANDBY);
418 	if (new_status != CEC_OP_POWER_STATUS_STANDBY)
419 		return standby_resume_standby(node, me, la, interactive);
420 
421 	node->remote[la].in_standby = true;
422 	if (unresponsive_time > 0)
423 		warn("The device stayed correctly in standby, but became unresponsive for %d s.\n",
424 		     unresponsive_time);
425 	return 0;
426 }
427 
wakeup_rc(struct node * node,unsigned me,unsigned la)428 static int wakeup_rc(struct node *node, unsigned me, unsigned la)
429 {
430 	struct cec_msg msg = {};
431 	struct cec_op_ui_command rc_press = {};
432 
433 	/* Todo: A release should be sent after this */
434 	cec_msg_init(&msg, me, la);
435 	rc_press.ui_cmd = CEC_OP_UI_CMD_POWER_ON_FUNCTION;
436 	cec_msg_user_control_pressed(&msg, &rc_press);
437 	fail_on_test(!transmit_timeout(node, &msg));
438 	fail_on_test(cec_msg_status_is_abort(&msg));
439 
440 	return 0;
441 }
442 
wakeup_tv(struct node * node,unsigned me,unsigned la)443 static int wakeup_tv(struct node *node, unsigned me, unsigned la)
444 {
445 	struct cec_msg msg = {};
446 
447 	cec_msg_init(&msg, me, la);
448 	cec_msg_image_view_on(&msg);
449 	msg.timeout = 2000;
450 	int res = doioctl(node, CEC_TRANSMIT, &msg);
451 	if (res == EHOSTDOWN && la == CEC_LOG_ADDR_TV) {
452 		msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
453 		res = doioctl(node, CEC_TRANSMIT, &msg);
454 	}
455 	fail_on_test(res || !(msg.tx_status & CEC_TX_STATUS_OK));
456 	if (!cec_msg_status_is_abort(&msg))
457 		return 0;
458 
459 	cec_msg_init(&msg, me, la);
460 	cec_msg_text_view_on(&msg);
461 	fail_on_test(!transmit_timeout(node, &msg));
462 	if (!cec_msg_status_is_abort(&msg))
463 		return 0;
464 
465 	return wakeup_rc(node, me, la);
466 }
467 
wakeup_source(struct node * node,unsigned me,unsigned la)468 static int wakeup_source(struct node *node, unsigned me, unsigned la)
469 {
470 	struct cec_msg msg = {};
471 
472 	cec_msg_init(&msg, me, la);
473 	cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
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 
standby_resume_wakeup(struct node * node,unsigned me,unsigned la,bool interactive)481 static int standby_resume_wakeup(struct node *node, unsigned me, unsigned la, bool interactive)
482 {
483 	if (!node->remote[la].in_standby)
484 		return NOTAPPLICABLE;
485 
486 	int ret;
487 
488 	if (is_tv(la, node->remote[la].prim_type))
489 		ret = wakeup_tv(node, me, la);
490 	else
491 		ret = wakeup_source(node, me, la);
492 	if (ret)
493 		return ret;
494 
495 	unsigned unresponsive_time = 0;
496 
497 	announce("Device is woken up");
498 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
499 	fail_on_test(interactive && !question("Is the device in On state?"));
500 
501 	if (unresponsive_time > 0)
502 		warn("The device went correctly out of standby, but became unresponsive for %d s during the transition.\n",
503 		     unresponsive_time);
504 
505 	return 0;
506 }
507 
standby_resume_wakeup_view_on(struct node * node,unsigned me,unsigned la,bool interactive,uint8_t opcode)508 static int standby_resume_wakeup_view_on(struct node *node, unsigned me, unsigned la, bool interactive, uint8_t opcode)
509 {
510 	if (!is_tv(la, node->remote[la].prim_type))
511 		return NOTAPPLICABLE;
512 
513 	unsigned unresponsive_time = 0;
514 
515 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
516 
517 	int ret = standby_resume_standby(node, me, la, interactive);
518 
519 	if (ret && opcode == CEC_MSG_TEXT_VIEW_ON) {
520 		ret = standby_resume_standby(node, me, la, interactive);
521 		if (!ret)
522 			warn("A STANDBY was sent right after the display reports it was powered on, but it was ignored.\n");
523 	}
524 
525 	if (ret)
526 		return ret;
527 
528 	sleep(6);
529 
530 	ret = one_touch_play_view_on(node, me, la, interactive, opcode);
531 
532 	if (ret)
533 		return ret;
534 
535 	announce("Device is woken up");
536 	unresponsive_time = 0;
537 	fail_on_test(!poll_stable_power_status(node, me, la, CEC_OP_POWER_STATUS_ON, unresponsive_time));
538 	fail_on_test(interactive && !question("Is the device in On state?"));
539 
540 	struct cec_msg msg = {};
541 
542 	cec_msg_init(&msg, me, la);
543 	cec_msg_active_source(&msg, node->phys_addr);
544 	fail_on_test(!transmit_timeout(node, &msg));
545 
546 	if (unresponsive_time > 0)
547 		warn("The device went correctly out of standby, but became unresponsive for %d s during the transition.\n",
548 		     unresponsive_time);
549 
550 	return 0;
551 }
552 
standby_resume_wakeup_image_view_on(struct node * node,unsigned me,unsigned la,bool interactive)553 static int standby_resume_wakeup_image_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
554 {
555 	return standby_resume_wakeup_view_on(node, me, la, interactive, CEC_MSG_IMAGE_VIEW_ON);
556 }
557 
standby_resume_wakeup_text_view_on(struct node * node,unsigned me,unsigned la,bool interactive)558 static int standby_resume_wakeup_text_view_on(struct node *node, unsigned me, unsigned la, bool interactive)
559 {
560 	return standby_resume_wakeup_view_on(node, me, la, interactive, CEC_MSG_TEXT_VIEW_ON);
561 }
562 
power_state_transitions(struct node * node,unsigned me,unsigned la,bool interactive)563 static int power_state_transitions(struct node *node, unsigned me, unsigned la, bool interactive)
564 {
565 	struct cec_msg msg = {};
566 
567 	mode_set_follower(node);
568 	msg.timeout = 1000;
569 	doioctl(node, CEC_RECEIVE, &msg);
570 	cec_msg_init(&msg, me, la);
571 	cec_msg_standby(&msg);
572 	fail_on_test(!transmit_timeout(node, &msg));
573 	time_t start = time(NULL);
574 	int res = util_receive(node, la, long_timeout * 1000, &msg, CEC_MSG_STANDBY,
575 			       CEC_MSG_REPORT_POWER_STATUS);
576 	fail_on_test(!res);
577 	if (res < 0) {
578 		warn("No Report Power Status seen when going to standby. Probably due to this bug: https://patchwork.linuxtv.org/patch/60447\n");
579 		return OK_PRESUMED;
580 	}
581 	if (time(NULL) - start > 3)
582 		warn("The first Report Power Status broadcast arrived > 3s after sending <Standby>\n");
583 	if (msg.msg[2] == CEC_OP_POWER_STATUS_STANDBY)
584 		return 0;
585 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_TO_STANDBY);
586 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, CEC_MSG_STANDBY,
587 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
588 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_STANDBY);
589 
590 	cec_msg_init(&msg, me, la);
591 	uint8_t opcode;
592 	if (is_tv(la, node->remote[la].prim_type)) {
593 		cec_msg_image_view_on(&msg);
594 		opcode = msg.msg[2];
595 
596 		int res = doioctl(node, CEC_TRANSMIT, &msg);
597 
598 		if (res == EHOSTDOWN && la == CEC_LOG_ADDR_TV) {
599 			msg.msg[0] = (CEC_LOG_ADDR_UNREGISTERED << 4) | la;
600 			fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
601 		}
602 	} else {
603 		cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
604 		opcode = msg.msg[2];
605 		fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
606 	}
607 	fail_on_test(!(msg.tx_status & CEC_TX_STATUS_OK));
608 	start = time(NULL);
609 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, opcode,
610 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
611 	if (time(NULL) - start > 3)
612 		warn("The first Report Power Status broadcast arrived > 3s after sending <%s>\n",
613 		     opcode == CEC_MSG_IMAGE_VIEW_ON ? "Image View On" : "Set Stream Path");
614 	if (msg.msg[2] == CEC_OP_POWER_STATUS_ON)
615 		return 0;
616 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_TO_ON);
617 	fail_on_test(util_receive(node, la, long_timeout * 1000, &msg, opcode,
618 		     CEC_MSG_REPORT_POWER_STATUS) <= 0);
619 	fail_on_test(msg.msg[2] != CEC_OP_POWER_STATUS_ON);
620 
621 	return 0;
622 }
623 
624 struct remote_subtest standby_resume_subtests[] = {
625 	{ "Standby", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby },
626 	{ "Repeated Standby message does not wake up", CEC_LOG_ADDR_MASK_ALL, standby_resume_standby_toggle },
627 	{ "Standby: Feature aborts unknown messages", CEC_LOG_ADDR_MASK_ALL, core_unknown, true },
628 	{ "Standby: Feature aborts Abort message", CEC_LOG_ADDR_MASK_ALL, core_abort, true },
629 	{ "Standby: Polling Message", CEC_LOG_ADDR_MASK_ALL, system_info_polling, true },
630 	{ "Standby: Give Device Power Status", CEC_LOG_ADDR_MASK_ALL, power_status_give, true },
631 	{ "Standby: Give Physical Address", CEC_LOG_ADDR_MASK_ALL, system_info_phys_addr, true },
632 	{ "Standby: Give CEC Version", CEC_LOG_ADDR_MASK_ALL, system_info_version, true },
633 	{ "Standby: Give Device Vendor ID", CEC_LOG_ADDR_MASK_ALL, vendor_specific_commands_id, true },
634 	{ "Standby: Give OSD Name", CEC_LOG_ADDR_MASK_ALL, device_osd_transfer_give, true },
635 	{ "Standby: Get Menu Language", CEC_LOG_ADDR_MASK_ALL, system_info_get_menu_lang, true },
636 	{ "Standby: Give Device Features", CEC_LOG_ADDR_MASK_ALL, system_info_give_features, true },
637 	{ "No wakeup on Active Source", CEC_LOG_ADDR_MASK_ALL, standby_resume_active_source_nowake },
638 	{ "Wake up", CEC_LOG_ADDR_MASK_ALL, standby_resume_wakeup},
639 	{ "Wake up TV on Image View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_image_view_on },
640 	{ "Wake up TV on Text View On", CEC_LOG_ADDR_MASK_TV, standby_resume_wakeup_text_view_on },
641 	{ "Power State Transitions", CEC_LOG_ADDR_MASK_TV, power_state_transitions, false, true },
642 };
643 
644 const unsigned standby_resume_subtests_size = ARRAY_SIZE(standby_resume_subtests);
645