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