Merge branch 'master' into release
[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 "../CECProcessor.h"
38 #include "../LibCEC.h"
39
40 using namespace CEC;
41 using namespace std;
42 using namespace PLATFORM;
43
44 CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice) :
45 m_busDevice(busDevice),
46 m_processor(m_busDevice->GetProcessor()),
47 m_iTransmitTimeout(CEC_DEFAULT_TRANSMIT_TIMEOUT),
48 m_iTransmitWait(CEC_DEFAULT_TRANSMIT_WAIT),
49 m_iTransmitRetries(CEC_DEFAULT_TRANSMIT_RETRIES),
50 m_bHandlerInited(false),
51 m_bOPTSendDeckStatusUpdateOnActiveSource(false),
52 m_vendorId(CEC_VENDOR_UNKNOWN),
53 m_waitForResponse(new CWaitForResponse)
54 {
55 }
56
57 CCECCommandHandler::~CCECCommandHandler(void)
58 {
59 delete m_waitForResponse;
60 }
61
62 bool CCECCommandHandler::HandleCommand(const cec_command &command)
63 {
64 bool bHandled(true);
65
66 CLibCEC::AddCommand(command);
67
68 switch(command.opcode)
69 {
70 case CEC_OPCODE_REPORT_POWER_STATUS:
71 HandleReportPowerStatus(command);
72 break;
73 case CEC_OPCODE_CEC_VERSION:
74 HandleDeviceCecVersion(command);
75 break;
76 case CEC_OPCODE_SET_MENU_LANGUAGE:
77 HandleSetMenuLanguage(command);
78 break;
79 case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
80 if (m_processor->IsInitialised())
81 HandleGivePhysicalAddress(command);
82 break;
83 case CEC_OPCODE_GIVE_OSD_NAME:
84 if (m_processor->IsInitialised())
85 HandleGiveOSDName(command);
86 break;
87 case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
88 if (m_processor->IsInitialised())
89 HandleGiveDeviceVendorId(command);
90 break;
91 case CEC_OPCODE_DEVICE_VENDOR_ID:
92 HandleDeviceVendorId(command);
93 break;
94 case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
95 HandleDeviceVendorCommandWithId(command);
96 break;
97 case CEC_OPCODE_GIVE_DECK_STATUS:
98 if (m_processor->IsInitialised())
99 HandleGiveDeckStatus(command);
100 break;
101 case CEC_OPCODE_DECK_CONTROL:
102 HandleDeckControl(command);
103 break;
104 case CEC_OPCODE_MENU_REQUEST:
105 if (m_processor->IsInitialised())
106 HandleMenuRequest(command);
107 break;
108 case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
109 if (m_processor->IsInitialised())
110 HandleGiveDevicePowerStatus(command);
111 break;
112 case CEC_OPCODE_GET_CEC_VERSION:
113 if (m_processor->IsInitialised())
114 HandleGetCecVersion(command);
115 break;
116 case CEC_OPCODE_USER_CONTROL_PRESSED:
117 if (m_processor->IsInitialised())
118 HandleUserControlPressed(command);
119 break;
120 case CEC_OPCODE_USER_CONTROL_RELEASE:
121 if (m_processor->IsInitialised())
122 HandleUserControlRelease(command);
123 break;
124 case CEC_OPCODE_GIVE_AUDIO_STATUS:
125 if (m_processor->IsInitialised())
126 HandleGiveAudioStatus(command);
127 break;
128 case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
129 if (m_processor->IsInitialised())
130 HandleGiveSystemAudioModeStatus(command);
131 break;
132 case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
133 if (m_processor->IsInitialised())
134 HandleSystemAudioModeRequest(command);
135 break;
136 case CEC_OPCODE_REPORT_AUDIO_STATUS:
137 HandleReportAudioStatus(command);
138 break;
139 case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
140 HandleSystemAudioModeStatus(command);
141 break;
142 case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
143 HandleSetSystemAudioMode(command);
144 break;
145 case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
146 if (m_processor->IsInitialised())
147 HandleRequestActiveSource(command);
148 break;
149 case CEC_OPCODE_SET_STREAM_PATH:
150 HandleSetStreamPath(command);
151 break;
152 case CEC_OPCODE_ROUTING_CHANGE:
153 HandleRoutingChange(command);
154 break;
155 case CEC_OPCODE_ROUTING_INFORMATION:
156 HandleRoutingInformation(command);
157 break;
158 case CEC_OPCODE_STANDBY:
159 if (m_processor->IsInitialised())
160 HandleStandby(command);
161 break;
162 case CEC_OPCODE_ACTIVE_SOURCE:
163 HandleActiveSource(command);
164 break;
165 case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
166 HandleReportPhysicalAddress(command);
167 break;
168 case CEC_OPCODE_SET_OSD_NAME:
169 HandleSetOSDName(command);
170 break;
171 case CEC_OPCODE_IMAGE_VIEW_ON:
172 HandleImageViewOn(command);
173 break;
174 case CEC_OPCODE_TEXT_VIEW_ON:
175 HandleTextViewOn(command);
176 break;
177 case CEC_OPCODE_FEATURE_ABORT:
178 HandleFeatureAbort(command);
179 break;
180 case CEC_OPCODE_VENDOR_COMMAND:
181 HandleVendorCommand(command);
182 break;
183 default:
184 UnhandledCommand(command);
185 bHandled = false;
186 break;
187 }
188
189 if (bHandled)
190 m_waitForResponse->Received((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode);
191
192 return bHandled;
193 }
194
195 bool CCECCommandHandler::HandleActiveSource(const cec_command &command)
196 {
197 if (command.parameters.size == 2)
198 {
199 uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
200 return m_processor->SetActiveSource(iAddress);
201 }
202
203 return true;
204 }
205
206 bool CCECCommandHandler::HandleDeckControl(const cec_command &command)
207 {
208 CCECBusDevice *device = GetDevice(command.destination);
209 if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) && command.parameters.size > 0)
210 {
211 ((CCECPlaybackDevice *) device)->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]);
212 return true;
213 }
214
215 return false;
216 }
217
218 bool CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command)
219 {
220 if (command.parameters.size == 1)
221 {
222 CCECBusDevice *device = GetDevice(command.initiator);
223 if (device)
224 device->SetCecVersion((cec_version) command.parameters[0]);
225 }
226
227 return true;
228 }
229
230 bool CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command &command)
231 {
232 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
233 m_processor->TransmitAbort(command.initiator, command.opcode, CEC_ABORT_REASON_REFUSED);
234
235 return true;
236 }
237
238 bool CCECCommandHandler::HandleDeviceVendorId(const cec_command &command)
239 {
240 return SetVendorId(command);
241 }
242
243 bool CCECCommandHandler::HandleFeatureAbort(const cec_command &command)
244 {
245 if (command.parameters.size == 2 && command.parameters[1] == CEC_ABORT_REASON_UNRECOGNIZED_OPCODE)
246 m_processor->m_busDevices[command.initiator]->SetUnsupportedFeature((cec_opcode)command.parameters[0]);
247 return true;
248 }
249
250 bool CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
251 {
252 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
253 {
254 CCECBusDevice *device = GetDevice(command.destination);
255 if (device)
256 return device->TransmitCECVersion(command.initiator);
257 }
258
259 return false;
260 }
261
262 bool CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command)
263 {
264 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
265 {
266 CCECBusDevice *device = GetDevice(command.destination);
267 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
268 return ((CCECAudioSystem *) device)->TransmitAudioStatus(command.initiator);
269 }
270
271 return false;
272 }
273
274 bool CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
275 {
276 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
277 {
278 CCECBusDevice *device = GetDevice(command.destination);
279 if (device && (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
280 return ((CCECPlaybackDevice *) device)->TransmitDeckStatus(command.initiator);
281 }
282
283 return false;
284 }
285
286 bool CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
287 {
288 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
289 {
290 CCECBusDevice *device = GetDevice(command.destination);
291 if (device)
292 return device->TransmitPowerState(command.initiator);
293 }
294
295 return false;
296 }
297
298 bool CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
299 {
300 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
301 {
302 CCECBusDevice *device = GetDevice(command.destination);
303 if (device)
304 return device->TransmitVendorID(command.initiator);
305 }
306
307 return false;
308 }
309
310 bool CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
311 {
312 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
313 {
314 CCECBusDevice *device = GetDevice(command.destination);
315 if (device)
316 return device->TransmitOSDName(command.initiator);
317 }
318
319 return false;
320 }
321
322 bool CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
323 {
324 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
325 {
326 CCECBusDevice *device = GetDevice(command.destination);
327 if (device)
328 {
329 device->SetActiveSource();
330 return device->TransmitPhysicalAddress() &&
331 device->TransmitImageViewOn() &&
332 device->TransmitActiveSource();
333 }
334 }
335
336 return false;
337 }
338
339 bool CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command)
340 {
341 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
342 {
343 CCECBusDevice *device = GetDevice(command.destination);
344 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
345 return ((CCECAudioSystem *) device)->TransmitSystemAudioModeStatus(command.initiator);
346 }
347
348 return false;
349 }
350
351 bool CCECCommandHandler::HandleImageViewOn(const cec_command &command)
352 {
353 m_processor->m_busDevices[command.initiator]->SetActiveSource();
354 return true;
355 }
356
357 bool CCECCommandHandler::HandleMenuRequest(const cec_command &command)
358 {
359 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
360 {
361 if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_QUERY)
362 {
363 CCECBusDevice *device = GetDevice(command.destination);
364 if (device)
365 return device->TransmitMenuState(command.initiator);
366 }
367 }
368
369 return false;
370 }
371
372 bool CCECCommandHandler::HandleReportAudioStatus(const cec_command &command)
373 {
374 if (command.parameters.size == 1)
375 {
376 CCECBusDevice *device = GetDevice(command.initiator);
377 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
378 {
379 ((CCECAudioSystem *)device)->SetAudioStatus(command.parameters[0]);
380 return true;
381 }
382 }
383 return false;
384 }
385
386 bool CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command)
387 {
388 if (command.parameters.size == 3)
389 {
390 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
391 SetPhysicalAddress(command.initiator, iNewAddress);
392 }
393 return true;
394 }
395
396 bool CCECCommandHandler::HandleReportPowerStatus(const cec_command &command)
397 {
398 if (command.parameters.size == 1)
399 {
400 CCECBusDevice *device = GetDevice(command.initiator);
401 if (device)
402 device->SetPowerStatus((cec_power_status) command.parameters[0]);
403 }
404 return true;
405 }
406
407 bool CCECCommandHandler::HandleRequestActiveSource(const cec_command &command)
408 {
409 if (m_processor->IsRunning())
410 {
411 CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %i requests active source", (uint8_t) command.initiator);
412
413 vector<CCECBusDevice *> devices;
414 for (size_t iDevicePtr = 0; iDevicePtr < GetMyDevices(devices); iDevicePtr++)
415 devices[iDevicePtr]->TransmitActiveSource();
416
417 return true;
418 }
419 return false;
420 }
421
422 bool CCECCommandHandler::HandleRoutingChange(const cec_command &command)
423 {
424 if (command.parameters.size == 4)
425 {
426 uint16_t iOldAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
427 uint16_t iNewAddress = ((uint16_t)command.parameters[2] << 8) | ((uint16_t)command.parameters[3]);
428
429 CCECBusDevice *device = GetDevice(command.initiator);
430 if (device)
431 device->SetStreamPath(iNewAddress, iOldAddress);
432 }
433 return true;
434 }
435
436 bool CCECCommandHandler::HandleRoutingInformation(const cec_command &command)
437 {
438 if (command.parameters.size == 2)
439 {
440 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
441 m_processor->SetActiveSource(iNewAddress);
442 }
443
444 return false;
445 }
446
447 bool CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command)
448 {
449 if (command.parameters.size == 3)
450 {
451 CCECBusDevice *device = GetDevice(command.initiator);
452 if (device)
453 {
454 cec_menu_language language;
455 language.device = command.initiator;
456 for (uint8_t iPtr = 0; iPtr < 4; iPtr++)
457 language.language[iPtr] = command.parameters[iPtr];
458 language.language[3] = 0;
459 device->SetMenuLanguage(language);
460 return true;
461 }
462 }
463 return false;
464 }
465
466 bool CCECCommandHandler::HandleSetOSDName(const cec_command &command)
467 {
468 if (command.parameters.size > 0)
469 {
470 CCECBusDevice *device = GetDevice(command.initiator);
471 if (device)
472 {
473 char buf[1024];
474 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
475 buf[iPtr] = (char)command.parameters[iPtr];
476 buf[command.parameters.size] = 0;
477
478 CStdString strName(buf);
479 device->SetOSDName(strName);
480
481 return true;
482 }
483 }
484 return false;
485 }
486
487 bool CCECCommandHandler::HandleSetStreamPath(const cec_command &command)
488 {
489 if (m_processor->IsRunning() && command.parameters.size >= 2)
490 {
491 uint16_t iStreamAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
492 CLibCEC::AddLog(CEC_LOG_DEBUG, ">> %i sets stream path to physical address %04x", command.initiator, iStreamAddress);
493
494 /* one of the device handled by libCEC has been made active */
495 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamAddress);
496 if (device && m_busDevice->MyLogicalAddressContains(device->GetLogicalAddress()))
497 {
498 device->SetActiveSource();
499 device->TransmitImageViewOn();
500 device->TransmitActiveSource();
501
502 device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
503 device->TransmitMenuState(command.initiator);
504 }
505 }
506 return false;
507 }
508
509 bool CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command)
510 {
511 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
512 {
513 CCECBusDevice *device = GetDevice(command.destination);
514 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
515 {
516 if (command.parameters.size >= 2)
517 {
518 device->SetPowerStatus(CEC_POWER_STATUS_ON);
519 ((CCECAudioSystem *) device)->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_ON);
520 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
521 CCECBusDevice *newActiveDevice = GetDeviceByPhysicalAddress(iNewAddress);
522 if (newActiveDevice)
523 newActiveDevice->SetActiveSource();
524 return ((CCECAudioSystem *) device)->TransmitSetSystemAudioMode(command.initiator);
525 }
526 else
527 {
528 ((CCECAudioSystem *) device)->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF);
529 return ((CCECAudioSystem *) device)->TransmitSetSystemAudioMode(command.initiator);
530 }
531 }
532 }
533 return false;
534 }
535
536 bool CCECCommandHandler::HandleStandby(const cec_command &command)
537 {
538 CCECBusDevice *device = GetDevice(command.initiator);
539 if (device)
540 device->SetPowerStatus(CEC_POWER_STATUS_STANDBY);
541
542 return true;
543 }
544
545 bool CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command)
546 {
547 if (command.parameters.size == 1)
548 {
549 CCECBusDevice *device = GetDevice(command.initiator);
550 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
551 {
552 ((CCECAudioSystem *)device)->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
553 return true;
554 }
555 }
556
557 return false;
558 }
559
560 bool CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command)
561 {
562 if (command.parameters.size == 1)
563 {
564 CCECBusDevice *device = GetDevice(command.initiator);
565 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
566 {
567 ((CCECAudioSystem *)device)->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
568 return true;
569 }
570 }
571
572 return false;
573 }
574
575 bool CCECCommandHandler::HandleTextViewOn(const cec_command &command)
576 {
577 m_processor->m_busDevices[command.initiator]->SetActiveSource();
578 return true;
579 }
580
581 bool CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
582 {
583 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination) && command.parameters.size > 0)
584 {
585 CLibCEC::AddKey();
586
587 if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
588 {
589 if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER ||
590 command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION)
591 {
592 CCECBusDevice *device = GetDevice(command.destination);
593 if (device)
594 {
595 device->SetPowerStatus(CEC_POWER_STATUS_ON);
596 if (device->MyLogicalAddressContains(device->GetLogicalAddress()))
597 {
598 device->SetActiveSource();
599 device->TransmitImageViewOn();
600 device->TransmitActiveSource();
601
602 if (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
603 device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
604 ((CCECPlaybackDevice *)device)->TransmitDeckStatus(command.initiator);
605 }
606 }
607 }
608 else
609 {
610 CLibCEC::SetCurrentButton((cec_user_control_code) command.parameters[0]);
611 }
612 return true;
613 }
614 }
615 return false;
616 }
617
618 bool CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
619 {
620 if (m_processor->IsRunning() && m_busDevice->MyLogicalAddressContains(command.destination))
621 CLibCEC::AddKey();
622
623 return true;
624 }
625
626 bool CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command))
627 {
628 return true;
629 }
630
631 void CCECCommandHandler::UnhandledCommand(const cec_command &command)
632 {
633 CLibCEC::AddLog(CEC_LOG_DEBUG, "unhandled command with opcode %02x from address %d", command.opcode, command.initiator);
634 }
635
636 size_t CCECCommandHandler::GetMyDevices(vector<CCECBusDevice *> &devices) const
637 {
638 size_t iReturn(0);
639
640 cec_logical_addresses addresses = m_processor->GetLogicalAddresses();
641 for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
642 {
643 if (addresses[iPtr])
644 {
645 devices.push_back(GetDevice((cec_logical_address) iPtr));
646 ++iReturn;
647 }
648 }
649
650 return iReturn;
651 }
652
653 CCECBusDevice *CCECCommandHandler::GetDevice(cec_logical_address iLogicalAddress) const
654 {
655 CCECBusDevice *device = NULL;
656
657 if (iLogicalAddress >= CECDEVICE_TV && iLogicalAddress <= CECDEVICE_BROADCAST)
658 device = m_processor->m_busDevices[iLogicalAddress];
659
660 return device;
661 }
662
663 CCECBusDevice *CCECCommandHandler::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const
664 {
665 return m_processor->GetDeviceByPhysicalAddress(iPhysicalAddress);
666 }
667
668 CCECBusDevice *CCECCommandHandler::GetDeviceByType(cec_device_type type) const
669 {
670 return m_processor->GetDeviceByType(type);
671 }
672
673 bool CCECCommandHandler::SetVendorId(const cec_command &command)
674 {
675 bool bChanged(false);
676 if (command.parameters.size < 3)
677 {
678 CLibCEC::AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
679 return bChanged;
680 }
681
682 uint64_t iVendorId = ((uint64_t)command.parameters[0] << 16) +
683 ((uint64_t)command.parameters[1] << 8) +
684 (uint64_t)command.parameters[2];
685
686 CCECBusDevice *device = GetDevice((cec_logical_address) command.initiator);
687 if (device)
688 bChanged = device->SetVendorId(iVendorId);
689 return bChanged;
690 }
691
692 void CCECCommandHandler::SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress)
693 {
694 if (!m_busDevice->MyLogicalAddressContains(iAddress))
695 {
696 bool bOurAddress(m_processor->GetPhysicalAddress() == iNewAddress);
697 GetDevice(iAddress)->SetPhysicalAddress(iNewAddress);
698 if (bOurAddress)
699 {
700 /* another device reported the same physical address as ours
701 * since we don't have physical address detection yet, we'll just use the
702 * given address, increased by 0x100 for now */
703 m_processor->SetPhysicalAddress(iNewAddress + 0x100);
704 }
705 }
706 }
707
708 bool CCECCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
709 {
710 if (iDestination == CECDEVICE_TV)
711 return TransmitImageViewOn(iInitiator, iDestination);
712
713 return TransmitKeypress(iInitiator, iDestination, CEC_USER_CONTROL_CODE_POWER) &&
714 TransmitKeyRelease(iInitiator, iDestination);
715 }
716
717 bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
718 {
719 cec_command command;
720 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON);
721
722 return Transmit(command, false);
723 }
724
725 bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination)
726 {
727 cec_command command;
728 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_STANDBY);
729
730 return Transmit(command, false);
731 }
732
733 bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination)
734 {
735 cec_command command;
736 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_CEC_VERSION);
737
738 return Transmit(command, true, CEC_OPCODE_CEC_VERSION);
739 }
740
741 bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination)
742 {
743 cec_command command;
744 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_MENU_LANGUAGE);
745
746 return Transmit(command, true, CEC_OPCODE_SET_MENU_LANGUAGE);
747 }
748
749 bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination)
750 {
751 cec_command command;
752 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_OSD_NAME);
753
754 return Transmit(command, true, CEC_OPCODE_SET_OSD_NAME);
755 }
756
757 bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination)
758 {
759 cec_command command;
760 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
761
762 return Transmit(command, true, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
763 }
764
765 bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination)
766 {
767 cec_command command;
768 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
769
770 return Transmit(command, true, CEC_OPCODE_REPORT_POWER_STATUS);
771 }
772
773 bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination)
774 {
775 cec_command command;
776 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
777
778 return Transmit(command, true, CEC_OPCODE_DEVICE_VENDOR_ID);
779 }
780
781 bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
782 {
783 cec_command command;
784 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
785 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
786 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
787
788 return Transmit(command, false);
789 }
790
791 bool CCECCommandHandler::TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion)
792 {
793 cec_command command;
794 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_CEC_VERSION);
795 command.parameters.PushBack((uint8_t)cecVersion);
796
797 return Transmit(command, false);
798 }
799
800 bool CCECCommandHandler::TransmitInactiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
801 {
802 cec_command command;
803 cec_command::Format(command, iInitiator, CECDEVICE_TV, CEC_OPCODE_INACTIVE_SOURCE);
804 command.parameters.PushBack((iPhysicalAddress >> 8) & 0xFF);
805 command.parameters.PushBack(iPhysicalAddress & 0xFF);
806
807 return Transmit(command, false);
808 }
809
810 bool CCECCommandHandler::TransmitMenuState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_menu_state menuState)
811 {
812 cec_command command;
813 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_MENU_STATUS);
814 command.parameters.PushBack((uint8_t)menuState);
815
816 return Transmit(command, false);
817 }
818
819 bool CCECCommandHandler::TransmitOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, CStdString strDeviceName)
820 {
821 cec_command command;
822 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_NAME);
823 for (size_t iPtr = 0; iPtr < strDeviceName.length(); iPtr++)
824 command.parameters.PushBack(strDeviceName.at(iPtr));
825
826 return Transmit(command, false);
827 }
828
829 bool CCECCommandHandler::TransmitOSDString(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_display_control duration, const char *strMessage)
830 {
831 cec_command command;
832 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_STRING);
833 command.parameters.PushBack((uint8_t)duration);
834
835 size_t iLen = strlen(strMessage);
836 if (iLen > 13) iLen = 13;
837
838 for (size_t iPtr = 0; iPtr < iLen; iPtr++)
839 command.parameters.PushBack(strMessage[iPtr]);
840
841 return Transmit(command, false);
842 }
843
844 bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, cec_device_type type)
845 {
846 cec_command command;
847 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
848 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
849 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
850 command.parameters.PushBack((uint8_t) (type));
851
852 return Transmit(command, false);
853 }
854
855 bool CCECCommandHandler::TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination)
856 {
857 cec_command command;
858 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_NONE);
859
860 return Transmit(command, false);
861 }
862
863 bool CCECCommandHandler::TransmitPowerState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_power_status state)
864 {
865 cec_command command;
866 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_POWER_STATUS);
867 command.parameters.PushBack((uint8_t) state);
868
869 return Transmit(command, false);
870 }
871
872 bool CCECCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, uint64_t iVendorId)
873 {
874 cec_command command;
875 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_DEVICE_VENDOR_ID);
876
877 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 16) & 0xFF));
878 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 8) & 0xFF));
879 command.parameters.PushBack((uint8_t) ((uint64_t)iVendorId & 0xFF));
880
881 return Transmit(command, false);
882 }
883
884 bool CCECCommandHandler::TransmitAudioStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint8_t state)
885 {
886 cec_command command;
887 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_AUDIO_STATUS);
888 command.parameters.PushBack(state);
889
890 return Transmit(command, false);
891 }
892
893 bool CCECCommandHandler::TransmitSetSystemAudioMode(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
894 {
895 cec_command command;
896 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_SYSTEM_AUDIO_MODE);
897 command.parameters.PushBack((uint8_t)state);
898
899 return Transmit(command, false);
900 }
901
902 bool CCECCommandHandler::TransmitSetStreamPath(uint16_t iStreamPath)
903 {
904 cec_command command;
905 cec_command::Format(command, m_busDevice->GetLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_SET_STREAM_PATH);
906 command.parameters.PushBack((uint8_t) ((iStreamPath >> 8) & 0xFF));
907 command.parameters.PushBack((uint8_t) (iStreamPath & 0xFF));
908
909 return Transmit(command, false);
910 }
911
912 bool CCECCommandHandler::TransmitSystemAudioModeStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
913 {
914 cec_command command;
915 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS);
916 command.parameters.PushBack((uint8_t)state);
917
918 return Transmit(command, false);
919 }
920
921 bool CCECCommandHandler::TransmitDeckStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_deck_info state)
922 {
923 cec_command command;
924 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_DECK_STATUS);
925 command.PushBack((uint8_t)state);
926
927 return Transmit(command, false);
928 }
929
930 bool CCECCommandHandler::TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
931 {
932 cec_command command;
933 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_PRESSED);
934 command.parameters.PushBack((uint8_t)key);
935
936 return Transmit(command, bWait);
937 }
938
939 bool CCECCommandHandler::TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait /* = true */)
940 {
941 cec_command command;
942 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_RELEASE);
943
944 return Transmit(command, bWait);
945 }
946
947 bool CCECCommandHandler::Transmit(cec_command &command, bool bExpectResponse /* = true */, cec_opcode expectedResponse /* = CEC_OPCODE_NONE */)
948 {
949 bool bReturn(false);
950 command.transmit_timeout = m_iTransmitTimeout;
951
952 {
953 uint8_t iTries(0), iMaxTries(command.opcode == CEC_OPCODE_NONE ? 1 : m_iTransmitRetries + 1);
954 CLockObject writeLock(m_processor->m_transmitMutex);
955 while (!bReturn && ++iTries <= iMaxTries)
956 {
957 if ((bReturn = m_processor->Transmit(command)) == true)
958 {
959 CLibCEC::AddLog(CEC_LOG_DEBUG, "command transmitted");
960 if (bExpectResponse)
961 {
962 bReturn = m_waitForResponse->Wait(expectedResponse);
963 CLibCEC::AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, m_processor->ToString(expectedResponse));
964 }
965 }
966 }
967 }
968
969 return bReturn;
970 }
971
972 bool CCECCommandHandler::ActivateSource(void)
973 {
974 if (m_busDevice->IsActiveSource() &&
975 m_busDevice->GetStatus(false) == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
976 {
977 m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
978 m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
979
980 m_busDevice->TransmitImageViewOn();
981 m_busDevice->TransmitActiveSource();
982 m_busDevice->TransmitMenuState(CECDEVICE_TV);
983 if ((m_busDevice->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
984 m_busDevice->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
985 SendDeckStatusUpdateOnActiveSource())
986 ((CCECPlaybackDevice *)m_busDevice)->TransmitDeckStatus(CECDEVICE_TV);
987 m_bHandlerInited = true;
988 }
989 return true;
990 }