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