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