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