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