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