cosmetics
[deb_libcec.git] / src / lib / CECProcessor.cpp
CommitLineData
abbca718
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
b492c10e 4 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
abbca718
LOK
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
2abe74eb 33#include "CECProcessor.h"
abbca718 34
7bb4ed43 35#include "adapter/USBCECAdapterCommunication.h"
eafa9d46 36#include "devices/CECBusDevice.h"
51b2a094
LOK
37#include "devices/CECAudioSystem.h"
38#include "devices/CECPlaybackDevice.h"
39#include "devices/CECRecordingDevice.h"
40#include "devices/CECTuner.h"
41#include "devices/CECTV.h"
62f5527d 42#include "implementations/CECCommandHandler.h"
2abe74eb 43#include "LibCEC.h"
004b8382 44#include "CECClient.h"
ba65909d 45#include "platform/util/timeutils.h"
abbca718
LOK
46
47using namespace CEC;
48using namespace std;
f00ff009 49using namespace PLATFORM;
abbca718 50
b32ffd87
LOK
51#define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000
52
004b8382
LOK
53#define ToString(x) m_libcec->ToString(x)
54
55CCECProcessor::CCECProcessor(CLibCEC *libcec) :
caca2d81 56 m_bInitialised(false),
caca2d81 57 m_communication(NULL),
004b8382 58 m_libcec(libcec),
caca2d81 59 m_bMonitor(false),
004b8382 60 m_iPreviousAckMask(0),
caca2d81
LOK
61 m_iStandardLineTimeout(3),
62 m_iRetryLineTimeout(3),
30b4aac0 63 m_iLastTransmission(0)
caca2d81 64{
004b8382 65 m_busDevices = new CCECDeviceMap(this);
caca2d81
LOK
66}
67
004b8382 68CCECProcessor::~CCECProcessor(void)
f8513317 69{
004b8382
LOK
70 Close();
71 delete m_busDevices;
caca2d81
LOK
72}
73
004b8382 74bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
caca2d81 75{
004b8382 76 CLockObject lock(m_mutex);
c0152c09 77 // open a connection
004b8382
LOK
78 if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
79 return false;
80
c0152c09 81 // create the processor thread
004b8382 82 if (!IsRunning())
51b2a094 83 {
004b8382
LOK
84 if (CreateThread())
85 m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
86 else
51b2a094 87 {
004b8382
LOK
88 m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
89 return false;
51b2a094
LOK
90 }
91 }
f8513317 92
c0152c09 93 // mark as initialised
004b8382 94 SetCECInitialised(true);
7c63a480 95
004b8382 96 return true;
abbca718
LOK
97}
98
eca71746
LOK
99void CCECProcessor::Close(void)
100{
c0152c09 101 // mark as uninitialised
004b8382 102 SetCECInitialised(false);
c0152c09
LOK
103
104 // stop the processor
eca71746
LOK
105 StopThread();
106
c0152c09 107 // close the connection
004b8382 108 if (m_communication)
eca71746 109 {
eca71746
LOK
110 delete m_communication;
111 m_communication = NULL;
112 }
c0152c09 113}
99aeafb9 114
c0152c09
LOK
115void CCECProcessor::ResetMembers(void)
116{
117 // close the connection
118 if (m_communication)
119 {
120 delete m_communication;
121 m_communication = NULL;
122 }
123
124 // reset the other members to the initial state
99aeafb9
LOK
125 m_bMonitor = false;
126 m_iPreviousAckMask = 0;
127 m_iStandardLineTimeout = 3;
128 m_iRetryLineTimeout = 3;
129 m_iLastTransmission = 0;
130 m_busDevices->ResetDeviceStatus();
eca71746
LOK
131}
132
f80cd208 133bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */)
abbca718 134{
a674fd68 135 bool bReturn(false);
c0152c09
LOK
136 CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
137
138 // ensure that a previous connection is closed
80726797
LOK
139 Close();
140
c0152c09
LOK
141 // reset all member to the initial state
142 ResetMembers();
143
144 // check whether the Close() method deleted any previous connection
145 if (m_communication)
578c3905 146 {
c0152c09
LOK
147 m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed");
148 return bReturn;
578c3905 149 }
1113cb7d 150
c0152c09
LOK
151 // create a new connection
152 m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate);
c520af4f 153
c0152c09 154 // open a new connection
efed01e1 155 unsigned iConnectTry(0);
a75e3a5a 156 while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false)
3d78df91 157 {
004b8382 158 m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
d55f263f 159 m_communication->Close();
b32ffd87 160 CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT);
3d78df91 161 }
56035c86 162
004b8382 163 m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened");
12a36be9 164
578c3905
LOK
165 return bReturn;
166}
167
004b8382 168bool CCECProcessor::CECInitialised(void)
0cfdeb5a 169{
0b714871 170 CLockObject lock(m_threadMutex);
0cfdeb5a
LOK
171 return m_bInitialised;
172}
173
004b8382 174void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */)
578c3905 175{
c0152c09
LOK
176 {
177 CLockObject lock(m_mutex);
178 m_bInitialised = bSetTo;
179 }
004b8382
LOK
180 if (!bSetTo)
181 UnregisterClients();
abbca718
LOK
182}
183
b58d9277 184bool CCECProcessor::TryLogicalAddress(cec_logical_address address)
f8513317 185{
c0152c09 186 // find the device
004b8382
LOK
187 CCECBusDevice *device = m_busDevices->At(address);
188 if (device)
c39611ec 189 {
c0152c09 190 // check if it's already marked as present or used
004b8382
LOK
191 if (device->IsPresent() || device->IsHandledByLibCEC())
192 return false;
c39611ec 193
c0152c09 194 // poll the LA if not
004b8382
LOK
195 SetAckMask(0);
196 return device->TryLogicalAddress();
f8513317
LOK
197 }
198
004b8382 199 return false;
f8513317
LOK
200}
201
a3ffc068
LOK
202void CCECProcessor::ReplaceHandlers(void)
203{
004b8382 204 if (!CECInitialised())
0cfdeb5a 205 return;
004b8382 206
c0152c09 207 // check each device
004b8382
LOK
208 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
209 it->second->ReplaceHandler(true);
a3ffc068
LOK
210}
211
b1f94db1
LOK
212bool CCECProcessor::OnCommandReceived(const cec_command &command)
213{
a4b9f561 214 return m_inBuffer.Push(command);
b1f94db1
LOK
215}
216
2abe74eb 217void *CCECProcessor::Process(void)
f99bc831 218{
004b8382 219 m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
abbca718 220
a4b9f561 221 cec_command command;
a4b9f561 222
c0152c09 223 // as long as we're not being stopped and the connection is open
b1f94db1 224 while (!IsStopped() && m_communication->IsOpen())
abbca718 225 {
c0152c09 226 // wait for a new incoming command, and process it
b32ffd87 227 if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME))
c0152c09 228 ProcessCommand(command);
a4b9f561 229
004b8382
LOK
230 if (CECInitialised())
231 {
c0152c09 232 // check clients for keypress timeouts
004b8382 233 m_libcec->CheckKeypressTimeout();
c0152c09
LOK
234
235 // check if we need to replace handlers
236 ReplaceHandlers();
004b8382 237 }
8ac9c610
LOK
238 }
239
004b8382 240 return NULL;
8ac9c610
LOK
241}
242
c0152c09 243bool CCECProcessor::ActivateSource(uint16_t iStreamPath)
a9232a79 244{
004b8382 245 bool bReturn(false);
a9232a79 246
c0152c09 247 // find the device with the given PA
004b8382 248 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
c0152c09 249 // and make it the active source when found
004b8382
LOK
250 if (device)
251 bReturn = device->ActivateSource();
252 else
253 m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
a9232a79 254
004b8382 255 return bReturn;
a9232a79
LOK
256}
257
004b8382 258void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
842262d8 259{
004b8382
LOK
260 CLockObject lock(m_mutex);
261 m_iStandardLineTimeout = iTimeout;
842262d8
LOK
262}
263
c0152c09
LOK
264uint8_t CCECProcessor::GetStandardLineTimeout(void)
265{
266 CLockObject lock(m_mutex);
267 return m_iStandardLineTimeout;
268}
269
004b8382 270void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
6a1c0009 271{
004b8382
LOK
272 CLockObject lock(m_mutex);
273 m_iRetryLineTimeout = iTimeout;
6a1c0009
LOK
274}
275
c0152c09
LOK
276uint8_t CCECProcessor::GetRetryLineTimeout(void)
277{
278 CLockObject lock(m_mutex);
279 return m_iRetryLineTimeout;
280}
281
004b8382 282bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
ed21be2a 283{
004b8382
LOK
284 CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress);
285 return device != NULL;
286}
ed21be2a 287
004b8382
LOK
288void CCECProcessor::LogOutput(const cec_command &data)
289{
290 CStdString strTx;
c0152c09
LOK
291
292 // initiator and destination
004b8382 293 strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
c0152c09
LOK
294
295 // append the opcode
004b8382
LOK
296 if (data.opcode_set)
297 strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
ed21be2a 298
c0152c09 299 // append the parameters
004b8382
LOK
300 for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
301 strTx.AppendFormat(":%02x", data.parameters[iPtr]);
c0152c09
LOK
302
303 // and log it
004b8382 304 m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
ed21be2a
LOK
305}
306
004b8382 307bool CCECProcessor::SwitchMonitoring(bool bEnable)
a3269a0a 308{
004b8382
LOK
309 m_libcec->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
310
cc60ab1c 311 {
004b8382 312 CLockObject lock(m_mutex);
c0152c09 313 // switch to monitoring mode, which will stop processing of incoming messages
004b8382 314 m_bMonitor = bEnable;
c0152c09 315 // and store the current ackmask
004b8382 316 m_iPreviousAckMask = m_communication->GetAckMask();
cc60ab1c 317 }
a3269a0a 318
c0152c09 319 // set the mask to 0 when enabling monitor mode
004b8382
LOK
320 if (bEnable)
321 return SetAckMask(0);
c0152c09 322 // and restore the previous mask otherwise
004b8382
LOK
323 else
324 return SetAckMask(m_iPreviousAckMask);
e186a843
LOK
325}
326
004b8382 327bool CCECProcessor::PollDevice(cec_logical_address iAddress)
44c74256 328{
c0152c09 329 // try to find the primary device
004b8382 330 CCECBusDevice *primary = GetPrimaryDevice();
c0152c09
LOK
331 // poll the destination, with the primary as source
332 if (primary)
333 return primary->TransmitPoll(iAddress);
334
335 // try to find the destination
336 CCECBusDevice *device = m_busDevices->At(iAddress);
337 // and poll the destination, with the same LA as source
004b8382 338 if (device)
c0152c09
LOK
339 return device->TransmitPoll(iAddress);
340
cc60ab1c 341 return false;
44c74256
LOK
342}
343
004b8382 344CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */)
eab72c40 345{
004b8382
LOK
346 return m_busDevices ?
347 m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) :
348 NULL;
eab72c40
LOK
349}
350
004b8382 351CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const
e55f3f70 352{
004b8382
LOK
353 return m_busDevices ?
354 m_busDevices->At(address) :
355 NULL;
e55f3f70
LOK
356}
357
5734016c 358cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */)
b4b1b49b 359{
004b8382
LOK
360 // get the device that is marked as active source from the device map
361 CCECBusDevice *activeSource = m_busDevices->GetActiveSource();
362 if (activeSource)
363 return activeSource->GetLogicalAddress();
b4b1b49b 364
004b8382 365 if (bRequestActiveSource)
5734016c 366 {
004b8382
LOK
367 // request the active source from the bus
368 CCECBusDevice *primary = GetPrimaryDevice();
5734016c 369 if (primary)
004b8382 370 {
5734016c 371 primary->RequestActiveSource();
004b8382
LOK
372 return GetActiveSource(false);
373 }
5734016c
LOK
374 }
375
004b8382 376 // unknown or none
b4b1b49b
LOK
377 return CECDEVICE_UNKNOWN;
378}
379
380bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
381{
004b8382
LOK
382 CCECBusDevice *device = m_busDevices->At(iAddress);
383 return device && device->IsActiveSource();
b4b1b49b
LOK
384}
385
8d84e2c0 386bool CCECProcessor::Transmit(const cec_command &data)
825ddb96 387{
c0152c09
LOK
388 uint8_t iMaxTries(0);
389 bool bRetry(true);
390 uint8_t iTries(0);
391
392 // get the current timeout setting
393 uint8_t iLineTimeout(GetStandardLineTimeout());
394
395 // reset the state of this message to 'unknown'
396 cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
397
398 LogOutput(data);
399
400 // find the initiator device
004b8382
LOK
401 CCECBusDevice *initiator = m_busDevices->At(data.initiator);
402 if (!initiator)
c4287bcd 403 {
004b8382 404 m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator");
c4287bcd
LOK
405 return false;
406 }
407
c0152c09 408 // find the destination device, if it's not the broadcast address
004b8382
LOK
409 if (data.destination != CECDEVICE_BROADCAST)
410 {
c0152c09 411 // check if the device is marked as handled by libCEC
004b8382
LOK
412 CCECBusDevice *destination = m_busDevices->At(data.destination);
413 if (destination && destination->IsHandledByLibCEC())
414 {
c0152c09 415 // and reject the command if it's trying to send data to a device that is handled by libCEC
004b8382
LOK
416 m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!");
417 return false;
418 }
419 }
420
eb35e4ca 421 {
7bb4ed43 422 CLockObject lock(m_mutex);
5f316715 423 m_iLastTransmission = GetTimeMs();
c0152c09 424 // set the number of tries
004b8382 425 iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1;
2abe74eb
LOK
426 }
427
c0152c09 428 // and try to send the command
33dd87a9
MK
429 while (bRetry && ++iTries < iMaxTries)
430 {
004b8382 431 if (initiator->IsUnsupportedFeature(data.opcode))
33dd87a9
MK
432 return false;
433
c0152c09
LOK
434 adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ?
435 m_communication->Write(data, bRetry, iLineTimeout) :
436 ADAPTER_MESSAGE_STATE_ERROR;
33dd87a9
MK
437 iLineTimeout = m_iRetryLineTimeout;
438 }
439
440 return adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED;
825ddb96 441}
abbca718 442
004b8382 443void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
abbca718 444{
004b8382 445 m_libcec->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
9dee1670 446
06a1f7ce 447 cec_command command;
004b8382 448 cec_command::Format(command, source, destination, CEC_OPCODE_FEATURE_ABORT);
ab1469a0
LOK
449 command.parameters.PushBack((uint8_t)opcode);
450 command.parameters.PushBack((uint8_t)reason);
9dee1670
LOK
451
452 Transmit(command);
abbca718
LOK
453}
454
c0152c09 455void CCECProcessor::ProcessCommand(const cec_command &command)
abbca718 456{
c0152c09 457 // log the command
e9de9629 458 CStdString dataStr;
d297cbd4
LOK
459 dataStr.Format(">> %1x%1x", command.initiator, command.destination);
460 if (command.opcode_set == 1)
461 dataStr.AppendFormat(":%02x", command.opcode);
e9de9629
LOK
462 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
463 dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
004b8382 464 m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
dc113aca 465
c0152c09 466 // if we're not in monitor mode
004b8382 467 if (!m_bMonitor)
6d858ba4 468 {
c0152c09 469 // find the initiator
004b8382 470 CCECBusDevice *device = m_busDevices->At(command.initiator);
c0152c09 471 // and "handle" the command
004b8382
LOK
472 if (device)
473 device->HandleCommand(command);
6d858ba4 474 }
6d858ba4
LOK
475}
476
37b0c572 477bool CCECProcessor::IsPresentDevice(cec_logical_address address)
6d858ba4 478{
004b8382
LOK
479 CCECBusDevice *device = m_busDevices->At(address);
480 return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
6d858ba4
LOK
481}
482
37b0c572 483bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
6d858ba4 484{
004b8382
LOK
485 CECDEVICEVEC devices;
486 m_busDevices->GetByType(type, devices);
487 CCECDeviceMap::FilterActive(devices);
488 return !devices.empty();
6d858ba4
LOK
489}
490
004b8382 491uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const
0f23c85c 492{
004b8382 493 return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
0f23c85c
LOK
494}
495
06bfd4d7
LOK
496bool CCECProcessor::SetAckMask(uint16_t iMask)
497{
0b714871 498 return m_communication ? m_communication->SetAckMask(iMask) : false;
06bfd4d7 499}
a33794d8 500
004b8382 501bool CCECProcessor::StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
a33794d8 502{
004b8382
LOK
503 bool bReturn(true);
504 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
505 bReturn &= (*it)->Standby(initiator);
506 return bReturn;
a33794d8
LOK
507}
508
004b8382 509bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address)
a33794d8 510{
004b8382
LOK
511 CCECBusDevice *device = m_busDevices->At(address);
512 return device ? device->Standby(initiator) : false;
a33794d8 513}
7c63a480 514
004b8382 515bool CCECProcessor::PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
8670c970 516{
004b8382
LOK
517 bool bReturn(true);
518 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
519 bReturn &= (*it)->PowerOn(initiator);
520 return bReturn;
8670c970
LOK
521}
522
004b8382 523bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logical_address address)
ca27e6cf 524{
004b8382
LOK
525 CCECBusDevice *device = m_busDevices->At(address);
526 return device ? device->PowerOn(initiator) : false;
ca27e6cf
LOK
527}
528
004b8382 529bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */)
ca27e6cf 530{
004b8382 531 bool bReturn(false);
c0152c09 532 // open a connection if no connection has been opened
004b8382 533 if (!m_communication && strPort)
ca27e6cf 534 {
004b8382
LOK
535 IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort);
536 CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
537 int iConnectTry(0);
538 while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
539 {
540 m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
541 comm->Close();
542 Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
543 }
544 if (comm->IsOpen())
ca27e6cf 545 {
004b8382
LOK
546 bReturn = comm->StartBootloader();
547 delete comm;
ca27e6cf
LOK
548 }
549 return bReturn;
550 }
004b8382 551 else
3e61b350 552 {
004b8382
LOK
553 m_communication->StartBootloader();
554 Close();
555 bReturn = true;
3e61b350 556 }
3e61b350 557
004b8382 558 return bReturn;
03ae897d
LOK
559}
560
004b8382 561bool CCECProcessor::PingAdapter(void)
03ae897d 562{
004b8382 563 return m_communication->PingAdapter();
03ae897d
LOK
564}
565
004b8382 566void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
03ae897d 567{
004b8382
LOK
568 CCECBusDevice *device = m_busDevices->At(destination);
569 if (device)
570 device->HandlePollFrom(initiator);
03ae897d
LOK
571}
572
004b8382 573bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
03ae897d 574{
004b8382
LOK
575 CCECBusDevice *device = m_busDevices->At(initiator);
576 return !device || !device->HandleReceiveFailed();
03ae897d
LOK
577}
578
004b8382 579bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress)
03ae897d 580{
004b8382
LOK
581 // stream path changes are sent by the TV
582 return GetTV()->GetHandler()->TransmitSetStreamPath(iPhysicalAddress);
03ae897d
LOK
583}
584
004b8382 585bool CCECProcessor::CanPersistConfiguration(void)
03ae897d 586{
004b8382 587 return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false;
03ae897d
LOK
588}
589
c0152c09 590bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration)
03ae897d 591{
004b8382 592 return m_communication ? m_communication->PersistConfiguration(configuration) : false;
03ae897d
LOK
593}
594
004b8382 595void CCECProcessor::RescanActiveDevices(void)
03ae897d 596{
004b8382
LOK
597 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
598 it->second->GetStatus(true);
03ae897d
LOK
599}
600
004b8382 601bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */)
03ae897d 602{
004b8382
LOK
603 if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
604 return false;
03ae897d 605
004b8382
LOK
606 config->iFirmwareVersion = m_communication->GetFirmwareVersion();
607 config->iPhysicalAddress = m_communication->GetPhysicalAddress();
608 config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
03ae897d 609
004b8382 610 return true;
caca2d81
LOK
611}
612
004b8382 613bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
3efda01a 614{
004b8382
LOK
615 bool bReturn(true);
616 for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
617 bReturn &= it->second->TransmitPendingActiveSourceCommands();
618 return bReturn;
3efda01a
LOK
619}
620
004b8382 621CCECTV *CCECProcessor::GetTV(void) const
1113cb7d 622{
004b8382 623 return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV));
1113cb7d
LOK
624}
625
004b8382 626CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const
1113cb7d 627{
004b8382 628 return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM));
1113cb7d 629}
6729ac71 630
004b8382 631CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const
6729ac71 632{
004b8382 633 return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address));
6729ac71
LOK
634}
635
004b8382 636CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const
6729ac71 637{
004b8382 638 return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address));
6729ac71 639}
f42d3e0f 640
004b8382 641CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const
f42d3e0f 642{
004b8382 643 return CCECBusDevice::AsTuner(m_busDevices->At(address));
f42d3e0f 644}
d40928b5 645
004b8382 646bool CCECProcessor::RegisterClient(CCECClient *client)
30b4aac0 647{
004b8382
LOK
648 if (!client)
649 return false;
30b4aac0 650
c0152c09
LOK
651 // unregister the client first if it's already been marked as registered
652 if (client->IsRegistered())
653 UnregisterClient(client);
654
655 // get the configuration from the client
004b8382
LOK
656 libcec_configuration &configuration = *client->GetConfiguration();
657 m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion));
30b4aac0 658
c0152c09 659 // mark as uninitialised and unregistered
004b8382
LOK
660 client->SetRegistered(false);
661 client->SetInitialised(false);
30b4aac0 662
c0152c09 663 // get the current ackmask, so we can restore it if polling fails
004b8382 664 uint16_t iPreviousMask(m_communication->GetAckMask());
30a09f32 665
004b8382 666 // find logical addresses for this client
c0152c09 667 if (!client->AllocateLogicalAddresses())
8670c970 668 {
c0152c09 669 m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types");
004b8382
LOK
670 SetAckMask(iPreviousMask);
671 return false;
8670c970
LOK
672 }
673
004b8382
LOK
674 // register this client on the new addresses
675 CECDEVICEVEC devices;
676 m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
677 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
8670c970 678 {
c0152c09 679 // replace a previous client
004b8382
LOK
680 CLockObject lock(m_mutex);
681 m_clients.erase((*it)->GetLogicalAddress());
682 m_clients.insert(make_pair<cec_logical_address, CCECClient *>((*it)->GetLogicalAddress(), client));
8670c970 683 }
30b4aac0 684
004b8382
LOK
685 // get the settings from the rom
686 if (configuration.bGetSettingsFromROM == 1)
8670c970 687 {
004b8382 688 libcec_configuration config;
c0152c09 689 m_communication->GetConfiguration(config);
f0154449 690
004b8382
LOK
691 CLockObject lock(m_mutex);
692 if (!config.deviceTypes.IsEmpty())
693 configuration.deviceTypes = config.deviceTypes;
694 if (CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress))
695 configuration.iPhysicalAddress = config.iPhysicalAddress;
696 snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName);
30b4aac0
LOK
697 }
698
004b8382 699 // set the firmware version and build date
99aeafb9
LOK
700 configuration.serverVersion = LIBCEC_VERSION_CURRENT;
701 configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
004b8382
LOK
702 configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
703
c0152c09
LOK
704 // mark the client as registered
705 client->SetRegistered(true);
30b4aac0 706
c0152c09
LOK
707 // set the new ack mask
708 bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) &&
709 // and initialise the client
710 client->OnRegister();
41e3372a 711
c0152c09
LOK
712 // log the new registration
713 CStdString strLog;
714 strLog.Format("%s: %s", bReturn ? "CEC client registered" : "failed to register the CEC client", client->GetConnectionInfo().c_str());
715 m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog);
b98fc43d 716
004b8382 717 // display a warning if the firmware can be upgraded
c0152c09 718 if (bReturn && !IsRunningLatestFirmware())
3ef17606 719 {
004b8382
LOK
720 const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information.";
721 m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage);
c30acafa
LOK
722 libcec_parameter param;
723 param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING;
724 client->Alert(CEC_ALERT_SERVICE_DEVICE, param);
3ef17606 725 }
30b4aac0 726
43b2dfdd 727 return bReturn;
30b4aac0
LOK
728}
729
004b8382 730void CCECProcessor::UnregisterClient(CCECClient *client)
d40928b5 731{
c0152c09
LOK
732 if (!client)
733 return;
734
735 m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
736
737 // notify the client that it will be unregistered
738 client->OnUnregister();
739
7f274e72 740 {
c0152c09
LOK
741 CLockObject lock(m_mutex);
742 // find all devices that match the LA's of this client
743 CECDEVICEVEC devices;
744 m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses);
745 for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
746 {
747 // find the client
748 map<cec_logical_address, CCECClient *>::iterator entry = m_clients.find((*it)->GetLogicalAddress());
749 // unregister the client
750 if (entry != m_clients.end())
751 m_clients.erase(entry);
752
753 // reset the device status
754 (*it)->ResetDeviceStatus();
755 }
7f274e72 756 }
c0152c09
LOK
757
758 // set the new ackmask
759 SetAckMask(GetLogicalAddresses().AckMask());
d40928b5 760}
224ea877 761
004b8382 762void CCECProcessor::UnregisterClients(void)
224ea877 763{
c0152c09
LOK
764 m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering all CEC clients");
765
766 vector<CCECClient *> clients = m_libcec->GetClients();
767 for (vector<CCECClient *>::iterator client = clients.begin(); client != clients.end(); client++)
768 UnregisterClient(*client);
769
004b8382 770 CLockObject lock(m_mutex);
004b8382 771 m_clients.clear();
224ea877
LOK
772}
773
004b8382 774CCECClient *CCECProcessor::GetClient(const cec_logical_address address)
224ea877 775{
004b8382
LOK
776 CLockObject lock(m_mutex);
777 map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.find(address);
778 if (client != m_clients.end())
779 return client->second;
780 return NULL;
224ea877 781}
3efda01a 782
004b8382 783CCECClient *CCECProcessor::GetPrimaryClient(void)
3efda01a 784{
004b8382
LOK
785 CLockObject lock(m_mutex);
786 map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin();
787 if (client != m_clients.end())
788 return client->second;
789 return NULL;
3efda01a 790}
f80cd208 791
c0152c09 792CCECBusDevice *CCECProcessor::GetPrimaryDevice(void)
f80cd208 793{
004b8382
LOK
794 return m_busDevices->At(GetLogicalAddress());
795}
f80cd208 796
c0152c09 797cec_logical_address CCECProcessor::GetLogicalAddress(void)
004b8382
LOK
798{
799 cec_logical_addresses addresses = GetLogicalAddresses();
800 return addresses.primary;
f80cd208 801}
b78b4e33 802
c0152c09 803cec_logical_addresses CCECProcessor::GetLogicalAddresses(void)
b78b4e33 804{
c0152c09 805 CLockObject lock(m_mutex);
004b8382 806 cec_logical_addresses addresses;
c30acafa 807 addresses.Clear();
004b8382
LOK
808 for (map<cec_logical_address, CCECClient *>::const_iterator client = m_clients.begin(); client != m_clients.end(); client++)
809 addresses.Set(client->first);
810
811 return addresses;
b78b4e33 812}
d2d1660c 813
004b8382 814bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const
d2d1660c 815{
004b8382
LOK
816 CCECBusDevice *device = GetDevice(address);
817 return device && device->IsHandledByLibCEC();
d2d1660c 818}
c0152c09
LOK
819
820bool CCECProcessor::IsRunningLatestFirmware(void)
821{
822 return m_communication && m_communication->IsOpen() ?
823 m_communication->IsRunningLatestFirmware() :
824 true;
825}