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 <cstdlib>
7 #include <cstring>
8
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <inttypes.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <ctype.h>
17 #include <errno.h>
18 #include <sys/ioctl.h>
19 #include <config.h>
20 #include <sstream>
21 #include <vector>
22 #include <map>
23
24 #include "cec-compliance.h"
25
26 #define test_case(name, tags, subtests) {name, tags, subtests, ARRAY_SIZE(subtests)}
27 #define test_case_ext(name, tags, subtests) {name, tags, subtests, subtests##_size}
28
29 struct remote_test {
30 const char *name;
31 const unsigned tags;
32 struct remote_subtest *subtests;
33 unsigned num_subtests;
34 };
35
36
37 /* System Information */
38
system_info_polling(struct node * node,unsigned me,unsigned la,bool interactive)39 int system_info_polling(struct node *node, unsigned me, unsigned la, bool interactive)
40 {
41 struct cec_msg msg = { };
42
43 cec_msg_init(&msg, me, la);
44 fail_on_test(doioctl(node, CEC_TRANSMIT, &msg));
45 if (node->remote_la_mask & (1 << la)) {
46 if (!cec_msg_status_is_ok(&msg)) {
47 fail("Polling a valid remote LA failed\n");
48 return FAIL_CRITICAL;
49 }
50 } else {
51 if (cec_msg_status_is_ok(&msg)) {
52 fail("Polling an invalid remote LA was successful\n");
53 return FAIL_CRITICAL;
54 }
55 return OK_NOT_SUPPORTED;
56 }
57
58 return 0;
59 }
60
system_info_phys_addr(struct node * node,unsigned me,unsigned la,bool interactive)61 int system_info_phys_addr(struct node *node, unsigned me, unsigned la, bool interactive)
62 {
63 struct cec_msg msg = { };
64
65 cec_msg_init(&msg, me, la);
66 cec_msg_give_physical_addr(&msg, true);
67 if (!transmit_timeout(node, &msg) || timed_out_or_abort(&msg)) {
68 fail_or_warn(node, "Give Physical Addr timed out\n");
69 return node->in_standby ? 0 : FAIL_CRITICAL;
70 }
71 fail_on_test(node->remote[la].phys_addr != ((msg.msg[2] << 8) | msg.msg[3]));
72 fail_on_test(node->remote[la].prim_type != msg.msg[4]);
73 return 0;
74 }
75
system_info_version(struct node * node,unsigned me,unsigned la,bool interactive)76 int system_info_version(struct node *node, unsigned me, unsigned la, bool interactive)
77 {
78 struct cec_msg msg = {};
79
80 cec_msg_init(&msg, me, la);
81 cec_msg_get_cec_version(&msg, true);
82 if (!transmit_timeout(node, &msg) || timed_out(&msg))
83 return fail_or_warn(node, "Get CEC Version timed out\n");
84 if (unrecognized_op(&msg))
85 return OK_NOT_SUPPORTED;
86 if (refused(&msg))
87 return OK_REFUSED;
88
89 /* This needs to be kept in sync with newer CEC versions */
90 fail_on_test(msg.msg[2] < CEC_OP_CEC_VERSION_1_3A ||
91 msg.msg[2] > CEC_OP_CEC_VERSION_2_0);
92 fail_on_test(node->remote[la].cec_version != msg.msg[2]);
93
94 return 0;
95 }
96
system_info_get_menu_lang(struct node * node,unsigned me,unsigned la,bool interactive)97 int system_info_get_menu_lang(struct node *node, unsigned me, unsigned la, bool interactive)
98 {
99 struct cec_msg msg = {};
100 char language[4];
101
102 cec_msg_init(&msg, me, la);
103 cec_msg_get_menu_language(&msg, true);
104 if (!transmit_timeout(node, &msg) || timed_out(&msg))
105 return fail_or_warn(node, "Get Menu Languages timed out\n");
106
107 /* Devices other than TVs shall send Feature Abort [Unregcognized Opcode]
108 in reply to Get Menu Language. */
109 fail_on_test(!is_tv(la, node->remote[la].prim_type) && !unrecognized_op(&msg));
110
111 if (unrecognized_op(&msg)) {
112 if (is_tv(la, node->remote[la].prim_type))
113 warn("TV did not respond to Get Menu Language.\n");
114 return OK_NOT_SUPPORTED;
115 }
116 if (refused(&msg))
117 return OK_REFUSED;
118 if (cec_msg_status_is_abort(&msg))
119 return OK_PRESUMED;
120 cec_ops_set_menu_language(&msg, language);
121 fail_on_test(strcmp(node->remote[la].language, language));
122
123 return 0;
124 }
125
system_info_set_menu_lang(struct node * node,unsigned me,unsigned la,bool interactive)126 static int system_info_set_menu_lang(struct node *node, unsigned me, unsigned la, bool interactive)
127 {
128 struct cec_msg msg = {};
129
130 cec_msg_init(&msg, me, la);
131 cec_msg_set_menu_language(&msg, "eng");
132 fail_on_test(!transmit_timeout(node, &msg));
133 if (unrecognized_op(&msg))
134 return OK_NOT_SUPPORTED;
135 if (refused(&msg))
136 return OK_REFUSED;
137
138 return OK_PRESUMED;
139 }
140
system_info_give_features(struct node * node,unsigned me,unsigned la,bool interactive)141 int system_info_give_features(struct node *node, unsigned me, unsigned la, bool interactive)
142 {
143 struct cec_msg msg = { };
144
145 cec_msg_init(&msg, me, la);
146 cec_msg_give_features(&msg, true);
147 if (!transmit_timeout(node, &msg) || timed_out(&msg))
148 return fail_or_warn(node, "Give Features timed out\n");
149 if (unrecognized_op(&msg)) {
150 if (node->remote[la].cec_version < CEC_OP_CEC_VERSION_2_0)
151 return OK_NOT_SUPPORTED;
152 fail_on_test_v2(node->remote[la].cec_version, true);
153 }
154 if (refused(&msg))
155 return OK_REFUSED;
156 if (node->remote[la].cec_version < CEC_OP_CEC_VERSION_2_0)
157 info("Device has CEC Version < 2.0 but supports Give Features.\n");
158
159 /* RC Profile and Device Features are assumed to be 1 byte. As of CEC 2.0 only
160 1 byte is used, but this might be extended in future versions. */
161 uint8_t cec_version, all_device_types;
162 const uint8_t *rc_profile, *dev_features;
163
164 cec_ops_report_features(&msg, &cec_version, &all_device_types, &rc_profile, &dev_features);
165 fail_on_test(rc_profile == NULL || dev_features == NULL);
166 info("All Device Types: \t\t%s\n", cec_all_dev_types2s(all_device_types).c_str());
167 info("RC Profile: \t%s", cec_rc_src_prof2s(*rc_profile, "").c_str());
168 info("Device Features: \t%s", cec_dev_feat2s(*dev_features, "").c_str());
169
170 if (!(cec_has_playback(1 << la) || cec_has_record(1 << la) || cec_has_tuner(1 << la)) &&
171 (*dev_features & CEC_OP_FEAT_DEV_HAS_SET_AUDIO_RATE)) {
172 return fail("Only Playback, Recording or Tuner devices shall set the Set Audio Rate bit\n");
173 }
174 if (!(cec_has_playback(1 << la) || cec_has_record(1 << la)) &&
175 (*dev_features & CEC_OP_FEAT_DEV_HAS_DECK_CONTROL))
176 return fail("Only Playback and Recording devices shall set the Supports Deck Control bit\n");
177 if (!cec_has_tv(1 << la) && node->remote[la].has_rec_tv)
178 return fail("Only TVs shall set the Record TV Screen bit\n");
179 if (cec_has_playback(1 << la) && (*dev_features & CEC_OP_FEAT_DEV_SINK_HAS_ARC_TX))
180 return fail("A Playback device cannot set the Sink Supports ARC Tx bit\n");
181 if (cec_has_tv(1 << la) && (*dev_features & CEC_OP_FEAT_DEV_SOURCE_HAS_ARC_RX))
182 return fail("A TV cannot set the Source Supports ARC Rx bit\n");
183
184 fail_on_test(cec_version != node->remote[la].cec_version);
185 fail_on_test(node->remote[la].rc_profile != *rc_profile);
186 fail_on_test(node->remote[la].dev_features != *dev_features);
187 fail_on_test(node->remote[la].all_device_types != all_device_types);
188 return 0;
189 }
190
191 static struct remote_subtest system_info_subtests[] = {
192 { "Polling Message", CEC_LOG_ADDR_MASK_ALL, system_info_polling },
193 { "Give Physical Address", CEC_LOG_ADDR_MASK_ALL, system_info_phys_addr },
194 { "Give CEC Version", CEC_LOG_ADDR_MASK_ALL, system_info_version },
195 { "Get Menu Language", CEC_LOG_ADDR_MASK_ALL, system_info_get_menu_lang },
196 { "Set Menu Language", CEC_LOG_ADDR_MASK_ALL, system_info_set_menu_lang },
197 { "Give Device Features", CEC_LOG_ADDR_MASK_ALL, system_info_give_features },
198 };
199
200
201 /* Core behavior */
202
core_unknown(struct node * node,unsigned me,unsigned la,bool interactive)203 int core_unknown(struct node *node, unsigned me, unsigned la, bool interactive)
204 {
205 struct cec_msg msg = { };
206 const uint8_t unknown_opcode = 0xfe;
207
208 /* Unknown opcodes should be responded to with Feature Abort, with abort
209 reason Unknown Opcode.
210
211 For CEC 2.0 and before, 0xfe is an unused opcode. The test possibly
212 needs to be updated for future CEC versions. */
213 cec_msg_init(&msg, me, la);
214 msg.len = 2;
215 msg.msg[1] = unknown_opcode;
216 if (!transmit_timeout(node, &msg) || timed_out(&msg))
217 return fail_or_warn(node, "Unknown Opcode timed out\n");
218 fail_on_test(!cec_msg_status_is_abort(&msg));
219
220 uint8_t abort_msg, reason;
221
222 cec_ops_feature_abort(&msg, &abort_msg, &reason);
223 fail_on_test(reason != CEC_OP_ABORT_UNRECOGNIZED_OP);
224 fail_on_test(abort_msg != 0xfe);
225
226 /* Unknown opcodes that are broadcast should be ignored */
227 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
228 msg.len = 2;
229 msg.msg[1] = unknown_opcode;
230 fail_on_test(!transmit_timeout(node, &msg));
231 fail_on_test(!timed_out(&msg));
232
233 return 0;
234 }
235
core_abort(struct node * node,unsigned me,unsigned la,bool interactive)236 int core_abort(struct node *node, unsigned me, unsigned la, bool interactive)
237 {
238 struct cec_msg msg = {};
239
240 /* The Abort message should always be responded to with Feature Abort
241 (with any abort reason) */
242 cec_msg_init(&msg, me, la);
243 cec_msg_abort(&msg);
244 if (!transmit_timeout(node, &msg) || timed_out(&msg))
245 return fail_or_warn(node, "Abort timed out\n");
246 fail_on_test(!cec_msg_status_is_abort(&msg));
247 return 0;
248 }
249
250 static struct remote_subtest core_subtests[] = {
251 { "Feature aborts unknown messages", CEC_LOG_ADDR_MASK_ALL, core_unknown },
252 { "Feature aborts Abort message", CEC_LOG_ADDR_MASK_ALL, core_abort },
253 };
254
255
256 /* Vendor Specific Commands */
257
vendor_specific_commands_id(struct node * node,unsigned me,unsigned la,bool interactive)258 int vendor_specific_commands_id(struct node *node, unsigned me, unsigned la, bool interactive)
259 {
260 struct cec_msg msg = {};
261
262 cec_msg_init(&msg, me, la);
263 cec_msg_give_device_vendor_id(&msg, true);
264 if (!transmit(node, &msg))
265 return fail_or_warn(node, "Give Device Vendor ID timed out\n");
266 if (unrecognized_op(&msg))
267 return OK_NOT_SUPPORTED;
268 if (refused(&msg))
269 return OK_REFUSED;
270 if (cec_msg_status_is_abort(&msg))
271 return OK_PRESUMED;
272 fail_on_test(node->remote[la].vendor_id !=
273 (uint32_t)((msg.msg[2] << 16) | (msg.msg[3] << 8) | msg.msg[4]));
274
275 return 0;
276 }
277
278 static struct remote_subtest vendor_specific_subtests[] = {
279 { "Give Device Vendor ID", CEC_LOG_ADDR_MASK_ALL, vendor_specific_commands_id },
280 };
281
282
283 /* Device OSD Transfer */
284
device_osd_transfer_set(struct node * node,unsigned me,unsigned la,bool interactive)285 static int device_osd_transfer_set(struct node *node, unsigned me, unsigned la, bool interactive)
286 {
287 struct cec_msg msg = { };
288
289 cec_msg_init(&msg, me, la);
290 cec_msg_set_osd_name(&msg, "Whatever");
291 fail_on_test(!transmit_timeout(node, &msg));
292 if (unrecognized_op(&msg)) {
293 if (is_tv(la, node->remote[la].prim_type) &&
294 node->remote[la].cec_version >= CEC_OP_CEC_VERSION_2_0)
295 warn("TV feature aborted Set OSD Name\n");
296 return OK_NOT_SUPPORTED;
297 }
298 if (refused(&msg))
299 return OK_REFUSED;
300
301 return OK_PRESUMED;
302 }
303
device_osd_transfer_give(struct node * node,unsigned me,unsigned la,bool interactive)304 int device_osd_transfer_give(struct node *node, unsigned me, unsigned la, bool interactive)
305 {
306 struct cec_msg msg = { };
307
308 /* Todo: CEC 2.0: devices with several logical addresses shall report
309 the same for each logical address. */
310 cec_msg_init(&msg, me, la);
311 cec_msg_give_osd_name(&msg, true);
312 if (!transmit_timeout(node, &msg) || timed_out(&msg))
313 return fail_or_warn(node, "Give OSD Name timed out\n");
314 fail_on_test(!is_tv(la, node->remote[la].prim_type) && unrecognized_op(&msg));
315 if (unrecognized_op(&msg))
316 return OK_NOT_SUPPORTED;
317 if (refused(&msg))
318 return OK_REFUSED;
319 if (cec_msg_status_is_abort(&msg))
320 return OK_PRESUMED;
321 char osd_name[15];
322 cec_ops_set_osd_name(&msg, osd_name);
323 fail_on_test(!osd_name[0]);
324 fail_on_test(strcmp(node->remote[la].osd_name, osd_name));
325 fail_on_test(msg.len != strlen(osd_name) + 2);
326
327 return 0;
328 }
329
330 static struct remote_subtest device_osd_transfer_subtests[] = {
331 { "Set OSD Name", CEC_LOG_ADDR_MASK_ALL, device_osd_transfer_set },
332 { "Give OSD Name", CEC_LOG_ADDR_MASK_ALL, device_osd_transfer_give },
333 };
334
335
336 /* OSD Display */
337
osd_string_set_default(struct node * node,unsigned me,unsigned la,bool interactive)338 static int osd_string_set_default(struct node *node, unsigned me, unsigned la, bool interactive)
339 {
340 struct cec_msg msg = { };
341 char osd[14];
342 bool unsuitable = false;
343
344 sprintf(osd, "Rept %x from %x", la, me);
345
346 interactive_info(true, "You should see \"%s\" appear on the screen", osd);
347 cec_msg_init(&msg, me, la);
348 cec_msg_set_osd_string(&msg, CEC_OP_DISP_CTL_DEFAULT, osd);
349 fail_on_test(!transmit_timeout(node, &msg));
350 /* In CEC 2.0 it is mandatory for a TV to support this if it reports so
351 in its Device Features. */
352 fail_on_test_v2(node->remote[la].cec_version,
353 unrecognized_op(&msg) &&
354 (node->remote[la].dev_features & CEC_OP_FEAT_DEV_HAS_SET_OSD_STRING));
355 if (unrecognized_op(&msg))
356 return OK_NOT_SUPPORTED;
357 if (refused(&msg))
358 return OK_REFUSED;
359 if (cec_msg_status_is_abort(&msg)) {
360 warn("The device is in an unsuitable state or cannot display the complete message.\n");
361 unsuitable = true;
362 }
363 node->remote[la].has_osd = true;
364 if (!interactive)
365 return OK_PRESUMED;
366
367 /* The CEC 1.4b CTS specifies that one should wait at least 20 seconds for the
368 string to be cleared on the remote device */
369 interactive_info(true, "Waiting 20s for OSD string to be cleared on the remote device");
370 sleep(20);
371 fail_on_test(!unsuitable && interactive && !question("Did the string appear and then disappear?"));
372
373 return 0;
374 }
375
osd_string_set_until_clear(struct node * node,unsigned me,unsigned la,bool interactive)376 static int osd_string_set_until_clear(struct node *node, unsigned me, unsigned la, bool interactive)
377 {
378 if (!node->remote[la].has_osd)
379 return NOTAPPLICABLE;
380
381 struct cec_msg msg = { };
382 char osd[14];
383 bool unsuitable = false;
384
385 strcpy(osd, "Appears 1 sec");
386 // Make sure the string is the maximum possible length
387 fail_on_test(strlen(osd) != 13);
388
389 interactive_info(true, "You should see \"%s\" appear on the screen for approximately three seconds.", osd);
390 cec_msg_init(&msg, me, la);
391 cec_msg_set_osd_string(&msg, CEC_OP_DISP_CTL_UNTIL_CLEARED, osd);
392 fail_on_test(!transmit(node, &msg));
393 if (cec_msg_status_is_abort(&msg) && !unrecognized_op(&msg)) {
394 warn("The device is in an unsuitable state or cannot display the complete message.\n");
395 unsuitable = true;
396 }
397 sleep(3);
398
399 cec_msg_init(&msg, me, la);
400 cec_msg_set_osd_string(&msg, CEC_OP_DISP_CTL_CLEAR, "");
401 fail_on_test(!transmit_timeout(node, &msg, 250));
402 fail_on_test(cec_msg_status_is_abort(&msg));
403 fail_on_test(!unsuitable && interactive && !question("Did the string appear?"));
404
405 if (interactive)
406 return 0;
407
408 return OK_PRESUMED;
409 }
410
osd_string_invalid(struct node * node,unsigned me,unsigned la,bool interactive)411 static int osd_string_invalid(struct node *node, unsigned me, unsigned la, bool interactive)
412 {
413 if (!node->remote[la].has_osd)
414 return NOTAPPLICABLE;
415
416 struct cec_msg msg = { };
417
418 /* Send Set OSD String with an Display Control operand. A Feature Abort is
419 expected in reply. */
420 interactive_info(true, "You should observe no change on the on screen display");
421 cec_msg_init(&msg, me, la);
422 cec_msg_set_osd_string(&msg, 0xff, "");
423 fail_on_test(!transmit_timeout(node, &msg));
424 fail_on_test(timed_out(&msg));
425 fail_on_test(!cec_msg_status_is_abort(&msg));
426 fail_on_test(interactive && question("Did the display change?"));
427
428 return 0;
429 }
430
431 static struct remote_subtest osd_string_subtests[] = {
432 { "Set OSD String with default timeout", CEC_LOG_ADDR_MASK_TV, osd_string_set_default },
433 { "Set OSD String with no timeout", CEC_LOG_ADDR_MASK_TV, osd_string_set_until_clear },
434 { "Set OSD String with invalid operand", CEC_LOG_ADDR_MASK_TV, osd_string_invalid },
435 };
436
437
438 /* Routing Control */
439
routing_control_inactive_source(struct node * node,unsigned me,unsigned la,bool interactive)440 static int routing_control_inactive_source(struct node *node, unsigned me, unsigned la, bool interactive)
441 {
442 struct cec_msg msg = {};
443 int response;
444
445 interactive_info(true, "Please make sure that the TV is currently viewing this source.");
446 mode_set_follower(node);
447 cec_msg_init(&msg, me, la);
448 cec_msg_inactive_source(&msg, node->phys_addr);
449 fail_on_test(!transmit(node, &msg));
450 if (unrecognized_op(&msg))
451 return OK_NOT_SUPPORTED;
452 if (refused(&msg))
453 return OK_REFUSED;
454 // It may take a bit of time for the Inactive Source message to take
455 // effect, so sleep a bit.
456 response = util_receive(node, CEC_LOG_ADDR_TV, 3000, &msg,
457 CEC_MSG_INACTIVE_SOURCE,
458 CEC_MSG_ACTIVE_SOURCE, CEC_MSG_SET_STREAM_PATH);
459 if (me == CEC_LOG_ADDR_TV) {
460 // Inactive Source should be ignored by all other devices
461 if (response >= 0)
462 return fail("Unexpected reply to Inactive Source\n");
463 fail_on_test(response >= 0);
464 } else {
465 if (response < 0)
466 warn("Expected Active Source or Set Stream Path reply to Inactive Source\n");
467 fail_on_test(interactive && !question("Did the TV switch away from or stop showing this source?"));
468 }
469
470 return 0;
471 }
472
routing_control_active_source(struct node * node,unsigned me,unsigned la,bool interactive)473 static int routing_control_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
474 {
475 struct cec_msg msg = {};
476
477 interactive_info(true, "Please switch the TV to another source.");
478 cec_msg_init(&msg, me, la);
479 cec_msg_active_source(&msg, node->phys_addr);
480 fail_on_test(!transmit_timeout(node, &msg));
481 fail_on_test(interactive && !question("Did the TV switch to this source?"));
482
483 if (interactive)
484 return 0;
485
486 return OK_PRESUMED;
487 }
488
routing_control_req_active_source(struct node * node,unsigned me,unsigned la,bool interactive)489 static int routing_control_req_active_source(struct node *node, unsigned me, unsigned la, bool interactive)
490 {
491 struct cec_msg msg = {};
492
493 /* We have now said that we are active source, so receiving a reply to
494 Request Active Source should fail the test. */
495 cec_msg_init(&msg, me, la);
496 cec_msg_request_active_source(&msg, true);
497 fail_on_test(!transmit_timeout(node, &msg));
498 fail_on_test(!timed_out(&msg));
499
500 return 0;
501 }
502
routing_control_set_stream_path(struct node * node,unsigned me,unsigned la,bool interactive)503 static int routing_control_set_stream_path(struct node *node, unsigned me, unsigned la, bool interactive)
504 {
505 struct cec_msg msg = {};
506 uint16_t phys_addr;
507
508 /* Send Set Stream Path with the remote physical address. We expect the
509 source to eventually send Active Source. The timeout of long_timeout
510 seconds is necessary because the device might have to wake up from standby.
511
512 In CEC 2.0 it is mandatory for sources to send Active Source. */
513 if (is_tv(la, node->remote[la].prim_type))
514 interactive_info(true, "Please ensure that the device is in standby.");
515 announce("Sending Set Stream Path and waiting for reply. This may take up to %llu s.", (long long)long_timeout);
516 cec_msg_init(&msg, me, la);
517 cec_msg_set_stream_path(&msg, node->remote[la].phys_addr);
518 msg.reply = CEC_MSG_ACTIVE_SOURCE;
519 fail_on_test(!transmit_timeout(node, &msg, long_timeout * 1000));
520 if (timed_out(&msg) && is_tv(la, node->remote[la].prim_type))
521 return OK_NOT_SUPPORTED;
522 if (timed_out(&msg) && node->remote[la].cec_version < CEC_OP_CEC_VERSION_2_0) {
523 warn("Device did not respond to Set Stream Path.\n");
524 return OK_NOT_SUPPORTED;
525 }
526 fail_on_test_v2(node->remote[la].cec_version, timed_out(&msg));
527 cec_ops_active_source(&msg, &phys_addr);
528 fail_on_test(phys_addr != node->remote[la].phys_addr);
529 if (is_tv(la, node->remote[la].prim_type))
530 fail_on_test(interactive && !question("Did the device go out of standby?"));
531
532 if (interactive || node->remote[la].cec_version >= CEC_OP_CEC_VERSION_2_0)
533 return 0;
534
535 return OK_PRESUMED;
536 }
537
538 static struct remote_subtest routing_control_subtests[] = {
539 { "Active Source", CEC_LOG_ADDR_MASK_TV, routing_control_active_source },
540 { "Request Active Source", CEC_LOG_ADDR_MASK_ALL, routing_control_req_active_source },
541 { "Inactive Source", CEC_LOG_ADDR_MASK_TV, routing_control_inactive_source },
542 { "Set Stream Path", CEC_LOG_ADDR_MASK_ALL, routing_control_set_stream_path },
543 };
544
545
546 /* Remote Control Passthrough */
547
rc_passthrough_user_ctrl_pressed(struct node * node,unsigned me,unsigned la,bool interactive)548 static int rc_passthrough_user_ctrl_pressed(struct node *node, unsigned me, unsigned la, bool interactive)
549 {
550 struct cec_msg msg = {};
551 struct cec_op_ui_command rc_press;
552
553 cec_msg_init(&msg, me, la);
554 rc_press.ui_cmd = CEC_OP_UI_CMD_VOLUME_UP; // Volume up key (the key is not crucial here)
555 cec_msg_user_control_pressed(&msg, &rc_press);
556 fail_on_test(!transmit_timeout(node, &msg));
557 /* Mandatory for all except devices which have taken logical address 15 */
558 fail_on_test_v2(node->remote[la].cec_version,
559 unrecognized_op(&msg) && !(cec_is_unregistered(1 << la)));
560 if (unrecognized_op(&msg))
561 return OK_NOT_SUPPORTED;
562 if (refused(&msg))
563 return OK_REFUSED;
564
565 return OK_PRESUMED;
566 }
567
rc_passthrough_user_ctrl_released(struct node * node,unsigned me,unsigned la,bool interactive)568 static int rc_passthrough_user_ctrl_released(struct node *node, unsigned me, unsigned la, bool interactive)
569 {
570 struct cec_msg msg = {};
571
572 cec_msg_init(&msg, me, la);
573 cec_msg_user_control_released(&msg);
574 fail_on_test(!transmit_timeout(node, &msg));
575 fail_on_test_v2(node->remote[la].cec_version,
576 cec_msg_status_is_abort(&msg) && !(la & CEC_LOG_ADDR_MASK_UNREGISTERED));
577 if (unrecognized_op(&msg))
578 return OK_NOT_SUPPORTED;
579 if (refused(&msg))
580 return OK_REFUSED;
581 node->remote[la].has_remote_control_passthrough = true;
582
583 return OK_PRESUMED;
584 }
585
586 static struct remote_subtest rc_passthrough_subtests[] = {
587 { "User Control Pressed", CEC_LOG_ADDR_MASK_ALL, rc_passthrough_user_ctrl_pressed },
588 { "User Control Released", CEC_LOG_ADDR_MASK_ALL, rc_passthrough_user_ctrl_released },
589 };
590
591
592 /* Device Menu Control */
593
594 /*
595 TODO: These are very rudimentary tests which should be expanded.
596 */
597
dev_menu_ctl_request(struct node * node,unsigned me,unsigned la,bool interactive)598 static int dev_menu_ctl_request(struct node *node, unsigned me, unsigned la, bool interactive)
599 {
600 struct cec_msg msg = {};
601
602 cec_msg_init(&msg, me, la);
603 cec_msg_menu_request(&msg, true, CEC_OP_MENU_REQUEST_QUERY);
604 fail_on_test(!transmit_timeout(node, &msg));
605 if (unrecognized_op(&msg))
606 return OK_NOT_SUPPORTED;
607 if (refused(&msg))
608 return OK_REFUSED;
609 if (cec_msg_status_is_abort(&msg))
610 return OK_PRESUMED;
611 if (node->remote[la].cec_version >= CEC_OP_CEC_VERSION_2_0)
612 warn("The Device Menu Control feature is deprecated in CEC 2.0\n");
613
614 return 0;
615 }
616
617 static struct remote_subtest dev_menu_ctl_subtests[] = {
618 { "Menu Request", static_cast<uint16_t>(~CEC_LOG_ADDR_MASK_TV), dev_menu_ctl_request },
619 { "User Control Pressed", CEC_LOG_ADDR_MASK_ALL, rc_passthrough_user_ctrl_pressed },
620 { "User Control Released", CEC_LOG_ADDR_MASK_ALL, rc_passthrough_user_ctrl_released },
621 };
622
623
624 /* Deck Control */
625
626 /*
627 TODO: These are very rudimentary tests which should be expanded.
628 */
629
deck_ctl_give_status(struct node * node,unsigned me,unsigned la,bool interactive)630 static int deck_ctl_give_status(struct node *node, unsigned me, unsigned la, bool interactive)
631 {
632 struct cec_msg msg = {};
633
634 cec_msg_init(&msg, me, la);
635 cec_msg_give_deck_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
636 fail_on_test(!transmit_timeout(node, &msg));
637 fail_on_test(timed_out(&msg));
638 if (is_playback_or_rec(la)) {
639 fail_on_test_v2(node->remote[la].cec_version,
640 node->remote[la].has_deck_ctl && cec_msg_status_is_abort(&msg));
641 fail_on_test_v2(node->remote[la].cec_version,
642 !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
643 }
644 if (unrecognized_op(&msg))
645 return OK_NOT_SUPPORTED;
646 if (refused(&msg))
647 return OK_REFUSED;
648 if (cec_msg_status_is_abort(&msg))
649 return OK_PRESUMED;
650
651 return 0;
652 }
653
deck_ctl_deck_status(struct node * node,unsigned me,unsigned la,bool interactive)654 static int deck_ctl_deck_status(struct node *node, unsigned me, unsigned la, bool interactive)
655 {
656 struct cec_msg msg = {};
657
658 cec_msg_init(&msg, me, la);
659 cec_msg_deck_status(&msg, CEC_OP_DECK_INFO_STOP);
660 fail_on_test(!transmit_timeout(node, &msg));
661 if (unrecognized_op(&msg))
662 return OK_NOT_SUPPORTED;
663 if (refused(&msg))
664 return OK_REFUSED;
665 if (cec_msg_status_is_abort(&msg))
666 return OK_PRESUMED;
667
668 return 0;
669 }
670
deck_ctl_deck_ctl(struct node * node,unsigned me,unsigned la,bool interactive)671 static int deck_ctl_deck_ctl(struct node *node, unsigned me, unsigned la, bool interactive)
672 {
673 struct cec_msg msg = {};
674
675 cec_msg_init(&msg, me, la);
676 cec_msg_deck_control(&msg, CEC_OP_DECK_CTL_MODE_STOP);
677 fail_on_test(!transmit_timeout(node, &msg));
678 if (is_playback_or_rec(la)) {
679 fail_on_test_v2(node->remote[la].cec_version,
680 node->remote[la].has_deck_ctl && unrecognized_op(&msg));
681 fail_on_test_v2(node->remote[la].cec_version,
682 !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
683 }
684 if (unrecognized_op(&msg))
685 return OK_NOT_SUPPORTED;
686 if (refused(&msg))
687 return OK_REFUSED;
688 if (cec_msg_status_is_abort(&msg))
689 return OK_PRESUMED;
690
691 return OK_PRESUMED;
692 }
693
deck_ctl_play(struct node * node,unsigned me,unsigned la,bool interactive)694 static int deck_ctl_play(struct node *node, unsigned me, unsigned la, bool interactive)
695 {
696 struct cec_msg msg = {};
697
698 cec_msg_init(&msg, me, la);
699 cec_msg_play(&msg, CEC_OP_PLAY_MODE_PLAY_STILL);
700 fail_on_test(!transmit_timeout(node, &msg));
701 if (is_playback_or_rec(la)) {
702 fail_on_test_v2(node->remote[la].cec_version,
703 node->remote[la].has_deck_ctl && unrecognized_op(&msg));
704 fail_on_test_v2(node->remote[la].cec_version,
705 !node->remote[la].has_deck_ctl && !unrecognized_op(&msg));
706 }
707 if (unrecognized_op(&msg))
708 return OK_NOT_SUPPORTED;
709 if (refused(&msg))
710 return OK_REFUSED;
711 if (cec_msg_status_is_abort(&msg))
712 return OK_PRESUMED;
713
714 return OK_PRESUMED;
715 }
716
717 static struct remote_subtest deck_ctl_subtests[] = {
718 { "Give Deck Status",
719 CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
720 deck_ctl_give_status },
721 { "Deck Status",
722 CEC_LOG_ADDR_MASK_ALL,
723 deck_ctl_deck_status },
724 { "Deck Control",
725 CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
726 deck_ctl_deck_ctl },
727 { "Play",
728 CEC_LOG_ADDR_MASK_PLAYBACK | CEC_LOG_ADDR_MASK_RECORD,
729 deck_ctl_play },
730 };
731
732
733 /* Tuner Control */
734
bcast_type2s(uint8_t bcast_type)735 static const char *bcast_type2s(uint8_t bcast_type)
736 {
737 switch (bcast_type) {
738 case CEC_OP_ANA_BCAST_TYPE_CABLE:
739 return "Cable";
740 case CEC_OP_ANA_BCAST_TYPE_SATELLITE:
741 return "Satellite";
742 case CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL:
743 return "Terrestrial";
744 default:
745 return "Future use";
746 }
747 }
748
log_tuner_service(const struct cec_op_tuner_device_info & info,const char * prefix="")749 static int log_tuner_service(const struct cec_op_tuner_device_info &info,
750 const char *prefix = "")
751 {
752 printf("\t\t%s", prefix);
753
754 if (info.is_analog) {
755 double freq_mhz = (info.analog.ana_freq * 625) / 10000.0;
756
757 printf("Analog Channel %.2f MHz (%s, %s)\n", freq_mhz,
758 bcast_system2s(info.analog.bcast_system),
759 bcast_type2s(info.analog.ana_bcast_type));
760
761 switch (info.analog.bcast_system) {
762 case CEC_OP_BCAST_SYSTEM_PAL_BG:
763 case CEC_OP_BCAST_SYSTEM_SECAM_LQ:
764 case CEC_OP_BCAST_SYSTEM_PAL_M:
765 case CEC_OP_BCAST_SYSTEM_NTSC_M:
766 case CEC_OP_BCAST_SYSTEM_PAL_I:
767 case CEC_OP_BCAST_SYSTEM_SECAM_DK:
768 case CEC_OP_BCAST_SYSTEM_SECAM_BG:
769 case CEC_OP_BCAST_SYSTEM_SECAM_L:
770 case CEC_OP_BCAST_SYSTEM_PAL_DK:
771 break;
772 default:
773 return fail("invalid analog bcast_system %u", info.analog.bcast_system);
774 }
775 if (info.analog.ana_bcast_type > CEC_OP_ANA_BCAST_TYPE_TERRESTRIAL)
776 return fail("invalid analog bcast_type %u\n", info.analog.ana_bcast_type);
777 fail_on_test(!info.analog.ana_freq);
778 return 0;
779 }
780
781 uint8_t system = info.digital.dig_bcast_system;
782
783 printf("%s Channel ", dig_bcast_system2s(system));
784 if (info.digital.service_id_method) {
785 uint16_t major = info.digital.channel.major;
786 uint16_t minor = info.digital.channel.minor;
787
788 switch (info.digital.channel.channel_number_fmt) {
789 case CEC_OP_CHANNEL_NUMBER_FMT_2_PART:
790 printf("%u.%u\n", major, minor);
791 break;
792 case CEC_OP_CHANNEL_NUMBER_FMT_1_PART:
793 printf("%u\n", minor);
794 break;
795 default:
796 printf("%u.%u\n", major, minor);
797 return fail("invalid service ID method\n");
798 }
799 return 0;
800 }
801
802
803 switch (system) {
804 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
805 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
806 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
807 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T: {
808 uint16_t tsid = info.digital.arib.transport_id;
809 uint16_t sid = info.digital.arib.service_id;
810 uint16_t onid = info.digital.arib.orig_network_id;
811
812 printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
813 break;
814 }
815 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
816 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
817 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
818 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T: {
819 uint16_t tsid = info.digital.atsc.transport_id;
820 uint16_t pn = info.digital.atsc.program_number;
821
822 printf("TSID: %u, Program Number: %u\n", tsid, pn);
823 break;
824 }
825 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
826 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
827 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
828 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
829 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T: {
830 uint16_t tsid = info.digital.dvb.transport_id;
831 uint16_t sid = info.digital.dvb.service_id;
832 uint16_t onid = info.digital.dvb.orig_network_id;
833
834 printf("TSID: %u, SID: %u, ONID: %u\n", tsid, sid, onid);
835 break;
836 }
837 default:
838 break;
839 }
840
841 switch (system) {
842 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_GEN:
843 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_GEN:
844 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_GEN:
845 warn_once("generic digital broadcast systems should not be used");
846 break;
847 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_BS:
848 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_CS:
849 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ARIB_T:
850 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_CABLE:
851 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_SAT:
852 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_ATSC_T:
853 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_C:
854 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S:
855 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2:
856 case CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_T:
857 break;
858 default:
859 return fail("invalid digital broadcast system %u", system);
860 }
861
862 if (info.digital.service_id_method > CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL)
863 return fail("invalid service ID method %u\n", info.digital.service_id_method);
864
865 return 0;
866 }
867
tuner_ctl_test(struct node * node,unsigned me,unsigned la,bool interactive)868 static int tuner_ctl_test(struct node *node, unsigned me, unsigned la, bool interactive)
869 {
870 struct cec_msg msg = {};
871 struct cec_op_tuner_device_info info = {};
872 std::vector<struct cec_op_tuner_device_info> info_vec;
873 bool has_tuner = (1 << la) & (CEC_LOG_ADDR_MASK_TV | CEC_LOG_ADDR_MASK_TUNER);
874 int ret;
875
876 cec_msg_init(&msg, me, la);
877 cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
878 fail_on_test(!transmit_timeout(node, &msg));
879 fail_on_test(!has_tuner && !timed_out_or_abort(&msg));
880 if (!has_tuner)
881 return OK_NOT_SUPPORTED;
882 if (timed_out(&msg) || unrecognized_op(&msg))
883 return OK_NOT_SUPPORTED;
884 if (cec_msg_status_is_abort(&msg))
885 return OK_REFUSED;
886
887 printf("\t Start Channel Scan\n");
888 cec_ops_tuner_device_status(&msg, &info);
889 info_vec.push_back(info);
890 ret = log_tuner_service(info);
891 if (ret)
892 return ret;
893
894 while (true) {
895 cec_msg_init(&msg, me, la);
896 cec_msg_tuner_step_increment(&msg);
897 fail_on_test(!transmit(node, &msg));
898 fail_on_test(cec_msg_status_is_abort(&msg));
899 if (cec_msg_status_is_abort(&msg)) {
900 fail_on_test(abort_reason(&msg) == CEC_OP_ABORT_UNRECOGNIZED_OP);
901 if (abort_reason(&msg) == CEC_OP_ABORT_REFUSED) {
902 warn("Tuner step increment does not wrap.\n");
903 break;
904 }
905
906 warn("Tuner at end of service list did not receive feature abort refused.\n");
907 break;
908 }
909 cec_msg_init(&msg, me, la);
910 cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
911 fail_on_test(!transmit_timeout(node, &msg));
912 fail_on_test(timed_out_or_abort(&msg));
913 memset(&info, 0, sizeof(info));
914 cec_ops_tuner_device_status(&msg, &info);
915 if (!memcmp(&info, &info_vec[0], sizeof(info)))
916 break;
917 ret = log_tuner_service(info);
918 if (ret)
919 return ret;
920 info_vec.push_back(info);
921 }
922 printf("\t Finished Channel Scan\n");
923
924 printf("\t Start Channel Test\n");
925 for (std::vector<struct cec_op_tuner_device_info>::iterator iter = info_vec.begin();
926 iter != info_vec.end(); iter++) {
927 cec_msg_init(&msg, me, la);
928 log_tuner_service(*iter, "Select ");
929 if (iter->is_analog)
930 cec_msg_select_analogue_service(&msg, iter->analog.ana_bcast_type,
931 iter->analog.ana_freq, iter->analog.bcast_system);
932 else
933 cec_msg_select_digital_service(&msg, &iter->digital);
934 fail_on_test(!transmit(node, &msg));
935 fail_on_test(cec_msg_status_is_abort(&msg));
936 cec_msg_init(&msg, me, la);
937 cec_msg_give_tuner_device_status(&msg, true, CEC_OP_STATUS_REQ_ONCE);
938 fail_on_test(!transmit_timeout(node, &msg));
939 fail_on_test(timed_out_or_abort(&msg));
940 memset(&info, 0, sizeof(info));
941 cec_ops_tuner_device_status(&msg, &info);
942 if (memcmp(&info, &(*iter), sizeof(info))) {
943 log_tuner_service(info);
944 log_tuner_service(*iter);
945 }
946 fail_on_test(memcmp(&info, &(*iter), sizeof(info)));
947 }
948 printf("\t Finished Channel Test\n");
949
950 cec_msg_init(&msg, me, la);
951 cec_msg_select_analogue_service(&msg, 3, 16000, 9);
952 printf("\t\tSelect invalid analog channel\n");
953 fail_on_test(!transmit_timeout(node, &msg));
954 fail_on_test(!cec_msg_status_is_abort(&msg));
955 fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
956 cec_msg_init(&msg, me, la);
957 info.digital.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_DIG_ID;
958 info.digital.dig_bcast_system = CEC_OP_DIG_SERVICE_BCAST_SYSTEM_DVB_S2;
959 info.digital.dvb.transport_id = 0;
960 info.digital.dvb.service_id = 0;
961 info.digital.dvb.orig_network_id = 0;
962 cec_msg_select_digital_service(&msg, &info.digital);
963 printf("\t\tSelect invalid digital channel\n");
964 fail_on_test(!transmit_timeout(node, &msg));
965 fail_on_test(!cec_msg_status_is_abort(&msg));
966 fail_on_test(abort_reason(&msg) != CEC_OP_ABORT_INVALID_OP);
967
968 return 0;
969 }
970
971 static struct remote_subtest tuner_ctl_subtests[] = {
972 { "Tuner Control", CEC_LOG_ADDR_MASK_TUNER | CEC_LOG_ADDR_MASK_TV, tuner_ctl_test },
973 };
974
975
976 /* One Touch Record */
977
978 /*
979 TODO: These are very rudimentary tests which should be expanded.
980
981 - The HDMI CEC 1.4b spec details that Standby shall not be acted upon while the
982 device is recording, but it should remember that it received Standby.
983 */
984
one_touch_rec_tv_screen(struct node * node,unsigned me,unsigned la,bool interactive)985 static int one_touch_rec_tv_screen(struct node *node, unsigned me, unsigned la, bool interactive)
986 {
987 /*
988 TODO:
989 - Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
990 checked for.
991 - The TV should ignore this message when received from other LA than Recording or
992 Reserved.
993 */
994 struct cec_msg msg = {};
995
996 cec_msg_init(&msg, me, la);
997 cec_msg_record_tv_screen(&msg, true);
998 fail_on_test(!transmit_timeout(node, &msg));
999 fail_on_test_v2(node->remote[la].cec_version,
1000 node->remote[la].has_rec_tv && unrecognized_op(&msg));
1001 fail_on_test_v2(node->remote[la].cec_version,
1002 !node->remote[la].has_rec_tv && !unrecognized_op(&msg));
1003 if (unrecognized_op(&msg))
1004 return OK_NOT_SUPPORTED;
1005 if (refused(&msg))
1006 return OK_REFUSED;
1007 if (cec_msg_status_is_abort(&msg))
1008 return OK_PRESUMED;
1009
1010 return 0;
1011 }
1012
one_touch_rec_on(struct node * node,unsigned me,unsigned la,bool interactive)1013 static int one_touch_rec_on(struct node *node, unsigned me, unsigned la, bool interactive)
1014 {
1015 /*
1016 TODO: Page 36 in HDMI CEC 1.4b spec lists additional behaviors that should be
1017 checked for.
1018 */
1019 struct cec_msg msg = {};
1020 struct cec_op_record_src rec_src = {};
1021
1022 rec_src.type = CEC_OP_RECORD_SRC_OWN;
1023 cec_msg_init(&msg, me, la);
1024 cec_msg_record_on(&msg, true, &rec_src);
1025 fail_on_test(!transmit_timeout(node, &msg));
1026 fail_on_test(timed_out(&msg));
1027 fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
1028 if (unrecognized_op(&msg))
1029 return OK_NOT_SUPPORTED;
1030 if (refused(&msg))
1031 return OK_REFUSED;
1032 if (cec_msg_status_is_abort(&msg))
1033 return OK_PRESUMED;
1034
1035 return 0;
1036 }
1037
one_touch_rec_off(struct node * node,unsigned me,unsigned la,bool interactive)1038 static int one_touch_rec_off(struct node *node, unsigned me, unsigned la, bool interactive)
1039 {
1040 struct cec_msg msg = {};
1041
1042 cec_msg_init(&msg, me, la);
1043 cec_msg_record_off(&msg, false);
1044 fail_on_test(!transmit_timeout(node, &msg));
1045 fail_on_test(cec_has_record(1 << la) && unrecognized_op(&msg));
1046 if (unrecognized_op(&msg))
1047 return OK_NOT_SUPPORTED;
1048 if (refused(&msg))
1049 return OK_REFUSED;
1050 if (cec_msg_status_is_abort(&msg))
1051 return OK_PRESUMED;
1052 if (timed_out(&msg))
1053 return OK_PRESUMED;
1054
1055 return 0;
1056 }
1057
one_touch_rec_status(struct node * node,unsigned me,unsigned la,bool interactive)1058 static int one_touch_rec_status(struct node *node, unsigned me, unsigned la, bool interactive)
1059 {
1060 struct cec_msg msg = {};
1061
1062 cec_msg_init(&msg, me, la);
1063 cec_msg_record_status(&msg, CEC_OP_RECORD_STATUS_DIG_SERVICE);
1064 fail_on_test(!transmit_timeout(node, &msg));
1065 if (unrecognized_op(&msg))
1066 return OK_NOT_SUPPORTED;
1067 if (refused(&msg))
1068 return OK_REFUSED;
1069 if (cec_msg_status_is_abort(&msg))
1070 return OK_PRESUMED;
1071
1072 return 0;
1073 }
1074
1075 static struct remote_subtest one_touch_rec_subtests[] = {
1076 { "Record TV Screen", CEC_LOG_ADDR_MASK_TV, one_touch_rec_tv_screen },
1077 { "Record On", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_on },
1078 { "Record Off", CEC_LOG_ADDR_MASK_RECORD, one_touch_rec_off },
1079 { "Record Status", CEC_LOG_ADDR_MASK_ALL, one_touch_rec_status },
1080 };
1081
1082
1083 /* Timer Programming */
1084
1085 /*
1086 TODO: These are very rudimentary tests which should be expanded.
1087 */
1088
timer_prog_set_analog_timer(struct node * node,unsigned me,unsigned la,bool interactive)1089 static int timer_prog_set_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1090 {
1091 /* TODO: Check the timer status for possible errors, etc. */
1092
1093 struct cec_msg msg = {};
1094
1095 cec_msg_init(&msg, me, la);
1096 cec_msg_set_analogue_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1097 CEC_OP_ANA_BCAST_TYPE_CABLE,
1098 7668, // 479.25 MHz
1099 node->remote[la].bcast_sys);
1100 fail_on_test(!transmit_timeout(node, &msg, 10000));
1101 if (timed_out(&msg)) {
1102 warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
1103 return OK_PRESUMED;
1104 }
1105 if (unrecognized_op(&msg))
1106 return OK_NOT_SUPPORTED;
1107 if (refused(&msg))
1108 return OK_REFUSED;
1109 if (cec_msg_status_is_abort(&msg))
1110 return OK_PRESUMED;
1111
1112 return 0;
1113 }
1114
timer_prog_set_digital_timer(struct node * node,unsigned me,unsigned la,bool interactive)1115 static int timer_prog_set_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1116 {
1117 /* TODO: Check the timer status for possible errors, etc. */
1118
1119 struct cec_msg msg = {};
1120 struct cec_op_digital_service_id digital_service_id = {};
1121
1122 digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
1123 digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
1124 digital_service_id.channel.minor = 1;
1125 digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
1126 cec_msg_init(&msg, me, la);
1127 cec_msg_set_digital_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1128 &digital_service_id);
1129 fail_on_test(!transmit_timeout(node, &msg, 10000));
1130 if (timed_out(&msg)) {
1131 warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
1132 return OK_PRESUMED;
1133 }
1134 if (unrecognized_op(&msg))
1135 return OK_NOT_SUPPORTED;
1136 if (refused(&msg))
1137 return OK_REFUSED;
1138 if (cec_msg_status_is_abort(&msg))
1139 return OK_PRESUMED;
1140
1141 return 0;
1142 }
1143
timer_prog_set_ext_timer(struct node * node,unsigned me,unsigned la,bool interactive)1144 static int timer_prog_set_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1145 {
1146 /* TODO: Check the timer status. */
1147
1148 struct cec_msg msg = {};
1149
1150 cec_msg_init(&msg, me, la);
1151 cec_msg_set_ext_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1152 CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
1153 fail_on_test(!transmit_timeout(node, &msg, 10000));
1154 if (timed_out(&msg)) {
1155 warn("Timed out waiting for Timer Status. Assuming timer was set.\n");
1156 return OK_PRESUMED;
1157 }
1158 if (unrecognized_op(&msg))
1159 return OK_NOT_SUPPORTED;
1160 if (refused(&msg))
1161 return OK_REFUSED;
1162 if (cec_msg_status_is_abort(&msg))
1163 return OK_PRESUMED;
1164
1165 return 0;
1166 }
1167
timer_prog_clear_analog_timer(struct node * node,unsigned me,unsigned la,bool interactive)1168 static int timer_prog_clear_analog_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1169 {
1170 /* TODO: Check the timer cleared status. */
1171
1172 struct cec_msg msg = {};
1173
1174 cec_msg_init(&msg, me, la);
1175 cec_msg_clear_analogue_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1176 CEC_OP_ANA_BCAST_TYPE_CABLE,
1177 7668, // 479.25 MHz
1178 node->remote[la].bcast_sys);
1179 fail_on_test(!transmit_timeout(node, &msg, 10000));
1180 if (timed_out(&msg)) {
1181 warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
1182 return OK_PRESUMED;
1183 }
1184 if (unrecognized_op(&msg))
1185 return OK_NOT_SUPPORTED;
1186 if (refused(&msg))
1187 return OK_REFUSED;
1188 if (cec_msg_status_is_abort(&msg))
1189 return OK_PRESUMED;
1190
1191 return 0;
1192 }
1193
timer_prog_clear_digital_timer(struct node * node,unsigned me,unsigned la,bool interactive)1194 static int timer_prog_clear_digital_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1195 {
1196 /* TODO: Check the timer cleared status. */
1197
1198 struct cec_msg msg = {};
1199 struct cec_op_digital_service_id digital_service_id = {};
1200
1201 digital_service_id.service_id_method = CEC_OP_SERVICE_ID_METHOD_BY_CHANNEL;
1202 digital_service_id.channel.channel_number_fmt = CEC_OP_CHANNEL_NUMBER_FMT_1_PART;
1203 digital_service_id.channel.minor = 1;
1204 digital_service_id.dig_bcast_system = node->remote[la].dig_bcast_sys;
1205 cec_msg_init(&msg, me, la);
1206 cec_msg_clear_digital_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1207 &digital_service_id);
1208 fail_on_test(!transmit_timeout(node, &msg, 10000));
1209 if (timed_out(&msg)) {
1210 warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
1211 return OK_PRESUMED;
1212 }
1213 if (unrecognized_op(&msg))
1214 return OK_NOT_SUPPORTED;
1215 if (refused(&msg))
1216 return OK_REFUSED;
1217 if (cec_msg_status_is_abort(&msg))
1218 return OK_PRESUMED;
1219
1220 return 0;
1221 }
1222
timer_prog_clear_ext_timer(struct node * node,unsigned me,unsigned la,bool interactive)1223 static int timer_prog_clear_ext_timer(struct node *node, unsigned me, unsigned la, bool interactive)
1224 {
1225 /* TODO: Check the timer cleared status. */
1226
1227 struct cec_msg msg = {};
1228
1229 cec_msg_init(&msg, me, la);
1230 cec_msg_clear_ext_timer(&msg, true, 1, 1, 0, 0, 1, 0, CEC_OP_REC_SEQ_ONCE_ONLY,
1231 CEC_OP_EXT_SRC_PHYS_ADDR, 0, node->phys_addr);
1232 fail_on_test(!transmit_timeout(node, &msg, 10000));
1233 if (timed_out(&msg)) {
1234 warn("Timed out waiting for Timer Cleared Status. Assuming timer was cleared.\n");
1235 return OK_PRESUMED;
1236 }
1237 if (unrecognized_op(&msg))
1238 return OK_NOT_SUPPORTED;
1239 if (refused(&msg))
1240 return OK_REFUSED;
1241 if (cec_msg_status_is_abort(&msg))
1242 return OK_PRESUMED;
1243
1244 return 0;
1245 }
1246
timer_prog_set_prog_title(struct node * node,unsigned me,unsigned la,bool interactive)1247 static int timer_prog_set_prog_title(struct node *node, unsigned me, unsigned la, bool interactive)
1248 {
1249 struct cec_msg msg = {};
1250
1251 cec_msg_init(&msg, me, la);
1252 cec_msg_set_timer_program_title(&msg, "Super-Hans II");
1253 fail_on_test(!transmit_timeout(node, &msg));
1254 if (unrecognized_op(&msg))
1255 return OK_NOT_SUPPORTED;
1256 if (refused(&msg))
1257 return OK_REFUSED;
1258
1259 return OK_PRESUMED;
1260 }
1261
timer_prog_timer_status(struct node * node,unsigned me,unsigned la,bool interactive)1262 static int timer_prog_timer_status(struct node *node, unsigned me, unsigned la, bool interactive)
1263 {
1264 struct cec_msg msg = {};
1265
1266 cec_msg_init(&msg, me, la);
1267 cec_msg_timer_status(&msg, CEC_OP_TIMER_OVERLAP_WARNING_NO_OVERLAP,
1268 CEC_OP_MEDIA_INFO_NO_MEDIA,
1269 CEC_OP_PROG_INFO_ENOUGH_SPACE,
1270 0, 0, 0);
1271 fail_on_test(!transmit_timeout(node, &msg));
1272 if (unrecognized_op(&msg))
1273 return OK_NOT_SUPPORTED;
1274 if (refused(&msg))
1275 return OK_REFUSED;
1276
1277 return OK_PRESUMED;
1278 }
1279
timer_prog_timer_clear_status(struct node * node,unsigned me,unsigned la,bool interactive)1280 static int timer_prog_timer_clear_status(struct node *node, unsigned me, unsigned la, bool interactive)
1281 {
1282 struct cec_msg msg = {};
1283
1284 cec_msg_init(&msg, me, la);
1285 cec_msg_timer_cleared_status(&msg, CEC_OP_TIMER_CLR_STAT_CLEARED);
1286 fail_on_test(!transmit_timeout(node, &msg));
1287 if (unrecognized_op(&msg))
1288 return OK_NOT_SUPPORTED;
1289 if (refused(&msg))
1290 return OK_REFUSED;
1291
1292 return OK_PRESUMED;
1293 }
1294
1295 static struct remote_subtest timer_prog_subtests[] = {
1296 { "Set Analogue Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_analog_timer },
1297 { "Set Digital Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_digital_timer },
1298 { "Set Timer Program Title", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_prog_title },
1299 { "Set External Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_set_ext_timer },
1300 { "Clear Analogue Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_analog_timer },
1301 { "Clear Digital Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_digital_timer },
1302 { "Clear External Timer", CEC_LOG_ADDR_MASK_RECORD, timer_prog_clear_ext_timer },
1303 { "Timer Status", CEC_LOG_ADDR_MASK_RECORD, timer_prog_timer_status },
1304 { "Timer Cleared Status", CEC_LOG_ADDR_MASK_RECORD, timer_prog_timer_clear_status },
1305 };
1306
cdc_hec_discover(struct node * node,unsigned me,unsigned la,bool print)1307 static int cdc_hec_discover(struct node *node, unsigned me, unsigned la, bool print)
1308 {
1309 /* TODO: For future use cases, it might be necessary to store the results
1310 from the HEC discovery to know which HECs are possible to form, etc. */
1311 struct cec_msg msg = {};
1312 uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_FOLLOWER;
1313 bool has_cdc = false;
1314
1315 doioctl(node, CEC_S_MODE, &mode);
1316 cec_msg_init(&msg, me, la);
1317 cec_msg_cdc_hec_discover(&msg);
1318 fail_on_test(!transmit(node, &msg));
1319
1320 /* The spec describes that we shall wait for messages
1321 up to 1 second, and extend the deadline for every received
1322 message. The maximum time to wait for incoming state reports
1323 is 5 seconds. */
1324 unsigned ts_start = get_ts_ms();
1325 while (get_ts_ms() - ts_start < 5000) {
1326 uint8_t from;
1327
1328 memset(&msg, 0, sizeof(msg));
1329 msg.timeout = 1000;
1330 if (doioctl(node, CEC_RECEIVE, &msg))
1331 break;
1332 from = cec_msg_initiator(&msg);
1333 if (msg.msg[1] == CEC_MSG_FEATURE_ABORT) {
1334 if (from == la)
1335 return fail("Device replied Feature Abort to broadcast message\n");
1336
1337 warn("Device %d replied Feature Abort to broadcast message\n", cec_msg_initiator(&msg));
1338 }
1339 if (msg.msg[1] != CEC_MSG_CDC_MESSAGE)
1340 continue;
1341 if (msg.msg[4] != CEC_MSG_CDC_HEC_REPORT_STATE)
1342 continue;
1343
1344 uint16_t phys_addr, target_phys_addr, hec_field;
1345 uint8_t hec_func_state, host_func_state, enc_func_state, cdc_errcode, has_field;
1346
1347 cec_ops_cdc_hec_report_state(&msg, &phys_addr, &target_phys_addr,
1348 &hec_func_state, &host_func_state,
1349 &enc_func_state, &cdc_errcode,
1350 &has_field, &hec_field);
1351
1352 if (target_phys_addr != node->phys_addr)
1353 continue;
1354 if (phys_addr == node->remote[la].phys_addr)
1355 has_cdc = true;
1356 if (!print)
1357 continue;
1358
1359 from = cec_msg_initiator(&msg);
1360 info("Received CDC HEC State report from device %d (%s):\n", from, cec_la2s(from));
1361 info("Physical address : %x.%x.%x.%x\n",
1362 cec_phys_addr_exp(phys_addr));
1363 info("Target physical address : %x.%x.%x.%x\n",
1364 cec_phys_addr_exp(target_phys_addr));
1365 info("HEC Functionality State : %s\n", hec_func_state2s(hec_func_state));
1366 info("Host Functionality State : %s\n", host_func_state2s(host_func_state));
1367 info("ENC Functionality State : %s\n", enc_func_state2s(enc_func_state));
1368 info("CDC Error Code : %s\n", cdc_errcode2s(cdc_errcode));
1369
1370 if (has_field) {
1371 std::ostringstream oss;
1372
1373 /* Bit 14 indicates whether or not the device's HDMI
1374 output has HEC support/is active. */
1375 if (!hec_field)
1376 oss << "None";
1377 else {
1378 if (hec_field & (1 << 14))
1379 oss << "out, ";
1380 for (int i = 13; i >= 0; i--) {
1381 if (hec_field & (1 << i))
1382 oss << "in" << (14 - i) << ", ";
1383 }
1384 oss << "\b\b ";
1385 }
1386 info("HEC Support Field : %s\n", oss.str().c_str());
1387 }
1388 }
1389
1390 mode = CEC_MODE_INITIATOR;
1391 doioctl(node, CEC_S_MODE, &mode);
1392
1393 if (has_cdc)
1394 return 0;
1395 return OK_NOT_SUPPORTED;
1396 }
1397
1398 static struct remote_subtest cdc_subtests[] = {
1399 { "CDC_HEC_Discover", CEC_LOG_ADDR_MASK_ALL, cdc_hec_discover },
1400 };
1401
1402
1403 /* Post-test checks */
1404
post_test_check_recognized(struct node * node,unsigned me,unsigned la,bool interactive)1405 static int post_test_check_recognized(struct node *node, unsigned me, unsigned la, bool interactive)
1406 {
1407 bool fail = false;
1408
1409 for (unsigned i = 0; i < 256; i++) {
1410 if (node->remote[la].recognized_op[i] && node->remote[la].unrecognized_op[i]) {
1411 struct cec_msg msg = {};
1412 msg.msg[1] = i;
1413 fail("Opcode %s has been both recognized by and has been replied\n", opcode2s(&msg).c_str());
1414 fail("Feature Abort [Unrecognized Opcode] to by the device.\n");
1415 fail = true;
1416 }
1417 }
1418 fail_on_test(fail);
1419
1420 return 0;
1421 }
1422
1423 static struct remote_subtest post_test_subtests[] = {
1424 { "Recognized/unrecognized message consistency", CEC_LOG_ADDR_MASK_ALL, post_test_check_recognized },
1425 };
1426
1427
1428 static struct remote_test tests[] = {
1429 test_case("Core",
1430 TAG_CORE,
1431 core_subtests),
1432 test_case_ext("Give Device Power Status feature",
1433 TAG_POWER_STATUS,
1434 power_status_subtests),
1435 test_case("System Information feature",
1436 TAG_SYSTEM_INFORMATION,
1437 system_info_subtests),
1438 test_case("Vendor Specific Commands feature",
1439 TAG_VENDOR_SPECIFIC_COMMANDS,
1440 vendor_specific_subtests),
1441 test_case("Device OSD Transfer feature",
1442 TAG_DEVICE_OSD_TRANSFER,
1443 device_osd_transfer_subtests),
1444 test_case("OSD String feature",
1445 TAG_OSD_DISPLAY,
1446 osd_string_subtests),
1447 test_case("Remote Control Passthrough feature",
1448 TAG_REMOTE_CONTROL_PASSTHROUGH,
1449 rc_passthrough_subtests),
1450 test_case("Device Menu Control feature",
1451 TAG_DEVICE_MENU_CONTROL,
1452 dev_menu_ctl_subtests),
1453 test_case("Deck Control feature",
1454 TAG_DECK_CONTROL,
1455 deck_ctl_subtests),
1456 test_case("Tuner Control feature",
1457 TAG_TUNER_CONTROL,
1458 tuner_ctl_subtests),
1459 test_case("One Touch Record feature",
1460 TAG_ONE_TOUCH_RECORD,
1461 one_touch_rec_subtests),
1462 test_case("Timer Programming feature",
1463 TAG_TIMER_PROGRAMMING,
1464 timer_prog_subtests),
1465 test_case("Capability Discovery and Control feature",
1466 TAG_CAP_DISCOVERY_CONTROL,
1467 cdc_subtests),
1468 test_case_ext("Dynamic Auto Lipsync feature",
1469 TAG_DYNAMIC_AUTO_LIPSYNC,
1470 dal_subtests),
1471 test_case_ext("Audio Return Channel feature",
1472 TAG_ARC_CONTROL,
1473 arc_subtests),
1474 test_case_ext("System Audio Control feature",
1475 TAG_SYSTEM_AUDIO_CONTROL,
1476 sac_subtests),
1477 test_case_ext("Audio Rate Control feature",
1478 TAG_AUDIO_RATE_CONTROL,
1479 audio_rate_ctl_subtests),
1480 test_case_ext("One Touch Play feature",
1481 TAG_ONE_TOUCH_PLAY,
1482 one_touch_play_subtests),
1483 test_case("Routing Control feature",
1484 TAG_ROUTING_CONTROL,
1485 routing_control_subtests),
1486 test_case_ext("Standby/Resume and Power Status",
1487 TAG_POWER_STATUS | TAG_STANDBY_RESUME,
1488 standby_resume_subtests),
1489 test_case("Post-test checks",
1490 TAG_CORE,
1491 post_test_subtests),
1492 };
1493
1494 static const unsigned num_tests = sizeof(tests) / sizeof(struct remote_test);
1495
1496 static std::map<std::string, int> mapTests;
1497 static std::map<std::string, bool> mapTestsNoWarnings;
1498
collectTests()1499 void collectTests()
1500 {
1501 std::map<std::string, uint64_t> mapTestFuncs;
1502
1503 for (unsigned i = 0; i < num_tests; i++) {
1504 for (unsigned j = 0; j < tests[i].num_subtests; j++) {
1505 std::string name = safename(tests[i].subtests[j].name);
1506 uint64_t func = (uint64_t)tests[i].subtests[j].test_fn;
1507
1508 if (mapTestFuncs.find(name) != mapTestFuncs.end() &&
1509 mapTestFuncs[name] != func) {
1510 fprintf(stderr, "Duplicate subtest name, but different tests: %s\n",
1511 tests[i].subtests[j].name);
1512 std::exit(EXIT_FAILURE);
1513 }
1514 mapTestFuncs[name] = func;
1515 mapTests[name] = DONT_CARE;
1516 mapTestsNoWarnings[name] = false;
1517 }
1518 }
1519 }
1520
listTests()1521 void listTests()
1522 {
1523 for (unsigned i = 0; i < num_tests; i++) {
1524 printf("%s:\n", tests[i].name);
1525 for (unsigned j = 0; j < tests[i].num_subtests; j++) {
1526 std::string name = safename(tests[i].subtests[j].name);
1527
1528 printf("\t%s\n", name.c_str());
1529 }
1530 }
1531 }
1532
setExpectedResult(char * optarg,bool no_warnings)1533 int setExpectedResult(char *optarg, bool no_warnings)
1534 {
1535 char *equal = std::strchr(optarg, '=');
1536
1537 if (!equal || equal == optarg || !isdigit(equal[1]))
1538 return 1;
1539 *equal = 0;
1540 std::string name = safename(optarg);
1541 if (mapTests.find(name) == mapTests.end())
1542 return 1;
1543 mapTests[name] = strtoul(equal + 1, NULL, 0);
1544 mapTestsNoWarnings[name] = no_warnings;
1545 return 0;
1546 }
1547
testRemote(struct node * node,unsigned me,unsigned la,unsigned test_tags,bool interactive)1548 void testRemote(struct node *node, unsigned me, unsigned la, unsigned test_tags,
1549 bool interactive)
1550 {
1551 printf("testing CEC local LA %d (%s) to remote LA %d (%s):\n",
1552 me, cec_la2s(me), la, cec_la2s(la));
1553
1554 if (!util_interactive_ensure_power_state(node, me, la, interactive, CEC_OP_POWER_STATUS_ON))
1555 return;
1556 if (node->remote[la].in_standby && !interactive) {
1557 announce("The remote device is in standby. It should be powered on when testing. Aborting.");
1558 return;
1559 }
1560 if (!node->remote[la].has_power_status) {
1561 announce("The device didn't support Give Device Power Status.");
1562 announce("Assuming that the device is powered on.");
1563 }
1564
1565 int ret = 0;
1566
1567 for (unsigned i = 0; i < num_tests; i++) {
1568 if ((tests[i].tags & test_tags) != tests[i].tags)
1569 continue;
1570
1571 printf("\t%s:\n", tests[i].name);
1572 for (unsigned j = 0; j < tests[i].num_subtests; j++) {
1573 const char *name = tests[i].subtests[j].name;
1574
1575 if (tests[i].subtests[j].for_cec20 &&
1576 (node->remote[la].cec_version < CEC_OP_CEC_VERSION_2_0 ||
1577 !node->has_cec20))
1578 continue;
1579
1580 if (tests[i].subtests[j].in_standby) {
1581 struct cec_log_addrs laddrs = { };
1582 doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1583
1584 if (!laddrs.log_addr_mask)
1585 continue;
1586 }
1587 node->in_standby = tests[i].subtests[j].in_standby;
1588 mode_set_initiator(node);
1589 unsigned old_warnings = warnings;
1590 ret = tests[i].subtests[j].test_fn(node, me, la, interactive);
1591 bool has_warnings = old_warnings < warnings;
1592 if (!(tests[i].subtests[j].la_mask & (1 << la)) && !ret)
1593 ret = OK_UNEXPECTED;
1594
1595 if (mapTests[safename(name)] != DONT_CARE) {
1596 if (ret != mapTests[safename(name)])
1597 printf("\t %s: %s (Expected '%s', got '%s')\n",
1598 name, ok(FAIL),
1599 result_name(mapTests[safename(name)], false),
1600 result_name(ret, false));
1601 else if (has_warnings && mapTestsNoWarnings[safename(name)])
1602 printf("\t %s: %s (Expected no warnings, got %d warnings)\n",
1603 name, ok(FAIL), warnings - old_warnings);
1604 else if (ret == FAIL)
1605 printf("\t %s: %s\n", name, ok(OK_EXPECTED_FAIL));
1606 else
1607 printf("\t %s: %s\n", name, ok(ret));
1608 } else if (ret != NOTAPPLICABLE)
1609 printf("\t %s: %s\n", name, ok(ret));
1610 if (ret == FAIL_CRITICAL)
1611 return;
1612 }
1613 printf("\n");
1614 }
1615 }
1616