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