don't crash LibCecSharp when receiving an alert with a NULL param (like the connectio...
[deb_libcec.git] / src / lib / CECProcessor.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 "CECProcessor.h"
35
36 #include "adapter/AdapterFactory.h"
37 #include "devices/CECBusDevice.h"
38 #include "devices/CECAudioSystem.h"
39 #include "devices/CECPlaybackDevice.h"
40 #include "devices/CECRecordingDevice.h"
41 #include "devices/CECTuner.h"
42 #include "devices/CECTV.h"
43 #include "implementations/CECCommandHandler.h"
44 #include "LibCEC.h"
45 #include "CECClient.h"
46 #include "CECTypeUtils.h"
47 #include "platform/util/timeutils.h"
48 #include "platform/util/util.h"
49
50 using namespace CEC;
51 using namespace std;
52 using namespace PLATFORM;
53
54 #define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
55 #define ACTIVE_SOURCE_CHECK_INTERVAL 500
56 #define TV_PRESENT_CHECK_INTERVAL 30000
57
58 #define ToString(x) CCECTypeUtils::ToString(x)
59
60 CCECStandbyProtection::CCECStandbyProtection(CCECProcessor* processor) :
61 m_processor(processor) {}
62 CCECStandbyProtection::~CCECStandbyProtection(void) {}
63
64 void* CCECStandbyProtection::Process(void)
65 {
66 int64_t last = GetTimeMs();
67 int64_t next;
68 while (!IsStopped())
69 {
70 PLATFORM::CEvent::Sleep(1000);
71
72 next = GetTimeMs();
73
74 // reset the connection if the clock changed
75 if (next < last || next - last > 10000)
76 {
77 libcec_parameter param;
78 param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
79 m_processor->GetLib()->Alert(CEC_ALERT_CONNECTION_LOST, param);
80 break;
81 }
82
83 last = next;
84 }
85 return NULL;
86 }
87
88 CCECProcessor::CCECProcessor(CLibCEC *libcec) :
89 m_bInitialised(false),
90 m_communication(NULL),
91 m_libcec(libcec),
92 m_iStandardLineTimeout(3),
93 m_iRetryLineTimeout(3),
94 m_iLastTransmission(0),
95 m_bMonitor(true),
96 m_addrAllocator(NULL),
97 m_bStallCommunication(false),
98 m_connCheck(NULL)
99 {
100 m_busDevices = new CCECDeviceMap(this);
101 }
102
103 CCECProcessor::~CCECProcessor(void)
104 {
105 m_bStallCommunication = false;
106 DELETE_AND_NULL(m_addrAllocator);
107 Close();
108 DELETE_AND_NULL(m_busDevices);
109 }
110
111 bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
112 {
113 CLockObject lock(m_mutex);
114 // open a connection
115 if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
116 return false;
117
118 // create the processor thread
119 if (!IsRunning())
120 {
121 if (!CreateThread())
122 {
123 m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
124 return false;
125 }
126 }
127
128 return true;
129 }
130
131 void CCECProcessor::Close(void)
132 {
133 // mark as uninitialised
134 SetCECInitialised(false);
135
136 // stop the processor
137 DELETE_AND_NULL(m_connCheck);
138 StopThread(-1);
139 m_inBuffer.Broadcast();
140 StopThread();
141
142 // close the connection
143 DELETE_AND_NULL(m_communication);
144 }
145
146 void CCECProcessor::ResetMembers(void)
147 {
148 // close the connection
149 DELETE_AND_NULL(m_communication);
150
151 // reset the other members to the initial state
152 m_iStandardLineTimeout = 3;
153 m_iRetryLineTimeout = 3;
154 m_iLastTransmission = 0;
155 m_busDevices->ResetDeviceStatus();
156 }
157
158 bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */)
159 {
160 bool bReturn(false);
161 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
162
163 // ensure that a previous connection is closed
164 Close();
165
166 // reset all member to the initial state
167 ResetMembers();
168
169 // check whether the Close() method deleted any previous connection
170 if (m_communication)
171 {
172 m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed");
173 return bReturn;
174 }
175
176 // create a new connection
177 m_communication = CAdapterFactory(this->m_libcec).GetInstance(strPort, iBaudRate);
178
179 // open a new connection
180 unsigned iConnectTry(0);
181 while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false)
182 {
183 m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
184 m_communication->Close();
185 CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT);
186 }
187
188 m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened");
189
190 // mark as initialised
191 SetCECInitialised(true);
192
193 return bReturn;
194 }
195
196 bool CCECProcessor::CECInitialised(void)
197 {
198 CLockObject lock(m_threadMutex);
199 return m_bInitialised;
200 }
201
202 void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */)
203 {
204 {
205 CLockObject lock(m_mutex);
206 m_bInitialised = bSetTo;
207 }
208 if (!bSetTo)
209 UnregisterClients();
210 }
211
212 bool CCECProcessor::TryLogicalAddress(cec_logical_address address, cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */)
213 {
214 // find the device
215 CCECBusDevice *device = m_busDevices->At(address);
216 if (device)
217 {
218 // check if it's already marked as present or used
219 if (device->IsPresent() || device->IsHandledByLibCEC())
220 return false;
221
222 // poll the LA if not
223 return device->TryLogicalAddress(libCECSpecVersion);
224 }
225
226 return false;
227 }
228
229 void CCECProcessor::ReplaceHandlers(void)
230 {
231 if (!CECInitialised())
232 return;
233
234 // check each device
235 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
236 it->second->ReplaceHandler(true);
237 }
238
239 bool CCECProcessor::OnCommandReceived(const cec_command &command)
240 {
241 return m_inBuffer.Push(command);
242 }
243
244 void *CCECProcessor::Process(void)
245 {
246 m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
247
248 if (!m_connCheck)
249 m_connCheck = new CCECStandbyProtection(this);
250 m_connCheck->CreateThread();
251
252 cec_command command; command.Clear();
253 CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_INTERVAL);
254 CTimeout tvPresentCheck(TV_PRESENT_CHECK_INTERVAL);
255
256 // as long as we're not being stopped and the connection is open
257 while (!IsStopped() && m_communication->IsOpen())
258 {
259 // wait for a new incoming command, and process it
260 if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME))
261 ProcessCommand(command);
262
263 if (CECInitialised() && !IsStopped())
264 {
265 // check clients for keypress timeouts
266 m_libcec->CheckKeypressTimeout();
267
268 // check if we need to replace handlers
269 ReplaceHandlers();
270
271 // check whether we need to activate a source, if it failed before
272 if (activeSourceCheck.TimeLeft() == 0)
273 {
274 if (CECInitialised())
275 TransmitPendingActiveSourceCommands();
276 activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_INTERVAL);
277 }
278
279 // check whether the TV is present and responding
280 if (tvPresentCheck.TimeLeft() == 0)
281 {
282 CCECClient *primary = GetPrimaryClient();
283 // only check whether the tv responds to polls when a client is connected and not in monitoring mode
284 if (primary && primary->GetConfiguration()->bMonitorOnly != 1)
285 {
286 if (!m_busDevices->At(CECDEVICE_TV)->IsPresent())
287 {
288 libcec_parameter param;
289 param.paramType = CEC_PARAMETER_TYPE_STRING;
290 param.paramData = (void*)"TV does not respond to CEC polls";
291 primary->Alert(CEC_ALERT_TV_POLL_FAILED, param);
292 }
293 }
294 tvPresentCheck.Init(TV_PRESENT_CHECK_INTERVAL);
295 }
296 }
297 }
298
299 return NULL;
300 }
301
302 bool CCECProcessor::ActivateSource(uint16_t iStreamPath)
303 {
304 bool bReturn(false);
305
306 // find the device with the given PA
307 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
308 // and make it the active source when found
309 if (device)
310 bReturn = device->ActivateSource();
311 else
312 m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
313
314 return bReturn;
315 }
316
317 void CCECProcessor::SetActiveSource(bool bSetTo, bool bClientUnregistered)
318 {
319 if (m_communication)
320 m_communication->SetActiveSource(bSetTo, bClientUnregistered);
321 }
322
323 void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
324 {
325 CLockObject lock(m_mutex);
326 m_iStandardLineTimeout = iTimeout;
327 }
328
329 uint8_t CCECProcessor::GetStandardLineTimeout(void)
330 {
331 CLockObject lock(m_mutex);
332 return m_iStandardLineTimeout;
333 }
334
335 void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
336 {
337 CLockObject lock(m_mutex);
338 m_iRetryLineTimeout = iTimeout;
339 }
340
341 uint8_t CCECProcessor::GetRetryLineTimeout(void)
342 {
343 CLockObject lock(m_mutex);
344 return m_iRetryLineTimeout;
345 }
346
347 bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
348 {
349 CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress);
350 return device != NULL;
351 }
352
353 void CCECProcessor::LogOutput(const cec_command &data)
354 {
355 CStdString strTx;
356
357 // initiator and destination
358 strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
359
360 // append the opcode
361 if (data.opcode_set)
362 strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
363
364 // append the parameters
365 for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
366 strTx.AppendFormat(":%02x", data.parameters[iPtr]);
367
368 // and log it
369 m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
370 }
371
372 bool CCECProcessor::PollDevice(cec_logical_address iAddress)
373 {
374 // try to find the primary device
375 CCECBusDevice *primary = GetPrimaryDevice();
376 // poll the destination, with the primary as source
377 if (primary)
378 return primary->TransmitPoll(iAddress, true);
379
380 CCECBusDevice *device = m_busDevices->At(CECDEVICE_UNREGISTERED);
381 if (device)
382 return device->TransmitPoll(iAddress, true);
383
384 return false;
385 }
386
387 CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
388 {
389 return m_busDevices ?
390 m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) :
391 NULL;
392 }
393
394 CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const
395 {
396 return m_busDevices ?
397 m_busDevices->At(address) :
398 NULL;
399 }
400
401 cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */)
402 {
403 // get the device that is marked as active source from the device map
404 CCECBusDevice *activeSource = m_busDevices->GetActiveSource();
405 if (activeSource)
406 return activeSource->GetLogicalAddress();
407
408 if (bRequestActiveSource)
409 {
410 // request the active source from the bus
411 CCECBusDevice *primary = GetPrimaryDevice();
412 if (primary)
413 {
414 primary->RequestActiveSource();
415 return GetActiveSource(false);
416 }
417 }
418
419 // unknown or none
420 return CECDEVICE_UNKNOWN;
421 }
422
423 bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
424 {
425 CCECBusDevice *device = m_busDevices->At(iAddress);
426 return device && device->IsActiveSource();
427 }
428
429 bool CCECProcessor::Transmit(const cec_command &data, bool bIsReply)
430 {
431 cec_command transmitData(data);
432 uint8_t iMaxTries(0);
433 bool bRetry(true);
434 uint8_t iTries(0);
435
436 // get the current timeout setting
437 uint8_t iLineTimeout(GetStandardLineTimeout());
438
439 // reset the state of this message to 'unknown'
440 cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
441
442 if (!m_communication->SupportsSourceLogicalAddress(transmitData.initiator))
443 {
444 if (transmitData.initiator == CECDEVICE_UNREGISTERED && m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE))
445 {
446 m_libcec->AddLog(CEC_LOG_DEBUG, "initiator '%s' is not supported by the CEC adapter. using '%s' instead", ToString(transmitData.initiator), ToString(CECDEVICE_FREEUSE));
447 transmitData.initiator = CECDEVICE_FREEUSE;
448 }
449 else
450 {
451 m_libcec->AddLog(CEC_LOG_DEBUG, "initiator '%s' is not supported by the CEC adapter", ToString(transmitData.initiator));
452 return false;
453 }
454 }
455
456 LogOutput(transmitData);
457
458 // find the initiator device
459 CCECBusDevice *initiator = m_busDevices->At(transmitData.initiator);
460 if (!initiator)
461 {
462 m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator");
463 return false;
464 }
465
466 // find the destination device, if it's not the broadcast address
467 if (transmitData.destination != CECDEVICE_BROADCAST)
468 {
469 // check if the device is marked as handled by libCEC
470 CCECBusDevice *destination = m_busDevices->At(transmitData.destination);
471 if (destination && destination->IsHandledByLibCEC())
472 {
473 // and reject the command if it's trying to send data to a device that is handled by libCEC
474 m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!");
475 return false;
476 }
477 }
478
479 // wait until we finished allocating a new LA if it got lost
480 while (m_bStallCommunication) Sleep(5);
481
482 {
483 CLockObject lock(m_mutex);
484 m_iLastTransmission = GetTimeMs();
485 // set the number of tries
486 iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1;
487 initiator->MarkHandlerReady();
488 }
489
490 // and try to send the command
491 while (bRetry && ++iTries < iMaxTries)
492 {
493 if (initiator->IsUnsupportedFeature(transmitData.opcode))
494 return false;
495
496 adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ?
497 m_communication->Write(transmitData, bRetry, iLineTimeout, bIsReply) :
498 ADAPTER_MESSAGE_STATE_ERROR;
499 iLineTimeout = m_iRetryLineTimeout;
500 }
501
502 return bIsReply ?
503 adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED || adapterState == ADAPTER_MESSAGE_STATE_SENT || adapterState == ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT :
504 adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED;
505 }
506
507 void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
508 {
509 m_libcec->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
510
511 cec_command command;
512 cec_command::Format(command, source, destination, CEC_OPCODE_FEATURE_ABORT);
513 command.parameters.PushBack((uint8_t)opcode);
514 command.parameters.PushBack((uint8_t)reason);
515
516 Transmit(command, true);
517 }
518
519 void CCECProcessor::ProcessCommand(const cec_command &command)
520 {
521 // log the command
522 m_libcec->AddLog(CEC_LOG_TRAFFIC, ToString(command).c_str());
523
524 // find the initiator
525 CCECBusDevice *device = m_busDevices->At(command.initiator);
526
527 if (device)
528 device->HandleCommand(command);
529 }
530
531 bool CCECProcessor::IsPresentDevice(cec_logical_address address)
532 {
533 CCECBusDevice *device = m_busDevices->At(address);
534 return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
535 }
536
537 bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
538 {
539 CECDEVICEVEC devices;
540 m_busDevices->GetByType(type, devices);
541 CCECDeviceMap::FilterActive(devices);
542 return !devices.empty();
543 }
544
545 uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const
546 {
547 return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
548 }
549
550 bool CCECProcessor::ClearLogicalAddresses(void)
551 {
552 cec_logical_addresses addresses; addresses.Clear();
553 return SetLogicalAddresses(addresses);
554 }
555
556 bool CCECProcessor::SetLogicalAddresses(const cec_logical_addresses &addresses)
557 {
558 return m_communication ? m_communication->SetLogicalAddresses(addresses) : false;
559 }
560
561 bool CCECProcessor::StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
562 {
563 bool bReturn(true);
564 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
565 bReturn &= (*it)->Standby(initiator);
566 return bReturn;
567 }
568
569 bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address)
570 {
571 CCECBusDevice *device = m_busDevices->At(address);
572 return device ? device->Standby(initiator) : false;
573 }
574
575 bool CCECProcessor::PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
576 {
577 bool bReturn(true);
578 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
579 bReturn &= (*it)->PowerOn(initiator);
580 return bReturn;
581 }
582
583 bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logical_address address)
584 {
585 CCECBusDevice *device = m_busDevices->At(address);
586 return device ? device->PowerOn(initiator) : false;
587 }
588
589 bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
590 {
591 bool bReturn(false);
592 // open a connection if no connection has been opened
593 if (!m_communication && strPort)
594 {
595 CAdapterFactory factory(this->m_libcec);
596 IAdapterCommunication *comm = factory.GetInstance(strPort);
597 CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
598 int iConnectTry(0);
599 while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
600 {
601 m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
602 comm->Close();
603 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
604 }
605 if (comm->IsOpen())
606 {
607 bReturn = comm->StartBootloader();
608 DELETE_AND_NULL(comm);
609 }
610 return bReturn;
611 }
612 else
613 {
614 m_communication->StartBootloader();
615 Close();
616 bReturn = true;
617 }
618
619 return bReturn;
620 }
621
622 bool CCECProcessor::PingAdapter(void)
623 {
624 return m_communication->PingAdapter();
625 }
626
627 void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
628 {
629 CCECBusDevice *device = m_busDevices->At(destination);
630 if (device)
631 device->HandlePollFrom(initiator);
632 }
633
634 bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
635 {
636 CCECBusDevice *device = m_busDevices->At(initiator);
637 return !device || !device->HandleReceiveFailed();
638 }
639
640 bool CCECProcessor::CanPersistConfiguration(void)
641 {
642 return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false;
643 }
644
645 bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration)
646 {
647 libcec_configuration persistConfiguration = configuration;
648 if (!CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
649 {
650 CCECBusDevice *device = GetPrimaryDevice();
651 if (device)
652 persistConfiguration.iPhysicalAddress = device->GetCurrentPhysicalAddress();
653 }
654
655 return m_communication ? m_communication->PersistConfiguration(persistConfiguration) : false;
656 }
657
658 void CCECProcessor::RescanActiveDevices(void)
659 {
660 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
661 it->second->GetStatus(true);
662 }
663
664 bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
665 {
666 if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
667 return false;
668
669 config->iFirmwareVersion = m_communication->GetFirmwareVersion();
670 config->iPhysicalAddress = m_communication->GetPhysicalAddress();
671 config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
672 config->adapterType = m_communication->GetAdapterType();
673
674 Close();
675
676 return true;
677 }
678
679 bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
680 {
681 bool bReturn(true);
682 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
683 bReturn &= it->second->TransmitPendingActiveSourceCommands();
684 return bReturn;
685 }
686
687 CCECTV *CCECProcessor::GetTV(void) const
688 {
689 return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV));
690 }
691
692 CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const
693 {
694 return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM));
695 }
696
697 CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const
698 {
699 return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address));
700 }
701
702 CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const
703 {
704 return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address));
705 }
706
707 CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const
708 {
709 return CCECBusDevice::AsTuner(m_busDevices->At(address));
710 }
711
712 bool CCECProcessor::AllocateLogicalAddresses(CCECClient* client)
713 {
714 libcec_configuration &configuration = *client->GetConfiguration();
715
716 // mark as unregistered
717 client->SetRegistered(false);
718
719 // unregister this client from the old addresses
720 CECDEVICEVEC devices;
721 m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
722 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
723 {
724 // remove client entry
725 CLockObject lock(m_mutex);
726 m_clients.erase((*it)->GetLogicalAddress());
727 }
728
729 // find logical addresses for this client
730 if (!client->AllocateLogicalAddresses())
731 {
732 m_libcec->AddLog(CEC_LOG_ERROR, "failed to find a free logical address for the client");
733 return false;
734 }
735
736 // register this client on the new addresses
737 devices.clear();
738 m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
739 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
740 {
741 // set the physical address of the device at this LA
742 if (CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
743 (*it)->SetPhysicalAddress(configuration.iPhysicalAddress);
744
745 // replace a previous client
746 CLockObject lock(m_mutex);
747 m_clients.erase((*it)->GetLogicalAddress());
748 m_clients.insert(make_pair((*it)->GetLogicalAddress(), client));
749 }
750
751 // set the new ackmask
752 SetLogicalAddresses(GetLogicalAddresses());
753
754 // resume outgoing communication
755 m_bStallCommunication = false;
756
757 return true;
758 }
759
760 uint16_t CCECProcessor::GetPhysicalAddressFromEeprom(void)
761 {
762 libcec_configuration config; config.Clear();
763 if (m_communication)
764 m_communication->GetConfiguration(config);
765 return config.iPhysicalAddress;
766 }
767
768 bool CCECProcessor::RegisterClient(CCECClient *client)
769 {
770 if (!client)
771 return false;
772
773 libcec_configuration &configuration = *client->GetConfiguration();
774
775 if (configuration.clientVersion < CEC_CLIENT_VERSION_2_0_0)
776 {
777 m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: client version %s is no longer supported", ToString((cec_client_version)configuration.clientVersion));
778 return false;
779 }
780
781 if (configuration.bMonitorOnly == 1)
782 return true;
783
784 if (!CECInitialised())
785 {
786 m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised");
787 return false;
788 }
789
790 // unregister the client first if it's already been marked as registered
791 if (client->IsRegistered())
792 UnregisterClient(client);
793
794 // ensure that controlled mode is enabled
795 m_communication->SetControlledMode(true);
796 m_bMonitor = false;
797
798 // source logical address for requests
799 cec_logical_address sourceAddress(CECDEVICE_UNREGISTERED);
800 if (!m_communication->SupportsSourceLogicalAddress(CECDEVICE_UNREGISTERED))
801 {
802 if (m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE))
803 sourceAddress = CECDEVICE_FREEUSE;
804 else
805 {
806 m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: both unregistered and free use are not supported by the device");
807 return false;
808 }
809 }
810
811 // ensure that we know the vendor id of the TV
812 CCECBusDevice *tv = GetTV();
813 cec_vendor_id tvVendor(tv->GetVendorId(sourceAddress));
814
815 // wait until the handler is replaced, to avoid double registrations
816 if (tvVendor != CEC_VENDOR_UNKNOWN &&
817 CCECCommandHandler::HasSpecificHandler(tvVendor))
818 {
819 while (!tv->ReplaceHandler(false))
820 CEvent::Sleep(5);
821 }
822
823 // get the configuration from the client
824 m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion));
825
826 // get the current ackmask, so we can restore it if polling fails
827 cec_logical_addresses previousMask = GetLogicalAddresses();
828
829 // mark as uninitialised
830 client->SetInitialised(false);
831
832 // find logical addresses for this client
833 if (!AllocateLogicalAddresses(client))
834 {
835 m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types");
836 SetLogicalAddresses(previousMask);
837 return false;
838 }
839
840 // get the settings from the rom
841 if (configuration.bGetSettingsFromROM == 1)
842 {
843 libcec_configuration config; config.Clear();
844 m_communication->GetConfiguration(config);
845
846 CLockObject lock(m_mutex);
847 if (!config.deviceTypes.IsEmpty())
848 configuration.deviceTypes = config.deviceTypes;
849 if (CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress))
850 configuration.iPhysicalAddress = config.iPhysicalAddress;
851 snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName);
852 }
853
854 // set the firmware version and build date
855 configuration.serverVersion = LIBCEC_VERSION_CURRENT;
856 configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
857 configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
858 configuration.adapterType = m_communication->GetAdapterType();
859
860 // mark the client as registered
861 client->SetRegistered(true);
862
863 sourceAddress = client->GetPrimaryLogicalAdddress();
864
865 // initialise the client
866 bool bReturn = client->OnRegister();
867
868 // log the new registration
869 CStdString strLog;
870 strLog.Format("%s: %s", bReturn ? "CEC client registered" : "failed to register the CEC client", client->GetConnectionInfo().c_str());
871 m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog);
872
873 // display a warning if the firmware can be upgraded
874 if (bReturn && !IsRunningLatestFirmware())
875 {
876 const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information.";
877 m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage);
878 libcec_parameter param;
879 param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING;
880 client->Alert(CEC_ALERT_SERVICE_DEVICE, param);
881 }
882
883 // ensure that the command handler for the TV is initialised
884 if (bReturn)
885 {
886 CCECCommandHandler *handler = GetTV()->GetHandler();
887 if (handler)
888 handler->InitHandler();
889 GetTV()->MarkHandlerReady();
890 }
891
892 // report our OSD name to the TV, since some TVs don't request it
893 client->GetPrimaryDevice()->TransmitOSDName(CECDEVICE_TV, false);
894
895 // request the power status of the TV
896 tv->RequestPowerStatus(sourceAddress, true, true);
897
898 return bReturn;
899 }
900
901 bool CCECProcessor::UnregisterClient(CCECClient *client)
902 {
903 if (!client)
904 return false;
905
906 if (client->IsRegistered())
907 m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
908
909 // notify the client that it will be unregistered
910 client->OnUnregister();
911
912 {
913 CLockObject lock(m_mutex);
914 // find all devices that match the LA's of this client
915 CECDEVICEVEC devices;
916 m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses);
917 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
918 {
919 // find the client
920 map<cec_logical_address, CCECClient *>::iterator entry = m_clients.find((*it)->GetLogicalAddress());
921 // unregister the client
922 if (entry != m_clients.end())
923 m_clients.erase(entry);
924
925 // reset the device status
926 (*it)->ResetDeviceStatus(true);
927 }
928 }
929
930 // set the new ackmask
931 cec_logical_addresses addresses = GetLogicalAddresses();
932 if (SetLogicalAddresses(addresses))
933 {
934 // no more clients left, disable controlled mode
935 if (addresses.IsEmpty() && !m_bMonitor)
936 m_communication->SetControlledMode(false);
937
938 return true;
939 }
940
941 return false;
942 }
943
944 void CCECProcessor::UnregisterClients(void)
945 {
946 m_libcec->AddLog(CEC_LOG_DEBUG, "unregistering all CEC clients");
947
948 vector<CCECClient *> clients = m_libcec->GetClients();
949 for (vector<CCECClient *>::iterator client = clients.begin(); client != clients.end(); client++)
950 UnregisterClient(*client);
951
952 CLockObject lock(m_mutex);
953 m_clients.clear();
954 }
955
956 CCECClient *CCECProcessor::GetClient(const cec_logical_address address)
957 {
958 CLockObject lock(m_mutex);
959 map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.find(address);
960 if (client != m_clients.end())
961 return client->second;
962 return NULL;
963 }
964
965 CCECClient *CCECProcessor::GetPrimaryClient(void)
966 {
967 CLockObject lock(m_mutex);
968 map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin();
969 if (client != m_clients.end())
970 return client->second;
971 return NULL;
972 }
973
974 CCECBusDevice *CCECProcessor::GetPrimaryDevice(void)
975 {
976 return m_busDevices->At(GetLogicalAddress());
977 }
978
979 cec_logical_address CCECProcessor::GetLogicalAddress(void)
980 {
981 cec_logical_addresses addresses = GetLogicalAddresses();
982 return addresses.primary;
983 }
984
985 cec_logical_addresses CCECProcessor::GetLogicalAddresses(void)
986 {
987 CLockObject lock(m_mutex);
988 cec_logical_addresses addresses;
989 addresses.Clear();
990 for (map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin(); client != m_clients.end(); client++)
991 addresses.Set(client->first);
992
993 return addresses;
994 }
995
996 bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const
997 {
998 CCECBusDevice *device = GetDevice(address);
999 return device && device->IsHandledByLibCEC();
1000 }
1001
1002 bool CCECProcessor::IsRunningLatestFirmware(void)
1003 {
1004 return m_communication && m_communication->IsOpen() ?
1005 m_communication->IsRunningLatestFirmware() :
1006 true;
1007 }
1008
1009 void CCECProcessor::SwitchMonitoring(bool bSwitchTo)
1010 {
1011 {
1012 CLockObject lock(m_mutex);
1013 m_bMonitor = bSwitchTo;
1014 }
1015 if (bSwitchTo)
1016 UnregisterClients();
1017 }
1018
1019 void CCECProcessor::HandleLogicalAddressLost(cec_logical_address oldAddress)
1020 {
1021 // stall outgoing messages until we know our new LA
1022 m_bStallCommunication = true;
1023
1024 m_libcec->AddLog(CEC_LOG_NOTICE, "logical address %x was taken by another device, allocating a new address", oldAddress);
1025 CCECClient* client = GetClient(oldAddress);
1026 if (!client)
1027 client = GetPrimaryClient();
1028 if (client)
1029 {
1030 if (m_addrAllocator)
1031 while (m_addrAllocator->IsRunning()) Sleep(5);
1032 delete m_addrAllocator;
1033
1034 m_addrAllocator = new CCECAllocateLogicalAddress(this, client);
1035 m_addrAllocator->CreateThread();
1036 }
1037 }
1038
1039 void CCECProcessor::HandlePhysicalAddressChanged(uint16_t iNewAddress)
1040 {
1041 m_libcec->AddLog(CEC_LOG_NOTICE, "physical address changed to %04x", iNewAddress);
1042 CCECClient* client = GetPrimaryClient();
1043 if (client)
1044 client->SetPhysicalAddress(iNewAddress);
1045 }
1046
1047 uint16_t CCECProcessor::GetAdapterVendorId(void) const
1048 {
1049 return m_communication ? m_communication->GetAdapterVendorId() : 0;
1050 }
1051
1052 uint16_t CCECProcessor::GetAdapterProductId(void) const
1053 {
1054 return m_communication ? m_communication->GetAdapterProductId() : 0;
1055 }
1056
1057 CCECAllocateLogicalAddress::CCECAllocateLogicalAddress(CCECProcessor* processor, CCECClient* client) :
1058 m_processor(processor),
1059 m_client(client) { }
1060
1061 void* CCECAllocateLogicalAddress::Process(void)
1062 {
1063 m_processor->AllocateLogicalAddresses(m_client);
1064 return NULL;
1065 }