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