ensure that we only send 'image view on' when needed
[deb_libcec.git] / src / lib / devices / CECBusDevice.cpp
... / ...
CommitLineData
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 "env.h"
34#include "CECBusDevice.h"
35
36#include "lib/CECProcessor.h"
37#include "lib/CECClient.h"
38#include "lib/implementations/ANCommandHandler.h"
39#include "lib/implementations/CECCommandHandler.h"
40#include "lib/implementations/SLCommandHandler.h"
41#include "lib/implementations/VLCommandHandler.h"
42#include "lib/LibCEC.h"
43#include "lib/CECTypeUtils.h"
44#include "lib/platform/util/timeutils.h"
45#include "lib/platform/util/util.h"
46
47#include "CECAudioSystem.h"
48#include "CECPlaybackDevice.h"
49#include "CECRecordingDevice.h"
50#include "CECTuner.h"
51#include "CECTV.h"
52
53using namespace std;
54using namespace CEC;
55using namespace PLATFORM;
56
57#define LIB_CEC m_processor->GetLib()
58#define ToString(p) CCECTypeUtils::ToString(p)
59
60CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */) :
61 m_type (CEC_DEVICE_TYPE_RESERVED),
62 m_iPhysicalAddress (iPhysicalAddress),
63 m_iStreamPath (CEC_INVALID_PHYSICAL_ADDRESS),
64 m_iLogicalAddress (iLogicalAddress),
65 m_powerStatus (CEC_POWER_STATUS_UNKNOWN),
66 m_processor (processor),
67 m_vendor (CEC_VENDOR_UNKNOWN),
68 m_bReplaceHandler (false),
69 m_menuState (CEC_MENU_STATE_ACTIVATED),
70 m_bActiveSource (false),
71 m_iLastActive (0),
72 m_iLastPowerStateUpdate (0),
73 m_cecVersion (CEC_VERSION_UNKNOWN),
74 m_deviceStatus (CEC_DEVICE_STATUS_UNKNOWN),
75 m_iHandlerUseCount (0),
76 m_bAwaitingReceiveFailed(false),
77 m_bVendorIdRequested (false),
78 m_waitForResponse (new CWaitForResponse),
79 m_bImageViewOnSent (false)
80{
81 m_handler = new CCECCommandHandler(this);
82
83 for (unsigned int iPtr = 0; iPtr < 4; iPtr++)
84 m_menuLanguage.language[iPtr] = '?';
85 m_menuLanguage.language[3] = 0;
86 m_menuLanguage.device = iLogicalAddress;
87
88 m_strDeviceName = ToString(m_iLogicalAddress);
89}
90
91CCECBusDevice::~CCECBusDevice(void)
92{
93 DELETE_AND_NULL(m_handler);
94 DELETE_AND_NULL(m_waitForResponse);
95}
96
97bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
98{
99 if (m_iLogicalAddress == CECDEVICE_BROADCAST)
100 return false;
101
102 bool bInitHandler(false);
103 {
104 CLockObject lock(m_mutex);
105 CLockObject handlerLock(m_handlerMutex);
106 if (m_iHandlerUseCount > 0)
107 return false;
108
109 MarkBusy();
110
111 if (m_vendor != m_handler->GetVendorId())
112 {
113 if (CCECCommandHandler::HasSpecificHandler(m_vendor))
114 {
115 LIB_CEC->AddLog(CEC_LOG_DEBUG, "replacing the command handler for device '%s' (%x)", GetLogicalAddressName(), GetLogicalAddress());
116
117 int32_t iTransmitTimeout = m_handler->m_iTransmitTimeout;
118 int32_t iTransmitWait = m_handler->m_iTransmitWait;
119 int8_t iTransmitRetries = m_handler->m_iTransmitRetries;
120 int64_t iActiveSourcePending = m_handler->m_iActiveSourcePending;
121
122 DELETE_AND_NULL(m_handler);
123
124 switch (m_vendor)
125 {
126 case CEC_VENDOR_SAMSUNG:
127 m_handler = new CANCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
128 break;
129 case CEC_VENDOR_LG:
130 m_handler = new CSLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
131 break;
132 case CEC_VENDOR_PANASONIC:
133 m_handler = new CVLCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
134 break;
135 default:
136 m_handler = new CCECCommandHandler(this, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending);
137 break;
138 }
139
140 m_handler->SetVendorId(m_vendor);
141 bInitHandler = true;
142 }
143 }
144 }
145
146 if (bInitHandler)
147 {
148 CCECBusDevice *primary = GetProcessor()->GetPrimaryDevice();
149 if (primary->GetLogicalAddress() != CECDEVICE_UNREGISTERED)
150 {
151 m_handler->InitHandler();
152
153 if (bActivateSource && IsHandledByLibCEC() && IsActiveSource())
154 m_handler->ActivateSource();
155 }
156 }
157
158 MarkReady();
159
160 return true;
161}
162
163CCECCommandHandler *CCECBusDevice::GetHandler(void)
164{
165 ReplaceHandler(false);
166 MarkBusy();
167 return m_handler;
168}
169
170bool CCECBusDevice::HandleCommand(const cec_command &command)
171{
172 bool bHandled(false);
173
174 /* update "last active" */
175 {
176 CLockObject lock(m_mutex);
177 m_iLastActive = GetTimeMs();
178 MarkBusy();
179 }
180
181 /* handle the command */
182 bHandled = m_handler->HandleCommand(command);
183
184 /* change status to present */
185 if (bHandled && GetLogicalAddress() != CECDEVICE_BROADCAST)
186 {
187 CLockObject lock(m_mutex);
188 if (m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
189 {
190 if (m_deviceStatus != CEC_DEVICE_STATUS_PRESENT)
191 LIB_CEC->AddLog(CEC_LOG_DEBUG, "device %s (%x) status changed to present after command %s", GetLogicalAddressName(), (uint8_t)GetLogicalAddress(), ToString(command.opcode));
192 m_deviceStatus = CEC_DEVICE_STATUS_PRESENT;
193 }
194 }
195
196 MarkReady();
197 return bHandled;
198}
199
200const char* CCECBusDevice::GetLogicalAddressName(void) const
201{
202 return ToString(m_iLogicalAddress);
203}
204
205bool CCECBusDevice::IsPresent(void)
206{
207 CLockObject lock(m_mutex);
208 return m_deviceStatus == CEC_DEVICE_STATUS_PRESENT;
209}
210
211bool CCECBusDevice::IsHandledByLibCEC(void)
212{
213 CLockObject lock(m_mutex);
214 return m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC;
215}
216
217void CCECBusDevice::SetUnsupportedFeature(cec_opcode opcode)
218{
219 // some commands should never be marked as unsupported
220 if (opcode == CEC_OPCODE_VENDOR_COMMAND ||
221 opcode == CEC_OPCODE_VENDOR_COMMAND_WITH_ID ||
222 opcode == CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN ||
223 opcode == CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP ||
224 opcode == CEC_OPCODE_ABORT ||
225 opcode == CEC_OPCODE_FEATURE_ABORT ||
226 opcode == CEC_OPCODE_NONE)
227 return;
228
229 {
230 CLockObject lock(m_mutex);
231 if (m_unsupportedFeatures.find(opcode) == m_unsupportedFeatures.end())
232 {
233 LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking opcode '%s' as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
234 m_unsupportedFeatures.insert(opcode);
235 }
236 }
237
238 // signal threads that are waiting for a reponse
239 MarkBusy();
240 SignalOpcode(cec_command::GetResponseOpcode(opcode));
241 MarkReady();
242}
243
244bool CCECBusDevice::IsUnsupportedFeature(cec_opcode opcode)
245{
246 CLockObject lock(m_mutex);
247 bool bUnsupported = (m_unsupportedFeatures.find(opcode) != m_unsupportedFeatures.end());
248 if (bUnsupported)
249 LIB_CEC->AddLog(CEC_LOG_DEBUG, "'%s' is marked as unsupported feature for device '%s'", ToString(opcode), GetLogicalAddressName());
250 return bUnsupported;
251}
252
253bool CCECBusDevice::TransmitKeypress(const cec_logical_address initiator, cec_user_control_code key, bool bWait /* = true */)
254{
255 MarkBusy();
256 bool bReturn = m_handler->TransmitKeypress(initiator, m_iLogicalAddress, key, bWait);
257 MarkReady();
258 return bReturn;
259}
260
261bool CCECBusDevice::TransmitKeyRelease(const cec_logical_address initiator, bool bWait /* = true */)
262{
263 MarkBusy();
264 bool bReturn = m_handler->TransmitKeyRelease(initiator, m_iLogicalAddress, bWait);
265 MarkReady();
266 return bReturn;
267}
268
269cec_version CCECBusDevice::GetCecVersion(const cec_logical_address initiator, bool bUpdate /* = false */)
270{
271 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
272 bool bRequestUpdate(false);
273 {
274 CLockObject lock(m_mutex);
275 bRequestUpdate = bIsPresent &&
276 (bUpdate || m_cecVersion == CEC_VERSION_UNKNOWN);
277 }
278
279 if (bRequestUpdate)
280 {
281 CheckVendorIdRequested(initiator);
282 RequestCecVersion(initiator);
283 }
284
285 CLockObject lock(m_mutex);
286 return m_cecVersion;
287}
288
289void CCECBusDevice::SetCecVersion(const cec_version newVersion)
290{
291 CLockObject lock(m_mutex);
292 if (m_cecVersion != newVersion)
293 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): CEC version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(newVersion));
294 m_cecVersion = newVersion;
295}
296
297bool CCECBusDevice::RequestCecVersion(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
298{
299 bool bReturn(false);
300
301 if (!IsHandledByLibCEC() &&
302 !IsUnsupportedFeature(CEC_OPCODE_GET_CEC_VERSION))
303 {
304 MarkBusy();
305 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting CEC version of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
306 bReturn = m_handler->TransmitRequestCecVersion(initiator, m_iLogicalAddress, bWaitForResponse);
307 MarkReady();
308 }
309 return bReturn;
310}
311
312bool CCECBusDevice::TransmitCECVersion(const cec_logical_address destination, bool bIsReply)
313{
314 cec_version version;
315 {
316 CLockObject lock(m_mutex);
317 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): cec version %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString(m_cecVersion));
318 version = m_cecVersion;
319 }
320
321 MarkBusy();
322 bool bReturn = m_handler->TransmitCECVersion(m_iLogicalAddress, destination, version, bIsReply);
323 MarkReady();
324 return bReturn;
325}
326
327cec_menu_language &CCECBusDevice::GetMenuLanguage(const cec_logical_address initiator, bool bUpdate /* = false */)
328{
329 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
330 bool bRequestUpdate(false);
331 {
332 CLockObject lock(m_mutex);
333 bRequestUpdate = (bIsPresent &&
334 (bUpdate || !strcmp(m_menuLanguage.language, "???")));
335 }
336
337 if (bRequestUpdate)
338 {
339 CheckVendorIdRequested(initiator);
340 RequestMenuLanguage(initiator);
341 }
342
343 CLockObject lock(m_mutex);
344 return m_menuLanguage;
345}
346
347void CCECBusDevice::SetMenuLanguage(const char *strLanguage)
348{
349 if (!strLanguage)
350 return;
351
352 CLockObject lock(m_mutex);
353 if (strcmp(strLanguage, m_menuLanguage.language))
354 {
355 memcpy(m_menuLanguage.language, strLanguage, 3);
356 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): menu language set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, m_menuLanguage.language);
357 }
358}
359
360void CCECBusDevice::SetMenuLanguage(const cec_menu_language &language)
361{
362 if (language.device == m_iLogicalAddress)
363 SetMenuLanguage(language.language);
364}
365
366bool CCECBusDevice::RequestMenuLanguage(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
367{
368 bool bReturn(false);
369
370 if (!IsHandledByLibCEC() &&
371 !IsUnsupportedFeature(CEC_OPCODE_GET_MENU_LANGUAGE))
372 {
373 MarkBusy();
374 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting menu language of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
375 bReturn = m_handler->TransmitRequestMenuLanguage(initiator, m_iLogicalAddress, bWaitForResponse);
376 MarkReady();
377 }
378 return bReturn;
379}
380
381bool CCECBusDevice::TransmitSetMenuLanguage(const cec_logical_address destination, bool bIsReply)
382{
383 bool bReturn(false);
384 cec_menu_language language;
385 {
386 CLockObject lock(m_mutex);
387 language = m_menuLanguage;
388 }
389
390 char lang[3];
391 {
392 CLockObject lock(m_mutex);
393 lang[0] = language.language[0];
394 lang[1] = language.language[1];
395 lang[2] = language.language[2];
396 }
397
398 MarkBusy();
399 if (lang[0] == '?' && lang[1] == '?' && lang[2] == '?')
400 {
401 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): menu language feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination);
402 m_processor->TransmitAbort(m_iLogicalAddress, destination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
403 bReturn = true;
404 }
405 else
406 {
407 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> broadcast (F): menu language '%s'", GetLogicalAddressName(), m_iLogicalAddress, lang);
408 bReturn = m_handler->TransmitSetMenuLanguage(m_iLogicalAddress, lang, bIsReply);
409 }
410 MarkReady();
411 return bReturn;
412}
413
414bool CCECBusDevice::TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage, bool bIsReply)
415{
416 bool bReturn(false);
417 if (!m_processor->GetDevice(destination)->IsUnsupportedFeature(CEC_OPCODE_SET_OSD_STRING))
418 {
419 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): display OSD message '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, strMessage);
420 MarkBusy();
421 bReturn = m_handler->TransmitOSDString(m_iLogicalAddress, destination, duration, strMessage, bIsReply);
422 MarkReady();
423 }
424 return bReturn;
425}
426
427CStdString CCECBusDevice::GetCurrentOSDName(void)
428{
429 CLockObject lock(m_mutex);
430 return m_strDeviceName;
431}
432
433CStdString CCECBusDevice::GetOSDName(const cec_logical_address initiator, bool bUpdate /* = false */)
434{
435 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
436 bool bRequestUpdate(false);
437 {
438 CLockObject lock(m_mutex);
439 bRequestUpdate = bIsPresent &&
440 (bUpdate || m_strDeviceName.Equals(ToString(m_iLogicalAddress))) &&
441 m_type != CEC_DEVICE_TYPE_TV;
442 }
443
444 if (bRequestUpdate)
445 {
446 CheckVendorIdRequested(initiator);
447 RequestOSDName(initiator);
448 }
449
450 CLockObject lock(m_mutex);
451 return m_strDeviceName;
452}
453
454void CCECBusDevice::SetOSDName(CStdString strName)
455{
456 CLockObject lock(m_mutex);
457 if (m_strDeviceName != strName)
458 {
459 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): osd name set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, strName.c_str());
460 m_strDeviceName = strName;
461 }
462}
463
464bool CCECBusDevice::RequestOSDName(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
465{
466 bool bReturn(false);
467
468 if (!IsHandledByLibCEC() &&
469 !IsUnsupportedFeature(CEC_OPCODE_GIVE_OSD_NAME))
470 {
471 MarkBusy();
472 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting OSD name of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
473 bReturn = m_handler->TransmitRequestOSDName(initiator, m_iLogicalAddress, bWaitForResponse);
474 MarkReady();
475 }
476 return bReturn;
477}
478
479bool CCECBusDevice::TransmitOSDName(const cec_logical_address destination, bool bIsReply)
480{
481 CStdString strDeviceName;
482 {
483 CLockObject lock(m_mutex);
484 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): OSD name '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, m_strDeviceName.c_str());
485 strDeviceName = m_strDeviceName;
486 }
487
488 MarkBusy();
489 bool bReturn = m_handler->TransmitOSDName(m_iLogicalAddress, destination, strDeviceName, bIsReply);
490 MarkReady();
491 return bReturn;
492}
493
494bool CCECBusDevice::HasValidPhysicalAddress(void)
495{
496 CLockObject lock(m_mutex);
497 return CLibCEC::IsValidPhysicalAddress(m_iPhysicalAddress);
498}
499
500uint16_t CCECBusDevice::GetCurrentPhysicalAddress(void)
501{
502 CLockObject lock(m_mutex);
503 return m_iPhysicalAddress;
504}
505
506uint16_t CCECBusDevice::GetPhysicalAddress(const cec_logical_address initiator, bool bSuppressUpdate /* = false */)
507{
508 if (!bSuppressUpdate)
509 {
510 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
511 bool bRequestUpdate(false);
512 {
513 CLockObject lock(m_mutex);
514 bRequestUpdate = bIsPresent && m_iPhysicalAddress == CEC_INVALID_PHYSICAL_ADDRESS;
515 }
516
517 if (bRequestUpdate)
518 {
519 CheckVendorIdRequested(initiator);
520 if (!RequestPhysicalAddress(initiator))
521 LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to request the physical address");
522 }
523 }
524
525 CLockObject lock(m_mutex);
526 return m_iPhysicalAddress;
527}
528
529bool CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress)
530{
531 CLockObject lock(m_mutex);
532 if (iNewAddress > 0 && m_iPhysicalAddress != iNewAddress)
533 {
534 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): physical address changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress, iNewAddress);
535 m_iPhysicalAddress = iNewAddress;
536
537 if (m_processor->GetDevices()->GetActiveSourceAddress() == iNewAddress)
538 MarkAsActiveSource();
539 }
540 return true;
541}
542
543bool CCECBusDevice::RequestPhysicalAddress(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
544{
545 bool bReturn(false);
546
547 if (!IsHandledByLibCEC())
548 {
549 MarkBusy();
550 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting physical address of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
551 bReturn = m_handler->TransmitRequestPhysicalAddress(initiator, m_iLogicalAddress, bWaitForResponse);
552 MarkReady();
553 }
554 return bReturn;
555}
556
557bool CCECBusDevice::TransmitPhysicalAddress(bool bIsReply)
558{
559 uint16_t iPhysicalAddress;
560 cec_device_type type;
561 {
562 CLockObject lock(m_mutex);
563 if (m_iPhysicalAddress == CEC_INVALID_PHYSICAL_ADDRESS)
564 return false;
565
566 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> broadcast (F): physical adddress %4x", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
567 iPhysicalAddress = m_iPhysicalAddress;
568 type = m_type;
569 }
570
571 MarkBusy();
572 bool bReturn = m_handler->TransmitPhysicalAddress(m_iLogicalAddress, iPhysicalAddress, type, bIsReply);
573 MarkReady();
574 return bReturn;
575}
576
577cec_power_status CCECBusDevice::GetCurrentPowerStatus(void)
578{
579 CLockObject lock(m_mutex);
580 return m_powerStatus;
581}
582
583cec_power_status CCECBusDevice::GetPowerStatus(const cec_logical_address initiator, bool bUpdate /* = false */)
584{
585 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
586 bool bRequestUpdate(false);
587 {
588 CLockObject lock(m_mutex);
589 bRequestUpdate = (bIsPresent &&
590 (bUpdate || m_powerStatus == CEC_POWER_STATUS_UNKNOWN ||
591 m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON ||
592 m_powerStatus == CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY ||
593 GetTimeMs() - m_iLastPowerStateUpdate >= CEC_POWER_STATE_REFRESH_TIME));
594 }
595
596 if (bRequestUpdate)
597 {
598 CheckVendorIdRequested(initiator);
599 RequestPowerStatus(initiator);
600 }
601
602 CLockObject lock(m_mutex);
603 return m_powerStatus;
604}
605
606void CCECBusDevice::SetPowerStatus(const cec_power_status powerStatus)
607{
608 CLockObject lock(m_mutex);
609 if (m_powerStatus != powerStatus)
610 {
611 m_iLastPowerStateUpdate = GetTimeMs();
612 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(powerStatus));
613 m_powerStatus = powerStatus;
614 }
615}
616
617void CCECBusDevice::OnImageViewOnSent(bool bSentByLibCEC)
618{
619 CLockObject lock(m_mutex);
620 m_bImageViewOnSent = bSentByLibCEC;
621
622 if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
623 {
624 m_iLastPowerStateUpdate = GetTimeMs();
625 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): power status changed from '%s' to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_powerStatus), ToString(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON));
626 m_powerStatus = CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON;
627 }
628}
629
630bool CCECBusDevice::ImageViewOnSent(void)
631{
632 CLockObject lock(m_mutex);
633 return m_bImageViewOnSent;
634}
635
636bool CCECBusDevice::RequestPowerStatus(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
637{
638 bool bReturn(false);
639
640 if (!IsHandledByLibCEC() &&
641 !IsUnsupportedFeature(CEC_OPCODE_GIVE_DEVICE_POWER_STATUS))
642 {
643 MarkBusy();
644 bReturn = m_handler->TransmitRequestPowerStatus(initiator, m_iLogicalAddress, bWaitForResponse);
645 MarkReady();
646 }
647 return bReturn;
648}
649
650bool CCECBusDevice::TransmitPowerState(const cec_logical_address destination, bool bIsReply)
651{
652 cec_power_status state;
653 {
654 CLockObject lock(m_mutex);
655 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): %s", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString(m_powerStatus));
656 state = m_powerStatus;
657 }
658
659 MarkBusy();
660 bool bReturn = m_handler->TransmitPowerState(m_iLogicalAddress, destination, state, bIsReply);
661 MarkReady();
662 return bReturn;
663}
664
665cec_vendor_id CCECBusDevice::GetCurrentVendorId(void)
666{
667 CLockObject lock(m_mutex);
668 return m_vendor;
669}
670
671cec_vendor_id CCECBusDevice::GetVendorId(const cec_logical_address initiator, bool bUpdate /* = false */)
672{
673 bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT);
674 bool bRequestUpdate(false);
675 {
676 CLockObject lock(m_mutex);
677 bRequestUpdate = (bIsPresent &&
678 (bUpdate || m_vendor == CEC_VENDOR_UNKNOWN));
679 }
680
681 if (bRequestUpdate)
682 RequestVendorId(initiator);
683
684 CLockObject lock(m_mutex);
685 return m_vendor;
686}
687
688const char *CCECBusDevice::GetVendorName(const cec_logical_address initiator, bool bUpdate /* = false */)
689{
690 return ToString(GetVendorId(initiator, bUpdate));
691}
692
693bool CCECBusDevice::SetVendorId(uint64_t iVendorId)
694{
695 bool bVendorChanged(false);
696
697 {
698 CLockObject lock(m_mutex);
699 bVendorChanged = (m_vendor != (cec_vendor_id)iVendorId);
700 m_vendor = (cec_vendor_id)iVendorId;
701 }
702
703 if (bVendorChanged)
704 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): vendor = %s (%06x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_vendor), m_vendor);
705
706 return bVendorChanged;
707}
708
709bool CCECBusDevice::RequestVendorId(const cec_logical_address initiator, bool bWaitForResponse /* = true */)
710{
711 bool bReturn(false);
712
713 if (!IsHandledByLibCEC() && initiator != CECDEVICE_UNKNOWN)
714 {
715 MarkBusy();
716 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting vendor ID of '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
717 bReturn = m_handler->TransmitRequestVendorId(initiator, m_iLogicalAddress, bWaitForResponse);
718 MarkReady();
719
720 if (bWaitForResponse)
721 ReplaceHandler(true);
722 }
723 return bReturn;
724}
725
726bool CCECBusDevice::TransmitVendorID(const cec_logical_address destination, bool bSendAbort, bool bIsReply)
727{
728 bool bReturn(false);
729 uint64_t iVendorId;
730 {
731 CLockObject lock(m_mutex);
732 iVendorId = (uint64_t)m_vendor;
733 }
734
735 MarkBusy();
736 if (iVendorId == CEC_VENDOR_UNKNOWN)
737 {
738 if (bSendAbort)
739 {
740 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): vendor id feature abort", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination);
741 m_processor->TransmitAbort(m_iLogicalAddress, destination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
742 bReturn = true;
743 }
744 }
745 else
746 {
747 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): vendor id %s (%x)", GetLogicalAddressName(), m_iLogicalAddress, ToString(destination), destination, ToString((cec_vendor_id)iVendorId), iVendorId);
748 bReturn = m_handler->TransmitVendorID(m_iLogicalAddress, iVendorId, bIsReply);
749 }
750 MarkReady();
751 return bReturn;
752}
753
754cec_bus_device_status CCECBusDevice::GetStatus(bool bForcePoll /* = false */, bool bSuppressPoll /* = false */)
755{
756 if (m_iLogicalAddress == CECDEVICE_BROADCAST)
757 return CEC_DEVICE_STATUS_NOT_PRESENT;
758
759 cec_bus_device_status status(CEC_DEVICE_STATUS_UNKNOWN);
760 bool bNeedsPoll(false);
761
762 {
763 CLockObject lock(m_mutex);
764 status = m_deviceStatus;
765 bNeedsPoll = !bSuppressPoll &&
766 m_deviceStatus != CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC &&
767 // poll forced
768 (bForcePoll ||
769 // don't know the status
770 m_deviceStatus == CEC_DEVICE_STATUS_UNKNOWN ||
771 // always poll the TV if it's marked as not present
772 (m_deviceStatus == CEC_DEVICE_STATUS_NOT_PRESENT && m_iLogicalAddress == CECDEVICE_TV));
773 }
774
775 if (bNeedsPoll)
776 {
777 bool bPollAcked(false);
778 if (bNeedsPoll && NeedsPoll())
779 bPollAcked = m_processor->PollDevice(m_iLogicalAddress);
780
781 status = bPollAcked ? CEC_DEVICE_STATUS_PRESENT : CEC_DEVICE_STATUS_NOT_PRESENT;
782 SetDeviceStatus(status);
783 }
784
785 return status;
786}
787
788void CCECBusDevice::SetDeviceStatus(const cec_bus_device_status newStatus, cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
789{
790 if (m_iLogicalAddress == CECDEVICE_UNREGISTERED)
791 return;
792
793 {
794 CLockObject lock(m_mutex);
795 switch (newStatus)
796 {
797 case CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC:
798 if (m_deviceStatus != newStatus)
799 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'handled by libCEC'", GetLogicalAddressName(), m_iLogicalAddress);
800 SetPowerStatus (CEC_POWER_STATUS_ON);
801 SetVendorId (CEC_VENDOR_UNKNOWN);
802 SetMenuState (CEC_MENU_STATE_ACTIVATED);
803 SetCecVersion (libCECSpecVersion);
804 SetStreamPath (CEC_INVALID_PHYSICAL_ADDRESS);
805 MarkAsInactiveSource();
806 m_iLastActive = 0;
807 m_deviceStatus = newStatus;
808 break;
809 case CEC_DEVICE_STATUS_PRESENT:
810 if (m_deviceStatus != newStatus)
811 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'present'", GetLogicalAddressName(), m_iLogicalAddress);
812 m_deviceStatus = newStatus;
813 m_iLastActive = GetTimeMs();
814 break;
815 case CEC_DEVICE_STATUS_NOT_PRESENT:
816 if (m_deviceStatus != newStatus)
817 {
818 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'not present'", GetLogicalAddressName(), m_iLogicalAddress);
819 ResetDeviceStatus(true);
820 m_deviceStatus = newStatus;
821 }
822 break;
823 default:
824 ResetDeviceStatus();
825 break;
826 }
827 }
828}
829
830void CCECBusDevice::ResetDeviceStatus(bool bClientUnregistered /* = false */)
831{
832 CLockObject lock(m_mutex);
833 SetPowerStatus (CEC_POWER_STATUS_UNKNOWN);
834 SetVendorId (CEC_VENDOR_UNKNOWN);
835 SetMenuState (CEC_MENU_STATE_ACTIVATED);
836 SetCecVersion (CEC_VERSION_UNKNOWN);
837 SetStreamPath (CEC_INVALID_PHYSICAL_ADDRESS);
838 SetOSDName (ToString(m_iLogicalAddress));
839 MarkAsInactiveSource(bClientUnregistered);
840
841 m_iLastActive = 0;
842 m_bVendorIdRequested = false;
843 m_unsupportedFeatures.clear();
844 m_waitForResponse->Clear();
845
846 if (m_deviceStatus != CEC_DEVICE_STATUS_UNKNOWN)
847 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): device status changed into 'unknown'", GetLogicalAddressName(), m_iLogicalAddress);
848 m_deviceStatus = CEC_DEVICE_STATUS_UNKNOWN;
849}
850
851bool CCECBusDevice::TransmitPoll(const cec_logical_address dest, bool bUpdateDeviceStatus)
852{
853 bool bReturn(false);
854 cec_logical_address destination(dest);
855 if (destination == CECDEVICE_UNKNOWN)
856 destination = m_iLogicalAddress;
857
858 CCECBusDevice *destDevice = m_processor->GetDevice(destination);
859 if (destDevice->m_deviceStatus == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC)
860 return bReturn;
861
862 MarkBusy();
863 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): POLL", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest);
864 bReturn = m_handler->TransmitPoll(m_iLogicalAddress, destination, false);
865 LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? ">> POLL sent" : ">> POLL not sent");
866
867 if (bUpdateDeviceStatus)
868 destDevice->SetDeviceStatus(bReturn ? CEC_DEVICE_STATUS_PRESENT : CEC_DEVICE_STATUS_NOT_PRESENT);
869
870 MarkReady();
871 return bReturn;
872}
873
874void CCECBusDevice::HandlePoll(const cec_logical_address destination)
875{
876 if (destination >= 0 && destination < CECDEVICE_BROADCAST)
877 {
878 CCECBusDevice *device = m_processor->GetDevice(destination);
879 if (device)
880 device->HandlePollFrom(m_iLogicalAddress);
881 }
882}
883
884void CCECBusDevice::HandlePollFrom(const cec_logical_address initiator)
885{
886 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< POLL: %s (%x) -> %s (%x)", ToString(initiator), initiator, ToString(m_iLogicalAddress), m_iLogicalAddress);
887 m_bAwaitingReceiveFailed = true;
888}
889
890bool CCECBusDevice::HandleReceiveFailed(void)
891{
892 bool bReturn = m_bAwaitingReceiveFailed;
893 m_bAwaitingReceiveFailed = false;
894 return bReturn;
895}
896
897cec_menu_state CCECBusDevice::GetMenuState(const cec_logical_address UNUSED(initiator))
898{
899 CLockObject lock(m_mutex);
900 return m_menuState;
901}
902
903void CCECBusDevice::SetMenuState(const cec_menu_state state)
904{
905 CLockObject lock(m_mutex);
906 if (m_menuState != state)
907 {
908 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): menu state set to '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(m_menuState));
909 m_menuState = state;
910 }
911}
912
913bool CCECBusDevice::TransmitMenuState(const cec_logical_address dest, bool bIsReply)
914{
915 cec_menu_state menuState;
916 {
917 CLockObject lock(m_mutex);
918 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) -> %s (%X): menu state '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(dest), dest, ToString(m_menuState));
919 menuState = m_menuState;
920 }
921
922 MarkBusy();
923 bool bReturn = m_handler->TransmitMenuState(m_iLogicalAddress, dest, menuState, bIsReply);
924 MarkReady();
925 return bReturn;
926}
927
928bool CCECBusDevice::ActivateSource(uint64_t iDelay /* = 0 */)
929{
930 MarkAsActiveSource();
931 MarkBusy();
932 bool bReturn(true);
933 if (iDelay == 0)
934 {
935 LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending active source message for '%s'", ToString(m_iLogicalAddress));
936 bReturn = m_handler->ActivateSource();
937 }
938 else
939 {
940 LIB_CEC->AddLog(CEC_LOG_DEBUG, "scheduling active source message for '%s'", ToString(m_iLogicalAddress));
941 m_handler->ScheduleActivateSource(iDelay);
942 }
943 MarkReady();
944 return bReturn;
945}
946
947bool CCECBusDevice::RequestActiveSource(bool bWaitForResponse /* = true */)
948{
949 bool bReturn(false);
950
951 if (IsHandledByLibCEC())
952 {
953 MarkBusy();
954 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< requesting active source");
955
956 bReturn = m_handler->TransmitRequestActiveSource(m_iLogicalAddress, bWaitForResponse);
957 MarkReady();
958 }
959 return bReturn;
960}
961
962void CCECBusDevice::MarkAsActiveSource(void)
963{
964 bool bWasActivated(false);
965
966 // set the power status to powered on
967 SetPowerStatus(CEC_POWER_STATUS_ON);
968
969 // mark this device as active source
970 {
971 CLockObject lock(m_mutex);
972 if (!m_bActiveSource)
973 {
974 LIB_CEC->AddLog(CEC_LOG_DEBUG, "making %s (%x) the active source", GetLogicalAddressName(), m_iLogicalAddress);
975 bWasActivated = true;
976 }
977 else
978 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%x) was already marked as active source", GetLogicalAddressName(), m_iLogicalAddress);
979
980 m_bActiveSource = true;
981 }
982
983 CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
984 if (tv)
985 tv->OnImageViewOnSent(false);
986
987 // mark other devices as inactive sources
988 CECDEVICEVEC devices;
989 m_processor->GetDevices()->Get(devices);
990 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
991 if ((*it)->GetLogicalAddress() != m_iLogicalAddress)
992 (*it)->MarkAsInactiveSource();
993
994 if (bWasActivated)
995 {
996 if (IsHandledByLibCEC())
997 m_processor->SetActiveSource(true, false);
998 CCECClient *client = GetClient();
999 if (client)
1000 client->SourceActivated(m_iLogicalAddress);
1001 }
1002}
1003
1004void CCECBusDevice::MarkAsInactiveSource(bool bClientUnregistered /* = false */)
1005{
1006 bool bWasDeactivated(false);
1007 {
1008 CLockObject lock(m_mutex);
1009 if (m_bActiveSource)
1010 {
1011 LIB_CEC->AddLog(CEC_LOG_DEBUG, "marking %s (%X) as inactive source", GetLogicalAddressName(), m_iLogicalAddress);
1012 bWasDeactivated = true;
1013 }
1014 m_bActiveSource = false;
1015 }
1016
1017 if (bWasDeactivated)
1018 {
1019 if (IsHandledByLibCEC())
1020 m_processor->SetActiveSource(false, bClientUnregistered);
1021 CCECClient *client = GetClient();
1022 if (client)
1023 client->SourceDeactivated(m_iLogicalAddress);
1024 }
1025}
1026
1027bool CCECBusDevice::TransmitActiveSource(bool bIsReply)
1028{
1029 bool bSendActiveSource(false);
1030 uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS);
1031
1032 {
1033 CLockObject lock(m_mutex);
1034 if (!HasValidPhysicalAddress())
1035 {
1036 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X) has an invalid physical address (%04x), not sending active source commands", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
1037 return false;
1038 }
1039
1040 iPhysicalAddress = m_iPhysicalAddress;
1041
1042 if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
1043 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
1044 else if (m_bActiveSource)
1045 {
1046 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): active source (%4x)", GetLogicalAddressName(), m_iLogicalAddress, m_iPhysicalAddress);
1047 bSendActiveSource = true;
1048 }
1049 else
1050 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not the active source", GetLogicalAddressName(), m_iLogicalAddress);
1051 }
1052
1053 bool bActiveSourceSent(false);
1054 if (bSendActiveSource)
1055 {
1056 MarkBusy();
1057 bActiveSourceSent = m_handler->TransmitActiveSource(m_iLogicalAddress, iPhysicalAddress, bIsReply);
1058 MarkReady();
1059 }
1060
1061 return bActiveSourceSent;
1062}
1063
1064bool CCECBusDevice::TransmitImageViewOn(void)
1065{
1066 {
1067 CLockObject lock(m_mutex);
1068 if (m_powerStatus != CEC_POWER_STATUS_ON && m_powerStatus != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON)
1069 {
1070 LIB_CEC->AddLog(CEC_LOG_DEBUG, "<< %s (%X) is not powered on", GetLogicalAddressName(), m_iLogicalAddress);
1071 return false;
1072 }
1073 }
1074
1075 CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
1076 if (!tv)
1077 {
1078 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - couldn't get TV instance", __FUNCTION__);
1079 return false;
1080 }
1081
1082 if (tv->ImageViewOnSent())
1083 {
1084 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - 'image view on' already sent", __FUNCTION__);
1085 return true;
1086 }
1087
1088 bool bImageViewOnSent(false);
1089 MarkBusy();
1090 bImageViewOnSent = m_handler->TransmitImageViewOn(m_iLogicalAddress, CECDEVICE_TV);
1091 MarkReady();
1092
1093 if (bImageViewOnSent)
1094 tv->OnImageViewOnSent(true);
1095
1096 return bImageViewOnSent;
1097}
1098
1099bool CCECBusDevice::TransmitInactiveSource(void)
1100{
1101 uint16_t iPhysicalAddress;
1102 {
1103 CLockObject lock(m_mutex);
1104 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< %s (%X) -> broadcast (F): inactive source", GetLogicalAddressName(), m_iLogicalAddress);
1105 iPhysicalAddress = m_iPhysicalAddress;
1106 }
1107
1108 MarkBusy();
1109 bool bReturn = m_handler->TransmitInactiveSource(m_iLogicalAddress, iPhysicalAddress);
1110 MarkReady();
1111 return bReturn;
1112}
1113
1114bool CCECBusDevice::TransmitPendingActiveSourceCommands(void)
1115{
1116 MarkBusy();
1117 bool bReturn = m_handler->ActivateSource(true);
1118 MarkReady();
1119 return bReturn;
1120}
1121
1122void CCECBusDevice::SetActiveRoute(uint16_t iRoute)
1123{
1124 CCECDeviceMap* map = m_processor->GetDevices();
1125 if (!map)
1126 return;
1127
1128 CCECBusDevice* previouslyActive = map->GetActiveSource();
1129 if (!previouslyActive)
1130 return;
1131
1132 CECDEVICEVEC devices;
1133 m_processor->GetDevices()->GetChildrenOf(devices, this);
1134
1135 for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++)
1136 {
1137 if (!CCECTypeUtils::PhysicalAddressIsIncluded(iRoute, (*it)->GetCurrentPhysicalAddress()))
1138 (*it)->MarkAsInactiveSource();
1139 }
1140}
1141
1142void CCECBusDevice::SetStreamPath(uint16_t iNewAddress, uint16_t iOldAddress /* = CEC_INVALID_PHYSICAL_ADDRESS */)
1143{
1144 CLockObject lock(m_mutex);
1145 if (iNewAddress != m_iStreamPath)
1146 {
1147 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s (%X): stream path changed from %04x to %04x", GetLogicalAddressName(), m_iLogicalAddress, iOldAddress == 0 ? m_iStreamPath : iOldAddress, iNewAddress);
1148 m_iStreamPath = iNewAddress;
1149 }
1150
1151 if (!LIB_CEC->IsValidPhysicalAddress(iNewAddress))
1152 return;
1153
1154 CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
1155 if (device)
1156 {
1157 // if a device is found with the new physical address, mark it as active, which will automatically mark all other devices as inactive
1158 device->MarkAsActiveSource();
1159
1160 // respond with an active source message if this device is handled by libCEC
1161 if (device->IsHandledByLibCEC())
1162 device->TransmitActiveSource(true);
1163 }
1164 else
1165 {
1166 // try to find the device with the old address, and mark it as inactive when found
1167 device = m_processor->GetDeviceByPhysicalAddress(iOldAddress);
1168 if (device)
1169 device->MarkAsInactiveSource();
1170 }
1171}
1172
1173bool CCECBusDevice::PowerOn(const cec_logical_address initiator)
1174{
1175 bool bReturn(false);
1176 GetVendorId(initiator); // ensure that we got the vendor id, because the implementations vary per vendor
1177
1178 MarkBusy();
1179 cec_power_status currentStatus;
1180 if (m_iLogicalAddress == CECDEVICE_TV ||
1181 ((currentStatus = GetPowerStatus(initiator, false)) != CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON &&
1182 currentStatus != CEC_POWER_STATUS_ON))
1183 {
1184 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< powering on '%s' (%X)", GetLogicalAddressName(), m_iLogicalAddress);
1185 bReturn = m_handler->PowerOn(initiator, m_iLogicalAddress);
1186 }
1187 else
1188 {
1189 LIB_CEC->AddLog(CEC_LOG_DEBUG, "'%s' (%X) is already '%s'", GetLogicalAddressName(), m_iLogicalAddress, ToString(currentStatus));
1190 }
1191
1192 MarkReady();
1193 return bReturn;
1194}
1195
1196bool CCECBusDevice::Standby(const cec_logical_address initiator)
1197{
1198 GetVendorId(initiator); // ensure that we got the vendor id, because the implementations vary per vendor
1199
1200 LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< putting '%s' (%X) in standby mode", GetLogicalAddressName(), m_iLogicalAddress);
1201 MarkBusy();
1202 bool bReturn = m_handler->TransmitStandby(initiator, m_iLogicalAddress);
1203 MarkReady();
1204 return bReturn;
1205}
1206
1207bool CCECBusDevice::NeedsPoll(void)
1208{
1209 bool bSendPoll(false);
1210 cec_logical_address pollAddress(CECDEVICE_UNKNOWN);
1211 switch (m_iLogicalAddress)
1212 {
1213 case CECDEVICE_PLAYBACKDEVICE3:
1214 pollAddress = CECDEVICE_PLAYBACKDEVICE2;
1215 break;
1216 case CECDEVICE_PLAYBACKDEVICE2:
1217 pollAddress = CECDEVICE_PLAYBACKDEVICE1;
1218 break;
1219 case CECDEVICE_RECORDINGDEVICE3:
1220 pollAddress = CECDEVICE_RECORDINGDEVICE2;
1221 break;
1222 case CECDEVICE_RECORDINGDEVICE2:
1223 pollAddress = CECDEVICE_RECORDINGDEVICE1;
1224 break;
1225 case CECDEVICE_TUNER4:
1226 pollAddress = CECDEVICE_TUNER3;
1227 break;
1228 case CECDEVICE_TUNER3:
1229 pollAddress = CECDEVICE_TUNER2;
1230 break;
1231 case CECDEVICE_TUNER2:
1232 pollAddress = CECDEVICE_TUNER1;
1233 break;
1234 case CECDEVICE_AUDIOSYSTEM:
1235 case CECDEVICE_PLAYBACKDEVICE1:
1236 case CECDEVICE_RECORDINGDEVICE1:
1237 case CECDEVICE_TUNER1:
1238 case CECDEVICE_TV:
1239 bSendPoll = true;
1240 break;
1241 default:
1242 break;
1243 }
1244
1245 if (!bSendPoll && pollAddress != CECDEVICE_UNKNOWN)
1246 {
1247 CCECBusDevice *device = m_processor->GetDevice(pollAddress);
1248 if (device)
1249 {
1250 cec_bus_device_status status = device->GetStatus();
1251 bSendPoll = (status == CEC_DEVICE_STATUS_PRESENT || status == CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
1252 }
1253 else
1254 {
1255 bSendPoll = true;
1256 }
1257 }
1258
1259 return bSendPoll;
1260}
1261
1262void CCECBusDevice::CheckVendorIdRequested(const cec_logical_address initiator)
1263{
1264 bool bRequestVendorId(false);
1265 {
1266 CLockObject lock(m_mutex);
1267 bRequestVendorId = !m_bVendorIdRequested;
1268 m_bVendorIdRequested = true;
1269 }
1270
1271 if (bRequestVendorId)
1272 {
1273 ReplaceHandler(false);
1274 GetVendorId(initiator);
1275 }
1276}
1277//@}
1278
1279CCECAudioSystem *CCECBusDevice::AsAudioSystem(void)
1280{
1281 return AsAudioSystem(this);
1282}
1283
1284CCECPlaybackDevice *CCECBusDevice::AsPlaybackDevice(void)
1285{
1286 return AsPlaybackDevice(this);
1287}
1288
1289CCECRecordingDevice *CCECBusDevice::AsRecordingDevice(void)
1290{
1291 return AsRecordingDevice(this);
1292}
1293
1294CCECTuner *CCECBusDevice::AsTuner(void)
1295{
1296 return AsTuner(this);
1297}
1298
1299CCECTV *CCECBusDevice::AsTV(void)
1300{
1301 return AsTV(this);
1302}
1303
1304CCECAudioSystem *CCECBusDevice::AsAudioSystem(CCECBusDevice *device)
1305{
1306 if (device && device->GetType() == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
1307 return static_cast<CCECAudioSystem *>(device);
1308 return NULL;
1309}
1310
1311CCECPlaybackDevice *CCECBusDevice::AsPlaybackDevice(CCECBusDevice *device)
1312{
1313 if (device &&
1314 (device->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
1315 device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE))
1316 return static_cast<CCECPlaybackDevice *>(device);
1317 return NULL;
1318}
1319
1320CCECRecordingDevice *CCECBusDevice::AsRecordingDevice(CCECBusDevice *device)
1321{
1322 if (device && device->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
1323 return static_cast<CCECRecordingDevice *>(device);
1324 return NULL;
1325}
1326
1327CCECTuner *CCECBusDevice::AsTuner(CCECBusDevice *device)
1328{
1329 if (device && device->GetType() == CEC_DEVICE_TYPE_TUNER)
1330 return static_cast<CCECTuner *>(device);
1331 return NULL;
1332}
1333
1334CCECTV *CCECBusDevice::AsTV(CCECBusDevice *device)
1335{
1336 if (device && device->GetType() == CEC_DEVICE_TYPE_TV)
1337 return static_cast<CCECTV *>(device);
1338 return NULL;
1339}
1340
1341void CCECBusDevice::MarkBusy(void)
1342{
1343 CLockObject handlerLock(m_handlerMutex);
1344 ++m_iHandlerUseCount;
1345}
1346
1347void CCECBusDevice::MarkReady(void)
1348{
1349 CLockObject handlerLock(m_handlerMutex);
1350 if (m_iHandlerUseCount > 0)
1351 --m_iHandlerUseCount;
1352}
1353
1354bool CCECBusDevice::TryLogicalAddress(cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
1355{
1356 LIB_CEC->AddLog(CEC_LOG_DEBUG, "trying logical address '%s'", GetLogicalAddressName());
1357
1358 if (!TransmitPoll(m_iLogicalAddress, false))
1359 {
1360 LIB_CEC->AddLog(CEC_LOG_DEBUG, "using logical address '%s'", GetLogicalAddressName());
1361 SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC, libCECSpecVersion);
1362
1363 return true;
1364 }
1365
1366 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address '%s' already taken", GetLogicalAddressName());
1367 SetDeviceStatus(CEC_DEVICE_STATUS_PRESENT);
1368 return false;
1369}
1370
1371CCECClient *CCECBusDevice::GetClient(void)
1372{
1373 return m_processor->GetClient(m_iLogicalAddress);
1374}
1375
1376void CCECBusDevice::SignalOpcode(cec_opcode opcode)
1377{
1378 m_waitForResponse->Received(opcode);
1379}
1380
1381bool CCECBusDevice::WaitForOpcode(cec_opcode opcode)
1382{
1383 return m_waitForResponse->Wait(opcode);
1384}