1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 /*
3 * Copyright 2016 Cisco Systems, Inc. and/or its affiliates. All rights reserved.
4 */
5
6 #include <cerrno>
7 #include <ctime>
8 #include <string>
9
10 #include <sys/ioctl.h>
11 #include <sys/time.h>
12
13 #include "cec-follower.h"
14 #include "compiler.h"
15
16 #define VOLUME_MAX 0x64
17 #define VOLUME_MIN 0
18
19 /* States for the RC handling */
20 #define NOPRESS 0
21 #define PRESS 1
22 #define PRESS_HOLD 2
23
24 /* The follower safety timeout as defined in the spec */
25 #define FOLLOWER_SAFETY_TIMEOUT 450
26 #define MIN_INITIATOR_REP_TIME 200
27 #define MAX_INITIATOR_REP_TIME 500
28
29 /* Time between each polling message sent to a device */
30 #define POLL_PERIOD 15000
31
32 /* The maximum interval in nanoseconds between audio rate messages as defined in the spec */
33 #define MAX_AUD_RATE_MSG_INTERVAL_NS (2 * 1000000000ULL)
34
35 /* The maximum interval in nanoseconds to allow a deck to skip forward/reverse */
36 #define MAX_DECK_SKIP_NS (2 * 1000000000ULL)
37
38 struct cec_enum_values {
39 const char *type_name;
40 __u8 value;
41 };
42
43 struct la_info la_info[15];
44
45 static struct timespec start_monotonic;
46 static struct timeval start_timeofday;
47 static constexpr time_t time_to_transient = 1;
48 static constexpr time_t time_to_stable = 8;
49
get_ui_cmd_string(__u8 ui_cmd)50 static const char *get_ui_cmd_string(__u8 ui_cmd)
51 {
52 return cec_log_ui_cmd_string(ui_cmd) ? : "Unknown";
53 }
54
ts2s(__u64 ts,bool wallclock)55 static std::string ts2s(__u64 ts, bool wallclock)
56 {
57 std::string s;
58 struct timeval sub;
59 struct timeval res;
60 __u64 diff;
61 char buf[64];
62 time_t t;
63
64 if (!wallclock) {
65 sprintf(buf, "%llu.%03llus", ts / 1000000000, (ts % 1000000000) / 1000000);
66 return buf;
67 }
68 diff = ts - start_monotonic.tv_sec * 1000000000ULL - start_monotonic.tv_nsec;
69 sub.tv_sec = diff / 1000000000ULL;
70 sub.tv_usec = (diff % 1000000000ULL) / 1000;
71 timeradd(&start_timeofday, &sub, &res);
72 t = res.tv_sec;
73 s = ctime(&t);
74 s = s.substr(0, s.length() - 6);
75 sprintf(buf, "%03llu", (__u64)res.tv_usec / 1000);
76 return s + "." + buf;
77 }
78
log_event(struct cec_event & ev,bool wallclock)79 static void log_event(struct cec_event &ev, bool wallclock)
80 {
81 __u16 pa;
82
83 if (ev.flags & CEC_EVENT_FL_DROPPED_EVENTS)
84 printf("(Note: events were lost)\n");
85 if (ev.flags & CEC_EVENT_FL_INITIAL_STATE)
86 printf("Initial ");
87 switch (ev.event) {
88 case CEC_EVENT_STATE_CHANGE:
89 pa = ev.state_change.phys_addr;
90 printf("Event: State Change: PA: %x.%x.%x.%x, LA mask: 0x%04x\n",
91 cec_phys_addr_exp(pa),
92 ev.state_change.log_addr_mask);
93 break;
94 case CEC_EVENT_LOST_MSGS:
95 printf("Event: Lost Messages\n");
96 break;
97 case CEC_EVENT_PIN_HPD_LOW:
98 case CEC_EVENT_PIN_HPD_HIGH:
99 printf("Event: HPD Pin %s\n",
100 ev.event == CEC_EVENT_PIN_HPD_HIGH ? "High" : "Low");
101 warn("Unexpected HPD pin event!\n");
102 break;
103 case CEC_EVENT_PIN_CEC_LOW:
104 case CEC_EVENT_PIN_CEC_HIGH:
105 printf("Event: CEC Pin %s\n",
106 ev.event == CEC_EVENT_PIN_CEC_HIGH ? "High" : "Low");
107 warn("Unexpected CEC pin event!\n");
108 break;
109 default:
110 printf("Event: Unknown (0x%x)\n", ev.event);
111 break;
112 }
113 if (show_info)
114 printf("\tTimestamp: %s\n", ts2s(ev.ts, wallclock).c_str());
115 }
116
reply_feature_abort(struct node * node,struct cec_msg * msg,__u8 reason)117 void reply_feature_abort(struct node *node, struct cec_msg *msg, __u8 reason)
118 {
119 unsigned la = cec_msg_initiator(msg);
120 __u8 opcode = cec_msg_opcode(msg);
121 __u64 ts_now = get_ts();
122
123 if (cec_msg_is_broadcast(msg) || cec_msg_initiator(msg) == CEC_LOG_ADDR_UNREGISTERED)
124 return;
125 if (reason == CEC_OP_ABORT_UNRECOGNIZED_OP) {
126 la_info[la].feature_aborted[opcode].count++;
127 if (la_info[la].feature_aborted[opcode].count == 2) {
128 /* If the Abort Reason was "Unrecognized opcode", the Initiator should not send
129 the same message to the same Follower again at that time to avoid saturating
130 the bus. */
131 warn("Received message %s from LA %d (%s) shortly after\n",
132 opcode2s(msg).c_str(), la, cec_la2s(la));
133 warn("replying Feature Abort [Unrecognized Opcode] to the same message.\n");
134 }
135 }
136 else if (la_info[la].feature_aborted[opcode].count) {
137 warn("Replying Feature Abort with abort reason different than [Unrecognized Opcode]\n");
138 warn("to message that has previously been replied Feature Abort to with [Unrecognized Opcode].\n");
139 }
140 else
141 la_info[la].feature_aborted[opcode].ts = ts_now;
142
143 cec_msg_reply_feature_abort(msg, reason);
144 transmit(node, msg);
145 }
146
exit_standby(struct node * node)147 static bool exit_standby(struct node *node)
148 {
149 /* Cancel any standby request that was pending. */
150 node->state.record_received_standby = false;
151
152 if (node->state.power_status == CEC_OP_POWER_STATUS_STANDBY ||
153 node->state.power_status == CEC_OP_POWER_STATUS_TO_STANDBY) {
154 node->state.old_power_status = node->state.power_status;
155 node->state.power_status = CEC_OP_POWER_STATUS_ON;
156 node->state.power_status_changed_time = time(nullptr);
157 dev_info("Changing state to on\n");
158 return true;
159 }
160 return false;
161 }
162
enter_standby(struct node * node)163 bool enter_standby(struct node *node)
164 {
165 if (node->state.power_status == CEC_OP_POWER_STATUS_ON ||
166 node->state.power_status == CEC_OP_POWER_STATUS_TO_ON) {
167 /*
168 * Standby should not interrupt a recording in progress, but
169 * remember to go to standby once the recording is finished.
170 */
171 if (node->state.one_touch_record_on) {
172 node->state.record_received_standby = true;
173 return false;
174 }
175 node->state.old_power_status = node->state.power_status;
176 node->state.power_status = CEC_OP_POWER_STATUS_STANDBY;
177 node->state.power_status_changed_time = time(nullptr);
178 node->state.deck_skip_start = 0;
179 node->state.record_received_standby = false;
180 dev_info("Changing state to standby\n");
181 return true;
182 }
183 return false;
184 }
185
get_duration_ms(__u64 ts_a,__u64 ts_b)186 static unsigned get_duration_ms(__u64 ts_a, __u64 ts_b)
187 {
188 return (ts_a - ts_b) / 1000000;
189 }
190
rc_press_hold_stop(const struct state * state)191 static void rc_press_hold_stop(const struct state *state)
192 {
193 unsigned mean_duration = state->rc_duration_sum / state->rc_press_hold_count;
194
195 dev_info("Stop Press and Hold. Mean duration between User Control Pressed messages: %dms\n",
196 mean_duration);
197 if (mean_duration < MIN_INITIATOR_REP_TIME) {
198 warn("The mean duration between User Control Pressed messages is lower\n");
199 warn("than the Minimum Initiator Repetition Time (200ms).\n");
200 }
201 if (mean_duration > MAX_INITIATOR_REP_TIME) {
202 warn("The mean duration between User Control Pressed messages is higher\n");
203 warn("than the Maximum Initiator Repetition Time (500ms).\n");
204 }
205 }
206
pa_common_mask(__u16 pa1,__u16 pa2)207 static __u16 pa_common_mask(__u16 pa1, __u16 pa2)
208 {
209 __u16 mask = 0xf000;
210
211 for (int i = 0; i < 3; i++) {
212 if ((pa1 & mask) != (pa2 & mask))
213 break;
214 mask = (mask >> 4) | 0xf000;
215 }
216 return mask << 4;
217 }
pa_are_adjacent(__u16 pa1,__u16 pa2)218 static bool pa_are_adjacent(__u16 pa1, __u16 pa2)
219 {
220 const __u16 mask = pa_common_mask(pa1, pa2);
221 const __u16 trail_mask = ((~mask) & 0xffff) >> 4;
222
223 if (pa1 == CEC_PHYS_ADDR_INVALID || pa2 == CEC_PHYS_ADDR_INVALID || pa1 == pa2)
224 return false;
225 if ((pa1 & trail_mask) || (pa2 & trail_mask))
226 return false;
227 return !((pa1 & ~mask) && (pa2 & ~mask));
228 }
229
pa_is_upstream_from(__u16 pa1,__u16 pa2)230 static bool pa_is_upstream_from(__u16 pa1, __u16 pa2)
231 {
232 const __u16 mask = pa_common_mask(pa1, pa2);
233
234 if (pa1 == CEC_PHYS_ADDR_INVALID || pa2 == CEC_PHYS_ADDR_INVALID)
235 return false;
236 return !(pa1 & ~mask) && (pa2 & ~mask);
237 }
238
current_power_state(struct node * node)239 static __u8 current_power_state(struct node *node)
240 {
241 time_t t = time(nullptr);
242
243 if (t - node->state.power_status_changed_time <= time_to_transient)
244 return node->state.old_power_status;
245 if (t - node->state.power_status_changed_time >= time_to_stable)
246 return node->state.power_status;
247 if (node->state.power_status == CEC_OP_POWER_STATUS_ON)
248 return CEC_OP_POWER_STATUS_TO_ON;
249 return CEC_OP_POWER_STATUS_TO_STANDBY;
250 }
251
aud_rate_msg_interval_check(struct node * node,__u64 ts_new)252 static void aud_rate_msg_interval_check(struct node *node, __u64 ts_new)
253 {
254 /*
255 * The interval since the last audio rate message is only relevant
256 * if the Source is currently in audio rate controlled mode
257 * (i.e. state.last_aud_rate_rx_ts != 0).
258 */
259 __u64 ts_old = node->state.last_aud_rate_rx_ts;
260
261 if (ts_old) {
262 __u64 interval = ts_new - ts_old;
263
264 if (interval > MAX_AUD_RATE_MSG_INTERVAL_NS) {
265 warn("The interval since the last Audio Rate Control message was > 2s.\n");
266 node->state.last_aud_rate_rx_ts = 0;
267 }
268 }
269 }
270
update_deck_state(struct node * node,unsigned me,__u8 deck_state_new)271 static void update_deck_state(struct node *node, unsigned me, __u8 deck_state_new)
272 {
273 if (node->state.deck_state != deck_state_new) {
274 node->state.deck_state = deck_state_new;
275
276 if (node->state.deck_report_changes) {
277 struct cec_msg msg;
278
279 cec_msg_init(&msg, me, node->state.deck_report_changes_to);
280 cec_msg_deck_status(&msg, node->state.deck_state);
281 transmit(node, &msg);
282 }
283 }
284 }
285
processMsg(struct node * node,struct cec_msg & msg,unsigned me,__u8 type)286 static void processMsg(struct node *node, struct cec_msg &msg, unsigned me, __u8 type)
287 {
288 __u8 to = cec_msg_destination(&msg);
289 __u8 from = cec_msg_initiator(&msg);
290 bool is_bcast = cec_msg_is_broadcast(&msg);
291 __u16 remote_pa = (from < 15) ? node->remote_phys_addr[from] : CEC_PHYS_ADDR_INVALID;
292
293 switch (msg.msg[1]) {
294
295 /* OSD Display */
296
297 case CEC_MSG_SET_OSD_STRING: {
298 __u8 disp_ctl;
299 char osd[14];
300
301 if (to != 0 && to != 14)
302 break;
303 if (!node->has_osd_string)
304 break;
305
306 cec_ops_set_osd_string(&msg, &disp_ctl, osd);
307 switch (disp_ctl) {
308 case CEC_OP_DISP_CTL_DEFAULT:
309 case CEC_OP_DISP_CTL_UNTIL_CLEARED:
310 case CEC_OP_DISP_CTL_CLEAR:
311 return;
312 }
313 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
314 transmit(node, &msg);
315 return;
316 }
317
318
319 /* Give Device Power Status */
320
321 case CEC_MSG_GIVE_DEVICE_POWER_STATUS:
322 cec_msg_set_reply_to(&msg, &msg);
323 cec_msg_report_power_status(&msg, current_power_state(node));
324 transmit(node, &msg);
325 return;
326
327 case CEC_MSG_REPORT_POWER_STATUS:
328 // Nothing to do here for now.
329 return;
330
331
332 /* Standby */
333
334 case CEC_MSG_STANDBY:
335 if (!node->ignore_standby || (++node->standby_cnt % node->ignore_standby))
336 enter_standby(node);
337 return;
338
339
340 /* One Touch Play and Routing Control */
341
342 case CEC_MSG_ACTIVE_SOURCE: {
343 __u16 phys_addr;
344
345 cec_ops_active_source(&msg, &phys_addr);
346 node->state.active_source_pa = phys_addr;
347 dev_info("New active source: %x.%x.%x.%x\n", cec_phys_addr_exp(phys_addr));
348 return;
349 }
350 case CEC_MSG_INACTIVE_SOURCE: {
351 __u16 phys_addr;
352
353 if (node->phys_addr)
354 break;
355
356 cec_ops_active_source(&msg, &phys_addr);
357 if (node->state.active_source_pa != phys_addr)
358 break;
359 node->state.active_source_pa = 0;
360 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
361 cec_msg_active_source(&msg, node->phys_addr);
362 transmit(node, &msg);
363 dev_info("New active source: 0.0.0.0\n");
364 return;
365 }
366 case CEC_MSG_IMAGE_VIEW_ON:
367 case CEC_MSG_TEXT_VIEW_ON:
368 if (!cec_has_tv(1 << me))
369 break;
370 if (!node->ignore_view_on || (++node->view_on_cnt % node->ignore_view_on))
371 exit_standby(node);
372 return;
373 case CEC_MSG_SET_STREAM_PATH: {
374 __u16 phys_addr;
375
376 cec_ops_set_stream_path(&msg, &phys_addr);
377 if (phys_addr != node->phys_addr)
378 return;
379
380 if (!cec_has_tv(1 << me))
381 exit_standby(node);
382
383 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
384 cec_msg_active_source(&msg, node->phys_addr);
385 transmit(node, &msg);
386 dev_info("Stream Path is directed to this device\n");
387 return;
388 }
389 case CEC_MSG_ROUTING_INFORMATION: {
390 __u8 la = cec_msg_initiator(&msg);
391
392 if (cec_has_tv(1 << la) && la_info[la].phys_addr == 0)
393 warn("TV (0) at 0.0.0.0 sent Routing Information.");
394 return;
395 }
396
397 /* System Information */
398
399 case CEC_MSG_GET_MENU_LANGUAGE:
400 if (!cec_has_tv(1 << me))
401 break;
402 cec_msg_set_reply_to(&msg, &msg);
403 cec_msg_set_menu_language(&msg, node->state.menu_language);
404 transmit(node, &msg);
405 return;
406 case CEC_MSG_CEC_VERSION:
407 return;
408 case CEC_MSG_REPORT_PHYSICAL_ADDR: {
409 __u16 phys_addr;
410 __u8 prim_dev_type;
411
412 cec_ops_report_physical_addr(&msg, &phys_addr, &prim_dev_type);
413 if (from < 15) {
414 node->remote_phys_addr[from] = phys_addr;
415 node->remote_prim_devtype[from] = prim_dev_type;
416 }
417 return;
418 }
419
420
421 /* Remote Control Passthrough
422 (System Audio Control, Device Menu Control) */
423
424 case CEC_MSG_USER_CONTROL_PRESSED: {
425 struct cec_op_ui_command rc_press;
426 unsigned new_state;
427 unsigned duration;
428
429 cec_ops_user_control_pressed(&msg, &rc_press);
430 duration = get_duration_ms(msg.rx_ts, node->state.rc_press_rx_ts);
431
432 new_state = PRESS;
433 if (node->state.rc_state == NOPRESS)
434 dev_info("Button press: %s\n", get_ui_cmd_string(rc_press.ui_cmd));
435 else if (rc_press.ui_cmd != node->state.rc_ui_cmd) {
436 /* We have not yet received User Control Released, but have received
437 another User Control Pressed with a different UI Command. */
438 if (node->state.rc_state == PRESS_HOLD)
439 rc_press_hold_stop(&node->state);
440 dev_info("Button press (no User Control Released between): %s\n",
441 get_ui_cmd_string(rc_press.ui_cmd));
442
443 /* The device shall send User Control Released if the time between
444 two messages is longer than the maximum Initiator Repetition Time. */
445 if (duration > MAX_INITIATOR_REP_TIME)
446 warn("Device waited more than the maximum Initiatior Repetition Time and should have sent a User Control Released message.");
447 } else if (duration < FOLLOWER_SAFETY_TIMEOUT) {
448 /* We have not yet received a User Control Released, but received
449 another User Control Pressed, with the same UI Command as the
450 previous, which means that the Press and Hold behavior should
451 be invoked. */
452 new_state = PRESS_HOLD;
453 if (node->state.rc_state != PRESS_HOLD) {
454 dev_info("Start Press and Hold with button %s\n",
455 get_ui_cmd_string(rc_press.ui_cmd));
456 node->state.rc_duration_sum = 0;
457 node->state.rc_press_hold_count = 0;
458 }
459 node->state.rc_duration_sum += duration;
460 node->state.rc_press_hold_count++;
461 }
462
463 node->state.rc_state = new_state;
464 node->state.rc_ui_cmd = rc_press.ui_cmd;
465 node->state.rc_press_rx_ts = msg.rx_ts;
466
467 switch (rc_press.ui_cmd) {
468 case CEC_OP_UI_CMD_VOLUME_UP:
469 if (node->state.volume < VOLUME_MAX)
470 node->state.volume++;
471 break;
472 case CEC_OP_UI_CMD_VOLUME_DOWN:
473 if (node->state.volume > VOLUME_MIN)
474 node->state.volume--;
475 break;
476 case CEC_OP_UI_CMD_MUTE:
477 node->state.mute = !node->state.mute;
478 break;
479 case CEC_OP_UI_CMD_MUTE_FUNCTION:
480 node->state.mute = true;
481 break;
482 case CEC_OP_UI_CMD_RESTORE_VOLUME_FUNCTION:
483 node->state.mute = false;
484 break;
485 case CEC_OP_UI_CMD_POWER_TOGGLE_FUNCTION:
486 if (!enter_standby(node))
487 exit_standby(node);
488 break;
489 case CEC_OP_UI_CMD_POWER_OFF_FUNCTION:
490 enter_standby(node);
491 break;
492 case CEC_OP_UI_CMD_POWER_ON_FUNCTION:
493 exit_standby(node);
494 break;
495 }
496 if (rc_press.ui_cmd >= CEC_OP_UI_CMD_VOLUME_UP && rc_press.ui_cmd <= CEC_OP_UI_CMD_MUTE) {
497 dev_info("Volume: %d%s\n", node->state.volume, node->state.mute ? ", muted" : "");
498 cec_msg_set_reply_to(&msg, &msg);
499 cec_msg_report_audio_status(&msg, node->state.mute ? 1 : 0, node->state.volume);
500 transmit(node, &msg);
501 }
502
503 return;
504 }
505 case CEC_MSG_USER_CONTROL_RELEASED:
506 if (node->state.rc_state == PRESS_HOLD)
507 rc_press_hold_stop(&node->state);
508
509 if (node->state.rc_state == NOPRESS)
510 dev_info("Button release (unexpected, %ums after last press)\n",
511 ts_to_ms(msg.rx_ts - node->state.rc_press_rx_ts));
512 else if (get_duration_ms(msg.rx_ts, node->state.rc_press_rx_ts) > FOLLOWER_SAFETY_TIMEOUT)
513 dev_info("Button release: %s (after timeout, %ums after last press)\n",
514 get_ui_cmd_string(node->state.rc_ui_cmd),
515 ts_to_ms(msg.rx_ts - node->state.rc_press_rx_ts));
516 else
517 dev_info("Button release: %s (%ums after last press)\n",
518 get_ui_cmd_string(node->state.rc_ui_cmd),
519 ts_to_ms(msg.rx_ts - node->state.rc_press_rx_ts));
520 node->state.rc_state = NOPRESS;
521 return;
522
523
524 /* Device Menu Control */
525
526 case CEC_MSG_MENU_REQUEST: {
527 if (node->cec_version < CEC_OP_CEC_VERSION_2_0 &&
528 !cec_has_tv(1 << me)) {
529 cec_msg_set_reply_to(&msg, &msg);
530 cec_msg_menu_status(&msg, CEC_OP_MENU_STATE_ACTIVATED);
531 transmit(node, &msg);
532 return;
533 }
534 break;
535 }
536 case CEC_MSG_MENU_STATUS:
537 if (node->cec_version < CEC_OP_CEC_VERSION_2_0 &&
538 cec_has_tv(1 << me))
539 return;
540 break;
541
542
543 /* Deck Control */
544
545 case CEC_MSG_GIVE_DECK_STATUS:
546 if (!node->has_deck_ctl)
547 break;
548
549 __u8 status_req;
550 cec_ops_give_deck_status(&msg, &status_req);
551
552 switch (status_req) {
553 case CEC_OP_STATUS_REQ_ON:
554 node->state.deck_report_changes = true;
555 node->state.deck_report_changes_to = cec_msg_initiator(&msg);
556 fallthrough;
557 case CEC_OP_STATUS_REQ_ONCE:
558 cec_msg_set_reply_to(&msg, &msg);
559 cec_msg_deck_status(&msg, node->state.deck_state);
560 transmit(node, &msg);
561 return;
562 case CEC_OP_STATUS_REQ_OFF:
563 node->state.deck_report_changes = false;
564 return;
565 default:
566 reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
567 return;
568 }
569 case CEC_MSG_PLAY:
570 if (!node->has_deck_ctl)
571 break;
572
573 __u8 deck_state;
574 __u8 play_mode;
575
576 cec_ops_play(&msg, &play_mode);
577
578 switch (play_mode) {
579 case CEC_OP_PLAY_MODE_PLAY_FWD:
580 exit_standby(node);
581 deck_state = CEC_OP_DECK_INFO_PLAY;
582 break;
583 case CEC_OP_PLAY_MODE_PLAY_REV:
584 deck_state = CEC_OP_DECK_INFO_PLAY_REV;
585 break;
586 case CEC_OP_PLAY_MODE_PLAY_STILL:
587 deck_state = CEC_OP_DECK_INFO_STILL;
588 break;
589 case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MIN:
590 case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MED:
591 case CEC_OP_PLAY_MODE_PLAY_FAST_FWD_MAX:
592 deck_state = CEC_OP_DECK_INFO_FAST_FWD;
593 break;
594 case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MIN:
595 case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MED:
596 case CEC_OP_PLAY_MODE_PLAY_FAST_REV_MAX:
597 deck_state = CEC_OP_DECK_INFO_FAST_REV;
598 break;
599 case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MIN:
600 case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MED:
601 case CEC_OP_PLAY_MODE_PLAY_SLOW_FWD_MAX:
602 deck_state = CEC_OP_DECK_INFO_SLOW;
603 break;
604 case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MIN:
605 case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MED:
606 case CEC_OP_PLAY_MODE_PLAY_SLOW_REV_MAX:
607 deck_state = CEC_OP_DECK_INFO_SLOW_REV;
608 break;
609 default:
610 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
611 transmit(node, &msg);
612 return;
613 }
614 /* Only Play Forward and Play Still will close tray if open. */
615 if (play_mode != CEC_OP_PLAY_MODE_PLAY_FWD &&
616 play_mode != CEC_OP_PLAY_MODE_PLAY_STILL &&
617 node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
618 reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
619 return;
620 }
621 node->state.deck_skip_start = 0;
622 update_deck_state(node, me, deck_state);
623 return;
624 case CEC_MSG_DECK_CONTROL:
625 if (!node->has_deck_ctl)
626 break;
627
628 __u8 deck_control_mode;
629
630 cec_ops_deck_control(&msg, &deck_control_mode);
631
632 switch (deck_control_mode) {
633 case CEC_OP_DECK_CTL_MODE_STOP:
634 deck_state = CEC_OP_DECK_INFO_STOP;
635 node->state.deck_skip_start = 0;
636 break;
637 case CEC_OP_DECK_CTL_MODE_EJECT:
638 exit_standby(node);
639 deck_state = CEC_OP_DECK_INFO_NO_MEDIA;
640 node->state.deck_skip_start = 0;
641 break;
642 case CEC_OP_DECK_CTL_MODE_SKIP_FWD:
643 /* Skip Forward will not retract the deck tray. */
644 if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
645 reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
646 return;
647 }
648 deck_state = CEC_OP_DECK_INFO_SKIP_FWD;
649 node->state.deck_skip_start = msg.rx_ts;
650 break;
651 case CEC_OP_DECK_CTL_MODE_SKIP_REV:
652 /* Skip Reverse will not retract the deck tray. */
653 if (node->state.deck_state == CEC_OP_DECK_INFO_NO_MEDIA) {
654 reply_feature_abort(node, &msg, CEC_OP_ABORT_INCORRECT_MODE);
655 return;
656 }
657 deck_state = CEC_OP_DECK_INFO_SKIP_REV;
658 node->state.deck_skip_start = msg.rx_ts;
659 break;
660 default:
661 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
662 transmit(node, &msg);
663 return;
664 }
665 update_deck_state(node, me, deck_state);
666 return;
667 case CEC_MSG_DECK_STATUS:
668 return;
669
670 /* Tuner/Record/Timer Messages */
671
672 case CEC_MSG_GIVE_TUNER_DEVICE_STATUS:
673 case CEC_MSG_TUNER_DEVICE_STATUS:
674 case CEC_MSG_SELECT_ANALOGUE_SERVICE:
675 case CEC_MSG_SELECT_DIGITAL_SERVICE:
676 case CEC_MSG_TUNER_STEP_DECREMENT:
677 case CEC_MSG_TUNER_STEP_INCREMENT:
678 process_tuner_msgs(node, msg, me, type);
679 return;
680 case CEC_MSG_RECORD_TV_SCREEN:
681 case CEC_MSG_RECORD_ON:
682 case CEC_MSG_RECORD_OFF:
683 case CEC_MSG_RECORD_STATUS:
684 process_record_msgs(node, msg, me, type);
685 return;
686 case CEC_MSG_SET_ANALOGUE_TIMER:
687 case CEC_MSG_SET_DIGITAL_TIMER:
688 case CEC_MSG_SET_EXT_TIMER:
689 case CEC_MSG_CLEAR_ANALOGUE_TIMER:
690 case CEC_MSG_CLEAR_DIGITAL_TIMER:
691 case CEC_MSG_CLEAR_EXT_TIMER:
692 case CEC_MSG_SET_TIMER_PROGRAM_TITLE:
693 case CEC_MSG_TIMER_CLEARED_STATUS:
694 case CEC_MSG_TIMER_STATUS:
695 process_timer_msgs(node, msg, me, type);
696 return;
697
698 /* Dynamic Auto Lipsync */
699
700 case CEC_MSG_REQUEST_CURRENT_LATENCY: {
701 __u16 phys_addr;
702
703 cec_ops_request_current_latency(&msg, &phys_addr);
704 if (phys_addr == node->phys_addr) {
705 cec_msg_init(&msg, me, from);
706 cec_msg_report_current_latency(&msg, phys_addr,
707 node->state.video_latency,
708 node->state.low_latency_mode,
709 node->state.audio_out_compensated,
710 node->state.audio_out_delay);
711 transmit(node, &msg);
712 }
713 return;
714 }
715
716
717 /* Audio Return Channel Control */
718
719 case CEC_MSG_INITIATE_ARC:
720 if (node->sink_has_arc_tx) {
721 if (!pa_is_upstream_from(node->phys_addr, remote_pa) ||
722 !pa_are_adjacent(node->phys_addr, remote_pa)) {
723 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_REFUSED);
724 transmit(node, &msg);
725 return;
726 }
727 cec_msg_set_reply_to(&msg, &msg);
728 cec_msg_report_arc_initiated(&msg);
729 transmit(node, &msg);
730 node->state.arc_active = true;
731 dev_info("ARC is initiated");
732 return;
733 }
734 break;
735 case CEC_MSG_TERMINATE_ARC:
736 if (node->sink_has_arc_tx) {
737 if (!pa_is_upstream_from(node->phys_addr, remote_pa) ||
738 !pa_are_adjacent(node->phys_addr, remote_pa)) {
739 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_REFUSED);
740 transmit(node, &msg);
741 return;
742 }
743 cec_msg_set_reply_to(&msg, &msg);
744 cec_msg_report_arc_terminated(&msg);
745 transmit(node, &msg);
746 node->state.arc_active = false;
747 dev_info("ARC is terminated\n");
748 return;
749 }
750 break;
751 case CEC_MSG_REQUEST_ARC_INITIATION:
752 if (node->source_has_arc_rx) {
753 if (pa_is_upstream_from(node->phys_addr, remote_pa) ||
754 !pa_are_adjacent(node->phys_addr, remote_pa)) {
755 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_REFUSED);
756 transmit(node, &msg);
757 return;
758 }
759 cec_msg_set_reply_to(&msg, &msg);
760 cec_msg_initiate_arc(&msg, false);
761 transmit(node, &msg);
762 dev_info("ARC initiation has been requested.");
763 return;
764 }
765 break;
766 case CEC_MSG_REQUEST_ARC_TERMINATION:
767 if (node->source_has_arc_rx) {
768 if (pa_is_upstream_from(node->phys_addr, remote_pa) ||
769 !pa_are_adjacent(node->phys_addr, remote_pa)) {
770 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_REFUSED);
771 transmit(node, &msg);
772 return;
773 }
774 cec_msg_set_reply_to(&msg, &msg);
775 cec_msg_terminate_arc(&msg, false);
776 transmit(node, &msg);
777 dev_info("ARC initiation has been requested.");
778 return;
779 }
780 break;
781 case CEC_MSG_REPORT_ARC_INITIATED:
782 node->state.arc_active = true;
783 dev_info("ARC is initiated\n");
784 return;
785 case CEC_MSG_REPORT_ARC_TERMINATED:
786 node->state.arc_active = false;
787 dev_info("ARC is terminated\n");
788 return;
789
790
791 /* System Audio Control */
792
793 case CEC_MSG_SYSTEM_AUDIO_MODE_REQUEST: {
794 if (!cec_has_audiosystem(1 << me))
795 break;
796
797 __u16 phys_addr;
798
799 cec_ops_system_audio_mode_request(&msg, &phys_addr);
800 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
801 if (phys_addr != CEC_PHYS_ADDR_INVALID) {
802 cec_msg_set_system_audio_mode(&msg, CEC_OP_SYS_AUD_STATUS_ON);
803 transmit(node, &msg);
804 node->state.sac_active = true;
805 }
806 else {
807 cec_msg_set_system_audio_mode(&msg, CEC_OP_SYS_AUD_STATUS_OFF);
808 transmit(node, &msg);
809 node->state.sac_active = false;
810 }
811 dev_info("System Audio Mode: %s\n", node->state.sac_active ? "on" : "off");
812 return;
813 }
814 case CEC_MSG_SET_SYSTEM_AUDIO_MODE: {
815 __u8 system_audio_status;
816
817 if (!cec_msg_is_broadcast(&msg)) {
818 /* Directly addressed Set System Audio Mode is used to see
819 if the TV supports the feature. If we time out, we
820 signalize that we support SAC. */
821 if (cec_has_tv(1 << me))
822 return;
823
824 break;
825 }
826 cec_ops_set_system_audio_mode(&msg, &system_audio_status);
827 if (system_audio_status == CEC_OP_SYS_AUD_STATUS_ON)
828 node->state.sac_active = true;
829 else if (system_audio_status == CEC_OP_SYS_AUD_STATUS_OFF)
830 node->state.sac_active = false;
831 dev_info("System Audio Mode: %s\n", node->state.sac_active ? "on" : "off");
832 return;
833 }
834 case CEC_MSG_REPORT_AUDIO_STATUS:
835 return;
836 case CEC_MSG_GIVE_SYSTEM_AUDIO_MODE_STATUS:
837 if (!cec_has_audiosystem(1 << me))
838 break;
839 cec_msg_set_reply_to(&msg, &msg);
840 cec_msg_system_audio_mode_status(&msg, node->state.sac_active ? CEC_OP_SYS_AUD_STATUS_ON :
841 CEC_OP_SYS_AUD_STATUS_OFF);
842 transmit(node, &msg);
843 fallthrough;
844 case CEC_MSG_GIVE_AUDIO_STATUS:
845 if (!cec_has_audiosystem(1 << me))
846 break;
847 cec_msg_set_reply_to(&msg, &msg);
848 cec_msg_report_audio_status(&msg, node->state.mute ? 1 : 0, node->state.volume);
849 transmit(node, &msg);
850 return;
851 case CEC_MSG_SYSTEM_AUDIO_MODE_STATUS:
852 return;
853 case CEC_MSG_REQUEST_SHORT_AUDIO_DESCRIPTOR: {
854 if (!cec_has_audiosystem(1 << me))
855 break;
856
857 /* The list of formats that the follower 'supports' */
858 const struct short_audio_desc supported_formats[] = {
859 { 2, SAD_FMT_CODE_AC3,
860 SAD_SAMPLE_FREQ_MASK_32 | SAD_SAMPLE_FREQ_MASK_44_1,
861 { 64 }, 0, 0 },
862 { 4, SAD_FMT_CODE_AC3,
863 SAD_SAMPLE_FREQ_MASK_32,
864 { 32 }, 0, 0 },
865 { 4, SAD_FMT_CODE_ONE_BIT_AUDIO,
866 SAD_SAMPLE_FREQ_MASK_48 | SAD_SAMPLE_FREQ_MASK_192,
867 { 123 }, 0, 0 },
868 { 8, SAD_FMT_CODE_EXTENDED,
869 SAD_SAMPLE_FREQ_MASK_96,
870 { 0 }, 0, SAD_EXT_TYPE_DRA },
871 { 2, SAD_FMT_CODE_EXTENDED,
872 SAD_SAMPLE_FREQ_MASK_176_4,
873 { SAD_FRAME_LENGTH_MASK_960 | SAD_FRAME_LENGTH_MASK_1024 }, 1, SAD_EXT_TYPE_MPEG4_HE_AAC_SURROUND },
874 { 2, SAD_FMT_CODE_EXTENDED,
875 SAD_SAMPLE_FREQ_MASK_44_1,
876 { SAD_BIT_DEPTH_MASK_16 | SAD_BIT_DEPTH_MASK_24 }, 0, SAD_EXT_TYPE_LPCM_3D_AUDIO },
877 };
878
879 __u8 num_descriptors, audio_format_id[4], audio_format_code[4];
880 __u32 descriptors[4];
881 std::string format_list;
882
883 cec_ops_request_short_audio_descriptor(&msg, &num_descriptors, audio_format_id, audio_format_code);
884 if (num_descriptors == 0) {
885 warn("Got Request Short Audio Descriptor with no operands");
886 reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
887 return;
888 }
889
890 unsigned found_descs = 0;
891
892 for (int i = 0; i < num_descriptors; i++)
893 format_list += audio_format_id_code2s(audio_format_id[i], audio_format_code[i]) + ",";
894 format_list.erase(format_list.end() - 1);
895 dev_info("Requested descriptors: %s\n", format_list.c_str());
896 for (unsigned i = 0; i < num_descriptors; i++) {
897 for (const auto &supported_format : supported_formats) {
898 if (found_descs >= 4)
899 break;
900 if ((audio_format_id[i] == 0 &&
901 audio_format_code[i] == supported_format.format_code) ||
902 (audio_format_id[i] == 1 &&
903 audio_format_code[i] == supported_format.extension_type_code))
904 sad_encode(&supported_format, &descriptors[found_descs++]);
905 }
906 }
907
908 if (found_descs > 0) {
909 cec_msg_set_reply_to(&msg, &msg);
910 cec_msg_report_short_audio_descriptor(&msg, found_descs, descriptors);
911 transmit(node, &msg);
912 }
913 else
914 reply_feature_abort(node, &msg, CEC_OP_ABORT_INVALID_OP);
915 return;
916 }
917
918
919 /* Device OSD Transfer */
920
921 case CEC_MSG_SET_OSD_NAME:
922 return;
923
924
925 /* Audio Rate Control */
926
927 case CEC_MSG_SET_AUDIO_RATE:
928 if (!node->has_aud_rate)
929 break;
930
931 switch (msg.msg[2]) {
932 case CEC_OP_AUD_RATE_OFF:
933 aud_rate_msg_interval_check(node, msg.rx_ts);
934 node->state.last_aud_rate_rx_ts = 0;
935 return;
936 case CEC_OP_AUD_RATE_WIDE_STD:
937 case CEC_OP_AUD_RATE_WIDE_FAST:
938 case CEC_OP_AUD_RATE_WIDE_SLOW:
939 case CEC_OP_AUD_RATE_NARROW_STD:
940 case CEC_OP_AUD_RATE_NARROW_FAST:
941 case CEC_OP_AUD_RATE_NARROW_SLOW:
942 aud_rate_msg_interval_check(node, msg.rx_ts);
943 node->state.last_aud_rate_rx_ts = msg.rx_ts;
944 return;
945 default:
946 cec_msg_reply_feature_abort(&msg, CEC_OP_ABORT_INVALID_OP);
947 transmit(node, &msg);
948 break;
949 }
950 break;
951
952
953 /* CDC */
954
955 case CEC_MSG_CDC_MESSAGE: {
956 switch (msg.msg[4]) {
957 case CEC_MSG_CDC_HEC_DISCOVER:
958 __u16 phys_addr;
959
960 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
961 cec_ops_cdc_hec_discover(&msg, &phys_addr);
962 cec_msg_cdc_hec_report_state(&msg, phys_addr,
963 CEC_OP_HEC_FUNC_STATE_NOT_SUPPORTED,
964 CEC_OP_HOST_FUNC_STATE_NOT_SUPPORTED,
965 CEC_OP_ENC_FUNC_STATE_EXT_CON_NOT_SUPPORTED,
966 CEC_OP_CDC_ERROR_CODE_NONE,
967 1, 0); // We do not support HEC on any HDMI connections
968 transmit(node, &msg);
969 return;
970 }
971 }
972
973
974 /* Core */
975
976 case CEC_MSG_FEATURE_ABORT:
977 return;
978 default:
979 break;
980 }
981
982 if (is_bcast)
983 return;
984
985 reply_feature_abort(node, &msg);
986 }
987
poll_remote_devs(struct node * node,unsigned me)988 static void poll_remote_devs(struct node *node, unsigned me)
989 {
990 node->remote_la_mask = 0;
991 for (unsigned short & i : node->remote_phys_addr)
992 i = CEC_PHYS_ADDR_INVALID;
993
994 if (!(node->caps & CEC_CAP_TRANSMIT))
995 return;
996
997 for (unsigned i = 0; i < 15; i++) {
998 struct cec_msg msg;
999
1000 cec_msg_init(&msg, me, i);
1001
1002 doioctl(node, CEC_TRANSMIT, &msg);
1003 if (msg.tx_status & CEC_TX_STATUS_OK) {
1004 node->remote_la_mask |= 1 << i;
1005 cec_msg_init(&msg, me, i);
1006 cec_msg_give_physical_addr(&msg, true);
1007 doioctl(node, CEC_TRANSMIT, &msg);
1008 if (cec_msg_status_is_ok(&msg))
1009 node->remote_phys_addr[i] = (msg.msg[2] << 8) | msg.msg[3];
1010 }
1011 }
1012 }
1013
update_programmed_timers(struct node * node)1014 static void update_programmed_timers(struct node *node)
1015 {
1016 auto it = programmed_timers.begin();
1017 /* Use the current minute because timers do not have second precision. */
1018 time_t current_minute = time(nullptr) / 60;
1019 time_t timer_start_minute = it->start_time / 60;
1020 time_t timer_end_minute = (it->start_time + it->duration) / 60;
1021
1022 /* Start the timed recording only if the deck is not already recording. */
1023 if (timer_start_minute == current_minute && !node->state.one_touch_record_on) {
1024 node->state.one_touch_record_on = true;
1025 node->state.recording_controlled_by_timer = true;
1026 print_timers(node);
1027 }
1028
1029 /* Delete an overlapped timer. Recording will be at best incomplete. */
1030 if (timer_start_minute < current_minute &&
1031 (!node->state.recording_controlled_by_timer || !node->state.one_touch_record_on)) {
1032 programmed_timers.erase(*it);
1033 if (show_info)
1034 printf("Deleted overlapped timer.\n");
1035 print_timers(node);
1036 }
1037
1038 if (timer_end_minute != current_minute || !node->state.recording_controlled_by_timer)
1039 return;
1040
1041 /* Delete finished timers. */
1042 node->state.one_touch_record_on = false;
1043 node->state.recording_controlled_by_timer = false;
1044 node->state.media_space_available -= it->duration; /* 1 MB per second */
1045 /*
1046 * TODO: We are only ever decreasing the amount of space available,
1047 * there is no heuristic that reclaims the space.
1048 */
1049
1050 if (it->recording_seq) {
1051 struct tm *last_start_time = localtime(&(it->start_time));
1052 int next_wday = (last_start_time->tm_wday + 1) % 7;
1053 int days_to_move_ahead = 1;
1054
1055 while ((it->recording_seq & (1 << next_wday)) == 0) {
1056 days_to_move_ahead++;
1057 next_wday = (next_wday + 1) % 7;
1058 }
1059 struct Timer next_timer = {};
1060 next_timer = *it;
1061 last_start_time->tm_mday += days_to_move_ahead;
1062 last_start_time->tm_isdst = -1;
1063 next_timer.start_time = mktime(last_start_time);
1064 programmed_timers.insert(next_timer);
1065 }
1066 programmed_timers.erase(*it);
1067 if (show_info)
1068 printf("Deleted finished timer.\n");
1069 print_timers(node);
1070 /*
1071 * If the finished timer was recording, and standby was received during recording,
1072 * enter standby when the recording stops unless recording device is the active source.
1073 */
1074 if (node->state.record_received_standby) {
1075 if (node->phys_addr != node->state.active_source_pa)
1076 enter_standby(node);
1077 node->state.record_received_standby = false;
1078 }
1079 }
1080
testProcessing(struct node * node,bool exclusive,bool wallclock)1081 void testProcessing(struct node *node, bool exclusive, bool wallclock)
1082 {
1083 struct cec_log_addrs laddrs;
1084 fd_set rd_fds;
1085 fd_set ex_fds;
1086 int fd = node->fd;
1087 __u32 mode = CEC_MODE_INITIATOR |
1088 (exclusive ? CEC_MODE_EXCL_FOLLOWER : CEC_MODE_FOLLOWER);
1089 unsigned me;
1090 unsigned last_poll_la = 15;
1091 __u8 last_pwr_state = current_power_state(node);
1092 time_t last_pwr_status_toggle = time(nullptr);
1093
1094 clock_gettime(CLOCK_MONOTONIC, &start_monotonic);
1095 gettimeofday(&start_timeofday, nullptr);
1096
1097 doioctl(node, CEC_S_MODE, &mode);
1098 doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1099 me = laddrs.log_addr[0];
1100 __u8 type = laddrs.log_addr_type[0];
1101
1102 poll_remote_devs(node, me);
1103
1104 while (true) {
1105 int res;
1106 struct timeval timeval = {};
1107
1108 fflush(stdout);
1109 timeval.tv_sec = 1;
1110 FD_ZERO(&rd_fds);
1111 FD_ZERO(&ex_fds);
1112 FD_SET(fd, &rd_fds);
1113 FD_SET(fd, &ex_fds);
1114 res = select(fd + 1, &rd_fds, nullptr, &ex_fds, &timeval);
1115 if (res < 0)
1116 break;
1117 if (FD_ISSET(fd, &ex_fds)) {
1118 struct cec_event ev;
1119
1120 res = doioctl(node, CEC_DQEVENT, &ev);
1121 if (res == ENODEV) {
1122 printf("Device was disconnected.\n");
1123 break;
1124 }
1125 if (res)
1126 continue;
1127 log_event(ev, wallclock);
1128 if (ev.event == CEC_EVENT_STATE_CHANGE) {
1129 dev_info("CEC adapter state change.\n");
1130 node->phys_addr = ev.state_change.phys_addr;
1131 node->adap_la_mask = ev.state_change.log_addr_mask;
1132 if (node->adap_la_mask) {
1133 doioctl(node, CEC_ADAP_G_LOG_ADDRS, &laddrs);
1134 me = laddrs.log_addr[0];
1135 } else {
1136 node->state.active_source_pa = CEC_PHYS_ADDR_INVALID;
1137 me = CEC_LOG_ADDR_INVALID;
1138 }
1139 memset(la_info, 0, sizeof(la_info));
1140 }
1141 }
1142 if (FD_ISSET(fd, &rd_fds)) {
1143 struct cec_msg msg = { };
1144
1145 res = doioctl(node, CEC_RECEIVE, &msg);
1146 if (res == ENODEV) {
1147 printf("Device was disconnected.\n");
1148 break;
1149 }
1150 if (res)
1151 continue;
1152
1153 __u8 from = cec_msg_initiator(&msg);
1154 __u8 to = cec_msg_destination(&msg);
1155 __u8 opcode = cec_msg_opcode(&msg);
1156
1157 if (node->ignore_la[from])
1158 continue;
1159 if (node->ignore_opcode[msg.msg[1]] & (1 << from))
1160 continue;
1161
1162 if (from != CEC_LOG_ADDR_UNREGISTERED &&
1163 la_info[from].feature_aborted[opcode].ts &&
1164 ts_to_ms(get_ts() - la_info[from].feature_aborted[opcode].ts) < 200) {
1165 warn("Received message %s from LA %d (%s) less than 200 ms after\n",
1166 opcode2s(&msg).c_str(), from, cec_la2s(from));
1167 warn("replying Feature Abort (not [Unrecognized Opcode]) to the same message.\n");
1168 }
1169 if (from != CEC_LOG_ADDR_UNREGISTERED && !la_info[from].ts)
1170 dev_info("Logical address %d (%s) discovered.\n", from, cec_la2s(from));
1171 if (show_msgs) {
1172 printf(" %s to %s (%d to %d): ",
1173 cec_la2s(from), to == 0xf ? "all" : cec_la2s(to), from, to);
1174 cec_log_msg(&msg);
1175 if (show_info)
1176 printf("\tSequence: %u Rx Timestamp: %s\n",
1177 msg.sequence, ts2s(msg.rx_ts, wallclock).c_str());
1178 }
1179 if (node->adap_la_mask)
1180 processMsg(node, msg, me, type);
1181 }
1182
1183 __u8 pwr_state = current_power_state(node);
1184 if (node->cec_version >= CEC_OP_CEC_VERSION_2_0 &&
1185 last_pwr_state != pwr_state &&
1186 (time_to_stable > 2 || pwr_state < CEC_OP_POWER_STATUS_TO_ON)) {
1187 struct cec_msg msg;
1188
1189 cec_msg_init(&msg, me, CEC_LOG_ADDR_BROADCAST);
1190 cec_msg_report_power_status(&msg, pwr_state);
1191 transmit(node, &msg);
1192 last_pwr_state = pwr_state;
1193 }
1194
1195 if (node->state.toggle_power_status && cec_has_tv(1 << me) &&
1196 (time(nullptr) - last_pwr_status_toggle > node->state.toggle_power_status)) {
1197 last_pwr_status_toggle = time(nullptr);
1198 if (pwr_state & 1) // standby or to-standby
1199 exit_standby(node);
1200 else
1201 enter_standby(node);
1202 }
1203
1204 __u64 ts_now = get_ts();
1205 unsigned poll_la = ts_to_s(ts_now) % 16;
1206
1207 if (poll_la != me &&
1208 poll_la != last_poll_la && poll_la < 15 && la_info[poll_la].ts &&
1209 ts_to_ms(ts_now - la_info[poll_la].ts) > POLL_PERIOD) {
1210 struct cec_msg msg;
1211
1212 cec_msg_init(&msg, me, poll_la);
1213 transmit(node, &msg);
1214 if (msg.tx_status & CEC_TX_STATUS_NACK) {
1215 dev_info("Logical address %d stopped responding to polling message.\n", poll_la);
1216 memset(&la_info[poll_la], 0, sizeof(la_info[poll_la]));
1217 node->remote_la_mask &= ~(1 << poll_la);
1218 node->remote_phys_addr[poll_la] = CEC_PHYS_ADDR_INVALID;
1219 }
1220 }
1221 last_poll_la = poll_la;
1222
1223 unsigned ms_since_press = ts_to_ms(ts_now - node->state.rc_press_rx_ts);
1224
1225 if (ms_since_press > FOLLOWER_SAFETY_TIMEOUT) {
1226 if (node->state.rc_state == PRESS_HOLD)
1227 rc_press_hold_stop(&node->state);
1228 else if (node->state.rc_state == PRESS) {
1229 dev_info("Button timeout: %s\n", get_ui_cmd_string(node->state.rc_ui_cmd));
1230 node->state.rc_state = NOPRESS;
1231 }
1232 }
1233
1234 if (node->has_aud_rate)
1235 aud_rate_msg_interval_check(node, ts_now);
1236
1237 if (node->state.deck_skip_start && ts_now - node->state.deck_skip_start > MAX_DECK_SKIP_NS) {
1238 node->state.deck_skip_start = 0;
1239 update_deck_state(node, me, CEC_OP_DECK_INFO_PLAY);
1240 }
1241
1242 if (!programmed_timers.empty())
1243 update_programmed_timers(node);
1244 }
1245 mode = CEC_MODE_INITIATOR;
1246 doioctl(node, CEC_S_MODE, &mode);
1247 }
1248