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