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