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