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