ffe78b839589f3ca6a3a2c16f1120c34219aadd9
[deb_libcec.git] / src / lib / implementations / CECCommandHandler.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
31 */
32
33 #include "CECCommandHandler.h"
34 #include "../devices/CECBusDevice.h"
35 #include "../devices/CECAudioSystem.h"
36 #include "../devices/CECPlaybackDevice.h"
37 #include "../CECClient.h"
38 #include "../CECProcessor.h"
39 #include "../LibCEC.h"
40 #include "../CECTypeUtils.h"
41 #include "../platform/util/util.h"
42
43 using namespace CEC;
44 using namespace std;
45 using namespace PLATFORM;
46
47 #define LIB_CEC m_busDevice->GetProcessor()->GetLib()
48 #define ToString(p) CCECTypeUtils::ToString(p)
49
50 CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice,
51 int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */,
52 int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */,
53 int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */,
54 int64_t iActiveSourcePending /* = 0 */) :
55 m_busDevice(busDevice),
56 m_processor(m_busDevice->GetProcessor()),
57 m_iTransmitTimeout(iTransmitTimeout),
58 m_iTransmitWait(iTransmitWait),
59 m_iTransmitRetries(iTransmitRetries),
60 m_bHandlerInited(false),
61 m_bOPTSendDeckStatusUpdateOnActiveSource(false),
62 m_vendorId(CEC_VENDOR_UNKNOWN),
63 m_iActiveSourcePending(iActiveSourcePending)
64 {
65 }
66
67 bool CCECCommandHandler::HandleCommand(const cec_command &command)
68 {
69 if (command.opcode_set == 0)
70 return HandlePoll(command);
71
72 int iHandled(CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
73
74 LIB_CEC->AddCommand(command);
75
76 switch(command.opcode)
77 {
78 case CEC_OPCODE_REPORT_POWER_STATUS:
79 iHandled = HandleReportPowerStatus(command);
80 break;
81 case CEC_OPCODE_CEC_VERSION:
82 iHandled = HandleDeviceCecVersion(command);
83 break;
84 case CEC_OPCODE_SET_MENU_LANGUAGE:
85 iHandled = HandleSetMenuLanguage(command);
86 break;
87 case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
88 iHandled = HandleGivePhysicalAddress(command);
89 break;
90 case CEC_OPCODE_GET_MENU_LANGUAGE:
91 iHandled = HandleGiveMenuLanguage(command);
92 break;
93 case CEC_OPCODE_GIVE_OSD_NAME:
94 iHandled = HandleGiveOSDName(command);
95 break;
96 case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
97 iHandled = HandleGiveDeviceVendorId(command);
98 break;
99 case CEC_OPCODE_DEVICE_VENDOR_ID:
100 iHandled = HandleDeviceVendorId(command);
101 break;
102 case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
103 iHandled = HandleDeviceVendorCommandWithId(command);
104 break;
105 case CEC_OPCODE_GIVE_DECK_STATUS:
106 iHandled = HandleGiveDeckStatus(command);
107 break;
108 case CEC_OPCODE_DECK_CONTROL:
109 iHandled = HandleDeckControl(command);
110 break;
111 case CEC_OPCODE_MENU_REQUEST:
112 iHandled = HandleMenuRequest(command);
113 break;
114 case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
115 iHandled = HandleGiveDevicePowerStatus(command);
116 break;
117 case CEC_OPCODE_GET_CEC_VERSION:
118 iHandled = HandleGetCecVersion(command);
119 break;
120 case CEC_OPCODE_USER_CONTROL_PRESSED:
121 iHandled = HandleUserControlPressed(command);
122 break;
123 case CEC_OPCODE_USER_CONTROL_RELEASE:
124 iHandled = HandleUserControlRelease(command);
125 break;
126 case CEC_OPCODE_GIVE_AUDIO_STATUS:
127 iHandled = HandleGiveAudioStatus(command);
128 break;
129 case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
130 iHandled = HandleGiveSystemAudioModeStatus(command);
131 break;
132 case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
133 iHandled = HandleSystemAudioModeRequest(command);
134 break;
135 case CEC_OPCODE_REPORT_AUDIO_STATUS:
136 iHandled = HandleReportAudioStatus(command);
137 break;
138 case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
139 iHandled = HandleSystemAudioModeStatus(command);
140 break;
141 case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
142 iHandled = HandleSetSystemAudioMode(command);
143 break;
144 case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
145 iHandled = HandleRequestActiveSource(command);
146 break;
147 case CEC_OPCODE_SET_STREAM_PATH:
148 iHandled = HandleSetStreamPath(command);
149 break;
150 case CEC_OPCODE_ROUTING_CHANGE:
151 iHandled = HandleRoutingChange(command);
152 break;
153 case CEC_OPCODE_ROUTING_INFORMATION:
154 iHandled = HandleRoutingInformation(command);
155 break;
156 case CEC_OPCODE_STANDBY:
157 iHandled = HandleStandby(command);
158 break;
159 case CEC_OPCODE_ACTIVE_SOURCE:
160 iHandled = HandleActiveSource(command);
161 break;
162 case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
163 iHandled = HandleReportPhysicalAddress(command);
164 break;
165 case CEC_OPCODE_SET_OSD_NAME:
166 iHandled = HandleSetOSDName(command);
167 break;
168 case CEC_OPCODE_IMAGE_VIEW_ON:
169 iHandled = HandleImageViewOn(command);
170 break;
171 case CEC_OPCODE_TEXT_VIEW_ON:
172 iHandled = HandleTextViewOn(command);
173 break;
174 case CEC_OPCODE_FEATURE_ABORT:
175 iHandled = HandleFeatureAbort(command);
176 break;
177 case CEC_OPCODE_VENDOR_COMMAND:
178 iHandled = HandleVendorCommand(command);
179 break;
180 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
181 iHandled = HandleVendorRemoteButtonDown(command);
182 break;
183 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
184 iHandled = HandleVendorRemoteButtonUp(command);
185 break;
186 case CEC_OPCODE_PLAY:
187 // libCEC (currently) doesn't need to do anything with this, since player applications handle it
188 // but it should not respond with a feature abort
189 iHandled = COMMAND_HANDLED;
190 break;
191 default:
192 break;
193 }
194
195 if (iHandled == COMMAND_HANDLED)
196 m_busDevice->SignalOpcode((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode);
197 else
198 UnhandledCommand(command, (cec_abort_reason)iHandled);
199
200 return iHandled == COMMAND_HANDLED;
201 }
202
203 int CCECCommandHandler::HandleActiveSource(const cec_command &command)
204 {
205 if (command.parameters.size == 2)
206 {
207 uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
208 CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress);
209 if (device)
210 {
211 device->MarkAsActiveSource();
212 return COMMAND_HANDLED;
213 }
214 }
215
216 return CEC_ABORT_REASON_INVALID_OPERAND;
217 }
218
219 int CCECCommandHandler::HandleDeckControl(const cec_command &command)
220 {
221 CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
222 if (device && command.parameters.size > 0)
223 {
224 device->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]);
225 return COMMAND_HANDLED;
226 }
227
228 return CEC_ABORT_REASON_INVALID_OPERAND;
229 }
230
231 int CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command)
232 {
233 if (command.parameters.size == 1)
234 {
235 CCECBusDevice *device = GetDevice(command.initiator);
236 if (device)
237 device->SetCecVersion((cec_version) command.parameters[0]);
238
239 return COMMAND_HANDLED;
240 }
241
242 return CEC_ABORT_REASON_INVALID_OPERAND;
243 }
244
245 int CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command & UNUSED(command))
246 {
247 return CEC_ABORT_REASON_INVALID_OPERAND;
248 }
249
250 int CCECCommandHandler::HandleDeviceVendorId(const cec_command &command)
251 {
252 SetVendorId(command);
253 return COMMAND_HANDLED;
254 }
255
256 int CCECCommandHandler::HandleFeatureAbort(const cec_command &command)
257 {
258 if (command.parameters.size == 2 &&
259 (command.parameters[1] == CEC_ABORT_REASON_UNRECOGNIZED_OPCODE ||
260 command.parameters[1] == CEC_ABORT_REASON_REFUSED))
261 m_processor->GetDevice(command.initiator)->SetUnsupportedFeature((cec_opcode)command.parameters[0]);
262 return COMMAND_HANDLED;
263 }
264
265 int CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
266 {
267 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
268 {
269 CCECBusDevice *device = GetDevice(command.destination);
270 if (device && device->TransmitCECVersion(command.initiator))
271 return COMMAND_HANDLED;
272 return CEC_ABORT_REASON_INVALID_OPERAND;
273 }
274
275 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
276 }
277
278 int CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command)
279 {
280 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
281 {
282 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
283 if (device && device->TransmitAudioStatus(command.initiator))
284 return COMMAND_HANDLED;
285 return CEC_ABORT_REASON_INVALID_OPERAND;
286 }
287
288 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
289 }
290
291 int CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
292 {
293 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
294 {
295 CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
296 if (device && device->TransmitDeckStatus(command.initiator))
297 return COMMAND_HANDLED;
298 return CEC_ABORT_REASON_INVALID_OPERAND;
299 }
300
301 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
302 }
303
304 int CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
305 {
306 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
307 {
308 CCECBusDevice *device = GetDevice(command.destination);
309 if (device && device->TransmitPowerState(command.initiator))
310 return COMMAND_HANDLED;
311 return CEC_ABORT_REASON_INVALID_OPERAND;
312 }
313
314 return CEC_ABORT_REASON_INVALID_OPERAND;
315 }
316
317 int CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
318 {
319 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
320 {
321 CCECBusDevice *device = GetDevice(command.destination);
322 if (device && device->TransmitVendorID(command.initiator))
323 return COMMAND_HANDLED;
324 }
325
326 return CEC_ABORT_REASON_INVALID_OPERAND;
327 }
328
329 int CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
330 {
331 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
332 {
333 CCECBusDevice *device = GetDevice(command.destination);
334 if (device && device->TransmitOSDName(command.initiator))
335 return COMMAND_HANDLED;
336 }
337
338 return CEC_ABORT_REASON_INVALID_OPERAND;
339 }
340
341 int CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
342 {
343 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
344 {
345 CCECBusDevice *device = GetDevice(command.destination);
346 if (device && device->TransmitPhysicalAddress())
347 return COMMAND_HANDLED;
348 return CEC_ABORT_REASON_INVALID_OPERAND;
349 }
350
351 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
352 }
353
354 int CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command)
355 {
356 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
357 {
358 CCECBusDevice *device = GetDevice(command.destination);
359 if (device && device->TransmitSetMenuLanguage(command.initiator))
360 return COMMAND_HANDLED;
361 return CEC_ABORT_REASON_INVALID_OPERAND;
362 }
363
364 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
365 }
366
367 int CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command)
368 {
369 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
370 {
371 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
372 if (device && device->TransmitSystemAudioModeStatus(command.initiator))
373 return COMMAND_HANDLED;
374 return CEC_ABORT_REASON_INVALID_OPERAND;
375 }
376
377 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
378 }
379
380 int CCECCommandHandler::HandleImageViewOn(const cec_command &command)
381 {
382 m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
383 return COMMAND_HANDLED;
384 }
385
386 int CCECCommandHandler::HandleMenuRequest(const cec_command &command)
387 {
388 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
389 {
390 CCECBusDevice *device = GetDevice(command.destination);
391 if (device)
392 {
393 CCECClient *client = device->GetClient();
394 if (client)
395 {
396 if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_ACTIVATE)
397 {
398 if (client->MenuStateChanged(CEC_MENU_STATE_ACTIVATED) == 1)
399 device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
400 }
401 else if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_DEACTIVATE)
402 {
403 if (client->MenuStateChanged(CEC_MENU_STATE_DEACTIVATED) == 1)
404 device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
405 }
406 }
407 if (device->TransmitMenuState(command.initiator))
408 return COMMAND_HANDLED;
409 }
410 return CEC_ABORT_REASON_INVALID_OPERAND;
411 }
412
413 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
414 }
415
416 bool CCECCommandHandler::HandlePoll(const cec_command &command)
417 {
418 m_busDevice->HandlePoll(command.destination);
419 return true;
420 }
421
422 int CCECCommandHandler::HandleReportAudioStatus(const cec_command &command)
423 {
424 if (command.parameters.size == 1)
425 {
426 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
427 if (device)
428 {
429 device->SetAudioStatus(command.parameters[0]);
430 return COMMAND_HANDLED;
431 }
432 }
433 return CEC_ABORT_REASON_INVALID_OPERAND;
434 }
435
436 int CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command)
437 {
438 if (command.parameters.size == 3)
439 {
440 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
441 SetPhysicalAddress(command.initiator, iNewAddress);
442 return COMMAND_HANDLED;
443 }
444 return CEC_ABORT_REASON_INVALID_OPERAND;
445 }
446
447 int CCECCommandHandler::HandleReportPowerStatus(const cec_command &command)
448 {
449 if (command.parameters.size == 1)
450 {
451 CCECBusDevice *device = GetDevice(command.initiator);
452 if (device)
453 {
454 device->SetPowerStatus((cec_power_status) command.parameters[0]);
455 return COMMAND_HANDLED;
456 }
457 }
458 return CEC_ABORT_REASON_INVALID_OPERAND;
459 }
460
461 int CCECCommandHandler::HandleRequestActiveSource(const cec_command &command)
462 {
463 if (m_processor->CECInitialised())
464 {
465 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source", (uint8_t) command.initiator);
466 m_processor->GetDevice(command.initiator)->SetPowerStatus(CEC_POWER_STATUS_ON);
467
468 vector<CCECBusDevice *> devices;
469 for (size_t iDevicePtr = 0; iDevicePtr < GetMyDevices(devices); iDevicePtr++)
470 devices[iDevicePtr]->TransmitActiveSource();
471 }
472
473 return COMMAND_HANDLED;
474 }
475
476 int CCECCommandHandler::HandleRoutingChange(const cec_command &command)
477 {
478 if (command.parameters.size == 4)
479 {
480 uint16_t iOldAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
481 uint16_t iNewAddress = ((uint16_t)command.parameters[2] << 8) | ((uint16_t)command.parameters[3]);
482
483 CCECBusDevice *device = GetDevice(command.initiator);
484 if (device)
485 {
486 device->SetStreamPath(iNewAddress, iOldAddress);
487 return COMMAND_HANDLED;
488 }
489 }
490
491 return CEC_ABORT_REASON_INVALID_OPERAND;
492 }
493
494 int CCECCommandHandler::HandleRoutingInformation(const cec_command &command)
495 {
496 if (command.parameters.size == 2)
497 {
498 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
499 CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
500 if (device)
501 {
502 device->MarkAsActiveSource();
503 return COMMAND_HANDLED;
504 }
505 }
506
507 return CEC_ABORT_REASON_INVALID_OPERAND;
508 }
509
510 int CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command)
511 {
512 if (command.parameters.size == 3)
513 {
514 CCECBusDevice *device = GetDevice(command.initiator);
515 if (device)
516 {
517 cec_menu_language language;
518 language.device = command.initiator;
519 for (uint8_t iPtr = 0; iPtr < 4; iPtr++)
520 language.language[iPtr] = command.parameters[iPtr];
521 language.language[3] = 0;
522 device->SetMenuLanguage(language);
523 return COMMAND_HANDLED;
524 }
525 }
526
527 return CEC_ABORT_REASON_INVALID_OPERAND;
528 }
529
530 int CCECCommandHandler::HandleSetOSDName(const cec_command &command)
531 {
532 if (command.parameters.size > 0)
533 {
534 CCECBusDevice *device = GetDevice(command.initiator);
535 if (device)
536 {
537 char buf[1024];
538 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
539 buf[iPtr] = (char)command.parameters[iPtr];
540 buf[command.parameters.size] = 0;
541
542 CStdString strName(buf);
543 device->SetOSDName(strName);
544
545 return COMMAND_HANDLED;
546 }
547 }
548
549 return CEC_ABORT_REASON_INVALID_OPERAND;
550 }
551
552 int CCECCommandHandler::HandleSetStreamPath(const cec_command &command)
553 {
554 if (!m_processor->CECInitialised())
555 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
556
557 if (command.parameters.size >= 2)
558 {
559 uint16_t iStreamAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
560 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%x) sets stream path to physical address %04x", ToString(command.initiator), command.initiator, iStreamAddress);
561
562 // a device will only change the stream path when it's powered on
563 m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
564
565 /* one of the device handled by libCEC has been made active */
566 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamAddress);
567 if (device && device->IsHandledByLibCEC())
568 {
569 device->ActivateSource();
570 return COMMAND_HANDLED;
571 }
572 }
573
574 return CEC_ABORT_REASON_INVALID_OPERAND;
575 }
576
577 int CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command)
578 {
579 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
580 {
581 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
582 if (device)
583 {
584 if (command.parameters.size >= 2)
585 {
586 device->SetPowerStatus(CEC_POWER_STATUS_ON);
587 device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_ON);
588 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
589 CCECBusDevice *newActiveDevice = GetDeviceByPhysicalAddress(iNewAddress);
590 if (newActiveDevice)
591 newActiveDevice->MarkAsActiveSource();
592 if (device->TransmitSetSystemAudioMode(command.initiator))
593 return COMMAND_HANDLED;
594 }
595 else
596 {
597 device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF);
598 if (device->TransmitSetSystemAudioMode(command.initiator))
599 return COMMAND_HANDLED;
600 }
601 }
602 }
603
604 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
605 }
606
607 int CCECCommandHandler::HandleStandby(const cec_command &command)
608 {
609 CCECBusDevice *device = GetDevice(command.initiator);
610 if (device)
611 device->SetPowerStatus(CEC_POWER_STATUS_STANDBY);
612
613 return COMMAND_HANDLED;
614 }
615
616 int CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command)
617 {
618 if (command.parameters.size == 1)
619 {
620 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
621 if (device)
622 {
623 device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
624 return COMMAND_HANDLED;
625 }
626 }
627
628 return CEC_ABORT_REASON_INVALID_OPERAND;
629 }
630
631 int CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command)
632 {
633 if (command.parameters.size == 1)
634 {
635 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
636 if (device)
637 {
638 device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
639 return COMMAND_HANDLED;
640 }
641 }
642
643 return CEC_ABORT_REASON_INVALID_OPERAND;
644 }
645
646 int CCECCommandHandler::HandleTextViewOn(const cec_command &command)
647 {
648 m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
649 return COMMAND_HANDLED;
650 }
651
652 int CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
653 {
654 if (!m_processor->CECInitialised() ||
655 !m_processor->IsHandledByLibCEC(command.destination))
656 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
657
658 if (command.parameters.size == 0)
659 return CEC_ABORT_REASON_INVALID_OPERAND;
660
661 CCECBusDevice *device = GetDevice(command.destination);
662 if (!device)
663 return CEC_ABORT_REASON_INVALID_OPERAND;
664
665 CCECClient *client = device->GetClient();
666 if (client)
667 client->AddKey();
668
669 if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
670 client->SetCurrentButton((cec_user_control_code) command.parameters[0]);
671
672 if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER ||
673 command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION)
674 {
675 bool bPowerOn(true);
676
677 // CEC_USER_CONTROL_CODE_POWER operates as a toggle
678 // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not
679 if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER)
680 {
681 cec_power_status status = device->GetCurrentPowerStatus();
682 bPowerOn = !(status == CEC_POWER_STATUS_ON || status == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
683 }
684
685 if (bPowerOn)
686 {
687 device->ActivateSource();
688 }
689 else
690 {
691 device->MarkAsInactiveSource();
692 device->TransmitInactiveSource();
693 device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
694 }
695 }
696
697 return COMMAND_HANDLED;
698 }
699
700 int CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
701 {
702 if (!m_processor->CECInitialised() ||
703 !m_processor->IsHandledByLibCEC(command.destination))
704 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
705
706 CCECClient *client = m_processor->GetClient(command.destination);
707 if (client)
708 client->AddKey();
709
710 return COMMAND_HANDLED;
711 }
712
713 int CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command))
714 {
715 return CEC_ABORT_REASON_INVALID_OPERAND;
716 }
717
718 void CCECCommandHandler::UnhandledCommand(const cec_command &command, const cec_abort_reason reason)
719 {
720 if (m_processor->IsHandledByLibCEC(command.destination))
721 {
722 LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending abort with opcode %02x and reason '%s' to %s", command.opcode, ToString(reason), ToString(command.initiator));
723 m_processor->TransmitAbort(command.destination, command.initiator, command.opcode, reason);
724 }
725 }
726
727 size_t CCECCommandHandler::GetMyDevices(vector<CCECBusDevice *> &devices) const
728 {
729 size_t iReturn(0);
730
731 cec_logical_addresses addresses = m_processor->GetLogicalAddresses();
732 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
733 {
734 if (addresses[iPtr])
735 {
736 devices.push_back(GetDevice((cec_logical_address) iPtr));
737 ++iReturn;
738 }
739 }
740
741 return iReturn;
742 }
743
744 CCECBusDevice *CCECCommandHandler::GetDevice(cec_logical_address iLogicalAddress) const
745 {
746 return m_processor->GetDevice(iLogicalAddress);
747 }
748
749 CCECBusDevice *CCECCommandHandler::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const
750 {
751 return m_processor->GetDeviceByPhysicalAddress(iPhysicalAddress);
752 }
753
754 bool CCECCommandHandler::SetVendorId(const cec_command &command)
755 {
756 bool bChanged(false);
757 if (command.parameters.size < 3)
758 {
759 LIB_CEC->AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
760 return bChanged;
761 }
762
763 uint64_t iVendorId = ((uint64_t)command.parameters[0] << 16) +
764 ((uint64_t)command.parameters[1] << 8) +
765 (uint64_t)command.parameters[2];
766
767 CCECBusDevice *device = GetDevice((cec_logical_address) command.initiator);
768 if (device)
769 bChanged = device->SetVendorId(iVendorId);
770 return bChanged;
771 }
772
773 void CCECCommandHandler::SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress)
774 {
775 if (!m_processor->IsHandledByLibCEC(iAddress))
776 {
777 CCECBusDevice *otherDevice = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
778 CCECClient *client = otherDevice ? otherDevice->GetClient() : NULL;
779
780 CCECBusDevice *device = m_processor->GetDevice(iAddress);
781 if (device)
782 device->SetPhysicalAddress(iNewAddress);
783 else
784 {
785 LIB_CEC->AddLog(CEC_LOG_DEBUG, "device with logical address %X not found", iAddress);
786 }
787
788 /* another device reported the same physical address as ours */
789 if (client)
790 client->ResetPhysicalAddress();
791 }
792 else
793 {
794 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignore physical address report for device %s (%X) because it's marked as handled by libCEC", ToString(iAddress), iAddress);
795 }
796 }
797
798 bool CCECCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
799 {
800 if (iDestination == CECDEVICE_TV)
801 return TransmitImageViewOn(iInitiator, iDestination);
802
803 return TransmitKeypress(iInitiator, iDestination, CEC_USER_CONTROL_CODE_POWER) &&
804 TransmitKeyRelease(iInitiator, iDestination);
805 }
806
807 bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
808 {
809 cec_command command;
810 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON);
811
812 return Transmit(command);
813 }
814
815 bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination)
816 {
817 cec_command command;
818 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_STANDBY);
819
820 return Transmit(command);
821 }
822
823 bool CCECCommandHandler::TransmitRequestActiveSource(const cec_logical_address iInitiator, bool bWaitForResponse /* = true */)
824 {
825 cec_command command;
826 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_REQUEST_ACTIVE_SOURCE);
827
828 return Transmit(command, !bWaitForResponse);
829 }
830
831 bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
832 {
833 cec_command command;
834 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_CEC_VERSION);
835
836 return Transmit(command, !bWaitForResponse);
837 }
838
839 bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
840 {
841 cec_command command;
842 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_MENU_LANGUAGE);
843
844 return Transmit(command, !bWaitForResponse);
845 }
846
847 bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
848 {
849 cec_command command;
850 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_OSD_NAME);
851
852 return Transmit(command, !bWaitForResponse);
853 }
854
855 bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
856 {
857 cec_command command;
858 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
859
860 return Transmit(command, !bWaitForResponse);
861 }
862
863 bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
864 {
865 cec_command command;
866 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
867
868 return Transmit(command, !bWaitForResponse);
869 }
870
871 bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
872 {
873 cec_command command;
874 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
875
876 return Transmit(command, !bWaitForResponse);
877 }
878
879 bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
880 {
881 cec_command command;
882 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
883 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
884 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
885
886 return Transmit(command);
887 }
888
889 bool CCECCommandHandler::TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion)
890 {
891 cec_command command;
892 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_CEC_VERSION);
893 command.parameters.PushBack((uint8_t)cecVersion);
894
895 return Transmit(command);
896 }
897
898 bool CCECCommandHandler::TransmitInactiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
899 {
900 cec_command command;
901 cec_command::Format(command, iInitiator, CECDEVICE_TV, CEC_OPCODE_INACTIVE_SOURCE);
902 command.parameters.PushBack((iPhysicalAddress >> 8) & 0xFF);
903 command.parameters.PushBack(iPhysicalAddress & 0xFF);
904
905 return Transmit(command);
906 }
907
908 bool CCECCommandHandler::TransmitMenuState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_menu_state menuState)
909 {
910 cec_command command;
911 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_MENU_STATUS);
912 command.parameters.PushBack((uint8_t)menuState);
913
914 return Transmit(command);
915 }
916
917 bool CCECCommandHandler::TransmitOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, CStdString strDeviceName)
918 {
919 cec_command command;
920 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_NAME);
921 for (size_t iPtr = 0; iPtr < strDeviceName.length(); iPtr++)
922 command.parameters.PushBack(strDeviceName.at(iPtr));
923
924 return Transmit(command);
925 }
926
927 bool CCECCommandHandler::TransmitOSDString(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_display_control duration, const char *strMessage)
928 {
929 cec_command command;
930 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_STRING);
931 command.parameters.PushBack((uint8_t)duration);
932
933 size_t iLen = strlen(strMessage);
934 if (iLen > 13) iLen = 13;
935
936 for (size_t iPtr = 0; iPtr < iLen; iPtr++)
937 command.parameters.PushBack(strMessage[iPtr]);
938
939 return Transmit(command);
940 }
941
942 bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, cec_device_type type)
943 {
944 cec_command command;
945 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
946 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
947 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
948 command.parameters.PushBack((uint8_t) (type));
949
950 return Transmit(command);
951 }
952
953 bool CCECCommandHandler::TransmitSetMenuLanguage(const cec_logical_address iInitiator, const char lang[3])
954 {
955 cec_command command;
956 command.Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_SET_MENU_LANGUAGE);
957 command.parameters.PushBack((uint8_t) lang[0]);
958 command.parameters.PushBack((uint8_t) lang[1]);
959 command.parameters.PushBack((uint8_t) lang[2]);
960
961 return Transmit(command);
962 }
963
964 bool CCECCommandHandler::TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination)
965 {
966 cec_command command;
967 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_NONE);
968
969 return Transmit(command);
970 }
971
972 bool CCECCommandHandler::TransmitPowerState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_power_status state)
973 {
974 cec_command command;
975 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_POWER_STATUS);
976 command.parameters.PushBack((uint8_t) state);
977
978 return Transmit(command);
979 }
980
981 bool CCECCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, uint64_t iVendorId)
982 {
983 cec_command command;
984 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_DEVICE_VENDOR_ID);
985
986 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 16) & 0xFF));
987 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 8) & 0xFF));
988 command.parameters.PushBack((uint8_t) ((uint64_t)iVendorId & 0xFF));
989
990 return Transmit(command);
991 }
992
993 bool CCECCommandHandler::TransmitAudioStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint8_t state)
994 {
995 cec_command command;
996 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_AUDIO_STATUS);
997 command.parameters.PushBack(state);
998
999 return Transmit(command);
1000 }
1001
1002 bool CCECCommandHandler::TransmitSetSystemAudioMode(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
1003 {
1004 cec_command command;
1005 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_SYSTEM_AUDIO_MODE);
1006 command.parameters.PushBack((uint8_t)state);
1007
1008 return Transmit(command);
1009 }
1010
1011 bool CCECCommandHandler::TransmitSetStreamPath(uint16_t iStreamPath)
1012 {
1013 cec_command command;
1014 cec_command::Format(command, m_busDevice->GetLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_SET_STREAM_PATH);
1015 command.parameters.PushBack((uint8_t) ((iStreamPath >> 8) & 0xFF));
1016 command.parameters.PushBack((uint8_t) (iStreamPath & 0xFF));
1017
1018 return Transmit(command);
1019 }
1020
1021 bool CCECCommandHandler::TransmitSystemAudioModeStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
1022 {
1023 cec_command command;
1024 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS);
1025 command.parameters.PushBack((uint8_t)state);
1026
1027 return Transmit(command);
1028 }
1029
1030 bool CCECCommandHandler::TransmitDeckStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_deck_info state)
1031 {
1032 cec_command command;
1033 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_DECK_STATUS);
1034 command.PushBack((uint8_t)state);
1035
1036 return Transmit(command);
1037 }
1038
1039 bool CCECCommandHandler::TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
1040 {
1041 cec_command command;
1042 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_PRESSED);
1043 command.parameters.PushBack((uint8_t)key);
1044
1045 return Transmit(command, !bWait);
1046 }
1047
1048 bool CCECCommandHandler::TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait /* = true */)
1049 {
1050 cec_command command;
1051 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_RELEASE);
1052
1053 return Transmit(command, !bWait);
1054 }
1055
1056 bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* = false */)
1057 {
1058 bool bReturn(false);
1059 cec_opcode expectedResponse(cec_command::GetResponseOpcode(command.opcode));
1060 bool bExpectResponse(expectedResponse != CEC_OPCODE_NONE && !bSuppressWait);
1061 command.transmit_timeout = m_iTransmitTimeout;
1062
1063 if (command.initiator == CECDEVICE_UNKNOWN)
1064 {
1065 LIB_CEC->AddLog(CEC_LOG_ERROR, "not transmitting a command without a valid initiator");
1066 return bReturn;
1067 }
1068
1069 {
1070 uint8_t iTries(0), iMaxTries(!command.opcode_set ? 1 : m_iTransmitRetries + 1);
1071 while (!bReturn && ++iTries <= iMaxTries && !m_busDevice->IsUnsupportedFeature(command.opcode))
1072 {
1073 if ((bReturn = m_processor->Transmit(command)) == true)
1074 {
1075 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command transmitted");
1076 if (bExpectResponse)
1077 {
1078 bReturn = m_busDevice->WaitForOpcode(expectedResponse);
1079 LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, ToString(expectedResponse));
1080 }
1081 }
1082 }
1083 }
1084
1085 return bReturn;
1086 }
1087
1088 bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */)
1089 {
1090 if (m_busDevice->IsActiveSource() &&
1091 m_busDevice->IsHandledByLibCEC())
1092 {
1093 {
1094 CLockObject lock(m_mutex);
1095 // check if we need to send a delayed source switch
1096 if (bTransmitDelayedCommandsOnly)
1097 {
1098 if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending)
1099 return false;
1100
1101 LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
1102 }
1103
1104 // clear previous pending active source command
1105 m_iActiveSourcePending = 0;
1106 }
1107
1108 // update the power state and menu state
1109 m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
1110 m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); // TODO: LG
1111
1112 // power on the TV
1113 bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
1114
1115 // check if we're allowed to switch sources
1116 bool bSourceSwitchAllowed = SourceSwitchAllowed();
1117 if (!bSourceSwitchAllowed)
1118 LIB_CEC->AddLog(CEC_LOG_DEBUG, "source switch is currently not allowed by command handler");
1119
1120 // switch sources (if allowed)
1121 if (!bActiveSourceFailed && bSourceSwitchAllowed)
1122 {
1123 bActiveSourceFailed = !m_busDevice->TransmitActiveSource() ||
1124 !m_busDevice->TransmitMenuState(CECDEVICE_TV);
1125
1126 // update the deck status for playback devices
1127 if (!bActiveSourceFailed)
1128 {
1129 CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
1130 if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
1131 bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV);
1132 }
1133 }
1134
1135 // retry later
1136 if (bActiveSourceFailed || !bSourceSwitchAllowed)
1137 {
1138 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
1139 CLockObject lock(m_mutex);
1140 m_iActiveSourcePending = GetTimeMs() + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
1141 return false;
1142 }
1143
1144 // mark the handler as initialised
1145 CLockObject lock(m_mutex);
1146 m_bHandlerInited = true;
1147 }
1148 return true;
1149 }
1150
1151 void CCECCommandHandler::ScheduleActivateSource(uint64_t iDelay)
1152 {
1153 CLockObject lock(m_mutex);
1154 m_iActiveSourcePending = GetTimeMs() + iDelay;
1155 }