#include "env.h"
#include "CECProcessor.h"
#include "adapter/AdapterFactory.h"
#include "devices/CECBusDevice.h"
#include "devices/CECAudioSystem.h"
#include "devices/CECPlaybackDevice.h"
#include "devices/CECRecordingDevice.h"
#include "devices/CECTuner.h"
#include "devices/CECTV.h"
#include "implementations/CECCommandHandler.h"
#include "LibCEC.h"
#include "CECClient.h"
#include "CECTypeUtils.h"
#include "p8-platform/util/timeutils.h"
#include "p8-platform/util/util.h"
#include <stdio.h>
using namespace CEC;
using namespace P8PLATFORM;
#define ACTIVE_SOURCE_CHECK_INTERVAL 500
#define TV_PRESENT_CHECK_INTERVAL 30000
#define ToString(x) CCECTypeUtils::ToString(x)
CCECStandbyProtection::CCECStandbyProtection(CCECProcessor* processor) :
m_processor(processor) {}
CCECStandbyProtection::~CCECStandbyProtection(void) {}
void* CCECStandbyProtection::Process(void)
{
int64_t last = GetTimeMs();
int64_t next;
while (!IsStopped())
{
P8PLATFORM::CEvent::Sleep(1000);
next = GetTimeMs();
if (next < last || next - last > 10000)
{
libcec_parameter param;
param.paramData = NULL; param.paramType = CEC_PARAMETER_TYPE_UNKOWN;
m_processor->GetLib()->Alert(CEC_ALERT_CONNECTION_LOST, param);
break;
}
last = next;
}
return NULL;
}
CCECProcessor::CCECProcessor(CLibCEC *libcec) :
m_bInitialised(false),
m_communication(NULL),
m_libcec(libcec),
m_iStandardLineTimeout(3),
m_iRetryLineTimeout(3),
m_iLastTransmission(0),
m_bMonitor(true),
m_addrAllocator(NULL),
m_bStallCommunication(false),
m_connCheck(NULL)
{
m_busDevices = new CCECDeviceMap(this);
}
CCECProcessor::~CCECProcessor(void)
{
m_bStallCommunication = false;
SAFE_DELETE(m_addrAllocator);
Close();
SAFE_DELETE(m_busDevices);
}
bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate , uint32_t iTimeoutMs )
{
CLockObject lock(m_mutex);
if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
return false;
if (!IsRunning())
{
if (!CreateThread())
{
m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread");
return false;
}
}
return true;
}
void CCECProcessor::Close(void)
{
SetCECInitialised(false);
SAFE_DELETE(m_connCheck);
StopThread(-1);
m_inBuffer.Broadcast();
StopThread();
CLockObject lock(m_mutex);
SAFE_DELETE(m_communication);
}
void CCECProcessor::ResetMembers(void)
{
SAFE_DELETE(m_communication);
m_iStandardLineTimeout = 3;
m_iRetryLineTimeout = 3;
m_iLastTransmission = 0;
m_busDevices->ResetDeviceStatus();
}
bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening )
{
bool bReturn(false);
CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT);
if (m_communication)
Close();
ResetMembers();
if (m_communication)
{
m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed");
return bReturn;
}
m_communication = CAdapterFactory(this->m_libcec).GetInstance(strPort, iBaudRate);
unsigned iConnectTry(0);
while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false)
{
m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
m_communication->Close();
CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT);
}
m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened");
SetCECInitialised(true);
return bReturn;
}
bool CCECProcessor::CECInitialised(void)
{
CLockObject lock(m_mutex);
return m_bInitialised;
}
void CCECProcessor::SetCECInitialised(bool bSetTo )
{
{
CLockObject lock(m_mutex);
m_bInitialised = bSetTo;
}
if (!bSetTo)
UnregisterClients();
}
bool CCECProcessor::TryLogicalAddress(cec_logical_address address, cec_version libCECSpecVersion )
{
CCECBusDevice *device = m_busDevices->At(address);
if (device)
{
if (device->IsPresent() || device->IsHandledByLibCEC())
return false;
return device->TryLogicalAddress(libCECSpecVersion);
}
return false;
}
void CCECProcessor::ReplaceHandlers(void)
{
CLockObject lock(m_mutex);
if (!CECInitialised())
return;
for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); ++it)
it->second->ReplaceHandler(true);
for (std::vector<device_type_change_t>::const_iterator it = m_deviceTypeChanges.begin(); it != m_deviceTypeChanges.end(); ++it)
(*it).client->ChangeDeviceType((*it).from, (*it).to);
m_deviceTypeChanges.clear();
}
void CCECProcessor::ChangeDeviceType(CECClientPtr client, cec_device_type from, cec_device_type to)
{
CLockObject lock(m_mutex);
if (!CECInitialised())
return;
device_type_change_t change = { client, from, to };
m_deviceTypeChanges.push_back(change);
}
bool CCECProcessor::OnCommandReceived(const cec_command &command)
{
return m_inBuffer.Push(command);
}
void *CCECProcessor::Process(void)
{
uint16_t timeout = CEC_PROCESSOR_SIGNAL_WAIT_TIME;
m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started");
if (!m_connCheck)
m_connCheck = new CCECStandbyProtection(this);
m_connCheck->CreateThread();
cec_command command; command.Clear();
CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_INTERVAL);
CTimeout tvPresentCheck(TV_PRESENT_CHECK_INTERVAL);
while (!IsStopped() && m_communication->IsOpen())
{
if (m_inBuffer.Pop(command, timeout))
ProcessCommand(command);
if (CECInitialised() && !IsStopped())
{
timeout = m_libcec->CheckKeypressTimeout();
ReplaceHandlers();
if (activeSourceCheck.TimeLeft() == 0)
{
if (CECInitialised())
TransmitPendingActiveSourceCommands();
activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_INTERVAL);
}
if (tvPresentCheck.TimeLeft() == 0)
{
CECClientPtr primary = GetPrimaryClient();
if (primary && primary->GetConfiguration()->bMonitorOnly != 1)
{
if (!m_busDevices->At(CECDEVICE_TV)->IsPresent())
{
libcec_parameter param;
param.paramType = CEC_PARAMETER_TYPE_STRING;
param.paramData = (void*)"TV does not respond to CEC polls";
primary->Alert(CEC_ALERT_TV_POLL_FAILED, param);
}
}
tvPresentCheck.Init(TV_PRESENT_CHECK_INTERVAL);
}
}
else
timeout = CEC_PROCESSOR_SIGNAL_WAIT_TIME;
}
return NULL;
}
bool CCECProcessor::ActivateSource(uint16_t iStreamPath)
{
bool bReturn(false);
CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
if (device)
bReturn = device->ActivateSource();
else
m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath);
return bReturn;
}
#if CEC_LIB_VERSION_MAJOR >= 5
bool CCECProcessor::GetStats(struct cec_adapter_stats* stats)
{
return !!m_communication ?
m_communication->GetStats(stats) :
false;
}
#endif
void CCECProcessor::SetActiveSource(bool bSetTo, bool bClientUnregistered)
{
if (m_communication)
m_communication->SetActiveSource(bSetTo, bClientUnregistered);
}
void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
{
CLockObject lock(m_mutex);
m_iStandardLineTimeout = iTimeout;
}
uint8_t CCECProcessor::GetStandardLineTimeout(void)
{
CLockObject lock(m_mutex);
return m_iStandardLineTimeout;
}
void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
{
CLockObject lock(m_mutex);
m_iRetryLineTimeout = iTimeout;
}
uint8_t CCECProcessor::GetRetryLineTimeout(void)
{
CLockObject lock(m_mutex);
return m_iRetryLineTimeout;
}
bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
{
CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress);
return device != NULL;
}
void CCECProcessor::LogOutput(const cec_command &data)
{
std::string strTx;
strTx = StringUtils::Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
if (data.opcode_set)
strTx += StringUtils::Format(":%02x", (uint8_t)data.opcode);
for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
strTx += StringUtils::Format(":%02x", data.parameters[iPtr]);
m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
}
bool CCECProcessor::PollDevice(cec_logical_address iAddress)
{
CCECBusDevice *primary = GetPrimaryDevice();
if (primary)
return primary->TransmitPoll(iAddress, true);
CCECBusDevice *device = m_busDevices->At(CECDEVICE_UNREGISTERED);
if (device)
return device->TransmitPoll(iAddress, true);
return false;
}
CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate )
{
return m_busDevices ?
m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) :
NULL;
}
CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const
{
return m_busDevices ?
m_busDevices->At(address) :
NULL;
}
cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource )
{
CCECBusDevice *activeSource = m_busDevices->GetActiveSource();
if (activeSource)
return activeSource->GetLogicalAddress();
if (bRequestActiveSource)
{
CCECBusDevice *primary = GetPrimaryDevice();
if (primary)
{
primary->RequestActiveSource();
return GetActiveSource(false);
}
}
return CECDEVICE_UNKNOWN;
}
bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
{
CCECBusDevice *device = m_busDevices->At(iAddress);
return device && device->IsActiveSource();
}
bool CCECProcessor::Transmit(const cec_command &data, bool bIsReply)
{
cec_command transmitData(data);
uint8_t iMaxTries(0);
bool bRetry(true);
uint8_t iTries(0);
uint8_t iLineTimeout(GetStandardLineTimeout());
cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN;
if (data.initiator == CECDEVICE_UNKNOWN && data.destination == CECDEVICE_UNKNOWN)
return false;
CLockObject lock(m_mutex);
if (!m_communication)
return false;
if (!m_communication->SupportsSourceLogicalAddress(transmitData.initiator))
{
if (transmitData.initiator == CECDEVICE_UNREGISTERED && m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE))
{
m_libcec->AddLog(CEC_LOG_DEBUG, "initiator '%s' is not supported by the CEC adapter. using '%s' instead", ToString(transmitData.initiator), ToString(CECDEVICE_FREEUSE));
transmitData.initiator = CECDEVICE_FREEUSE;
}
else
{
m_libcec->AddLog(CEC_LOG_DEBUG, "initiator '%s' is not supported by the CEC adapter", ToString(transmitData.initiator));
return false;
}
}
LogOutput(transmitData);
CCECBusDevice *initiator = m_busDevices->At(transmitData.initiator);
if (!initiator)
{
m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator");
return false;
}
if (transmitData.destination != CECDEVICE_BROADCAST)
{
CCECBusDevice *destination = m_busDevices->At(transmitData.destination);
if (destination && destination->IsHandledByLibCEC())
{
m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!");
return false;
}
}
if (data.opcode_set)
{
lock.Unlock();
while (m_bStallCommunication) Sleep(5);
lock.Lock();
}
m_iLastTransmission = GetTimeMs();
iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1;
initiator->MarkHandlerReady();
while (bRetry && ++iTries < iMaxTries)
{
if (initiator->IsUnsupportedFeature(transmitData.opcode))
return false;
adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ?
m_communication->Write(transmitData, bRetry, iLineTimeout, bIsReply) :
ADAPTER_MESSAGE_STATE_ERROR;
iLineTimeout = m_iRetryLineTimeout;
}
return bIsReply ?
adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED || adapterState == ADAPTER_MESSAGE_STATE_SENT || adapterState == ADAPTER_MESSAGE_STATE_WAITING_TO_BE_SENT :
adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED;
}
void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason )
{
m_libcec->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
cec_command command;
cec_command::Format(command, source, destination, CEC_OPCODE_FEATURE_ABORT);
command.parameters.PushBack((uint8_t)opcode);
command.parameters.PushBack((uint8_t)reason);
Transmit(command, true);
}
void CCECProcessor::ProcessCommand(const cec_command &command)
{
m_libcec->AddLog(CEC_LOG_TRAFFIC, ToString(command).c_str());
CCECBusDevice *device = m_busDevices->At(command.initiator);
if (device)
device->HandleCommand(command);
}
bool CCECProcessor::IsPresentDevice(cec_logical_address address)
{
CCECBusDevice *device = m_busDevices->At(address);
return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
}
bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
{
CECDEVICEVEC devices;
m_busDevices->GetByType(type, devices);
CCECDeviceMap::FilterActive(devices);
return !devices.empty();
}
uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const
{
return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS;
}
bool CCECProcessor::ClearLogicalAddresses(void)
{
cec_logical_addresses addresses; addresses.Clear();
return SetLogicalAddresses(addresses);
}
bool CCECProcessor::SetLogicalAddresses(const cec_logical_addresses &addresses)
{
return m_communication ? m_communication->SetLogicalAddresses(addresses) : false;
}
bool CCECProcessor::StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
{
bool bReturn(true);
for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
bReturn &= (*it)->Standby(initiator);
return bReturn;
}
bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address)
{
CCECBusDevice *device = m_busDevices->At(address);
return device ? device->Standby(initiator) : false;
}
bool CCECProcessor::PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices)
{
bool bReturn(true);
for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
bReturn &= (*it)->PowerOn(initiator);
return bReturn;
}
bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logical_address address)
{
CCECBusDevice *device = m_busDevices->At(address);
return device ? device->PowerOn(initiator) : false;
}
bool CCECProcessor::StartBootloader(const char *strPort )
{
bool bReturn(false);
if (!m_communication && strPort)
{
CAdapterFactory factory(this->m_libcec);
IAdapterCommunication *comm = factory.GetInstance(strPort);
CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT);
int iConnectTry(0);
while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false)
{
m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry);
comm->Close();
Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT);
}
if (comm->IsOpen())
{
bReturn = comm->StartBootloader();
SAFE_DELETE(comm);
}
return bReturn;
}
else
{
m_communication->StartBootloader();
Close();
bReturn = true;
}
return bReturn;
}
bool CCECProcessor::PingAdapter(void)
{
return m_communication->PingAdapter();
}
void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
{
CCECBusDevice *device = m_busDevices->At(destination);
if (device)
device->HandlePollFrom(initiator);
}
bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator)
{
CCECBusDevice *device = m_busDevices->At(initiator);
return !device || !device->HandleReceiveFailed();
}
bool CCECProcessor::CanSaveConfiguration(void)
{
return !!m_communication ?
m_communication->GetFirmwareVersion() >= 2 :
false;
}
bool CCECProcessor::SaveConfiguration(const libcec_configuration &configuration)
{
libcec_configuration save_config = configuration;
if (!CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
{
CCECBusDevice *device = GetPrimaryDevice();
if (!!device)
save_config.iPhysicalAddress = device->GetCurrentPhysicalAddress();
}
return !!m_communication ?
m_communication->SaveConfiguration(save_config) :
false;
}
bool CCECProcessor::SetAutoMode(bool automode)
{
return !!m_communication ?
m_communication->SetAutoMode(automode) :
false;
}
void CCECProcessor::RescanActiveDevices(void)
{
for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
it->second->GetStatus(true);
}
bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs )
{
if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false))
return false;
config->iFirmwareVersion = m_communication->GetFirmwareVersion();
config->iPhysicalAddress = m_communication->GetPhysicalAddress();
config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
config->adapterType = m_communication->GetAdapterType();
Close();
return true;
}
bool CCECProcessor::TransmitPendingActiveSourceCommands(void)
{
bool bReturn(true);
for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++)
bReturn &= it->second->TransmitPendingActiveSourceCommands();
return bReturn;
}
CCECTV *CCECProcessor::GetTV(void) const
{
return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV));
}
CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const
{
return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM));
}
CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const
{
return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address));
}
CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const
{
return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address));
}
CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const
{
return CCECBusDevice::AsTuner(m_busDevices->At(address));
}
bool CCECProcessor::AllocateLogicalAddresses(CECClientPtr client)
{
libcec_configuration &configuration = *client->GetConfiguration();
client->SetRegistered(false);
CECDEVICEVEC devices;
m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
{
CLockObject lock(m_mutex);
m_clients.erase((*it)->GetLogicalAddress());
}
if (!client->AllocateLogicalAddresses())
{
m_libcec->AddLog(CEC_LOG_ERROR, "failed to find a free logical address for the client");
return false;
}
if (configuration.bAutodetectAddress)
client->AutodetectPhysicalAddress();
devices.clear();
m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses);
for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
{
if (CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
(*it)->SetPhysicalAddress(configuration.iPhysicalAddress);
CLockObject lock(m_mutex);
m_clients.erase((*it)->GetLogicalAddress());
m_clients.insert(make_pair((*it)->GetLogicalAddress(), client));
}
SetLogicalAddresses(GetLogicalAddresses());
m_bStallCommunication = false;
return true;
}
uint16_t CCECProcessor::GetPhysicalAddressFromEeprom(void)
{
libcec_configuration config; config.Clear();
if (m_communication)
m_communication->GetConfiguration(config);
return config.iPhysicalAddress;
}
bool CCECProcessor::RegisterClient(CCECClient* client)
{
for (std::map<cec_logical_address, CECClientPtr>::iterator it = m_clients.begin(); it != m_clients.end(); ++it)
{
if (it->second.get() == client)
return RegisterClient(it->second);
}
return RegisterClient(CECClientPtr(client));
}
bool CCECProcessor::RegisterClient(CECClientPtr client)
{
if (!client)
return false;
libcec_configuration &configuration = *client->GetConfiguration();
if (configuration.bMonitorOnly == 1)
return true;
if (!CECInitialised())
{
m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised");
return false;
}
if (client->IsRegistered())
UnregisterClient(client);
m_communication->SetControlledMode(true);
m_bMonitor = false;
cec_logical_address sourceAddress(CECDEVICE_UNREGISTERED);
if (!m_communication->SupportsSourceLogicalAddress(CECDEVICE_UNREGISTERED))
{
if (m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE))
sourceAddress = CECDEVICE_FREEUSE;
else
{
m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: both unregistered and free use are not supported by the device");
return false;
}
}
CCECBusDevice *tv = GetTV();
cec_vendor_id tvVendor(tv->GetVendorId(sourceAddress));
if (tvVendor != CEC_VENDOR_UNKNOWN &&
CCECCommandHandler::HasSpecificHandler(tvVendor))
{
while (!tv->ReplaceHandler(false))
CEvent::Sleep(5);
}
m_libcec->AddLog(CEC_LOG_DEBUG, "registering new CEC client - v%s", CCECTypeUtils::VersionToString(configuration.clientVersion).c_str());
cec_logical_addresses previousMask = GetLogicalAddresses();
client->SetInitialised(false);
uint16_t detectedAddress = 0;
if ((configuration.bAutodetectAddress == 1)
&& (!CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress))
&& ((detectedAddress = GetDetectedPhysicalAddress()) != CEC_INVALID_PHYSICAL_ADDRESS)
&& CLibCEC::IsValidPhysicalAddress(detectedAddress)) {
configuration.iPhysicalAddress = detectedAddress;
}
{
libcec_configuration config; config.Clear();
m_communication->GetConfiguration(config);
{
if (configuration.bGetSettingsFromROM == 1)
{
if (!config.deviceTypes.IsEmpty())
configuration.deviceTypes = config.deviceTypes;
if (!CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress)
&& CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress))
configuration.iPhysicalAddress = config.iPhysicalAddress;
snprintf(configuration.strDeviceName, LIBCEC_OSD_NAME_SIZE, "%s", config.strDeviceName);
}
#if CEC_LIB_VERSION_MAJOR >= 5
configuration.bAutoPowerOn = config.bAutoPowerOn;
#endif
}
client->SetConfiguration(configuration);
}
if (!AllocateLogicalAddresses(client))
{
m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types");
SetLogicalAddresses(previousMask);
return false;
}
configuration.serverVersion = LIBCEC_VERSION_CURRENT;
configuration.iFirmwareVersion = m_communication->GetFirmwareVersion();
configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate();
configuration.adapterType = m_communication->GetAdapterType();
client->SetRegistered(true);
sourceAddress = client->GetPrimaryLogicalAddress();
bool bReturn = client->OnRegister();
std::string strLog;
strLog = StringUtils::Format("%s: %s", bReturn ? "CEC client registered" : "failed to register the CEC client", client->GetConnectionInfo().c_str());
m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog.c_str());
if (bReturn && !IsRunningLatestFirmware())
{
const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information.";
m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage);
libcec_parameter param;
param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING;
client->Alert(CEC_ALERT_SERVICE_DEVICE, param);
}
if (bReturn)
{
CCECCommandHandler *handler = GetTV()->GetHandler();
if (handler)
handler->InitHandler();
GetTV()->MarkHandlerReady();
}
client->GetPrimaryDevice()->TransmitOSDName(CECDEVICE_TV, false);
tv->RequestPowerStatus(sourceAddress, true, true);
return bReturn;
}
bool CCECProcessor::UnregisterClient(CCECClient* client)
{
for (std::map<cec_logical_address, CECClientPtr>::iterator it = m_clients.begin(); it != m_clients.end(); ++it)
{
if (it->second.get() == client)
return UnregisterClient(it->second);
}
return true;
}
bool CCECProcessor::UnregisterClient(CECClientPtr client)
{
if (!client)
return false;
if (client->IsRegistered())
m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str());
client->OnUnregister();
{
CLockObject lock(m_mutex);
CECDEVICEVEC devices;
m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses);
for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++)
{
std::map<cec_logical_address, CECClientPtr>::iterator entry = m_clients.find((*it)->GetLogicalAddress());
if (entry != m_clients.end())
m_clients.erase(entry);
(*it)->ResetDeviceStatus(true);
}
}
cec_logical_addresses addresses = GetLogicalAddresses();
if (SetLogicalAddresses(addresses))
{
if (addresses.IsEmpty() && !m_bMonitor)
m_communication->SetControlledMode(false);
return true;
}
return false;
}
void CCECProcessor::UnregisterClients(void)
{
m_libcec->AddLog(CEC_LOG_DEBUG, "unregistering all CEC clients");
std::vector<CECClientPtr> clients = m_libcec->GetClients();
for (std::vector<CECClientPtr>::iterator it = clients.begin(); it != clients.end(); ++it)
UnregisterClient(*it);
}
CECClientPtr CCECProcessor::GetClient(const cec_logical_address address)
{
CLockObject lock(m_mutex);
std::map<cec_logical_address, CECClientPtr>::const_iterator client = m_clients.find(address);
if (client != m_clients.end())
return client->second;
return CECClientPtr();
}
CECClientPtr CCECProcessor::GetPrimaryClient(void)
{
CLockObject lock(m_mutex);
std::map<cec_logical_address, CECClientPtr>::const_iterator client = m_clients.begin();
if (client != m_clients.end())
return client->second;
return CECClientPtr();
}
CCECBusDevice *CCECProcessor::GetPrimaryDevice(void)
{
return m_busDevices->At(GetLogicalAddress());
}
cec_logical_address CCECProcessor::GetLogicalAddress(void)
{
cec_logical_addresses addresses = GetLogicalAddresses();
return addresses.primary;
}
cec_logical_addresses CCECProcessor::GetLogicalAddresses(void)
{
CLockObject lock(m_mutex);
cec_logical_addresses addresses;
addresses.Clear();
for (std::map<cec_logical_address, CECClientPtr>::const_iterator client = m_clients.begin(); client != m_clients.end(); client++)
addresses.Set(client->first);
return addresses;
}
bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const
{
CCECBusDevice *device = GetDevice(address);
return device && device->IsHandledByLibCEC();
}
bool CCECProcessor::IsRunningLatestFirmware(void)
{
return m_communication && m_communication->IsOpen() ?
m_communication->IsRunningLatestFirmware() :
true;
}
void CCECProcessor::SwitchMonitoring(bool bSwitchTo)
{
{
CLockObject lock(m_mutex);
m_bMonitor = bSwitchTo;
}
if (bSwitchTo)
UnregisterClients();
}
void CCECProcessor::HandleLogicalAddressLost(cec_logical_address oldAddress)
{
m_libcec->AddLog(CEC_LOG_NOTICE, "logical address %x was taken by another device, allocating a new address", oldAddress);
m_bStallCommunication = true;
GetTV()->SetDeviceStatus(CEC_DEVICE_STATUS_UNKNOWN);
if (oldAddress < CECDEVICE_BROADCAST)
m_busDevices->At(oldAddress)->SetDeviceStatus(CEC_DEVICE_STATUS_UNKNOWN);
GetTV()->GetVendorId(CECDEVICE_UNREGISTERED);
CECClientPtr client = GetClient(oldAddress);
if (!client)
client = GetPrimaryClient();
if (client)
{
if (m_addrAllocator)
while (m_addrAllocator->IsRunning()) Sleep(5);
delete m_addrAllocator;
m_addrAllocator = new CCECAllocateLogicalAddress(this, client);
m_addrAllocator->CreateThread();
}
}
void CCECProcessor::HandlePhysicalAddressChanged(uint16_t iNewAddress)
{
if (!m_bStallCommunication) {
CECClientPtr client = GetPrimaryClient();
if (!!client)
client->SetPhysicalAddress(iNewAddress);
}
}
uint16_t CCECProcessor::GetAdapterVendorId(void) const
{
return m_communication ? m_communication->GetAdapterVendorId() : 0;
}
uint16_t CCECProcessor::GetAdapterProductId(void) const
{
return m_communication ? m_communication->GetAdapterProductId() : 0;
}
CCECAllocateLogicalAddress::CCECAllocateLogicalAddress(CCECProcessor* processor, CECClientPtr client) :
m_processor(processor),
m_client(client) { }
void* CCECAllocateLogicalAddress::Process(void)
{
m_processor->AllocateLogicalAddresses(m_client);
return NULL;
}