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