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