[FreeBSD] update adapter detection
[deb_libcec.git] / src / lib / CECClient.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2013 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 "env.h"
34 #include "CECClient.h"
35
36 #include "CECProcessor.h"
37 #include "LibCEC.h"
38 #include "CECTypeUtils.h"
39 #include "devices/CECPlaybackDevice.h"
40 #include "devices/CECAudioSystem.h"
41 #include "devices/CECTV.h"
42 #include "implementations/CECCommandHandler.h"
43
44 using namespace CEC;
45 using namespace PLATFORM;
46
47 #define LIB_CEC m_processor->GetLib()
48 #define ToString(x) CCECTypeUtils::ToString(x)
49
50 CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) :
51 m_processor(processor),
52 m_bInitialised(false),
53 m_bRegistered(false),
54 m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
55 m_buttontime(0),
56 m_iPreventForwardingPowerOffCommand(0),
57 m_iLastKeypressTime(0)
58 {
59 m_lastKeypress.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
60 m_lastKeypress.duration = 0;
61 m_configuration.Clear();
62 // set the initial configuration
63 SetConfiguration(configuration);
64 }
65
66 CCECClient::~CCECClient(void)
67 {
68 // unregister the client
69 if (m_processor && IsRegistered())
70 m_processor->UnregisterClient(this);
71 }
72
73 bool CCECClient::IsInitialised(void)
74 {
75 CLockObject lock(m_mutex);
76 return m_bInitialised && m_processor;
77 }
78
79 void CCECClient::SetInitialised(bool bSetTo)
80 {
81 CLockObject lock(m_mutex);
82 m_bInitialised = bSetTo;
83 }
84
85 bool CCECClient::IsRegistered(void)
86 {
87 CLockObject lock(m_mutex);
88 return m_bRegistered && m_processor;
89 }
90
91 void CCECClient::SetRegistered(bool bSetTo)
92 {
93 CLockObject lock(m_mutex);
94 m_bRegistered = bSetTo;
95 }
96
97 bool CCECClient::OnRegister(void)
98 {
99 // return false if already initialised
100 if (IsInitialised())
101 return true;
102
103 // get all device we control
104 CECDEVICEVEC devices;
105 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
106
107 // return false when no devices were found
108 if (devices.empty())
109 {
110 LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", GetPrimaryLogicalAdddress());
111 return false;
112 }
113
114 // mark as initialised
115 SetInitialised(true);
116
117 // configure all devices
118 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
119 {
120 // only set our OSD name for the primary device
121 if ((*it)->GetLogicalAddress() == GetPrimaryLogicalAdddress())
122 (*it)->SetOSDName(m_configuration.strDeviceName);
123
124 // set the default menu language for devices we control
125 (*it)->SetMenuLanguage(m_configuration.strDeviceLanguage);
126 }
127
128 // set the physical address
129 SetPhysicalAddress(m_configuration);
130
131 // make the primary device the active source if the option is set
132 if (m_configuration.bActivateSource == 1)
133 GetPrimaryDevice()->ActivateSource(500);
134
135 return true;
136 }
137
138 bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce /* = false */)
139 {
140 bool bReturn(false);
141
142 // limit the HDMI port range to 1-15
143 if (iPort < CEC_MIN_HDMI_PORTNUMBER ||
144 iPort > CEC_MAX_HDMI_PORTNUMBER)
145 return bReturn;
146
147 LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
148
149 // update the configuration
150 {
151 CLockObject lock(m_mutex);
152 m_configuration.baseDevice = iBaseDevice;
153 m_configuration.iHDMIPort = iPort;
154 }
155
156 // don't continue if the connection isn't opened
157 if (!m_processor->CECInitialised() && !bForce)
158 return true;
159
160 // get the PA of the base device
161 uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
162 CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice);
163 if (baseDevice)
164 iPhysicalAddress = baseDevice->GetPhysicalAddress(GetPrimaryLogicalAdddress());
165
166 // add our port number
167 if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS)
168 {
169 if (iPhysicalAddress == 0)
170 iPhysicalAddress += 0x1000 * iPort;
171 else if (iPhysicalAddress % 0x1000 == 0)
172 iPhysicalAddress += 0x100 * iPort;
173 else if (iPhysicalAddress % 0x100 == 0)
174 iPhysicalAddress += 0x10 * iPort;
175 else if (iPhysicalAddress % 0x10 == 0)
176 iPhysicalAddress += iPort;
177
178 bReturn = true;
179 }
180
181 // set the default address when something went wrong
182 if (!bReturn)
183 {
184 uint16_t iEepromAddress = m_processor->GetPhysicalAddressFromEeprom();
185 if (CLibCEC::IsValidPhysicalAddress(iEepromAddress))
186 {
187 LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the value that was persisted in the eeprom, %04X", iPhysicalAddress, iEepromAddress);
188 iPhysicalAddress = iEepromAddress;
189 bReturn = true;
190 }
191 else
192 {
193 LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS);
194 iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS;
195 }
196 }
197
198 // and set the address
199 SetDevicePhysicalAddress(iPhysicalAddress);
200
201 CallbackConfigurationChanged(m_configuration);
202
203 return bReturn;
204 }
205
206 void CCECClient::ResetPhysicalAddress(void)
207 {
208 SetPhysicalAddress(CEC_DEFAULT_PHYSICAL_ADDRESS);
209 }
210
211 void CCECClient::SetPhysicalAddress(const libcec_configuration &configuration)
212 {
213 bool bPASet(false);
214
215 // override the physical address from configuration.iPhysicalAddress if it's set
216 if (!bPASet && CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
217 bPASet = SetPhysicalAddress(configuration.iPhysicalAddress);
218
219 // try to autodetect the address
220 if (!bPASet && m_processor->CECInitialised())
221 {
222 bPASet = AutodetectPhysicalAddress();
223 m_configuration.bAutodetectAddress = bPASet ? 1 : 0;
224 }
225
226 // use the base device + hdmi port settings
227 if (!bPASet)
228 bPASet = SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort);
229
230 // reset to defaults if something went wrong
231 if (!bPASet)
232 {
233 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__);
234 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
235 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
236 }
237 }
238
239 bool CCECClient::SetPhysicalAddress(const uint16_t iPhysicalAddress)
240 {
241 // update the configuration
242 bool bChanged(true);
243 {
244 CLockObject lock(m_mutex);
245 if (m_configuration.iPhysicalAddress == iPhysicalAddress)
246 bChanged = false;
247 else
248 m_configuration.iPhysicalAddress = iPhysicalAddress;
249 }
250 if (!bChanged)
251 {
252 LIB_CEC->AddLog(CEC_LOG_DEBUG, "physical address unchanged (%04X)", iPhysicalAddress);
253 return true;
254 }
255
256 LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress);
257
258 // set the physical address for each device
259 SetDevicePhysicalAddress(iPhysicalAddress);
260
261 // and send back the updated configuration
262 CallbackConfigurationChanged(m_configuration);
263
264 return true;
265 }
266
267 void CCECClient::SetSupportedDeviceTypes(void)
268 {
269 cec_device_type_list types;
270 types.Clear();
271
272 // get the command handler for the tv
273 CCECCommandHandler *tvHandler = m_processor->GetTV()->GetHandler();
274 if (!tvHandler)
275 return;
276
277 // check all device types
278 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
279 {
280 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
281 continue;
282
283 // get the supported device type. the handler will replace types it doesn't support by one it does support
284 cec_device_type type = tvHandler->GetReplacementDeviceType(m_configuration.deviceTypes.types[iPtr]);
285 if (!types.IsSet(type))
286 types.Add(type);
287 }
288 m_processor->GetTV()->MarkHandlerReady();
289
290 // set the new type list
291 m_configuration.deviceTypes = types;
292
293 // persist the new configuration
294 PersistConfiguration(m_configuration);
295 }
296
297 bool CCECClient::AllocateLogicalAddresses(void)
298 {
299 // reset all previous LAs that were set
300 m_configuration.logicalAddresses.Clear();
301
302 // get the supported device types from the command handler of the TV
303 SetSupportedDeviceTypes();
304
305 // display an error if no device types are set
306 if (m_configuration.deviceTypes.IsEmpty())
307 {
308 LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given");
309 return false;
310 }
311
312 // check each entry of the list
313 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
314 {
315 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
316 continue;
317
318 // find an LA for this type
319 cec_logical_address address(CECDEVICE_UNKNOWN);
320 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
321 address = AllocateLogicalAddressRecordingDevice();
322 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
323 address = AllocateLogicalAddressTuner();
324 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
325 address = AllocateLogicalAddressPlaybackDevice();
326 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
327 address = AllocateLogicalAddressAudioSystem();
328
329 // display an error if no LA could be allocated
330 if (address == CECDEVICE_UNKNOWN)
331 {
332 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]));
333 return false;
334 }
335
336 // display the registered LA
337 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address);
338 m_configuration.logicalAddresses.Set(address);
339 }
340
341 // persist the new configuration
342 PersistConfiguration(m_configuration);
343
344 return true;
345 }
346
347 cec_logical_address CCECClient::AllocateLogicalAddressRecordingDevice(void)
348 {
349 cec_logical_address retVal(CECDEVICE_UNKNOWN);
350
351 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
352 if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1, m_configuration.cecVersion))
353 retVal = CECDEVICE_RECORDINGDEVICE1;
354 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2, m_configuration.cecVersion))
355 retVal = CECDEVICE_RECORDINGDEVICE2;
356 else if (m_processor->TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3, m_configuration.cecVersion))
357 retVal = CECDEVICE_RECORDINGDEVICE3;
358
359 return retVal;
360 }
361
362 cec_logical_address CCECClient::AllocateLogicalAddressTuner(void)
363 {
364 cec_logical_address retVal(CECDEVICE_UNKNOWN);
365
366 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
367 if (m_processor->TryLogicalAddress(CECDEVICE_TUNER1, m_configuration.cecVersion))
368 retVal = CECDEVICE_TUNER1;
369 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER2, m_configuration.cecVersion))
370 retVal = CECDEVICE_TUNER2;
371 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER3, m_configuration.cecVersion))
372 retVal = CECDEVICE_TUNER3;
373 else if (m_processor->TryLogicalAddress(CECDEVICE_TUNER4, m_configuration.cecVersion))
374 retVal = CECDEVICE_TUNER4;
375
376 return retVal;
377 }
378
379 cec_logical_address CCECClient::AllocateLogicalAddressPlaybackDevice(void)
380 {
381 cec_logical_address retVal(CECDEVICE_UNKNOWN);
382
383 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
384 if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1, m_configuration.cecVersion))
385 retVal = CECDEVICE_PLAYBACKDEVICE1;
386 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2, m_configuration.cecVersion))
387 retVal = CECDEVICE_PLAYBACKDEVICE2;
388 else if (m_processor->TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3, m_configuration.cecVersion))
389 retVal = CECDEVICE_PLAYBACKDEVICE3;
390
391 return retVal;
392 }
393
394 cec_logical_address CCECClient::AllocateLogicalAddressAudioSystem(void)
395 {
396 cec_logical_address retVal(CECDEVICE_UNKNOWN);
397
398 LIB_CEC->AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audiosystem'");
399 if (m_processor->TryLogicalAddress(CECDEVICE_AUDIOSYSTEM, m_configuration.cecVersion))
400 retVal = CECDEVICE_AUDIOSYSTEM;
401
402 return retVal;
403 }
404
405 CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const
406 {
407 // get all devices that match our logical addresses
408 CECDEVICEVEC devices;
409 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
410
411 // filter the type we need
412 CCECDeviceMap::FilterType(type, devices);
413
414 return devices.empty() ?
415 NULL :
416 *devices.begin();
417 }
418
419 bool CCECClient::ChangeDeviceType(const cec_device_type from, const cec_device_type to)
420 {
421 if (from == to)
422 return true;
423
424 LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
425
426 {
427 CLockObject lock(m_mutex);
428
429 // get the previous device that was allocated
430 CCECBusDevice *previousDevice = GetDeviceByType(from);
431 if (!previousDevice)
432 return false;
433
434 // change the type in the device type list
435 bool bChanged(false);
436 for (uint8_t iPtr = 0; iPtr < 5; iPtr++)
437 {
438 if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
439 continue;
440
441 if (m_configuration.deviceTypes.types[iPtr] == from)
442 {
443 bChanged = true;
444 m_configuration.deviceTypes.types[iPtr] = to;
445 }
446 else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged)
447 {
448 // ensure that dupes are removed
449 m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
450 }
451 }
452 }
453
454 // re-register the client to set the new ackmask
455 if (!m_processor->RegisterClient(this))
456 return false;
457
458 // persist the new configuration
459 PersistConfiguration(m_configuration);
460
461 return true;
462 }
463
464 bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress)
465 {
466 bool bReturn(true);
467
468 if (GetPrimaryLogicalAdddress() != iLogicalAddress)
469 {
470 LIB_CEC->AddLog(CEC_LOG_NOTICE, "setting primary logical address to %1x", iLogicalAddress);
471 {
472 CLockObject lock(m_mutex);
473 m_configuration.logicalAddresses.primary = iLogicalAddress;
474 m_configuration.logicalAddresses.Set(iLogicalAddress);
475 }
476
477 bReturn = m_processor->RegisterClient(this);
478
479 // persist the new configuration
480 if (bReturn)
481 PersistConfiguration(m_configuration);
482 }
483
484 return bReturn;
485 }
486
487 bool CCECClient::Transmit(const cec_command &data, bool bIsReply)
488 {
489 return m_processor ? m_processor->Transmit(data, bIsReply) : false;
490 }
491
492 bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */)
493 {
494 // if the broadcast address if set as destination, read the wakeDevices setting
495 if (address == CECDEVICE_BROADCAST)
496 {
497 CECDEVICEVEC devices;
498 m_processor->GetDevices()->GetWakeDevices(m_configuration, devices);
499 return m_processor->PowerOnDevices(GetPrimaryLogicalAdddress(), devices);
500 }
501
502 return m_processor->PowerOnDevice(GetPrimaryLogicalAdddress(), address);
503 }
504
505 bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */)
506 {
507 // if the broadcast address if set as destination, read the standbyDevices setting
508 if (address == CECDEVICE_BROADCAST)
509 {
510 CECDEVICEVEC devices;
511 m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices);
512 return m_processor->StandbyDevices(GetPrimaryLogicalAdddress(), devices);
513 }
514
515 return m_processor->StandbyDevice(GetPrimaryLogicalAdddress(), address);
516 }
517
518 bool CCECClient::SendSetActiveSource(const cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
519 {
520 // get the devices that are controlled by us
521 CECDEVICEVEC devices;
522 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
523
524 // filter out the device that matches the given type
525 if (type != CEC_DEVICE_TYPE_RESERVED)
526 CCECDeviceMap::FilterType(type, devices);
527
528 // no devices left, re-fetch the list of devices that are controlled by us
529 if (devices.empty())
530 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
531
532 if (!devices.empty())
533 {
534 // get the first device from the list
535 CCECBusDevice *device = *devices.begin();
536
537 // and activate it
538 if (!m_processor->CECInitialised())
539 device->MarkAsActiveSource();
540 else if (device->HasValidPhysicalAddress())
541 return device->ActivateSource();
542 }
543
544 return false;
545 }
546
547 CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void)
548 {
549 CCECPlaybackDevice *device(NULL);
550 CECDEVICEVEC devices;
551
552 // get the playback devices
553 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
554 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices);
555
556 // no matches, get the recording devices
557 if (devices.empty())
558 {
559 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
560 CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices);
561 }
562
563 // get the first device that matches, and cast it to CCECPlaybackDevice
564 if (!devices.empty())
565 device = (*devices.begin())->AsPlaybackDevice();
566
567 return device;
568 }
569
570 cec_logical_address CCECClient::GetPrimaryLogicalAdddress(void)
571 {
572 CLockObject lock(m_mutex);
573 return m_configuration.logicalAddresses.primary;
574 }
575
576 CCECBusDevice *CCECClient::GetPrimaryDevice(void)
577 {
578 return m_processor->GetDevice(GetPrimaryLogicalAdddress());
579 }
580
581 bool CCECClient::SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate /* = true */)
582 {
583 // find a playback device that we control
584 CCECPlaybackDevice *device = GetPlaybackDevice();
585 if (device)
586 {
587 // and set the deck control mode if there is a match
588 device->SetDeckControlMode(mode);
589 if (bSendUpdate)
590 return device->TransmitDeckStatus(CECDEVICE_TV, false);
591 return true;
592 }
593
594 // no match
595 return false;
596 }
597
598 bool CCECClient::SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate /* = true */)
599 {
600 // find a playback device that we control
601 CCECPlaybackDevice *device = GetPlaybackDevice();
602 if (device)
603 {
604 // and set the deck status if there is a match
605 device->SetDeckStatus(info);
606 if (bSendUpdate)
607 return device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV, false);
608 return true;
609 }
610
611 // no match
612 return false;
613 }
614
615 bool CCECClient::SendSetMenuState(const cec_menu_state state, bool bSendUpdate /* = true */)
616 {
617 CECDEVICEVEC devices;
618
619 // set the menu state for all devices that are controlled by us
620 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
621 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
622 {
623 (*it)->SetMenuState(state);
624 if (bSendUpdate)
625 (*it)->TransmitMenuState(CECDEVICE_TV, false);
626 }
627
628 return true;
629 }
630
631 bool CCECClient::SendSetInactiveView(void)
632 {
633 CECDEVICEVEC devices;
634
635 // mark all devices that are controlled by us as inactive source
636 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
637 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
638 {
639 if ((*it)->IsActiveSource())
640 {
641 (*it)->MarkAsInactiveSource();
642 return (*it)->TransmitInactiveSource();
643 }
644 }
645
646 return true;
647 }
648
649 bool CCECClient::SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage)
650 {
651 CCECBusDevice *primary = GetPrimaryDevice();
652 if (primary)
653 return primary->TransmitOSDString(iLogicalAddress, duration, strMessage, false);
654
655 return false;
656 }
657
658 cec_version CCECClient::GetDeviceCecVersion(const cec_logical_address iAddress)
659 {
660 CCECBusDevice *device = m_processor->GetDevice(iAddress);
661 if (device)
662 return device->GetCecVersion(GetPrimaryLogicalAdddress());
663 return CEC_VERSION_UNKNOWN;
664 }
665
666 bool CCECClient::GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language)
667 {
668 CCECBusDevice *device = m_processor->GetDevice(iAddress);
669 if (device)
670 {
671 language = device->GetMenuLanguage(GetPrimaryLogicalAdddress());
672 return (strcmp(language.language, "???") != 0);
673 }
674 return false;
675 }
676
677 cec_osd_name CCECClient::GetDeviceOSDName(const cec_logical_address iAddress)
678 {
679 cec_osd_name retVal;
680 retVal.device = iAddress;
681 retVal.name[0] = 0;
682
683 CCECBusDevice *device = m_processor->GetDevice(iAddress);
684 if (device)
685 {
686 CStdString strOSDName = device->GetOSDName(GetPrimaryLogicalAdddress());
687 snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
688 retVal.device = iAddress;
689 }
690
691 return retVal;
692 }
693
694 uint16_t CCECClient::GetDevicePhysicalAddress(const cec_logical_address iAddress)
695 {
696 CCECBusDevice *device = m_processor->GetDevice(iAddress);
697 if (device)
698 return device->GetPhysicalAddress(GetPrimaryLogicalAdddress());
699 return CEC_INVALID_PHYSICAL_ADDRESS;
700 }
701
702 cec_power_status CCECClient::GetDevicePowerStatus(const cec_logical_address iAddress)
703 {
704 CCECBusDevice *device = m_processor->GetDevice(iAddress);
705 if (device)
706 return device->GetPowerStatus(GetPrimaryLogicalAdddress());
707 return CEC_POWER_STATUS_UNKNOWN;
708 }
709
710 uint64_t CCECClient::GetDeviceVendorId(const cec_logical_address iAddress)
711 {
712 CCECBusDevice *device = m_processor->GetDevice(iAddress);
713 if (device)
714 return device->GetVendorId(GetPrimaryLogicalAdddress());
715 return CEC_VENDOR_UNKNOWN;
716 }
717
718 uint8_t CCECClient::SendVolumeUp(bool bSendRelease /* = true */)
719 {
720 CCECBusDevice *device = GetPrimaryDevice();
721 CCECAudioSystem *audio = m_processor->GetAudioSystem();
722
723 return device && audio && audio->IsPresent() ?
724 audio->VolumeUp(device->GetLogicalAddress(), bSendRelease) :
725 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
726 }
727
728 uint8_t CCECClient::SendVolumeDown(bool bSendRelease /* = true */)
729 {
730 CCECBusDevice *device = GetPrimaryDevice();
731 CCECAudioSystem *audio = m_processor->GetAudioSystem();
732
733 return device && audio && audio->IsPresent() ?
734 audio->VolumeDown(device->GetLogicalAddress(), bSendRelease) :
735 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
736 }
737
738 uint8_t CCECClient::SendMuteAudio(void)
739 {
740 CCECBusDevice *device = GetPrimaryDevice();
741 CCECAudioSystem *audio = m_processor->GetAudioSystem();
742
743 return device && audio && audio->IsPresent() ?
744 audio->MuteAudio(device->GetLogicalAddress()) :
745 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
746 }
747
748 uint8_t CCECClient::AudioToggleMute(void)
749 {
750 CCECBusDevice *device = GetPrimaryDevice();
751 CCECAudioSystem *audio = m_processor->GetAudioSystem();
752
753 return device && audio && audio->IsPresent() ?
754 audio->MuteAudio(device->GetLogicalAddress()) :
755 (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
756 }
757
758 uint8_t CCECClient::AudioMute(void)
759 {
760 CCECBusDevice *device = GetPrimaryDevice();
761 CCECAudioSystem *audio = m_processor->GetAudioSystem();
762 uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
763 if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) != CEC_AUDIO_MUTE_STATUS_MASK)
764 iStatus = audio->MuteAudio(device->GetLogicalAddress());
765
766 return iStatus;
767 }
768
769 uint8_t CCECClient::AudioUnmute(void)
770 {
771 CCECBusDevice *device = GetPrimaryDevice();
772 CCECAudioSystem *audio = m_processor->GetAudioSystem();
773 uint8_t iStatus = device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
774 if ((iStatus & CEC_AUDIO_MUTE_STATUS_MASK) == CEC_AUDIO_MUTE_STATUS_MASK)
775 iStatus = audio->MuteAudio(device->GetLogicalAddress());
776
777 return iStatus;
778 }
779
780 uint8_t CCECClient::AudioStatus(void)
781 {
782 CCECBusDevice *device = GetPrimaryDevice();
783 CCECAudioSystem *audio = m_processor->GetAudioSystem();
784 return device && audio && audio->IsPresent() ? audio->GetAudioStatus(device->GetLogicalAddress()) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN;
785 }
786
787 bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */)
788 {
789 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
790
791 return dest ?
792 dest->TransmitKeypress(GetPrimaryLogicalAdddress(), key, bWait) :
793 false;
794 }
795
796 bool CCECClient::SendKeyRelease(const cec_logical_address iDestination, bool bWait /* = true */)
797 {
798 CCECBusDevice *dest = m_processor->GetDevice(iDestination);
799
800 return dest ?
801 dest->TransmitKeyRelease(GetPrimaryLogicalAdddress(), bWait) :
802 false;
803 }
804
805 bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration)
806 {
807 CLockObject lock(m_mutex);
808
809 // client version 1.5.0
810 snprintf(configuration.strDeviceName, 13, "%s", m_configuration.strDeviceName);
811 configuration.deviceTypes = m_configuration.deviceTypes;
812 configuration.bAutodetectAddress = m_configuration.bAutodetectAddress;
813 configuration.iPhysicalAddress = m_configuration.iPhysicalAddress;
814 configuration.baseDevice = m_configuration.baseDevice;
815 configuration.iHDMIPort = m_configuration.iHDMIPort;
816 configuration.clientVersion = m_configuration.clientVersion;
817 configuration.serverVersion = m_configuration.serverVersion;
818 configuration.tvVendor = m_configuration.tvVendor;
819 configuration.bGetSettingsFromROM = m_configuration.bGetSettingsFromROM;
820 configuration.bUseTVMenuLanguage = m_configuration.bUseTVMenuLanguage;
821 configuration.bActivateSource = m_configuration.bActivateSource;
822 configuration.wakeDevices = m_configuration.wakeDevices;
823 configuration.powerOffDevices = m_configuration.powerOffDevices;
824 configuration.bPowerOffScreensaver = m_configuration.bPowerOffScreensaver;
825 configuration.bPowerOnScreensaver = m_configuration.bPowerOnScreensaver;
826 configuration.bPowerOffOnStandby = m_configuration.bPowerOffOnStandby;
827 configuration.bSendInactiveSource = m_configuration.bSendInactiveSource;
828 configuration.logicalAddresses = m_configuration.logicalAddresses;
829 configuration.iFirmwareVersion = m_configuration.iFirmwareVersion;
830 configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby;
831 configuration.bShutdownOnStandby = m_configuration.bShutdownOnStandby;
832 memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3);
833 configuration.iFirmwareBuildDate = m_configuration.iFirmwareBuildDate;
834 configuration.bMonitorOnly = m_configuration.bMonitorOnly;
835 configuration.cecVersion = m_configuration.cecVersion;
836 configuration.adapterType = m_configuration.adapterType;
837
838 return true;
839 }
840
841 bool CCECClient::SetConfiguration(const libcec_configuration &configuration)
842 {
843 libcec_configuration defaultSettings;
844 bool bIsRunning(m_processor && m_processor->CECInitialised());
845 CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL;
846 uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
847
848 // update the callbacks
849 if (configuration.callbacks)
850 EnableCallbacks(configuration.callbackParam, configuration.callbacks);
851
852 // update the client version
853 SetClientVersion((cec_client_version)configuration.clientVersion);
854
855 // update the OSD name
856 CStdString strOSDName(configuration.strDeviceName);
857 SetOSDName(strOSDName);
858
859 // update the TV vendor override
860 SetTVVendorOverride((cec_vendor_id)configuration.tvVendor);
861
862 // just copy these
863 {
864 CLockObject lock(m_mutex);
865 m_configuration.bUseTVMenuLanguage = configuration.bUseTVMenuLanguage;
866 m_configuration.bActivateSource = configuration.bActivateSource;
867 m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM;
868 m_configuration.wakeDevices = configuration.wakeDevices;
869 m_configuration.powerOffDevices = configuration.powerOffDevices;
870 m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver;
871 m_configuration.bPowerOffOnStandby = configuration.bPowerOffOnStandby;
872 m_configuration.bSendInactiveSource = configuration.bSendInactiveSource;
873 m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby;
874 m_configuration.bShutdownOnStandby = configuration.bShutdownOnStandby;
875 memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3);
876 m_configuration.bMonitorOnly = configuration.bMonitorOnly;
877 m_configuration.cecVersion = configuration.cecVersion;
878 m_configuration.adapterType = configuration.adapterType;
879 m_configuration.iDoubleTapTimeoutMs = configuration.iDoubleTapTimeoutMs;
880 m_configuration.deviceTypes.Add(configuration.deviceTypes[0]);
881
882 if (m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5)
883 {
884 m_configuration.comboKey = configuration.comboKey;
885 m_configuration.iComboKeyTimeoutMs = configuration.iComboKeyTimeoutMs;
886 }
887 else
888 {
889 m_configuration.comboKey = defaultSettings.comboKey;
890 m_configuration.iComboKeyTimeoutMs = defaultSettings.iComboKeyTimeoutMs;
891 }
892
893 if (m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_1_0)
894 m_configuration.bPowerOnScreensaver = configuration.bPowerOnScreensaver;
895 else
896 m_configuration.bPowerOnScreensaver = defaultSettings.bPowerOnScreensaver;
897 }
898
899 bool bNeedReinit(false);
900
901 // device types
902 if (SetDeviceTypes(configuration.deviceTypes))
903 {
904 // the device type changed. just copy the rest, and re-register
905 {
906 CLockObject lock(m_mutex);
907 m_configuration.iPhysicalAddress = configuration.iPhysicalAddress;
908 m_configuration.baseDevice = configuration.baseDevice;
909 m_configuration.iHDMIPort = configuration.iHDMIPort;
910 bNeedReinit = true;
911 }
912 }
913 else
914 {
915 // set the physical address
916 SetPhysicalAddress(configuration);
917 }
918
919 // persist the new configuration
920 PersistConfiguration(m_configuration);
921
922 if (!primary)
923 primary = GetPrimaryDevice();
924
925 if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA)
926 {
927 // PA or device type changed
928 m_processor->RegisterClient(this);
929 }
930 else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource())
931 {
932 // activate the source if we're not already the active source
933 primary->ActivateSource();
934 }
935
936 return true;
937 }
938
939 void CCECClient::AddCommand(const cec_command &command)
940 {
941 // don't forward the standby opcode more than once every 10 seconds
942 if (command.opcode == CEC_OPCODE_STANDBY)
943 {
944 CLockObject lock(m_mutex);
945 if (m_iPreventForwardingPowerOffCommand != 0 &&
946 m_iPreventForwardingPowerOffCommand > GetTimeMs())
947 return;
948 else
949 m_iPreventForwardingPowerOffCommand = GetTimeMs() + CEC_FORWARD_STANDBY_MIN_INTERVAL;
950 }
951
952 if (command.destination == CECDEVICE_BROADCAST || GetLogicalAddresses().IsSet(command.destination))
953 {
954 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%X) -> %s (%X): %s (%2X)", ToString(command.initiator), command.initiator, ToString(command.destination), command.destination, ToString(command.opcode), command.opcode);
955 CallbackAddCommand(command);
956 }
957 }
958
959 int CCECClient::MenuStateChanged(const cec_menu_state newState)
960 {
961 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s: %s", ToString(CEC_OPCODE_MENU_REQUEST), ToString(newState));
962 return CallbackMenuStateChanged(newState);
963 }
964
965 void CCECClient::AddKey(bool bSendComboKey /* = false */)
966 {
967 cec_keypress key;
968 key.keycode = CEC_USER_CONTROL_CODE_UNKNOWN;
969
970 {
971 CLockObject lock(m_mutex);
972 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN)
973 {
974 key.duration = (unsigned int) (GetTimeMs() - m_buttontime);
975
976 if (key.duration > m_configuration.iComboKeyTimeoutMs ||
977 m_configuration.iComboKeyTimeoutMs == 0 ||
978 m_iCurrentButton != m_configuration.comboKey ||
979 bSendComboKey)
980 {
981 key.keycode = m_iCurrentButton;
982
983 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
984 m_buttontime = 0;
985 }
986 }
987 }
988
989 if (key.keycode != CEC_USER_CONTROL_CODE_UNKNOWN)
990 {
991 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key released: %s (%1x)", ToString(key.keycode), key.keycode);
992 CallbackAddKey(key);
993 }
994 }
995
996 void CCECClient::AddKey(const cec_keypress &key)
997 {
998 if (key.keycode > CEC_USER_CONTROL_CODE_MAX &&
999 key.keycode < CEC_USER_CONTROL_CODE_SELECT)
1000 {
1001 // send back the previous key if there is one
1002 AddKey();
1003 return;
1004 }
1005
1006 cec_keypress transmitKey(key);
1007 cec_user_control_code comboKey(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
1008 m_configuration.comboKey : CEC_USER_CONTROL_CODE_STOP);
1009
1010 {
1011 CLockObject lock(m_mutex);
1012 if (m_configuration.iComboKeyTimeoutMs > 0 && m_iCurrentButton == comboKey && key.duration == 0)
1013 {
1014 // stop + ok -> exit
1015 if (key.keycode == CEC_USER_CONTROL_CODE_SELECT)
1016 transmitKey.keycode = CEC_USER_CONTROL_CODE_EXIT;
1017 // stop + pause -> root menu
1018 else if (key.keycode == CEC_USER_CONTROL_CODE_PAUSE)
1019 transmitKey.keycode = CEC_USER_CONTROL_CODE_ROOT_MENU;
1020 // stop + play -> dot (which is handled as context menu in xbmc)
1021 else if (key.keycode == CEC_USER_CONTROL_CODE_PLAY)
1022 transmitKey.keycode = CEC_USER_CONTROL_CODE_DOT;
1023 // default, send back the previous key
1024 else
1025 AddKey(true);
1026 }
1027
1028 if (m_iCurrentButton == key.keycode)
1029 {
1030 m_buttontime = GetTimeMs();
1031 }
1032 else
1033 {
1034 AddKey();
1035 if (key.duration == 0)
1036 {
1037 m_iCurrentButton = transmitKey.keycode;
1038 m_buttontime = m_iCurrentButton == CEC_USER_CONTROL_CODE_UNKNOWN || key.duration > 0 ? 0 : GetTimeMs();
1039 }
1040 }
1041 }
1042
1043 if (key.keycode != comboKey || key.duration > 0)
1044 {
1045 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key pressed: %s (%1x)", ToString(transmitKey.keycode), transmitKey.keycode);
1046 CallbackAddKey(transmitKey);
1047 }
1048 }
1049
1050 void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode)
1051 {
1052 // push a keypress to the buffer with 0 duration and another with the duration set when released
1053 cec_keypress key;
1054 key.duration = 0;
1055 key.keycode = iButtonCode;
1056
1057 AddKey(key);
1058 }
1059
1060 void CCECClient::CheckKeypressTimeout(void)
1061 {
1062 cec_keypress key;
1063
1064 {
1065 CLockObject lock(m_mutex);
1066 uint64_t iNow = GetTimeMs();
1067 cec_user_control_code comboKey(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
1068 m_configuration.comboKey : CEC_USER_CONTROL_CODE_STOP);
1069 uint32_t iTimeoutMs(m_configuration.clientVersion >= CEC_CLIENT_VERSION_2_0_5 ?
1070 m_configuration.iComboKeyTimeoutMs : CEC_DEFAULT_COMBO_TIMEOUT_MS);
1071
1072 if (m_iCurrentButton != CEC_USER_CONTROL_CODE_UNKNOWN &&
1073 ((m_iCurrentButton == comboKey && iTimeoutMs > 0 && iNow - m_buttontime > iTimeoutMs) ||
1074 (m_iCurrentButton != comboKey && iNow - m_buttontime > CEC_BUTTON_TIMEOUT)))
1075 {
1076 key.duration = (unsigned int) (iNow - m_buttontime);
1077 key.keycode = m_iCurrentButton;
1078
1079 m_iCurrentButton = CEC_USER_CONTROL_CODE_UNKNOWN;
1080 m_buttontime = 0;
1081 }
1082 else
1083 {
1084 return;
1085 }
1086 }
1087
1088 LIB_CEC->AddLog(CEC_LOG_DEBUG, "key auto-released: %s (%1x)", ToString(key.keycode), key.keycode);
1089 CallbackAddKey(key);
1090 }
1091
1092 bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks)
1093 {
1094 CLockObject lock(m_cbMutex);
1095 m_configuration.callbackParam = cbParam;
1096 m_configuration.callbacks = callbacks;
1097 return true;
1098 }
1099
1100 bool CCECClient::PingAdapter(void)
1101 {
1102 return m_processor ? m_processor->PingAdapter() : false;
1103 }
1104
1105 std::string CCECClient::GetConnectionInfo(void)
1106 {
1107 CStdString strLog;
1108 strLog.Format("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);
1109 if (m_configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN)
1110 {
1111 time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate;
1112 strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime)));
1113 strLog = strLog.substr(0, strLog.length() > 0 ? (size_t)(strLog.length() - 1) : 0); // strip \n added by asctime
1114 strLog.append(" +0000");
1115 }
1116
1117 // log the addresses that are being used
1118 if (!m_configuration.logicalAddresses.IsEmpty())
1119 {
1120 strLog.append(", logical address(es) = ");
1121 CECDEVICEVEC devices;
1122 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1123 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1124 strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress());
1125 }
1126
1127 if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress))
1128 strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort);
1129 uint16_t iPhysicalAddress = GetPrimaryDevice()->GetPhysicalAddress(GetLogicalAddresses().primary, false);
1130 strLog.AppendFormat(", physical address: %x.%x.%x.%x", (iPhysicalAddress >> 12) & 0xF, (iPhysicalAddress >> 8) & 0xF, (iPhysicalAddress >> 4) & 0xF, iPhysicalAddress & 0xF);
1131
1132 strLog.AppendFormat(", %s", LIB_CEC->GetLibInfo());
1133
1134 std::string strReturn(strLog.c_str());
1135 return strReturn;
1136 }
1137
1138 void CCECClient::SetTVVendorOverride(const cec_vendor_id id)
1139 {
1140 {
1141 CLockObject lock(m_mutex);
1142 m_configuration.tvVendor = id;
1143 }
1144
1145 if (id != CEC_VENDOR_UNKNOWN)
1146 {
1147 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id));
1148
1149 CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL;
1150 if (tv)
1151 tv->SetVendorId((uint64_t)id);
1152 }
1153
1154 // persist the new configuration
1155 PersistConfiguration(m_configuration);
1156 }
1157
1158 cec_vendor_id CCECClient::GetTVVendorOverride(void)
1159 {
1160 CLockObject lock(m_mutex);
1161 return (cec_vendor_id)m_configuration.tvVendor;
1162 }
1163
1164 void CCECClient::SetOSDName(const std::string &strDeviceName)
1165 {
1166 {
1167 CLockObject lock(m_mutex);
1168 snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str());
1169 }
1170
1171 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str());
1172
1173 CCECBusDevice *primary = GetPrimaryDevice();
1174 if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName.c_str()))
1175 {
1176 primary->SetOSDName(strDeviceName);
1177 if (m_processor && m_processor->CECInitialised())
1178 primary->TransmitOSDName(CECDEVICE_TV, false);
1179 }
1180
1181 // persist the new configuration
1182 PersistConfiguration(m_configuration);
1183 }
1184
1185 std::string CCECClient::GetOSDName(void)
1186 {
1187 CLockObject lock(m_mutex);
1188 std::string strOSDName(m_configuration.strDeviceName);
1189 return strOSDName;
1190 }
1191
1192 void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses)
1193 {
1194 {
1195 CLockObject lock(m_mutex);
1196 m_configuration.wakeDevices = addresses;
1197 }
1198 // persist the new configuration
1199 PersistConfiguration(m_configuration);
1200 }
1201
1202 cec_logical_addresses CCECClient::GetWakeDevices(void)
1203 {
1204 CLockObject lock(m_mutex);
1205 return m_configuration.wakeDevices;
1206 }
1207
1208 bool CCECClient::AutodetectPhysicalAddress(void)
1209 {
1210 bool bPhysicalAutodetected(false);
1211 uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
1212
1213 if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1214 {
1215 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress);
1216
1217 CLockObject lock(m_mutex);
1218 m_configuration.iPhysicalAddress = iPhysicalAddress;
1219 m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE;
1220 m_configuration.baseDevice = CECDEVICE_UNKNOWN;
1221 bPhysicalAutodetected = true;
1222 }
1223
1224 SetDevicePhysicalAddress(iPhysicalAddress);
1225
1226 return bPhysicalAutodetected;
1227 }
1228
1229 void CCECClient::SetClientVersion(const cec_client_version version)
1230 {
1231 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version));
1232
1233 CLockObject lock(m_mutex);
1234 m_configuration.clientVersion = (uint32_t)version;
1235 }
1236
1237 cec_client_version CCECClient::GetClientVersion(void)
1238 {
1239 CLockObject lock(m_mutex);
1240 return (cec_client_version)m_configuration.clientVersion;
1241 }
1242
1243 bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes)
1244 {
1245 bool bNeedReinit(false);
1246
1247 {
1248 CLockObject lock(m_mutex);
1249 bNeedReinit = m_processor && m_processor->CECInitialised() &&
1250 (m_configuration.deviceTypes != deviceTypes);
1251 m_configuration.deviceTypes = deviceTypes;
1252 }
1253
1254 // persist the new configuration
1255 PersistConfiguration(m_configuration);
1256
1257 if (bNeedReinit)
1258 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0]));
1259
1260 return bNeedReinit;
1261 }
1262
1263 cec_device_type_list CCECClient::GetDeviceTypes(void)
1264 {
1265 cec_device_type_list retVal;
1266 CLockObject lock(m_mutex);
1267 retVal = m_configuration.deviceTypes;
1268 return retVal;
1269 }
1270
1271 bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress)
1272 {
1273 if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress))
1274 {
1275 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - not setting invalid physical address %04x", __FUNCTION__, iPhysicalAddress);
1276 return false;
1277 }
1278
1279 // reconfigure all devices
1280 cec_logical_address reactivateSource(CECDEVICE_UNKNOWN);
1281 CECDEVICEVEC devices;
1282 m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses);
1283 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1284 {
1285 // if this device was the active source, reactivate it afterwards
1286 if ((*it)->IsActiveSource())
1287 reactivateSource = (*it)->GetLogicalAddress();
1288
1289 // mark the device as inactive source
1290 if (IsInitialised())
1291 (*it)->MarkAsInactiveSource();
1292
1293 // set the new physical address
1294 (*it)->SetPhysicalAddress(iPhysicalAddress);
1295
1296 // and transmit it
1297 if (IsInitialised())
1298 (*it)->TransmitPhysicalAddress(false);
1299 }
1300
1301 // reactivate the previous active source
1302 if (reactivateSource != CECDEVICE_UNKNOWN &&
1303 m_processor->CECInitialised() &&
1304 IsInitialised())
1305 {
1306 CCECBusDevice *device = m_processor->GetDevice(reactivateSource);
1307 if (device)
1308 device->ActivateSource();
1309 }
1310
1311 // persist the new configuration
1312 PersistConfiguration(m_configuration);
1313
1314 return true;
1315 }
1316
1317 bool CCECClient::SwitchMonitoring(bool bEnable)
1318 {
1319 LIB_CEC->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
1320
1321 if (m_processor)
1322 {
1323 m_processor->SwitchMonitoring(bEnable);
1324 m_configuration.bMonitorOnly = bEnable;
1325 return bEnable ? true: m_processor->RegisterClient(this);
1326 }
1327
1328 return false;
1329 }
1330
1331 bool CCECClient::PollDevice(const cec_logical_address iAddress)
1332 {
1333 // try to find the primary device
1334 CCECBusDevice *primary = GetPrimaryDevice();
1335 // poll the destination, with the primary as source
1336 if (primary)
1337 return primary->TransmitPoll(iAddress, true);
1338
1339 return m_processor ? m_processor->PollDevice(iAddress) : false;
1340 }
1341
1342 cec_logical_addresses CCECClient::GetActiveDevices(void)
1343 {
1344 CECDEVICEVEC activeDevices;
1345 if (m_processor)
1346 m_processor->GetDevices()->GetActive(activeDevices);
1347 return CCECDeviceMap::ToLogicalAddresses(activeDevices);
1348 }
1349
1350 bool CCECClient::IsActiveDevice(const cec_logical_address iAddress)
1351 {
1352 cec_logical_addresses activeDevices = GetActiveDevices();
1353 return activeDevices.IsSet(iAddress);
1354 }
1355
1356 bool CCECClient::IsActiveDeviceType(const cec_device_type type)
1357 {
1358 CECDEVICEVEC activeDevices;
1359 if (m_processor)
1360 m_processor->GetDevices()->GetActive(activeDevices);
1361 CCECDeviceMap::FilterType(type, activeDevices);
1362 return !activeDevices.empty();
1363 }
1364
1365 cec_logical_address CCECClient::GetActiveSource(void)
1366 {
1367 return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN;
1368 }
1369
1370 bool CCECClient::IsActiveSource(const cec_logical_address iAddress)
1371 {
1372 return m_processor ? m_processor->IsActiveSource(iAddress) : false;
1373 }
1374
1375 bool CCECClient::SetStreamPath(const cec_logical_address iAddress)
1376 {
1377 uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress);
1378 if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS)
1379 return SetStreamPath(iPhysicalAddress);
1380 return false;
1381 }
1382
1383 bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress)
1384 {
1385 bool bReturn(false);
1386
1387 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_TV);
1388 if (device)
1389 {
1390 device->SetStreamPath(iPhysicalAddress);
1391 bReturn = device->GetHandler()->TransmitSetStreamPath(iPhysicalAddress, false);
1392 device->MarkHandlerReady();
1393 }
1394 else
1395 {
1396 LIB_CEC->AddLog(CEC_LOG_ERROR, "only the TV is allowed to send CEC_OPCODE_SET_STREAM_PATH");
1397 }
1398
1399 return bReturn;
1400 }
1401
1402 cec_logical_addresses CCECClient::GetLogicalAddresses(void)
1403 {
1404 cec_logical_addresses addresses;
1405 CLockObject lock(m_mutex);
1406 addresses = m_configuration.logicalAddresses;
1407 return addresses;
1408 }
1409
1410 bool CCECClient::CanPersistConfiguration(void)
1411 {
1412 return m_processor ? m_processor->CanPersistConfiguration() : false;
1413 }
1414
1415 bool CCECClient::PersistConfiguration(const libcec_configuration &configuration)
1416 {
1417 return m_processor && IsRegistered() ?
1418 m_processor->PersistConfiguration(configuration) :
1419 false;
1420 }
1421
1422 void CCECClient::RescanActiveDevices(void)
1423 {
1424 if (m_processor)
1425 m_processor->RescanActiveDevices();
1426 }
1427
1428 bool CCECClient::IsLibCECActiveSource(void)
1429 {
1430 bool bReturn(false);
1431 if (m_processor)
1432 {
1433 cec_logical_address activeSource = m_processor->GetActiveSource();
1434 CCECBusDevice *device = m_processor->GetDevice(activeSource);
1435 if (device)
1436 bReturn = device->IsHandledByLibCEC() && !device->GetHandler()->ActiveSourcePending();
1437 }
1438 return bReturn;
1439 }
1440
1441 void CCECClient::SourceActivated(const cec_logical_address logicalAddress)
1442 {
1443 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source activated: %s (%x)", ToString(logicalAddress), logicalAddress);
1444 CallbackSourceActivated(true, logicalAddress);
1445 }
1446
1447 void CCECClient::SourceDeactivated(const cec_logical_address logicalAddress)
1448 {
1449 LIB_CEC->AddLog(CEC_LOG_NOTICE, ">> source deactivated: %s (%x)", ToString(logicalAddress), logicalAddress);
1450 CallbackSourceActivated(false, logicalAddress);
1451 }
1452
1453 void CCECClient::CallbackAddCommand(const cec_command &command)
1454 {
1455 CLockObject lock(m_cbMutex);
1456 if (m_configuration.callbacks && m_configuration.callbacks->CBCecCommand)
1457 m_configuration.callbacks->CBCecCommand(m_configuration.callbackParam, command);
1458 }
1459
1460 void CCECClient::CallbackAddKey(const cec_keypress &key)
1461 {
1462 CLockObject lock(m_cbMutex);
1463 if (m_configuration.callbacks && m_configuration.callbacks->CBCecKeyPress)
1464 {
1465 // prevent double taps
1466 int64_t now = GetTimeMs();
1467 if (m_lastKeypress.keycode != key.keycode ||
1468 key.duration > 0 ||
1469 now - m_iLastKeypressTime >= m_configuration.iDoubleTapTimeoutMs)
1470 {
1471 // no double tap
1472 if (key.duration == 0)
1473 m_iLastKeypressTime = now;
1474 m_lastKeypress = key;
1475 m_configuration.callbacks->CBCecKeyPress(m_configuration.callbackParam, key);
1476 }
1477 }
1478 }
1479
1480 void CCECClient::CallbackAddLog(const cec_log_message &message)
1481 {
1482 CLockObject lock(m_cbMutex);
1483 if (m_configuration.callbacks && m_configuration.callbacks->CBCecLogMessage)
1484 m_configuration.callbacks->CBCecLogMessage(m_configuration.callbackParam, message);
1485 }
1486
1487 void CCECClient::CallbackConfigurationChanged(const libcec_configuration &config)
1488 {
1489 CLockObject lock(m_cbMutex);
1490 if (m_configuration.callbacks &&
1491 m_configuration.callbacks->CBCecConfigurationChanged &&
1492 m_processor->CECInitialised())
1493 m_configuration.callbacks->CBCecConfigurationChanged(m_configuration.callbackParam, config);
1494 }
1495
1496 void CCECClient::CallbackSourceActivated(bool bActivated, const cec_logical_address logicalAddress)
1497 {
1498 CLockObject lock(m_cbMutex);
1499 if (m_configuration.callbacks &&
1500 m_configuration.callbacks->CBCecSourceActivated)
1501 m_configuration.callbacks->CBCecSourceActivated(m_configuration.callbackParam, logicalAddress, bActivated ? 1 : 0);
1502 }
1503
1504 void CCECClient::CallbackAlert(const libcec_alert type, const libcec_parameter &param)
1505 {
1506 CLockObject lock(m_cbMutex);
1507 if (m_configuration.callbacks &&
1508 m_configuration.callbacks->CBCecAlert)
1509 m_configuration.callbacks->CBCecAlert(m_configuration.callbackParam, type, param);
1510 }
1511
1512 int CCECClient::CallbackMenuStateChanged(const cec_menu_state newState)
1513 {
1514 CLockObject lock(m_cbMutex);
1515 if (m_configuration.callbacks &&
1516 m_configuration.callbacks->CBCecMenuStateChanged)
1517 return m_configuration.callbacks->CBCecMenuStateChanged(m_configuration.callbackParam, newState);
1518 return 0;
1519 }