cec: display an alert message when the firmware of the adapter can be upgraded. bugzi...
[deb_libcec.git] / src / lib / CECProcessor.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 "CECProcessor.h"
34
35 #include "adapter/USBCECAdapterCommunication.h"
36 #include "devices/CECBusDevice.h"
37 #include "devices/CECAudioSystem.h"
38 #include "devices/CECPlaybackDevice.h"
39 #include "devices/CECRecordingDevice.h"
40 #include "devices/CECTuner.h"
41 #include "devices/CECTV.h"
42 #include "implementations/CECCommandHandler.h"
43 #include "LibCEC.h"
44 #include "platform/util/timeutils.h"
45
46 using namespace CEC;
47 using namespace std;
48 using namespace PLATFORM;
49
50 #define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
51
52 CCECProcessor::CCECProcessor(CLibCEC *controller, libcec_configuration *configuration) :
53 m_bConnectionOpened(false),
54 m_bInitialised(false),
55 m_communication(NULL),
56 m_controller(controller),
57 m_bMonitor(false),
58 m_iStandardLineTimeout(3),
59 m_iRetryLineTimeout(3),
60 m_iLastTransmission(0)
61 {
62 CreateBusDevices();
63 m_configuration.Clear();
64 m_configuration.serverVersion = LIBCEC_VERSION_CURRENT;
65 SetConfiguration(configuration);
66
67 if (m_configuration.tvVendor != CEC_VENDOR_UNKNOWN)
68 m_busDevices[CECDEVICE_TV]->ReplaceHandler(false);
69 }
70
71 CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types, uint16_t iPhysicalAddress) :
72 m_bConnectionOpened(false),
73 m_bInitialised(false),
74 m_communication(NULL),
75 m_controller(controller),
76 m_bMonitor(false),
77 m_iStandardLineTimeout(3),
78 m_iRetryLineTimeout(3),
79 m_iLastTransmission(0)
80 {
81 m_configuration.Clear();
82 m_configuration.serverVersion = LIBCEC_VERSION_CURRENT;
83
84 // client version < 1.5.0
85 m_configuration.clientVersion = (uint32_t)CEC_CLIENT_VERSION_PRE_1_5;
86 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName);
87 m_configuration.deviceTypes = types;
88 m_configuration.iPhysicalAddress = iPhysicalAddress;
89 m_configuration.baseDevice = (cec_logical_address)CEC_DEFAULT_BASE_DEVICE;
90 m_configuration.iHDMIPort = CEC_DEFAULT_HDMI_PORT;
91
92 if (m_configuration.deviceTypes.IsEmpty())
93 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
94 CreateBusDevices();
95 }
96
97 void CCECProcessor::CreateBusDevices(void)
98 {
99 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
100 {
101 switch(iPtr)
102 {
103 case CECDEVICE_AUDIOSYSTEM:
104 m_busDevices[iPtr] = new CCECAudioSystem(this, (cec_logical_address) iPtr);
105 break;
106 case CECDEVICE_PLAYBACKDEVICE1:
107 case CECDEVICE_PLAYBACKDEVICE2:
108 case CECDEVICE_PLAYBACKDEVICE3:
109 m_busDevices[iPtr] = new CCECPlaybackDevice(this, (cec_logical_address) iPtr);
110 break;
111 case CECDEVICE_RECORDINGDEVICE1:
112 case CECDEVICE_RECORDINGDEVICE2:
113 case CECDEVICE_RECORDINGDEVICE3:
114 m_busDevices[iPtr] = new CCECRecordingDevice(this, (cec_logical_address) iPtr);
115 break;
116 case CECDEVICE_TUNER1:
117 case CECDEVICE_TUNER2:
118 case CECDEVICE_TUNER3:
119 case CECDEVICE_TUNER4:
120 m_busDevices[iPtr] = new CCECTuner(this, (cec_logical_address) iPtr);
121 break;
122 case CECDEVICE_TV:
123 m_busDevices[iPtr] = new CCECTV(this, (cec_logical_address) iPtr);
124 break;
125 default:
126 m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr);
127 break;
128 }
129 }
130 }
131
132 CCECProcessor::~CCECProcessor(void)
133 {
134 Close();
135
136 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
137 {
138 delete m_busDevices[iPtr];
139 m_busDevices[iPtr] = NULL;
140 }
141 }
142
143 void CCECProcessor::Close(void)
144 {
145 StopThread(false);
146 SetInitialised(false);
147 StopThread();
148
149 bool bClose(false);
150 {
151 CLockObject lock(m_mutex);
152 bClose = m_bConnectionOpened;
153 m_bConnectionOpened = false;
154 }
155
156 if (bClose && m_communication)
157 {
158 delete m_communication;
159 m_communication = NULL;
160 }
161 }
162
163 bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */)
164 {
165 bool bReturn(false);
166 Close();
167
168 {
169 CLockObject lock(m_mutex);
170 if (m_bConnectionOpened)
171 {
172 CLibCEC::AddLog(CEC_LOG_ERROR, "connection already opened");
173 return false;
174 }
175 m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate);
176 m_bConnectionOpened = (m_communication != NULL);
177 }
178
179 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
180
181 /* open a new connection */
182 unsigned iConnectTry(0);
183 while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false)
184 {
185 CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
186 m_communication->Close();
187 CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT);
188 }
189
190 if (bReturn)
191 {
192 m_configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
193 m_configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
194 CStdString strLog;
195 strLog.Format("connected to the CEC adapter. libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion);
196 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
197 {
198 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
199 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
200 strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime
201 strLog.append(" +0000");
202 }
203 CLibCEC::AddLog(CEC_LOG_NOTICE, strLog);
204 }
205
206 if (!m_communication->IsRunningLatestFirmware())
207 {
208 const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information.";
209 CLibCEC::AddLog(CEC_LOG_WARNING, strUpgradeMessage);
210 CLibCEC::Alert(CEC_ALERT_SERVICE_DEVICE, libcec_parameter(strUpgradeMessage));
211 }
212 else
213 {
214 CLibCEC::AddLog(CEC_LOG_DEBUG, "the adapter is using the latest (known) firmware version");
215 }
216
217 if (m_configuration.bGetSettingsFromROM == 1)
218 {
219 libcec_configuration config;
220 config.Clear();
221 m_communication->GetConfiguration(&config);
222
223 CLockObject lock(m_mutex);
224 if (!config.deviceTypes.IsEmpty())
225 m_configuration.deviceTypes = config.deviceTypes;
226 if (IsValidPhysicalAddress(config.iPhysicalAddress))
227 m_configuration.iPhysicalAddress = config.iPhysicalAddress;
228 snprintf(m_configuration.strDeviceName, 13, "%s", config.strDeviceName);
229 }
230
231 return bReturn;
232 }
233
234 bool CCECProcessor::IsInitialised(void)
235 {
236 CLockObject lock(m_threadMutex);
237 return m_bInitialised;
238 }
239
240 void CCECProcessor::SetInitialised(bool bSetTo /* = true */)
241 {
242 CLockObject lock(m_mutex);
243 m_bInitialised = bSetTo;
244 }
245
246 bool CCECProcessor::Initialise(void)
247 {
248 bool bReturn(false);
249 {
250 CLockObject lock(m_mutex);
251 if (!m_configuration.logicalAddresses.IsEmpty())
252 m_configuration.logicalAddresses.Clear();
253
254 if (!FindLogicalAddresses())
255 {
256 CLibCEC::AddLog(CEC_LOG_ERROR, "could not detect our logical addresses");
257 return bReturn;
258 }
259
260 /* only set our OSD name for the primary device */
261 m_busDevices[m_configuration.logicalAddresses.primary]->m_strDeviceName = m_configuration.strDeviceName;
262
263 /* make the primary device the active source if the option is set */
264 if (m_configuration.bActivateSource == 1)
265 m_busDevices[m_configuration.logicalAddresses.primary]->m_bActiveSource = true;
266
267 /* set the default menu language for devices we control */
268 cec_menu_language language;
269 language.device = m_configuration.logicalAddresses.primary;
270 memcpy(language.language, m_configuration.strDeviceLanguage, 3);
271 language.language[3] = 0;
272
273 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
274 {
275 if (m_configuration.logicalAddresses[iPtr])
276 {
277 language.device = (cec_logical_address) iPtr;
278 m_busDevices[iPtr]->SetMenuLanguage(language);
279 }
280 }
281 }
282
283 /* get the vendor id from the TV, so we are using the correct handler */
284 m_busDevices[CECDEVICE_TV]->GetVendorId();
285
286 if (IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
287 {
288 CLibCEC::AddLog(CEC_LOG_NOTICE, "setting the physical address to %04X", m_configuration.iPhysicalAddress);
289 m_busDevices[m_configuration.logicalAddresses.primary]->m_iPhysicalAddress = m_configuration.iPhysicalAddress;
290 if ((bReturn = m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPhysicalAddress()) == false)
291 CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set the physical address to %04X", m_configuration.iPhysicalAddress);
292 }
293 else
294 {
295 if (!SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true))
296 CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set HDMI port %d on %s (%x)", m_configuration.iHDMIPort, ToString(m_configuration.baseDevice), (uint8_t)m_configuration.baseDevice);
297 bReturn = true;
298 }
299
300 if (bReturn && m_configuration.bActivateSource == 1)
301 m_busDevices[m_configuration.logicalAddresses.primary]->ActivateSource();
302
303 SetInitialised(bReturn);
304 if (bReturn)
305 CLibCEC::ConfigurationChanged(m_configuration);
306
307 return bReturn;
308 }
309
310 bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
311 {
312 bool bReturn(false);
313
314 {
315 CLockObject lock(m_mutex);
316 if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
317 return bReturn;
318
319 /* create the processor thread */
320 if (!CreateThread())
321 {
322 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
323 return bReturn;
324 }
325 }
326
327 if ((bReturn = Initialise()) == false)
328 {
329 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
330 StopThread(true);
331 }
332 else
333 {
334 CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
335 }
336
337 return bReturn;
338 }
339
340 bool CCECProcessor::TryLogicalAddress(cec_logical_address address)
341 {
342 if (m_busDevices[address]->TryLogicalAddress())
343 {
344 m_configuration.logicalAddresses.Set(address);
345 return true;
346 }
347
348 return false;
349 }
350
351 bool CCECProcessor::FindLogicalAddressRecordingDevice(void)
352 {
353 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
354 return TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1) ||
355 TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2) ||
356 TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3);
357 }
358
359 bool CCECProcessor::FindLogicalAddressTuner(void)
360 {
361 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
362 return TryLogicalAddress(CECDEVICE_TUNER1) ||
363 TryLogicalAddress(CECDEVICE_TUNER2) ||
364 TryLogicalAddress(CECDEVICE_TUNER3) ||
365 TryLogicalAddress(CECDEVICE_TUNER4);
366 }
367
368 bool CCECProcessor::FindLogicalAddressPlaybackDevice(void)
369 {
370 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
371 return TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1) ||
372 TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2) ||
373 TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3);
374 }
375
376 bool CCECProcessor::FindLogicalAddressAudioSystem(void)
377 {
378 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audio'");
379 return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM);
380 }
381
382 bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to)
383 {
384 bool bChanged(false);
385
386 CLibCEC::AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
387
388 CLockObject lock(m_mutex);
389 CCECBusDevice *previousDevice = GetDeviceByType(from);
390 m_configuration.logicalAddresses.primary = CECDEVICE_UNKNOWN;
391
392 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
393 {
394 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
395 continue;
396
397 if (m_configuration.deviceTypes.types[iPtr] == from)
398 {
399 bChanged = true;
400 m_configuration.deviceTypes.types[iPtr] = to;
401 }
402 else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
403 {
404 m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
405 }
406 }
407
408 if (bChanged)
409 {
410 FindLogicalAddresses();
411
412 CCECBusDevice *newDevice = GetDeviceByType(to);
413 if (previousDevice && newDevice)
414 {
415 newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
416 previousDevice->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT);
417
418 newDevice->SetCecVersion(previousDevice->GetCecVersion(false));
419 previousDevice->SetCecVersion(CEC_VERSION_UNKNOWN);
420
421 newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(false));
422
423 newDevice->SetMenuState(previousDevice->GetMenuState());
424 previousDevice->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
425
426 newDevice->SetOSDName(previousDevice->GetOSDName(false));
427 previousDevice->SetOSDName(ToString(previousDevice->GetLogicalAddress()));
428
429 newDevice->SetPhysicalAddress(previousDevice->GetPhysicalAddress());
430 previousDevice->SetPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
431
432 newDevice->SetPowerStatus(previousDevice->GetPowerStatus(false));
433 previousDevice->SetPowerStatus(CEC_POWER_STATUS_UNKNOWN);
434
435 newDevice->SetVendorId(previousDevice->GetVendorId(false));
436 previousDevice->SetVendorId(CEC_VENDOR_UNKNOWN);
437
438 if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
439 (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE))
440 {
441 ((CCECPlaybackDevice *) newDevice)->SetDeckControlMode(((CCECPlaybackDevice *) previousDevice)->GetDeckControlMode());
442 ((CCECPlaybackDevice *) previousDevice)->SetDeckControlMode(CEC_DECK_CONTROL_MODE_STOP);
443
444 ((CCECPlaybackDevice *) newDevice)->SetDeckStatus(((CCECPlaybackDevice *) previousDevice)->GetDeckStatus());
445 ((CCECPlaybackDevice *) previousDevice)->SetDeckStatus(CEC_DECK_INFO_STOP);
446 }
447 }
448 }
449
450 return true;
451 }
452
453 bool CCECProcessor::FindLogicalAddresses(void)
454 {
455 bool bReturn(true);
456 m_configuration.logicalAddresses.Clear();
457
458 if (m_configuration.deviceTypes.IsEmpty())
459 {
460 CLibCEC::AddLog(CEC_LOG_ERROR, "no device types set");
461 return false;
462 }
463
464 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
465 {
466 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
467 continue;
468
469 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - device %d: type %d", __FUNCTION__, iPtr, m_configuration.deviceTypes.types[iPtr]);
470
471 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
472 bReturn &= FindLogicalAddressRecordingDevice();
473 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
474 bReturn &= FindLogicalAddressTuner();
475 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
476 bReturn &= FindLogicalAddressPlaybackDevice();
477 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
478 bReturn &= FindLogicalAddressAudioSystem();
479 }
480
481 if (bReturn)
482 SetAckMask(m_configuration.logicalAddresses.AckMask());
483
484 return bReturn;
485 }
486
487 void CCECProcessor::ReplaceHandlers(void)
488 {
489 if (!IsInitialised())
490 return;
491 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
492 m_busDevices[iPtr]->ReplaceHandler(m_bInitialised);
493 }
494
495 bool CCECProcessor::OnCommandReceived(const cec_command &command)
496 {
497 return m_inBuffer.Push(command);
498 }
499
500 void *CCECProcessor::Process(void)
501 {
502 CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
503
504 cec_command command;
505 command.Clear();
506
507 while (!IsStopped() && m_communication->IsOpen())
508 {
509 if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME))
510 ParseCommand(command);
511
512 if (IsInitialised())
513 {
514 ReplaceHandlers();
515
516 m_controller->CheckKeypressTimeout();
517 }
518 }
519
520 return NULL;
521 }
522
523 bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
524 {
525 bool bReturn(false);
526
527 if (!IsRunning())
528 return bReturn;
529
530 cec_logical_address addr = m_configuration.logicalAddresses.primary;
531
532 if (type != CEC_DEVICE_TYPE_RESERVED)
533 {
534 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
535 {
536 if (m_configuration.logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type)
537 {
538 addr = (cec_logical_address) iPtr;
539 break;
540 }
541 }
542 }
543
544 m_busDevices[addr]->SetActiveSource();
545 if (IsValidPhysicalAddress(m_busDevices[addr]->GetPhysicalAddress()))
546 bReturn = m_busDevices[addr]->ActivateSource();
547
548 return bReturn;
549 }
550
551 bool CCECProcessor::SetActiveSource(uint16_t iStreamPath)
552 {
553 bool bReturn(false);
554
555 // suppress polls when searching for a device
556 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
557 if (device)
558 {
559 device->SetActiveSource();
560 bReturn = true;
561 }
562 else
563 {
564 CLibCEC::AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
565 }
566
567 return bReturn;
568 }
569
570 void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
571 {
572 CLockObject lock(m_mutex);
573 m_iStandardLineTimeout = iTimeout;
574 }
575
576 void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
577 {
578 CLockObject lock(m_mutex);
579 m_iRetryLineTimeout = iTimeout;
580 }
581
582 bool CCECProcessor::SetActiveView(void)
583 {
584 CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
585 return SetActiveSource(m_configuration.deviceTypes.IsEmpty() ? CEC_DEVICE_TYPE_RESERVED : m_configuration.deviceTypes[0]);
586 }
587
588 bool CCECProcessor::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */)
589 {
590 bool bReturn(false);
591
592 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
593 if (device)
594 {
595 ((CCECPlaybackDevice *) device)->SetDeckControlMode(mode);
596 if (bSendUpdate)
597 ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
598 bReturn = true;
599 }
600
601 return bReturn;
602 }
603
604 bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */)
605 {
606 bool bReturn(false);
607
608 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
609 if (device)
610 {
611 ((CCECPlaybackDevice *) device)->SetDeckStatus(info);
612 if (bSendUpdate)
613 ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
614 bReturn = true;
615 }
616
617 return bReturn;
618 }
619
620 bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */)
621 {
622 bool bReturn(false);
623
624 // limit the HDMI port range to 1-15
625 if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
626 iPort > CEC_MAX_HDMI_PORTNUMBER)
627 return bReturn;
628
629 {
630 CLockObject lock(m_mutex);
631 m_configuration.baseDevice = iBaseDevice;
632 m_configuration.iHDMIPort = iPort;
633 }
634
635 if (!IsRunning() && !bForce)
636 return true;
637
638 CLibCEC::AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
639
640 uint16_t iPhysicalAddress(0);
641 if (iBaseDevice > CECDEVICE_TV)
642 iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress(false);
643
644 if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
645 {
646 if (iPhysicalAddress == 0)
647 iPhysicalAddress += 0x1000 * iPort;
648 else if (iPhysicalAddress % 0x1000 == 0)
649 iPhysicalAddress += 0x100 * iPort;
650 else if (iPhysicalAddress % 0x100 == 0)
651 iPhysicalAddress += 0x10 * iPort;
652 else if (iPhysicalAddress % 0x10 == 0)
653 iPhysicalAddress += iPort;
654
655 bReturn = true;
656 }
657
658 if (!bReturn)
659 {
660 CLibCEC::AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
661 iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
662 }
663
664 SetPhysicalAddress(iPhysicalAddress);
665
666 return bReturn;
667 }
668
669 bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
670 {
671 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
672 {
673 if (m_busDevices[iPtr]->GetPhysicalAddress() == iPhysicalAddress)
674 return true;
675 }
676 return false;
677 }
678
679 bool CCECProcessor::TransmitInactiveSource(void)
680 {
681 if (!IsRunning())
682 return false;
683
684 if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
685 return m_busDevices[m_configuration.logicalAddresses.primary]->TransmitInactiveSource();
686 return false;
687 }
688
689 void CCECProcessor::LogOutput(const cec_command &data)
690 {
691 CStdString strTx;
692 strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
693 if (data.opcode_set)
694 strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
695
696 for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
697 strTx.AppendFormat(":%02x", data.parameters[iPtr]);
698 CLibCEC::AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
699 }
700
701 bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
702 {
703 CLockObject lock(m_mutex);
704 if (m_configuration.logicalAddresses.primary != iLogicalAddress)
705 {
706 CLibCEC::AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
707 m_configuration.logicalAddresses.primary = iLogicalAddress;
708 m_configuration.logicalAddresses.Set(iLogicalAddress);
709 return SetAckMask(m_configuration.logicalAddresses.AckMask());
710 }
711
712 return true;
713 }
714
715 bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */)
716 {
717 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
718 {
719 if (m_configuration.logicalAddresses[iPtr])
720 m_busDevices[iPtr]->SetMenuState(state);
721 }
722
723 if (bSendUpdate)
724 m_busDevices[m_configuration.logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV);
725
726 return true;
727 }
728
729 bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */)
730 {
731 bool bSendActiveView(false);
732 bool bReturn(false);
733 cec_logical_addresses sendUpdatesTo;
734 sendUpdatesTo.Clear();
735
736 {
737 CLockObject lock(m_mutex);
738 m_configuration.iPhysicalAddress = iPhysicalAddress;
739 CLibCEC::AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
740
741 if (!m_configuration.logicalAddresses.IsEmpty())
742 {
743 bool bWasActiveSource(false);
744 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
745 if (m_configuration.logicalAddresses[iPtr])
746 {
747 bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
748 m_busDevices[iPtr]->SetInactiveSource();
749 m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress);
750 if (bSendUpdate)
751 sendUpdatesTo.Set((cec_logical_address)iPtr);
752 }
753
754 bSendActiveView = bWasActiveSource && bSendUpdate;
755 bReturn = true;
756 }
757 }
758
759 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
760 if (sendUpdatesTo[iPtr])
761 m_busDevices[iPtr]->TransmitPhysicalAddress();
762
763 if (bSendActiveView)
764 SetActiveView();
765
766 if (bReturn)
767 {
768 libcec_configuration config;
769 {
770 CLockObject lock(m_mutex);
771 config = m_configuration;
772 }
773
774 PersistConfiguration(&config);
775 CLibCEC::ConfigurationChanged(config);
776 }
777
778 return bReturn;
779 }
780
781 bool CCECProcessor::SwitchMonitoring(bool bEnable)
782 {
783 CLibCEC::AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
784
785 {
786 CLockObject lock(m_mutex);
787 m_bMonitor = bEnable;
788 }
789
790 if (bEnable)
791 return SetAckMask(0);
792 else
793 return SetAckMask(m_configuration.logicalAddresses.AckMask());
794 }
795
796 bool CCECProcessor::PollDevice(cec_logical_address iAddress)
797 {
798 if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress])
799 {
800 return m_configuration.logicalAddresses.primary == CECDEVICE_UNKNOWN ?
801 m_busDevices[iAddress]->TransmitPoll(iAddress) :
802 m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPoll(iAddress);
803 }
804 return false;
805 }
806
807 uint8_t CCECProcessor::VolumeUp(bool bSendRelease /* = true */)
808 {
809 uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
810 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
811 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeUp(bSendRelease);
812
813 return status;
814 }
815
816 uint8_t CCECProcessor::VolumeDown(bool bSendRelease /* = true */)
817 {
818 uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
819 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
820 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeDown(bSendRelease);
821
822 return status;
823 }
824
825 uint8_t CCECProcessor::MuteAudio(bool bSendRelease /* = true */)
826 {
827 uint8_t status(CEC_AUDIO_VOLUME_STATUS_UNKNOWN);
828 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
829 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->MuteAudio(bSendRelease);
830
831 return status;
832 }
833
834 CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
835 {
836 CCECBusDevice *device(NULL);
837
838 // check each device until we found a match
839 for (uint8_t iPtr = CECDEVICE_TV; !device && iPtr < CECDEVICE_BROADCAST; iPtr++)
840 {
841 if (m_busDevices[iPtr]->GetPhysicalAddress(bSuppressUpdate) == iPhysicalAddress)
842 device = m_busDevices[iPtr];
843 }
844
845 return device;
846 }
847
848 CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
849 {
850 CCECBusDevice *device = NULL;
851
852 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
853 {
854 if (m_busDevices[iPtr]->m_type == type && m_configuration.logicalAddresses[iPtr])
855 {
856 device = m_busDevices[iPtr];
857 break;
858 }
859 }
860
861 return device;
862 }
863
864 CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const
865 {
866 CCECBusDevice *device(NULL);
867 cec_logical_address primary = m_configuration.logicalAddresses.primary;
868 if (primary != CECDEVICE_UNKNOWN)
869 device = m_busDevices[primary];
870 return device;
871 }
872
873 cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress)
874 {
875 return m_busDevices[iAddress]->GetCecVersion();
876 }
877
878 cec_osd_name CCECProcessor::GetDeviceOSDName(cec_logical_address iAddress)
879 {
880 CStdString strOSDName = m_busDevices[iAddress]->GetOSDName();
881 cec_osd_name retVal;
882
883 snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
884 retVal.device = iAddress;
885
886 return retVal;
887 }
888
889 bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
890 {
891 if (m_busDevices[iAddress])
892 {
893 *language = m_busDevices[iAddress]->GetMenuLanguage();
894 return (strcmp(language->language, "???") != 0);
895 }
896 return false;
897 }
898
899 CStdString CCECProcessor::GetDeviceName(void) const
900 {
901 CStdString strName;
902 strName = m_configuration.strDeviceName;
903 return strName;
904 }
905
906 uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress)
907 {
908 if (m_busDevices[iAddress])
909 return m_busDevices[iAddress]->GetVendorId();
910 return false;
911 }
912
913 uint16_t CCECProcessor::GetDevicePhysicalAddress(cec_logical_address iAddress)
914 {
915 if (m_busDevices[iAddress])
916 return m_busDevices[iAddress]->GetPhysicalAddress(false);
917 return false;
918 }
919
920 cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress)
921 {
922 if (m_busDevices[iAddress])
923 return m_busDevices[iAddress]->GetPowerStatus();
924 return CEC_POWER_STATUS_UNKNOWN;
925 }
926
927 cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */)
928 {
929 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
930 {
931 if (m_busDevices[iPtr]->IsActiveSource())
932 return (cec_logical_address)iPtr;
933 }
934
935 if (bRequestActiveSource && m_configuration.logicalAddresses.primary != CECDEVICE_UNKNOWN)
936 {
937 CCECBusDevice *primary = m_busDevices[m_configuration.logicalAddresses.primary];
938 if (primary)
939 primary->RequestActiveSource();
940
941 return GetActiveSource(false);
942 }
943
944 return CECDEVICE_UNKNOWN;
945 }
946
947 bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
948 {
949 return iAddress >= CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST ?
950 m_busDevices[iAddress]->IsActiveSource() :
951 false;
952 }
953
954 bool CCECProcessor::Transmit(const cec_command &data)
955 {
956 if (m_configuration.logicalAddresses[(uint8_t)data.destination])
957 {
958 CLibCEC::AddLog(CEC_LOG_WARNING, "not sending data to myself!");
959 return false;
960 }
961
962 uint8_t iMaxTries(0);
963 {
964 CLockObject lock(m_mutex);
965 if (IsStopped())
966 return false;
967 LogOutput(data);
968 m_iLastTransmission = GetTimeMs();
969 if (!m_communication || !m_communication->IsOpen())
970 {
971 CLibCEC::AddLog(CEC_LOG_ERROR, "cannot transmit command: connection closed");
972 return false;
973 }
974 iMaxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1;
975 }
976
977 bool bRetry(true);
978 uint8_t iTries(0);
979 uint8_t iLineTimeout = m_iStandardLineTimeout;
980 cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
981
982 while (bRetry && ++iTries < iMaxTries)
983 {
984 if (m_busDevices[data.initiator]->IsUnsupportedFeature(data.opcode))
985 return false;
986
987 adapterState = m_communication->Write(data, bRetry, iLineTimeout);
988 iLineTimeout = m_iRetryLineTimeout;
989 }
990
991 return adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED;
992 }
993
994 void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
995 {
996 CLibCEC::AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
997
998 cec_command command;
999 // TODO
1000 cec_command::Format(command, m_configuration.logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT);
1001 command.parameters.PushBack((uint8_t)opcode);
1002 command.parameters.PushBack((uint8_t)reason);
1003
1004 Transmit(command);
1005 }
1006
1007 void CCECProcessor::ParseCommand(const cec_command &command)
1008 {
1009 CStdString dataStr;
1010 dataStr.Format(">> %1x%1x", command.initiator, command.destination);
1011 if (command.opcode_set == 1)
1012 dataStr.AppendFormat(":%02x", command.opcode);
1013 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
1014 dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
1015 CLibCEC::AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
1016
1017 if (!m_bMonitor && command.initiator >= CECDEVICE_TV && command.initiator <= CECDEVICE_BROADCAST)
1018 m_busDevices[(uint8_t)command.initiator]->HandleCommand(command);
1019 }
1020
1021 cec_logical_addresses CCECProcessor::GetActiveDevices(void)
1022 {
1023 cec_logical_addresses addresses;
1024 addresses.Clear();
1025 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
1026 {
1027 if (m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
1028 addresses.Set((cec_logical_address) iPtr);
1029 }
1030 return addresses;
1031 }
1032
1033 bool CCECProcessor::IsPresentDevice(cec_logical_address address)
1034 {
1035 return m_busDevices[address]->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
1036 }
1037
1038 bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
1039 {
1040 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
1041 {
1042 if (m_busDevices[iPtr]->GetType() == type && m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
1043 return true;
1044 }
1045
1046 return false;
1047 }
1048
1049 uint16_t CCECProcessor::GetPhysicalAddress(void) const
1050 {
1051 if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary])
1052 return m_busDevices[m_configuration.logicalAddresses.primary]->GetPhysicalAddress();
1053 return false;
1054 }
1055
1056 bool CCECProcessor::SetAckMask(uint16_t iMask)
1057 {
1058 return m_communication ? m_communication->SetAckMask(iMask) : false;
1059 }
1060
1061 bool CCECProcessor::TransmitKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
1062 {
1063 return m_busDevices[iDestination]->TransmitKeypress(key, bWait);
1064 }
1065
1066 bool CCECProcessor::TransmitKeyRelease(cec_logical_address iDestination, bool bWait /* = true */)
1067 {
1068 return m_busDevices[iDestination]->TransmitKeyRelease(bWait);
1069 }
1070
1071 bool CCECProcessor::EnablePhysicalAddressDetection(void)
1072 {
1073 CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__);
1074 uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress();
1075 if (IsValidPhysicalAddress(iPhysicalAddress))
1076 {
1077 m_configuration.bAutodetectAddress = 1;
1078 m_configuration.iPhysicalAddress = iPhysicalAddress;
1079 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1080 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1081 return SetPhysicalAddress(iPhysicalAddress);
1082 }
1083 return false;
1084 }
1085
1086 bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
1087 {
1088 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
1089 {
1090 bool bReturn(true);
1091 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
1092 {
1093 if (m_configuration.powerOffDevices[iPtr])
1094 {
1095 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - putting '%s' in standby mode", __FUNCTION__, ToString((cec_logical_address)iPtr));
1096 bReturn &= m_busDevices[iPtr]->Standby();
1097 }
1098 }
1099 return bReturn;
1100 }
1101
1102 return m_busDevices[address]->Standby();
1103 }
1104
1105 bool CCECProcessor::PowerOnDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
1106 {
1107 if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0)
1108 {
1109 bool bReturn(true);
1110 for (uint8_t iPtr = CECDEVICE_TV; iPtr <= CECDEVICE_BROADCAST; iPtr++)
1111 {
1112 if (m_configuration.wakeDevices[iPtr])
1113 {
1114 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - powering on '%s'", __FUNCTION__, ToString((cec_logical_address)iPtr));
1115 bReturn &= m_busDevices[iPtr]->PowerOn();
1116 }
1117 }
1118 return bReturn;
1119 }
1120
1121 return m_busDevices[address]->PowerOn();
1122 }
1123
1124 const char *CCECProcessor::ToString(const cec_device_type type)
1125 {
1126 switch (type)
1127 {
1128 case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
1129 return "audio system";
1130 case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
1131 return "playback device";
1132 case CEC_DEVICE_TYPE_RECORDING_DEVICE:
1133 return "recording device";
1134 case CEC_DEVICE_TYPE_RESERVED:
1135 return "reserved";
1136 case CEC_DEVICE_TYPE_TUNER:
1137 return "tuner";
1138 case CEC_DEVICE_TYPE_TV:
1139 return "TV";
1140 default:
1141 return "unknown";
1142 }
1143 }
1144
1145 const char *CCECProcessor::ToString(const cec_menu_state state)
1146 {
1147 switch (state)
1148 {
1149 case CEC_MENU_STATE_ACTIVATED:
1150 return "activated";
1151 case CEC_MENU_STATE_DEACTIVATED:
1152 return "deactivated";
1153 default:
1154 return "unknown";
1155 }
1156 }
1157
1158 const char *CCECProcessor::ToString(const cec_version version)
1159 {
1160 switch (version)
1161 {
1162 case CEC_VERSION_1_2:
1163 return "1.2";
1164 case CEC_VERSION_1_2A:
1165 return "1.2a";
1166 case CEC_VERSION_1_3:
1167 return "1.3";
1168 case CEC_VERSION_1_3A:
1169 return "1.3a";
1170 case CEC_VERSION_1_4:
1171 return "1.4";
1172 default:
1173 return "unknown";
1174 }
1175 }
1176
1177 const char *CCECProcessor::ToString(const cec_power_status status)
1178 {
1179 switch (status)
1180 {
1181 case CEC_POWER_STATUS_ON:
1182 return "on";
1183 case CEC_POWER_STATUS_STANDBY:
1184 return "standby";
1185 case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
1186 return "in transition from on to standby";
1187 case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
1188 return "in transition from standby to on";
1189 default:
1190 return "unknown";
1191 }
1192 }
1193
1194 const char *CCECProcessor::ToString(const cec_logical_address address)
1195 {
1196 switch(address)
1197 {
1198 case CECDEVICE_AUDIOSYSTEM:
1199 return "Audio";
1200 case CECDEVICE_BROADCAST:
1201 return "Broadcast";
1202 case CECDEVICE_FREEUSE:
1203 return "Free use";
1204 case CECDEVICE_PLAYBACKDEVICE1:
1205 return "Playback 1";
1206 case CECDEVICE_PLAYBACKDEVICE2:
1207 return "Playback 2";
1208 case CECDEVICE_PLAYBACKDEVICE3:
1209 return "Playback 3";
1210 case CECDEVICE_RECORDINGDEVICE1:
1211 return "Recorder 1";
1212 case CECDEVICE_RECORDINGDEVICE2:
1213 return "Recorder 2";
1214 case CECDEVICE_RECORDINGDEVICE3:
1215 return "Recorder 3";
1216 case CECDEVICE_RESERVED1:
1217 return "Reserved 1";
1218 case CECDEVICE_RESERVED2:
1219 return "Reserved 2";
1220 case CECDEVICE_TUNER1:
1221 return "Tuner 1";
1222 case CECDEVICE_TUNER2:
1223 return "Tuner 2";
1224 case CECDEVICE_TUNER3:
1225 return "Tuner 3";
1226 case CECDEVICE_TUNER4:
1227 return "Tuner 4";
1228 case CECDEVICE_TV:
1229 return "TV";
1230 default:
1231 return "unknown";
1232 }
1233 }
1234
1235 const char *CCECProcessor::ToString(const cec_deck_control_mode mode)
1236 {
1237 switch (mode)
1238 {
1239 case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND:
1240 return "skip forward wind";
1241 case CEC_DECK_CONTROL_MODE_EJECT:
1242 return "eject";
1243 case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND:
1244 return "reverse rewind";
1245 case CEC_DECK_CONTROL_MODE_STOP:
1246 return "stop";
1247 default:
1248 return "unknown";
1249 }
1250 }
1251
1252 const char *CCECProcessor::ToString(const cec_deck_info status)
1253 {
1254 switch (status)
1255 {
1256 case CEC_DECK_INFO_PLAY:
1257 return "play";
1258 case CEC_DECK_INFO_RECORD:
1259 return "record";
1260 case CEC_DECK_INFO_PLAY_REVERSE:
1261 return "play reverse";
1262 case CEC_DECK_INFO_STILL:
1263 return "still";
1264 case CEC_DECK_INFO_SLOW:
1265 return "slow";
1266 case CEC_DECK_INFO_SLOW_REVERSE:
1267 return "slow reverse";
1268 case CEC_DECK_INFO_FAST_FORWARD:
1269 return "fast forward";
1270 case CEC_DECK_INFO_FAST_REVERSE:
1271 return "fast reverse";
1272 case CEC_DECK_INFO_NO_MEDIA:
1273 return "no media";
1274 case CEC_DECK_INFO_STOP:
1275 return "stop";
1276 case CEC_DECK_INFO_SKIP_FORWARD_WIND:
1277 return "info skip forward wind";
1278 case CEC_DECK_INFO_SKIP_REVERSE_REWIND:
1279 return "info skip reverse rewind";
1280 case CEC_DECK_INFO_INDEX_SEARCH_FORWARD:
1281 return "info index search forward";
1282 case CEC_DECK_INFO_INDEX_SEARCH_REVERSE:
1283 return "info index search reverse";
1284 case CEC_DECK_INFO_OTHER_STATUS:
1285 return "other";
1286 case CEC_DECK_INFO_OTHER_STATUS_LG:
1287 return "LG other";
1288 default:
1289 return "unknown";
1290 }
1291 }
1292
1293 const char *CCECProcessor::ToString(const cec_opcode opcode)
1294 {
1295 switch (opcode)
1296 {
1297 case CEC_OPCODE_ACTIVE_SOURCE:
1298 return "active source";
1299 case CEC_OPCODE_IMAGE_VIEW_ON:
1300 return "image view on";
1301 case CEC_OPCODE_TEXT_VIEW_ON:
1302 return "text view on";
1303 case CEC_OPCODE_INACTIVE_SOURCE:
1304 return "inactive source";
1305 case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
1306 return "request active source";
1307 case CEC_OPCODE_ROUTING_CHANGE:
1308 return "routing change";
1309 case CEC_OPCODE_ROUTING_INFORMATION:
1310 return "routing information";
1311 case CEC_OPCODE_SET_STREAM_PATH:
1312 return "set stream path";
1313 case CEC_OPCODE_STANDBY:
1314 return "standby";
1315 case CEC_OPCODE_RECORD_OFF:
1316 return "record off";
1317 case CEC_OPCODE_RECORD_ON:
1318 return "record on";
1319 case CEC_OPCODE_RECORD_STATUS:
1320 return "record status";
1321 case CEC_OPCODE_RECORD_TV_SCREEN:
1322 return "record tv screen";
1323 case CEC_OPCODE_CLEAR_ANALOGUE_TIMER:
1324 return "clear analogue timer";
1325 case CEC_OPCODE_CLEAR_DIGITAL_TIMER:
1326 return "clear digital timer";
1327 case CEC_OPCODE_CLEAR_EXTERNAL_TIMER:
1328 return "clear external timer";
1329 case CEC_OPCODE_SET_ANALOGUE_TIMER:
1330 return "set analogue timer";
1331 case CEC_OPCODE_SET_DIGITAL_TIMER:
1332 return "set digital timer";
1333 case CEC_OPCODE_SET_EXTERNAL_TIMER:
1334 return "set external timer";
1335 case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:
1336 return "set timer program title";
1337 case CEC_OPCODE_TIMER_CLEARED_STATUS:
1338 return "timer cleared status";
1339 case CEC_OPCODE_TIMER_STATUS:
1340 return "timer status";
1341 case CEC_OPCODE_CEC_VERSION:
1342 return "cec version";
1343 case CEC_OPCODE_GET_CEC_VERSION:
1344 return "get cec version";
1345 case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
1346 return "give physical address";
1347 case CEC_OPCODE_GET_MENU_LANGUAGE:
1348 return "get menu language";
1349 case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
1350 return "report physical address";
1351 case CEC_OPCODE_SET_MENU_LANGUAGE:
1352 return "set menu language";
1353 case CEC_OPCODE_DECK_CONTROL:
1354 return "deck control";
1355 case CEC_OPCODE_DECK_STATUS:
1356 return "deck status";
1357 case CEC_OPCODE_GIVE_DECK_STATUS:
1358 return "give deck status";
1359 case CEC_OPCODE_PLAY:
1360 return "play";
1361 case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
1362 return "give tuner status";
1363 case CEC_OPCODE_SELECT_ANALOGUE_SERVICE:
1364 return "select analogue service";
1365 case CEC_OPCODE_SELECT_DIGITAL_SERVICE:
1366 return "set digital service";
1367 case CEC_OPCODE_TUNER_DEVICE_STATUS:
1368 return "tuner device status";
1369 case CEC_OPCODE_TUNER_STEP_DECREMENT:
1370 return "tuner step decrement";
1371 case CEC_OPCODE_TUNER_STEP_INCREMENT:
1372 return "tuner step increment";
1373 case CEC_OPCODE_DEVICE_VENDOR_ID:
1374 return "device vendor id";
1375 case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
1376 return "give device vendor id";
1377 case CEC_OPCODE_VENDOR_COMMAND:
1378 return "vendor command";
1379 case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
1380 return "vendor command with id";
1381 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
1382 return "vendor remote button down";
1383 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
1384 return "vendor remote button up";
1385 case CEC_OPCODE_SET_OSD_STRING:
1386 return "set osd string";
1387 case CEC_OPCODE_GIVE_OSD_NAME:
1388 return "give osd name";
1389 case CEC_OPCODE_SET_OSD_NAME:
1390 return "set osd name";
1391 case CEC_OPCODE_MENU_REQUEST:
1392 return "menu request";
1393 case CEC_OPCODE_MENU_STATUS:
1394 return "menu status";
1395 case CEC_OPCODE_USER_CONTROL_PRESSED:
1396 return "user control pressed";
1397 case CEC_OPCODE_USER_CONTROL_RELEASE:
1398 return "user control release";
1399 case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
1400 return "give device power status";
1401 case CEC_OPCODE_REPORT_POWER_STATUS:
1402 return "report power status";
1403 case CEC_OPCODE_FEATURE_ABORT:
1404 return "feature abort";
1405 case CEC_OPCODE_ABORT:
1406 return "abort";
1407 case CEC_OPCODE_GIVE_AUDIO_STATUS:
1408 return "give audio status";
1409 case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
1410 return "give audio mode status";
1411 case CEC_OPCODE_REPORT_AUDIO_STATUS:
1412 return "report audio status";
1413 case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
1414 return "set system audio mode";
1415 case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
1416 return "system audio mode request";
1417 case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
1418 return "system audio mode status";
1419 case CEC_OPCODE_SET_AUDIO_RATE:
1420 return "set audio rate";
1421 case CEC_OPCODE_START_ARC:
1422 return "start ARC";
1423 case CEC_OPCODE_REPORT_ARC_STARTED:
1424 return "report ARC started";
1425 case CEC_OPCODE_REPORT_ARC_ENDED:
1426 return "report ARC ended";
1427 case CEC_OPCODE_REQUEST_ARC_START:
1428 return "request ARC start";
1429 case CEC_OPCODE_REQUEST_ARC_END:
1430 return "request ARC end";
1431 case CEC_OPCODE_END_ARC:
1432 return "end ARC";
1433 case CEC_OPCODE_CDC:
1434 return "CDC";
1435 case CEC_OPCODE_NONE:
1436 return "poll";
1437 default:
1438 return "UNKNOWN";
1439 }
1440 }
1441
1442 const char *CCECProcessor::ToString(const cec_system_audio_status mode)
1443 {
1444 switch(mode)
1445 {
1446 case CEC_SYSTEM_AUDIO_STATUS_ON:
1447 return "on";
1448 case CEC_SYSTEM_AUDIO_STATUS_OFF:
1449 return "off";
1450 default:
1451 return "unknown";
1452 }
1453 }
1454
1455 const char *CCECProcessor::ToString(const cec_audio_status UNUSED(status))
1456 {
1457 // TODO this is a mask
1458 return "TODO";
1459 }
1460
1461 const char *CCECProcessor::ToString(const cec_vendor_id vendor)
1462 {
1463 switch (vendor)
1464 {
1465 case CEC_VENDOR_SAMSUNG:
1466 return "Samsung";
1467 case CEC_VENDOR_LG:
1468 return "LG";
1469 case CEC_VENDOR_PANASONIC:
1470 return "Panasonic";
1471 case CEC_VENDOR_PIONEER:
1472 return "Pioneer";
1473 case CEC_VENDOR_ONKYO:
1474 return "Onkyo";
1475 case CEC_VENDOR_YAMAHA:
1476 return "Yamaha";
1477 case CEC_VENDOR_PHILIPS:
1478 return "Philips";
1479 case CEC_VENDOR_SONY:
1480 return "Sony";
1481 case CEC_VENDOR_TOSHIBA:
1482 return "Toshiba";
1483 default:
1484 return "Unknown";
1485 }
1486 }
1487
1488 const char *CCECProcessor::ToString(const cec_client_version version)
1489 {
1490 switch (version)
1491 {
1492 case CEC_CLIENT_VERSION_PRE_1_5:
1493 return "pre-1.5";
1494 case CEC_CLIENT_VERSION_1_5_0:
1495 return "1.5.0";
1496 case CEC_CLIENT_VERSION_1_5_1:
1497 return "1.5.1";
1498 case CEC_CLIENT_VERSION_1_5_2:
1499 return "1.5.2";
1500 case CEC_CLIENT_VERSION_1_5_3:
1501 return "1.5.3";
1502 case CEC_CLIENT_VERSION_1_6_0:
1503 return "1.6.0";
1504 case CEC_CLIENT_VERSION_1_6_1:
1505 return "1.6.1";
1506 case CEC_CLIENT_VERSION_1_6_2:
1507 return "1.6.2";
1508 default:
1509 return "Unknown";
1510 }
1511 }
1512
1513 const char *CCECProcessor::ToString(const cec_server_version version)
1514 {
1515 switch (version)
1516 {
1517 case CEC_SERVER_VERSION_PRE_1_5:
1518 return "pre-1.5";
1519 case CEC_SERVER_VERSION_1_5_0:
1520 return "1.5.0";
1521 case CEC_SERVER_VERSION_1_5_1:
1522 return "1.5.1";
1523 case CEC_SERVER_VERSION_1_5_2:
1524 return "1.5.2";
1525 case CEC_SERVER_VERSION_1_5_3:
1526 return "1.5.3";
1527 case CEC_SERVER_VERSION_1_6_0:
1528 return "1.6.0";
1529 case CEC_SERVER_VERSION_1_6_1:
1530 return "1.6.1";
1531 case CEC_SERVER_VERSION_1_6_2:
1532 return "1.6.2";
1533 default:
1534 return "Unknown";
1535 }
1536 }
1537
1538 bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
1539 {
1540 bool bReturn(false);
1541 if (!m_communication && strPort)
1542 {
1543 IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort);
1544 CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
1545 int iConnectTry(0);
1546 while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
1547 {
1548 CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
1549 comm->Close();
1550 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
1551 }
1552 if (comm->IsOpen())
1553 {
1554 bReturn = comm->StartBootloader();
1555 delete comm;
1556 }
1557 return bReturn;
1558 }
1559 else
1560 {
1561 m_communication->StartBootloader();
1562 Close();
1563 bReturn = true;
1564 }
1565
1566 return bReturn;
1567 }
1568
1569 bool CCECProcessor::PingAdapter(void)
1570 {
1571 return m_communication->PingAdapter();
1572 }
1573
1574 void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
1575 {
1576 if (destination < CECDEVICE_BROADCAST)
1577 m_busDevices[destination]->HandlePollFrom(initiator);
1578 }
1579
1580 bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
1581 {
1582 return !m_busDevices[initiator]->HandleReceiveFailed();
1583 }
1584
1585 bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress)
1586 {
1587 // stream path changes are sent by the TV
1588 return m_busDevices[CECDEVICE_TV]->GetHandler()->TransmitSetStreamPath(iPhysicalAddress);
1589 }
1590
1591 bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration)
1592 {
1593 bool bReinit(false);
1594 CCECBusDevice *primary = IsRunning() ? GetPrimaryDevice() : NULL;
1595 cec_device_type oldPrimaryType = primary ? primary->GetType() : CEC_DEVICE_TYPE_RECORDING_DEVICE;
1596 m_configuration.clientVersion = configuration->clientVersion;
1597 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion));
1598
1599 // client version 1.5.0
1600
1601 // device types
1602 bool bDeviceTypeChanged = IsRunning () && m_configuration.deviceTypes != configuration->deviceTypes;
1603 m_configuration.deviceTypes = configuration->deviceTypes;
1604 if (bDeviceTypeChanged)
1605 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0]));
1606
1607 bool bPhysicalAddressChanged(false);
1608
1609 // autodetect address
1610 bool bPhysicalAutodetected(false);
1611 if (IsRunning() && configuration->bAutodetectAddress == 1)
1612 {
1613 uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress();
1614 if (IsValidPhysicalAddress(iPhysicalAddress))
1615 {
1616 if (IsRunning())
1617 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1618 else
1619 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1620 bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress);
1621 m_configuration.iPhysicalAddress = iPhysicalAddress;
1622 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1623 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1624 bPhysicalAutodetected = true;
1625 }
1626 }
1627
1628 // physical address
1629 if (!bPhysicalAutodetected)
1630 {
1631 uint16_t iPhysicalAddress(IsValidPhysicalAddress(configuration->iPhysicalAddress) ? configuration->iPhysicalAddress : CEC_PHYSICAL_ADDRESS_TV);
1632 bPhysicalAddressChanged = IsRunning() && m_configuration.iPhysicalAddress != iPhysicalAddress;
1633 if (bPhysicalAddressChanged)
1634 {
1635 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1636 m_configuration.iPhysicalAddress = iPhysicalAddress;
1637 }
1638 }
1639
1640 bool bHdmiPortChanged(false);
1641 if (!bPhysicalAutodetected && !IsValidPhysicalAddress(configuration->iPhysicalAddress))
1642 {
1643 // base device
1644 bHdmiPortChanged = IsRunning() && m_configuration.baseDevice != configuration->baseDevice;
1645 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice);
1646 m_configuration.baseDevice = configuration->baseDevice;
1647
1648 // hdmi port
1649 bHdmiPortChanged |= IsRunning() && m_configuration.iHDMIPort != configuration->iHDMIPort;
1650 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort);
1651 m_configuration.iHDMIPort = configuration->iHDMIPort;
1652 }
1653 else
1654 {
1655 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
1656 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1657 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1658 }
1659
1660 bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged;
1661
1662 // device name
1663 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName);
1664 snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName);
1665 if (primary && !primary->GetOSDName().Equals(m_configuration.strDeviceName))
1666 {
1667 primary->SetOSDName(m_configuration.strDeviceName);
1668 if (!bReinit && IsRunning())
1669 primary->TransmitOSDName(CECDEVICE_TV);
1670 }
1671
1672 // tv vendor id override
1673 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor));
1674 if (m_configuration.tvVendor != configuration->tvVendor)
1675 {
1676 m_configuration.tvVendor= configuration->tvVendor;
1677 m_busDevices[CECDEVICE_TV]->SetVendorId((uint64_t)m_configuration.tvVendor);
1678 }
1679
1680 // wake CEC devices
1681 if (m_configuration.wakeDevices != configuration->wakeDevices)
1682 {
1683 m_configuration.wakeDevices = configuration->wakeDevices;
1684 if (!bReinit && IsRunning())
1685 PowerOnDevices();
1686 }
1687
1688 // just copy these
1689 m_configuration.bUseTVMenuLanguage = configuration->bUseTVMenuLanguage;
1690 m_configuration.bActivateSource = configuration->bActivateSource;
1691 m_configuration.bGetSettingsFromROM = configuration->bGetSettingsFromROM;
1692 m_configuration.powerOffDevices = configuration->powerOffDevices;
1693 m_configuration.bPowerOffScreensaver = configuration->bPowerOffScreensaver;
1694 m_configuration.bPowerOffOnStandby = configuration->bPowerOffOnStandby;
1695
1696 // client version 1.5.1
1697 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
1698 m_configuration.bSendInactiveSource = configuration->bSendInactiveSource;
1699
1700 // client version 1.6.0
1701 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
1702 {
1703 m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby;
1704 m_configuration.bShutdownOnStandby = configuration->bShutdownOnStandby;
1705 }
1706
1707 // client version 1.6.2
1708 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
1709 {
1710 memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3);
1711 }
1712
1713 // ensure that there is at least 1 device type set
1714 if (m_configuration.deviceTypes.IsEmpty())
1715 m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE);
1716
1717 bool bReturn(true);
1718 if (bReinit || m_configuration.logicalAddresses.IsEmpty())
1719 {
1720 if (bDeviceTypeChanged)
1721 bReturn = ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]);
1722 else if (IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1723 bReturn = SetPhysicalAddress(m_configuration.iPhysicalAddress);
1724 else if (m_configuration.baseDevice != CECDEVICE_UNKNOWN && m_configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE)
1725 bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort);
1726 }
1727 else if (m_configuration.bActivateSource == 1 && IsRunning() && !IsActiveSource(m_configuration.logicalAddresses.primary))
1728 {
1729 // activate the source if we're not already the active source
1730 SetActiveSource(m_configuration.deviceTypes.types[0]);
1731 }
1732
1733 // persist the configuration
1734 if (IsRunning())
1735 m_communication->PersistConfiguration(&m_configuration);
1736
1737 return bReturn;
1738 }
1739
1740 bool CCECProcessor::GetCurrentConfiguration(libcec_configuration *configuration)
1741 {
1742 // client version 1.5.0
1743 snprintf(configuration->strDeviceName, 13, "%s", m_configuration.strDeviceName);
1744 configuration->deviceTypes = m_configuration.deviceTypes;
1745 configuration->bAutodetectAddress = m_configuration.bAutodetectAddress;
1746 configuration->iPhysicalAddress = m_configuration.iPhysicalAddress;
1747 configuration->baseDevice = m_configuration.baseDevice;
1748 configuration->iHDMIPort = m_configuration.iHDMIPort;
1749 configuration->clientVersion = m_configuration.clientVersion;
1750 configuration->serverVersion = m_configuration.serverVersion;
1751 configuration->tvVendor = m_configuration.tvVendor;
1752
1753 configuration->bGetSettingsFromROM = m_configuration.bGetSettingsFromROM;
1754 configuration->bUseTVMenuLanguage = m_configuration.bUseTVMenuLanguage;
1755 configuration->bActivateSource = m_configuration.bActivateSource;
1756 configuration->wakeDevices = m_configuration.wakeDevices;
1757 configuration->powerOffDevices = m_configuration.powerOffDevices;
1758 configuration->bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
1759 configuration->bPowerOffOnStandby = m_configuration.bPowerOffOnStandby;
1760
1761 // client version 1.5.1
1762 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1)
1763 configuration->bSendInactiveSource = m_configuration.bSendInactiveSource;
1764
1765 // client version 1.5.3
1766 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_3)
1767 configuration->logicalAddresses = m_configuration.logicalAddresses;
1768
1769 // client version 1.6.0
1770 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0)
1771 {
1772 configuration->iFirmwareVersion = m_configuration.iFirmwareVersion;
1773 configuration->bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
1774 configuration->bShutdownOnStandby = m_configuration.bShutdownOnStandby;
1775 }
1776
1777 // client version 1.6.2
1778 if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2)
1779 {
1780 memcpy(configuration->strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
1781 configuration->iFirmwareBuildDate = m_configuration.iFirmwareBuildDate;
1782 }
1783 return true;
1784 }
1785
1786 bool CCECProcessor::CanPersistConfiguration(void)
1787 {
1788 return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false;
1789 }
1790
1791 bool CCECProcessor::PersistConfiguration(libcec_configuration *configuration)
1792 {
1793 return m_communication ? m_communication->PersistConfiguration(configuration) : false;
1794 }
1795
1796 void CCECProcessor::RescanActiveDevices(void)
1797 {
1798 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
1799 m_busDevices[iPtr]->GetStatus(true);
1800 }
1801
1802 bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
1803 {
1804 if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
1805 return false;
1806
1807 config->iFirmwareVersion = m_communication->GetFirmwareVersion();
1808 config->iPhysicalAddress = m_communication->GetPhysicalAddress();
1809 config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
1810
1811 return true;
1812 }
1813
1814 bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
1815 {
1816 bool bReturn(true);
1817 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
1818 bReturn &= m_busDevices[iPtr]->TransmitPendingActiveSourceCommands();
1819 return bReturn;
1820 }
1821
1822 bool CCECProcessor::IsValidPhysicalAddress(uint16_t iPhysicalAddress)
1823 {
1824 return iPhysicalAddress >= CEC_MIN_PHYSICAL_ADDRESS &&
1825 iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS;
1826 }