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