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