X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Flib%2FCECProcessor.cpp;h=f4e485ac3b6da0a7e3c01462780c037fc744c290;hb=42d28d15d07f893b491051970ade1bd56bb596eb;hp=11065ebfec6e5b6bb7198986825db3f93d8c9bd9;hpb=ef79edcce663c6156731ad73d63acbe0c6be85b9;p=deb_libcec.git diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index 11065eb..034c7e6 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -1,7 +1,7 @@ /* * This file is part of the libCEC(R) library. * - * libCEC(R) is Copyright (C) 2011 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. * libCEC(R) is an original work, containing original code. * * libCEC(R) is a trademark of Pulse-Eight Limited. @@ -32,7 +32,7 @@ #include "CECProcessor.h" -#include "AdapterCommunication.h" +#include "adapter/USBCECAdapterCommunication.h" #include "devices/CECBusDevice.h" #include "devices/CECAudioSystem.h" #include "devices/CECPlaybackDevice.h" @@ -41,1265 +41,793 @@ #include "devices/CECTV.h" #include "implementations/CECCommandHandler.h" #include "LibCEC.h" -#include "util/StdString.h" -#include "platform/timeutils.h" +#include "CECClient.h" +#include "CECTypeUtils.h" +#include "platform/util/timeutils.h" +#include "platform/util/util.h" using namespace CEC; using namespace std; +using namespace PLATFORM; -CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) : - m_bStarted(false), - m_iHDMIPort(CEC_DEFAULT_HDMI_PORT), - m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE), - m_lastInitiator(CECDEVICE_UNKNOWN), - m_strDeviceName(strDeviceName), - m_controller(controller), - m_bMonitor(false), - m_busScan(NULL) -{ - m_communication = new CAdapterCommunication(this); - m_logicalAddresses.Clear(); - m_logicalAddresses.Set(iLogicalAddress); - m_types.clear(); - for (int iPtr = 0; iPtr <= 16; iPtr++) - m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr, iPtr == iLogicalAddress ? iPhysicalAddress : 0); -} +#define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000 +#define ACTIVE_SOURCE_CHECK_TIMEOUT 10000 + +#define ToString(x) CCECTypeUtils::ToString(x) -CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types) : - m_bStarted(false), - m_iHDMIPort(CEC_DEFAULT_HDMI_PORT), - m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE), - m_strDeviceName(strDeviceName), - m_types(types), - m_controller(controller), - m_bMonitor(false) +CCECProcessor::CCECProcessor(CLibCEC *libcec) : + m_bInitialised(false), + m_communication(NULL), + m_libcec(libcec), + m_iStandardLineTimeout(3), + m_iRetryLineTimeout(3), + m_iLastTransmission(0) { - m_communication = new CAdapterCommunication(this); - m_logicalAddresses.Clear(); - for (int iPtr = 0; iPtr < 16; iPtr++) - { - switch(iPtr) - { - case CECDEVICE_AUDIOSYSTEM: - m_busDevices[iPtr] = new CCECAudioSystem(this, (cec_logical_address) iPtr, 0xFFFF); - break; - case CECDEVICE_PLAYBACKDEVICE1: - case CECDEVICE_PLAYBACKDEVICE2: - case CECDEVICE_PLAYBACKDEVICE3: - m_busDevices[iPtr] = new CCECPlaybackDevice(this, (cec_logical_address) iPtr, 0xFFFF); - break; - case CECDEVICE_RECORDINGDEVICE1: - case CECDEVICE_RECORDINGDEVICE2: - case CECDEVICE_RECORDINGDEVICE3: - m_busDevices[iPtr] = new CCECRecordingDevice(this, (cec_logical_address) iPtr, 0xFFFF); - break; - case CECDEVICE_TUNER1: - case CECDEVICE_TUNER2: - case CECDEVICE_TUNER3: - case CECDEVICE_TUNER4: - m_busDevices[iPtr] = new CCECTuner(this, (cec_logical_address) iPtr, 0xFFFF); - break; - case CECDEVICE_TV: - m_busDevices[iPtr] = new CCECTV(this, (cec_logical_address) iPtr, 0); - break; - default: - m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr, 0xFFFF); - break; - } - } + m_busDevices = new CCECDeviceMap(this); } CCECProcessor::~CCECProcessor(void) { - if (m_busScan) - { - m_busScan->StopThread(); - delete m_busScan; - } - - m_startCondition.Broadcast(); - StopThread(); - - delete m_communication; - m_communication = NULL; - m_controller = NULL; - for (unsigned int iPtr = 0; iPtr < 16; iPtr++) - delete m_busDevices[iPtr]; + Close(); + DELETE_AND_NULL(m_busDevices); } -bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */) +bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { - CLockObject lock(&m_mutex); - if (!m_communication || m_communication->IsOpen()) - { - m_controller->AddLog(CEC_LOG_ERROR, "connection already opened"); - return false; - } - - if (!m_communication->Open(strPort, iBaudRate, iTimeoutMs)) - { - m_controller->AddLog(CEC_LOG_ERROR, "could not open a connection"); + CLockObject lock(m_mutex); + // open a connection + if (!OpenConnection(strPort, iBaudRate, iTimeoutMs)) return false; - } - if (CreateThread()) + // create the processor thread + if (!IsRunning()) { - if (!m_startCondition.Wait(&m_mutex) || !m_bStarted) + if (!CreateThread()) { - m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); + m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); return false; } - - lock.Leave(); - if (SetAckMask(m_logicalAddresses.AckMask()) && - SetHDMIPort(m_iBaseDevice, m_iHDMIPort, true)) - { - m_controller->AddLog(CEC_LOG_ERROR, "processor thread started"); - m_busScan = new CCECBusScan(this); - m_busScan->CreateThread(true); - return true; - } - else - { - m_controller->AddLog(CEC_LOG_ERROR, "failed to initialise the processor"); - } } - m_controller->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); - return false; + return true; } -bool CCECProcessor::TryLogicalAddress(cec_logical_address address) +void CCECProcessor::Close(void) { - if (m_busDevices[address]->TryLogicalAddress()) - { - /* only set our OSD name and active source for the primary device */ - if (m_logicalAddresses.IsEmpty()) - { - m_busDevices[address]->m_strDeviceName = m_strDeviceName; - m_busDevices[address]->m_bActiveSource = true; - } - m_logicalAddresses.Set(address); - return true; - } + // mark as uninitialised + SetCECInitialised(false); - return false; -} + // stop the processor + StopThread(); -bool CCECProcessor::FindLogicalAddressRecordingDevice(void) -{ - AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'"); - return TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1) || - TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2) || - TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3); + // close the connection + DELETE_AND_NULL(m_communication); } -bool CCECProcessor::FindLogicalAddressTuner(void) +void CCECProcessor::ResetMembers(void) { - AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'"); - return TryLogicalAddress(CECDEVICE_TUNER1) || - TryLogicalAddress(CECDEVICE_TUNER2) || - TryLogicalAddress(CECDEVICE_TUNER3) || - TryLogicalAddress(CECDEVICE_TUNER4); -} + // close the connection + DELETE_AND_NULL(m_communication); -bool CCECProcessor::FindLogicalAddressPlaybackDevice(void) -{ - AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'"); - return TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1) || - TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2) || - TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3); + // reset the other members to the initial state + m_iStandardLineTimeout = 3; + m_iRetryLineTimeout = 3; + m_iLastTransmission = 0; + m_busDevices->ResetDeviceStatus(); } -bool CCECProcessor::FindLogicalAddressAudioSystem(void) +bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */) { - AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audio'"); - return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM); -} + bool bReturn(false); + CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); -bool CCECProcessor::FindLogicalAddresses(void) -{ - bool bReturn(true); - m_logicalAddresses.Clear(); - CStdString strLog; + // ensure that a previous connection is closed + Close(); + + // reset all member to the initial state + ResetMembers(); - for (unsigned int iPtr = 0; iPtr < 5; iPtr++) + // check whether the Close() method deleted any previous connection + if (m_communication) { - if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) - continue; - - strLog.Format("%s - device %d: type %d", __FUNCTION__, iPtr, m_types.types[iPtr]); - AddLog(CEC_LOG_DEBUG, strLog); - - if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE) - bReturn &= FindLogicalAddressRecordingDevice(); - if (m_types.types[iPtr] == CEC_DEVICE_TYPE_TUNER) - bReturn &= FindLogicalAddressTuner(); - if (m_types.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE) - bReturn &= FindLogicalAddressPlaybackDevice(); - if (m_types.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM) - bReturn &= FindLogicalAddressAudioSystem(); + m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed"); + return bReturn; } - return bReturn; -} - -void *CCECProcessor::Process(void) -{ - bool bParseFrame(false); - cec_command command; - CCECAdapterMessage msg; + // create a new connection + m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); - if (m_logicalAddresses.IsEmpty() && !FindLogicalAddresses()) - { - CLockObject lock(&m_mutex); - m_controller->AddLog(CEC_LOG_ERROR, "could not detect our logical addresses"); - m_startCondition.Signal(); - return NULL; - } - else + // open a new connection + unsigned iConnectTry(0); + while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false) { - m_busDevices[m_logicalAddresses.primary]->TransmitPhysicalAddress(); - CLockObject lock(&m_mutex); - m_bStarted = true; - m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started"); - m_startCondition.Signal(); + m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry); + m_communication->Close(); + CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT); } - while (!IsStopped()) - { - command.Clear(); - msg.clear(); + m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened"); - { - CLockObject lock(&m_mutex); - if (m_commandBuffer.Pop(command)) - { - bParseFrame = true; - } - else if (m_communication->IsOpen() && m_communication->Read(msg, 50)) - { - if ((bParseFrame = (ParseMessage(msg) && !IsStopped())) == true) - command = m_currentframe; - } - } + // mark as initialised + SetCECInitialised(true); - if (bParseFrame) - ParseCommand(command); - bParseFrame = false; - - Sleep(5); - - m_controller->CheckKeypressTimeout(); - } - - if (m_communication) - m_communication->Close(); - - return NULL; + return bReturn; } -bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */) +bool CCECProcessor::CECInitialised(void) { - bool bReturn(false); - - if (!IsRunning()) - return bReturn; - - cec_logical_address addr = m_logicalAddresses.primary; + CLockObject lock(m_threadMutex); + return m_bInitialised; +} - if (type != CEC_DEVICE_TYPE_RESERVED) +void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */) +{ { - for (uint8_t iPtr = 0; iPtr < 16; iPtr++) - { - if (m_logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type) - { - addr = (cec_logical_address) iPtr; - break; - } - } + CLockObject lock(m_mutex); + m_bInitialised = bSetTo; } + if (!bSetTo) + UnregisterClients(); +} - bReturn = m_busDevices[CECDEVICE_TV]->PowerOn() && - m_busDevices[addr]->TransmitActiveSource() && - SetStreamPath(m_busDevices[addr]->GetPhysicalAddress(false)); - - if (bReturn && (m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || - m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)) +bool CCECProcessor::TryLogicalAddress(cec_logical_address address) +{ + // find the device + CCECBusDevice *device = m_busDevices->At(address); + if (device) { - bReturn = ((CCECPlaybackDevice *)m_busDevices[addr])->TransmitDeckStatus(CECDEVICE_TV); + // check if it's already marked as present or used + if (device->IsPresent() || device->IsHandledByLibCEC()) + return false; + + // poll the LA if not + SetAckMask(0); + return device->TryLogicalAddress(); } - return bReturn; + return false; } -bool CCECProcessor::SetActiveSource(cec_logical_address iAddress) +void CCECProcessor::ReplaceHandlers(void) { - return SetStreamPath(m_busDevices[iAddress]->GetPhysicalAddress(false)); -} + if (!CECInitialised()) + return; -bool CCECProcessor::SetActiveView(void) -{ - return SetActiveSource(m_types.IsEmpty() ? CEC_DEVICE_TYPE_RESERVED : m_types[0]); + // check each device + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + it->second->ReplaceHandler(true); } -bool CCECProcessor::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */) +void CCECProcessor::CheckPendingActiveSource(void) { - bool bReturn(false); + if (!CECInitialised()) + return; - CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); - if (device) + // check each device + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) { - ((CCECPlaybackDevice *) device)->SetDeckControlMode(mode); - if (bSendUpdate) - ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV); - bReturn = true; + if (it->second->GetHandler()->ActiveSourcePending()) + it->second->ActivateSource(); } - - return bReturn; } -bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */) +bool CCECProcessor::OnCommandReceived(const cec_command &command) { - bool bReturn(false); - - CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); - if (device) - { - ((CCECPlaybackDevice *) device)->SetDeckStatus(info); - if (bSendUpdate) - ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV); - bReturn = true; - } - - return bReturn; + return m_inBuffer.Push(command); } -bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */) +void *CCECProcessor::Process(void) { - bool bReturn(false); + m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); - CStdString strLog; - strLog.Format("setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); - AddLog(CEC_LOG_DEBUG, strLog); - - m_iBaseDevice = iBaseDevice; - m_iHDMIPort = iPort; - if (!m_bStarted && !bForce) - return true; + cec_command command; + CTimeout activeSourceCheck(ACTIVE_SOURCE_CHECK_TIMEOUT); - uint16_t iPhysicalAddress(0); - iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress(); - uint16_t iPos = 0; - if (iPhysicalAddress == 0) - iPos = 0x1000; - else if (iPhysicalAddress % 0x1000 == 0) - iPos = 0x100; - else if (iPhysicalAddress % 0x100 == 0) - iPos = 0x10; - else if (iPhysicalAddress % 0x10 == 0) - iPos = 0x1; - - while(!bReturn && iPos > 0) + // as long as we're not being stopped and the connection is open + while (!IsStopped() && m_communication->IsOpen()) { - iPhysicalAddress += (uint16_t)(iPort * iPos); - strLog.Format("checking physical address %4x", iPhysicalAddress); - AddLog(CEC_LOG_DEBUG, strLog); - if (PhysicalAddressInUse(iPhysicalAddress)) - { - strLog.Format("physical address %4x is in use", iPhysicalAddress); - AddLog(CEC_LOG_DEBUG, strLog); - iPos = (iPos == 1) ? 0 : iPos / 0x10; - } - else + // wait for a new incoming command, and process it + if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME)) + ProcessCommand(command); + + if (CECInitialised()) { - strLog.Format("physical address %4x is free", iPhysicalAddress); - AddLog(CEC_LOG_DEBUG, strLog); - SetPhysicalAddress(iPhysicalAddress); - bReturn = true; - } - } + // check clients for keypress timeouts + m_libcec->CheckKeypressTimeout(); - return bReturn; -} + // check if we need to replace handlers + ReplaceHandlers(); -bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) -{ - for (unsigned int iPtr = 0; iPtr < 15; iPtr++) - { - if (m_busDevices[iPtr]->GetPhysicalAddress(false) == iPhysicalAddress) - return true; + // check whether we need to activate a source, if it failed before + if (activeSourceCheck.TimeLeft() == 0) + { + CheckPendingActiveSource(); + activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_TIMEOUT); + } + } } - return false; + + return NULL; } -bool CCECProcessor::SetStreamPath(uint16_t iStreamPath) +bool CCECProcessor::ActivateSource(uint16_t iStreamPath) { bool bReturn(false); + // find the device with the given PA CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath); + // and make it the active source when found if (device) - { - device->SetActiveDevice(); - bReturn = true; - } + bReturn = device->ActivateSource(); + else + m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath); return bReturn; } -bool CCECProcessor::TransmitInactiveSource(void) +void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout) { - if (!IsRunning()) - return false; + CLockObject lock(m_mutex); + m_iStandardLineTimeout = iTimeout; +} - if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary]) - return m_busDevices[m_logicalAddresses.primary]->TransmitInactiveSource(); - return false; +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) { CStdString strTx; + + // initiator and destination strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination); + + // append the opcode if (data.opcode_set) strTx.AppendFormat(":%02x", (uint8_t)data.opcode); + // append the parameters for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++) strTx.AppendFormat(":%02x", data.parameters[iPtr]); - m_controller->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); + + // and log it + m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); } -bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress) +bool CCECProcessor::PollDevice(cec_logical_address iAddress) { - if (m_logicalAddresses.primary != iLogicalAddress) - { - CStdString strLog; - strLog.Format("<< setting primary logical address to %1x", iLogicalAddress); - m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); - m_logicalAddresses.primary = iLogicalAddress; - m_logicalAddresses.Set(iLogicalAddress); - return SetAckMask(m_logicalAddresses.AckMask()); - } + // try to find the primary device + CCECBusDevice *primary = GetPrimaryDevice(); + // poll the destination, with the primary as source + if (primary) + return primary->TransmitPoll(iAddress); - return true; + // try to find the destination + CCECBusDevice *device = m_busDevices->At(iAddress); + // and poll the destination, with the same LA as source + if (device) + return device->TransmitPoll(iAddress); + + return false; } -bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */) +CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */) { - for (uint8_t iPtr = 0; iPtr < 16; iPtr++) - { - if (m_logicalAddresses[iPtr]) - m_busDevices[iPtr]->SetMenuState(state); - } - - if (bSendUpdate) - m_busDevices[m_logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV); - - return true; + return m_busDevices ? + m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) : + NULL; } -bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress) +CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const { - if (!m_logicalAddresses.IsEmpty()) - { - for (uint8_t iPtr = 0; iPtr < 15; iPtr++) - if (m_logicalAddresses[iPtr]) - m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress); - return SetActiveView(); - } - return false; + return m_busDevices ? + m_busDevices->At(address) : + NULL; } -bool CCECProcessor::SwitchMonitoring(bool bEnable) +cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */) { - CStdString strLog; - strLog.Format("== %s monitoring mode ==", bEnable ? "enabling" : "disabling"); - m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str()); + // get the device that is marked as active source from the device map + CCECBusDevice *activeSource = m_busDevices->GetActiveSource(); + if (activeSource) + return activeSource->GetLogicalAddress(); + if (bRequestActiveSource) { - CLockObject lock(&m_mutex); - m_bMonitor = bEnable; - - if (bEnable) - { - if (!m_busScan) - { - m_busScan = new CCECBusScan(this); - m_busScan->CreateThread(true); - } - } - else + // request the active source from the bus + CCECBusDevice *primary = GetPrimaryDevice(); + if (primary) { - if (m_busScan) - { - m_busScan->StopThread(); - delete m_busScan; - m_busScan = NULL; - } + primary->RequestActiveSource(); + return GetActiveSource(false); } } - if (bEnable) - return SetAckMask(0); - else - return SetAckMask(m_logicalAddresses.AckMask()); + // unknown or none + return CECDEVICE_UNKNOWN; } -bool CCECProcessor::PollDevice(cec_logical_address iAddress) +bool CCECProcessor::IsActiveSource(cec_logical_address iAddress) { - if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress]) - { - return m_logicalAddresses.primary == CECDEVICE_UNKNOWN ? - m_busDevices[iAddress]->TransmitPoll(iAddress) : - m_busDevices[m_logicalAddresses.primary]->TransmitPoll(iAddress); - } - return false; + CCECBusDevice *device = m_busDevices->At(iAddress); + return device && device->IsActiveSource(); } -uint8_t CCECProcessor::VolumeUp(bool bWait /* = true */) -{ - uint8_t status = 0; - if (IsActiveDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeUp(bWait); - - return status; -} - -uint8_t CCECProcessor::VolumeDown(bool bWait /* = true */) +bool CCECProcessor::Transmit(const cec_command &data) { - uint8_t status = 0; - if (IsActiveDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeDown(bWait); + uint8_t iMaxTries(0); + bool bRetry(true); + uint8_t iTries(0); - return status; -} + // get the current timeout setting + uint8_t iLineTimeout(GetStandardLineTimeout()); -uint8_t CCECProcessor::MuteAudio(bool bWait /* = true */) -{ - uint8_t status = 0; - if (IsActiveDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->MuteAudio(bWait); + // reset the state of this message to 'unknown' + cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; - return status; -} + LogOutput(data); -CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh /* = false */) const -{ - if (m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false) == iPhysicalAddress) - return m_busDevices[m_logicalAddresses.primary]; + // find the initiator device + CCECBusDevice *initiator = m_busDevices->At(data.initiator); + if (!initiator) + { + m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator"); + return false; + } - CCECBusDevice *device = NULL; - for (unsigned int iPtr = 0; iPtr < 16; iPtr++) + // find the destination device, if it's not the broadcast address + if (data.destination != CECDEVICE_BROADCAST) { - if (m_busDevices[iPtr]->GetPhysicalAddress(bRefresh) == iPhysicalAddress) + // check if the device is marked as handled by libCEC + CCECBusDevice *destination = m_busDevices->At(data.destination); + if (destination && destination->IsHandledByLibCEC()) { - device = m_busDevices[iPtr]; - break; + // and reject the command if it's trying to send data to a device that is handled by libCEC + m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!"); + return false; } } - return device; -} - -CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const -{ - CCECBusDevice *device = NULL; + { + CLockObject lock(m_mutex); + m_iLastTransmission = GetTimeMs(); + // set the number of tries + iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; + } - for (unsigned int iPtr = 0; iPtr < 16; iPtr++) + // and try to send the command + while (bRetry && ++iTries < iMaxTries) { - if (m_busDevices[iPtr]->m_type == type) - { - device = m_busDevices[iPtr]; - break; - } + if (initiator->IsUnsupportedFeature(data.opcode)) + return false; + + adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ? + m_communication->Write(data, bRetry, iLineTimeout) : + ADAPTER_MESSAGE_STATE_ERROR; + iLineTimeout = m_iRetryLineTimeout; } - return device; + return adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED; } -cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress) +void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) { - return m_busDevices[iAddress]->GetCecVersion(); + 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); } -cec_osd_name CCECProcessor::GetDeviceOSDName(cec_logical_address iAddress) +void CCECProcessor::ProcessCommand(const cec_command &command) { - CStdString strOSDName = m_busDevices[iAddress]->GetOSDName(); - cec_osd_name retVal; + // log the command + CStdString dataStr; + dataStr.Format(">> %1x%1x", command.initiator, command.destination); + if (command.opcode_set == 1) + dataStr.AppendFormat(":%02x", command.opcode); + for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++) + dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]); + m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str()); - snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str()); - retVal.device = iAddress; + // find the initiator + CCECBusDevice *device = m_busDevices->At(command.initiator); - return retVal; + if (device) + device->HandleCommand(command); } -bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language) +bool CCECProcessor::IsPresentDevice(cec_logical_address address) { - if (m_busDevices[iAddress]) - { - *language = m_busDevices[iAddress]->GetMenuLanguage(); - return (strcmp(language->language, "???") != 0); - } - return false; + CCECBusDevice *device = m_busDevices->At(address); + return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT; } -uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress) +bool CCECProcessor::IsPresentDeviceType(cec_device_type type) { - if (m_busDevices[iAddress]) - return m_busDevices[iAddress]->GetVendorId(); - return false; + CECDEVICEVEC devices; + m_busDevices->GetByType(type, devices); + CCECDeviceMap::FilterActive(devices); + return !devices.empty(); } -cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress) +uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const { - if (m_busDevices[iAddress]) - return m_busDevices[iAddress]->GetPowerStatus(); - return CEC_POWER_STATUS_UNKNOWN; + return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS; } -bool CCECProcessor::Transmit(const cec_command &data) +bool CCECProcessor::SetAckMask(uint16_t iMask) { - bool bReturn(false); - LogOutput(data); - - CCECAdapterMessage *output = new CCECAdapterMessage(data); - bReturn = Transmit(output); - delete output; + return m_communication ? m_communication->SetAckMask(iMask) : 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::Transmit(CCECAdapterMessage *output) +bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address) { - bool bReturn(false); - CLockObject lock(&m_mutex); - { - m_communication->SetLineTimeout(3); - - do - { - if (output->tries > 0) - m_communication->SetLineTimeout(5); - - CLockObject msgLock(&output->mutex); - if (!m_communication || !m_communication->Write(output)) - return bReturn; - else - { - output->condition.Wait(&output->mutex); - if (output->state != ADAPTER_MESSAGE_STATE_SENT) - { - m_controller->AddLog(CEC_LOG_ERROR, "command was not sent"); - return bReturn; - } - } - - if (output->transmit_timeout > 0) - { - if ((bReturn = WaitForTransmitSucceeded(output)) == false) - m_controller->AddLog(CEC_LOG_DEBUG, "did not receive ack"); - } - else - bReturn = true; - }while (output->transmit_timeout > 0 && output->needs_retry() && ++output->tries <= output->maxTries); - } - - m_communication->SetLineTimeout(3); + 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; } -void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) +bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logical_address address) { - m_controller->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message"); - - cec_command command; - // TODO - cec_command::Format(command, m_logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT); - command.parameters.PushBack((uint8_t)opcode); - command.parameters.PushBack((uint8_t)reason); - - Transmit(command); + CCECBusDevice *device = m_busDevices->At(address); + return device ? device->PowerOn(initiator) : false; } -bool CCECProcessor::WaitForTransmitSucceeded(CCECAdapterMessage *message) +bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */) { - bool bError(false); - bool bTransmitSucceeded(false); - uint8_t iPacketsLeft(message->size() / 4); - - int64_t iNow = GetTimeMs(); - int64_t iTargetTime = iNow + message->transmit_timeout; - - while (!bTransmitSucceeded && !bError && (message->transmit_timeout == 0 || iNow < iTargetTime)) + bool bReturn(false); + // open a connection if no connection has been opened + if (!m_communication && strPort) { - CCECAdapterMessage msg; - - if (!m_communication->Read(msg, message->transmit_timeout > 0 ? (int32_t)(iTargetTime - iNow) : 1000)) - { - iNow = GetTimeMs(); - continue; - } - - if (msg.message() == MSGCODE_FRAME_START && msg.ack()) - { - m_busDevices[msg.initiator()]->GetHandler()->HandlePoll(msg.initiator(), msg.destination()); - m_lastInitiator = msg.initiator(); - iNow = GetTimeMs(); - continue; - } - - bError = msg.is_error(); - if (msg.message() == MSGCODE_RECEIVE_FAILED && - m_lastInitiator != CECDEVICE_UNKNOWN && - !m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed()) - { - iNow = GetTimeMs(); - continue; - } - - if (bError) + IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort); + CTimeout timeout(CEC_DEFAULT_CONNECT_TIMEOUT); + int iConnectTry(0); + while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false) { - message->reply = msg.message(); - m_controller->AddLog(CEC_LOG_DEBUG, msg.ToString()); + m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry); + comm->Close(); + Sleep(CEC_DEFAULT_TRANSMIT_RETRY_WAIT); } - else + if (comm->IsOpen()) { - switch(msg.message()) - { - case MSGCODE_COMMAND_ACCEPTED: - m_controller->AddLog(CEC_LOG_DEBUG, msg.ToString()); - if (iPacketsLeft > 0) - iPacketsLeft--; - break; - case MSGCODE_TRANSMIT_SUCCEEDED: - m_controller->AddLog(CEC_LOG_DEBUG, msg.ToString()); - bTransmitSucceeded = (iPacketsLeft == 0); - bError = !bTransmitSucceeded; - message->reply = MSGCODE_TRANSMIT_SUCCEEDED; - break; - default: - // ignore other data while waiting - break; - } - - iNow = GetTimeMs(); + bReturn = comm->StartBootloader(); + DELETE_AND_NULL(comm); } + return bReturn; + } + else + { + m_communication->StartBootloader(); + Close(); + bReturn = true; } - return bTransmitSucceeded && !bError; + return bReturn; } -bool CCECProcessor::ParseMessage(const CCECAdapterMessage &msg) +bool CCECProcessor::PingAdapter(void) { - bool bEom(false); - bool bIsError(msg.is_error()); - - if (msg.empty()) - return bEom; + return m_communication->PingAdapter(); +} - switch(msg.message()) - { - case MSGCODE_FRAME_START: - { - m_currentframe.Clear(); - if (msg.size() >= 2) - { - m_currentframe.initiator = msg.initiator(); - m_currentframe.destination = msg.destination(); - m_currentframe.ack = msg.ack(); - m_currentframe.eom = msg.eom(); - } - if (m_currentframe.ack == true) - { - m_lastInitiator = m_currentframe.initiator; - m_busDevices[m_lastInitiator]->GetHandler()->HandlePoll(m_currentframe.initiator, m_currentframe.destination); - } - } - break; - case MSGCODE_RECEIVE_FAILED: - { - if (m_lastInitiator != CECDEVICE_UNKNOWN) - bIsError = m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed(); - } - break; - case MSGCODE_FRAME_DATA: - { - if (msg.size() >= 2) - { - m_currentframe.PushBack(msg[1]); - m_currentframe.eom = msg.eom(); - } - bEom = msg.eom(); - } - break; - default: - break; - } +void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination) +{ + CCECBusDevice *device = m_busDevices->At(destination); + if (device) + device->HandlePollFrom(initiator); +} - m_controller->AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString()); - return bEom; +bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator) +{ + CCECBusDevice *device = m_busDevices->At(initiator); + return !device || !device->HandleReceiveFailed(); } -void CCECProcessor::ParseCommand(cec_command &command) +bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress) { - CStdString dataStr; - dataStr.Format(">> %1x%1x:%02x", command.initiator, command.destination, command.opcode); - for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++) - dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]); - m_controller->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str()); + // stream path changes are sent by the TV + return GetTV()->GetHandler()->TransmitSetStreamPath(iPhysicalAddress); +} - if (!m_bMonitor && command.initiator >= CECDEVICE_TV && command.initiator <= CECDEVICE_BROADCAST) - m_busDevices[(uint8_t)command.initiator]->HandleCommand(command); +bool CCECProcessor::CanPersistConfiguration(void) +{ + return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false; } -cec_logical_addresses CCECProcessor::GetActiveDevices(void) +bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration) { - cec_logical_addresses addresses; - addresses.Clear(); - for (unsigned int iPtr = 0; iPtr < 15; iPtr++) - { - if (m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT) - addresses.Set((cec_logical_address) iPtr); - } - return addresses; + return m_communication ? m_communication->PersistConfiguration(configuration) : false; } -bool CCECProcessor::IsActiveDevice(cec_logical_address address) +void CCECProcessor::RescanActiveDevices(void) { - return m_busDevices[address]->GetStatus() == CEC_DEVICE_STATUS_PRESENT; + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + it->second->GetStatus(true); } -bool CCECProcessor::IsActiveDeviceType(cec_device_type type) +bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { - for (unsigned int iPtr = 0; iPtr < 15; iPtr++) - { - if (m_busDevices[iPtr]->GetType() == type && m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT) - return true; - } + if (!OpenConnection(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs, false)) + return false; - return false; + config->iFirmwareVersion = m_communication->GetFirmwareVersion(); + config->iPhysicalAddress = m_communication->GetPhysicalAddress(); + config->iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); + + return true; } -uint16_t CCECProcessor::GetPhysicalAddress(void) const +bool CCECProcessor::TransmitPendingActiveSourceCommands(void) { - if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary]) - return m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false); - return false; + bool bReturn(true); + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + bReturn &= it->second->TransmitPendingActiveSourceCommands(); + return bReturn; } -void CCECProcessor::SetCurrentButton(cec_user_control_code iButtonCode) +CCECTV *CCECProcessor::GetTV(void) const { - m_controller->SetCurrentButton(iButtonCode); + return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV)); } -void CCECProcessor::AddCommand(const cec_command &command) +CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const { - m_controller->AddCommand(command); + return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM)); } -void CCECProcessor::AddKey(cec_keypress &key) +CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const { - m_controller->AddKey(key); + return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address)); } -void CCECProcessor::AddKey(void) +CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const { - m_controller->AddKey(); + return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address)); } -void CCECProcessor::AddLog(cec_log_level level, const CStdString &strMessage) +CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const { - m_controller->AddLog(level, strMessage); + return CCECBusDevice::AsTuner(m_busDevices->At(address)); } -bool CCECProcessor::SetAckMask(uint16_t iMask) +bool CCECProcessor::RegisterClient(CCECClient *client) { - bool bReturn(false); - CStdString strLog; - strLog.Format("setting ackmask to %2x", iMask); - m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str()); + if (!client) + return false; - CCECAdapterMessage *output = new CCECAdapterMessage; + libcec_configuration &configuration = *client->GetConfiguration(); - output->push_back(MSGSTART); - output->push_escaped(MSGCODE_SET_ACK_MASK); - output->push_escaped(iMask >> 8); - output->push_escaped((uint8_t)iMask); - output->push_back(MSGEND); + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3 && configuration.bMonitorOnly == 1) + return true; - if ((bReturn = Transmit(output)) == false) - m_controller->AddLog(CEC_LOG_ERROR, "could not set the ackmask"); + if (!CECInitialised()) + { + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised"); + return false; + } - delete output; + // ensure that we know the vendor id of the TV + GetTV()->GetVendorId(CECDEVICE_UNREGISTERED); - return bReturn; -} + // unregister the client first if it's already been marked as registered + if (client->IsRegistered()) + UnregisterClient(client); -bool CCECProcessor::SendKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = false */) -{ - return m_busDevices[iDestination]->SendKeypress(key, bWait); -} + // get the configuration from the client + m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion)); -bool CCECProcessor::SendKeyRelease(cec_logical_address iDestination, bool bWait /* = false */) -{ - return m_busDevices[iDestination]->SendKeyRelease(bWait); -} + // mark as uninitialised and unregistered + client->SetRegistered(false); + client->SetInitialised(false); -const char *CCECProcessor::ToString(const cec_menu_state state) -{ - switch (state) + // get the current ackmask, so we can restore it if polling fails + uint16_t iPreviousMask(m_communication->GetAckMask()); + + // find logical addresses for this client + if (!client->AllocateLogicalAddresses()) { - case CEC_MENU_STATE_ACTIVATED: - return "activated"; - case CEC_MENU_STATE_DEACTIVATED: - return "deactivated"; - default: - return "unknown"; + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types"); + SetAckMask(iPreviousMask); + return false; } -} -const char *CCECProcessor::ToString(const cec_version version) -{ - switch (version) + // register this client on the new addresses + CECDEVICEVEC devices; + m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) { - case CEC_VERSION_1_2: - return "1.2"; - case CEC_VERSION_1_2A: - return "1.2a"; - case CEC_VERSION_1_3: - return "1.3"; - case CEC_VERSION_1_3A: - return "1.3a"; - case CEC_VERSION_1_4: - return "1.4"; - default: - return "unknown"; + // replace a previous client + CLockObject lock(m_mutex); + m_clients.erase((*it)->GetLogicalAddress()); + m_clients.insert(make_pair((*it)->GetLogicalAddress(), client)); } -} -const char *CCECProcessor::ToString(const cec_power_status status) -{ - switch (status) + // get the settings from the rom + if (configuration.bGetSettingsFromROM == 1) { - case CEC_POWER_STATUS_ON: - return "on"; - case CEC_POWER_STATUS_STANDBY: - return "standby"; - case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY: - return "in transition from on to standby"; - case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON: - return "in transition from standby to on"; - default: - return "unknown"; + libcec_configuration config; + m_communication->GetConfiguration(config); + + CLockObject lock(m_mutex); + if (!config.deviceTypes.IsEmpty()) + configuration.deviceTypes = config.deviceTypes; + if (CLibCEC::IsValidPhysicalAddress(config.iPhysicalAddress)) + configuration.iPhysicalAddress = config.iPhysicalAddress; + snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName); } -} -const char *CCECProcessor::ToString(const cec_logical_address address) -{ - switch(address) + // set the firmware version and build date + configuration.serverVersion = LIBCEC_VERSION_CURRENT; + configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); + configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); + + // mark the client as registered + client->SetRegistered(true); + + // set the new ack mask + bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) && + // and initialise the client + client->OnRegister(); + + // log the new registration + CStdString strLog; + strLog.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); + + // display a warning if the firmware can be upgraded + if (bReturn && !IsRunningLatestFirmware()) { - case CECDEVICE_AUDIOSYSTEM: - return "Audio"; - case CECDEVICE_BROADCAST: - return "Broadcast"; - case CECDEVICE_FREEUSE: - return "Free use"; - case CECDEVICE_PLAYBACKDEVICE1: - return "Playback 1"; - case CECDEVICE_PLAYBACKDEVICE2: - return "Playback 2"; - case CECDEVICE_PLAYBACKDEVICE3: - return "Playback 3"; - case CECDEVICE_RECORDINGDEVICE1: - return "Recorder 1"; - case CECDEVICE_RECORDINGDEVICE2: - return "Recorder 2"; - case CECDEVICE_RECORDINGDEVICE3: - return "Recorder 3"; - case CECDEVICE_RESERVED1: - return "Reserved 1"; - case CECDEVICE_RESERVED2: - return "Reserved 2"; - case CECDEVICE_TUNER1: - return "Tuner 1"; - case CECDEVICE_TUNER2: - return "Tuner 2"; - case CECDEVICE_TUNER3: - return "Tuner 3"; - case CECDEVICE_TUNER4: - return "Tuner 4"; - case CECDEVICE_TV: - return "TV"; - default: - return "unknown"; + 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); } -} -const char *CCECProcessor::ToString(const cec_deck_control_mode mode) -{ - switch (mode) + // ensure that the command handler for the TV is initialised + if (bReturn) { - case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND: - return "skip forward wind"; - case CEC_DECK_CONTROL_MODE_EJECT: - return "eject"; - case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND: - return "reverse rewind"; - case CEC_DECK_CONTROL_MODE_STOP: - return "stop"; - default: - return "unknown"; + CCECCommandHandler *handler = GetTV()->GetHandler(); + if (handler) + handler->InitHandler(); } + + return bReturn; } -const char *CCECProcessor::ToString(const cec_deck_info status) +bool CCECProcessor::UnregisterClient(CCECClient *client) { - switch (status) + if (!client) + return false; + + if (client->IsRegistered()) + m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str()); + + // notify the client that it will be unregistered + client->OnUnregister(); + { - case CEC_DECK_INFO_PLAY: - return "play"; - case CEC_DECK_INFO_RECORD: - return "record"; - case CEC_DECK_INFO_PLAY_REVERSE: - return "play reverse"; - case CEC_DECK_INFO_STILL: - return "still"; - case CEC_DECK_INFO_SLOW: - return "slow"; - case CEC_DECK_INFO_SLOW_REVERSE: - return "slow reverse"; - case CEC_DECK_INFO_FAST_FORWARD: - return "fast forward"; - case CEC_DECK_INFO_FAST_REVERSE: - return "fast reverse"; - case CEC_DECK_INFO_NO_MEDIA: - return "no media"; - case CEC_DECK_INFO_STOP: - return "stop"; - case CEC_DECK_INFO_SKIP_FORWARD_WIND: - return "info skip forward wind"; - case CEC_DECK_INFO_SKIP_REVERSE_REWIND: - return "info skip reverse rewind"; - case CEC_DECK_INFO_INDEX_SEARCH_FORWARD: - return "info index search forward"; - case CEC_DECK_INFO_INDEX_SEARCH_REVERSE: - return "info index search reverse"; - case CEC_DECK_INFO_OTHER_STATUS: - return "other"; - default: - return "unknown"; + CLockObject lock(m_mutex); + // find all devices that match the LA's of this client + CECDEVICEVEC devices; + m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + { + // find the client + map::iterator entry = m_clients.find((*it)->GetLogicalAddress()); + // unregister the client + if (entry != m_clients.end()) + m_clients.erase(entry); + + // reset the device status + (*it)->ResetDeviceStatus(); + } } + + // set the new ackmask + return SetAckMask(GetLogicalAddresses().AckMask()); } -const char *CCECProcessor::ToString(const cec_opcode opcode) +void CCECProcessor::UnregisterClients(void) { - switch (opcode) - { - case CEC_OPCODE_ACTIVE_SOURCE: - return "active source"; - case CEC_OPCODE_IMAGE_VIEW_ON: - return "image view on"; - case CEC_OPCODE_TEXT_VIEW_ON: - return "text view on"; - case CEC_OPCODE_INACTIVE_SOURCE: - return "inactive source"; - case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: - return "request active source"; - case CEC_OPCODE_ROUTING_CHANGE: - return "routing change"; - case CEC_OPCODE_ROUTING_INFORMATION: - return "routing information"; - case CEC_OPCODE_SET_STREAM_PATH: - return "set stream path"; - case CEC_OPCODE_STANDBY: - return "standby"; - case CEC_OPCODE_RECORD_OFF: - return "record off"; - case CEC_OPCODE_RECORD_ON: - return "record on"; - case CEC_OPCODE_RECORD_STATUS: - return "record status"; - case CEC_OPCODE_RECORD_TV_SCREEN: - return "record tv screen"; - case CEC_OPCODE_CLEAR_ANALOGUE_TIMER: - return "clear analogue timer"; - case CEC_OPCODE_CLEAR_DIGITAL_TIMER: - return "clear digital timer"; - case CEC_OPCODE_CLEAR_EXTERNAL_TIMER: - return "clear external timer"; - case CEC_OPCODE_SET_ANALOGUE_TIMER: - return "set analogue timer"; - case CEC_OPCODE_SET_DIGITAL_TIMER: - return "set digital timer"; - case CEC_OPCODE_SET_EXTERNAL_TIMER: - return "set external timer"; - case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE: - return "set timer program title"; - case CEC_OPCODE_TIMER_CLEARED_STATUS: - return "timer cleared status"; - case CEC_OPCODE_TIMER_STATUS: - return "timer status"; - case CEC_OPCODE_CEC_VERSION: - return "cec version"; - case CEC_OPCODE_GET_CEC_VERSION: - return "get cec version"; - case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: - return "give physical address"; - case CEC_OPCODE_GET_MENU_LANGUAGE: - return "get menu language"; - case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS: - return "report physical address"; - case CEC_OPCODE_SET_MENU_LANGUAGE: - return "set menu language"; - case CEC_OPCODE_DECK_CONTROL: - return "deck control"; - case CEC_OPCODE_DECK_STATUS: - return "deck status"; - case CEC_OPCODE_GIVE_DECK_STATUS: - return "give deck status"; - case CEC_OPCODE_PLAY: - return "play"; - case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS: - return "give tuner status"; - case CEC_OPCODE_SELECT_ANALOGUE_SERVICE: - return "select analogue service"; - case CEC_OPCODE_SELECT_DIGITAL_SERVICE: - return "set digital service"; - case CEC_OPCODE_TUNER_DEVICE_STATUS: - return "tuner device status"; - case CEC_OPCODE_TUNER_STEP_DECREMENT: - return "tuner step decrement"; - case CEC_OPCODE_TUNER_STEP_INCREMENT: - return "tuner step increment"; - case CEC_OPCODE_DEVICE_VENDOR_ID: - return "device vendor id"; - case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID: - return "give device vendor id"; - case CEC_OPCODE_VENDOR_COMMAND: - return "vendor command"; - case CEC_OPCODE_VENDOR_COMMAND_WITH_ID: - return "vendor command with id"; - case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN: - return "vendor remote button down"; - case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP: - return "vendor remote button up"; - case CEC_OPCODE_SET_OSD_STRING: - return "set osd string"; - case CEC_OPCODE_GIVE_OSD_NAME: - return "give osd name"; - case CEC_OPCODE_SET_OSD_NAME: - return "set osd name"; - case CEC_OPCODE_MENU_REQUEST: - return "menu request"; - case CEC_OPCODE_MENU_STATUS: - return "menu status"; - case CEC_OPCODE_USER_CONTROL_PRESSED: - return "user control pressed"; - case CEC_OPCODE_USER_CONTROL_RELEASE: - return "user control release"; - case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS: - return "give device power status"; - case CEC_OPCODE_REPORT_POWER_STATUS: - return "report power status"; - case CEC_OPCODE_FEATURE_ABORT: - return "feature abort"; - case CEC_OPCODE_ABORT: - return "abort"; - case CEC_OPCODE_GIVE_AUDIO_STATUS: - return "give audio status"; - case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS: - return "give audio mode status"; - case CEC_OPCODE_REPORT_AUDIO_STATUS: - return "report audio status"; - case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE: - return "set system audio mode"; - case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST: - return "system audio mode request"; - case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS: - return "system audio mode status"; - case CEC_OPCODE_SET_AUDIO_RATE: - return "set audio rate"; - default: - return "UNKNOWN"; - } + m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering all CEC clients"); + + vector clients = m_libcec->GetClients(); + for (vector::iterator client = clients.begin(); client != clients.end(); client++) + UnregisterClient(*client); + + CLockObject lock(m_mutex); + m_clients.clear(); } -const char *CCECProcessor::ToString(const cec_system_audio_status mode) +CCECClient *CCECProcessor::GetClient(const cec_logical_address address) { - switch(mode) - { - case CEC_SYSTEM_AUDIO_STATUS_ON: - return "on"; - case CEC_SYSTEM_AUDIO_STATUS_OFF: - return "off"; - default: - return "unknown"; - } + CLockObject lock(m_mutex); + map::const_iterator client = m_clients.find(address); + if (client != m_clients.end()) + return client->second; + return NULL; } -const char *CCECProcessor::ToString(const cec_audio_status status) +CCECClient *CCECProcessor::GetPrimaryClient(void) { - // TODO this is a mask - return "TODO"; + CLockObject lock(m_mutex); + map::const_iterator client = m_clients.begin(); + if (client != m_clients.end()) + return client->second; + return NULL; } -const char *CCECProcessor::ToString(const cec_vendor_id vendor) +CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) { - switch (vendor) - { - case CEC_VENDOR_SAMSUNG: - return "Samsung"; - case CEC_VENDOR_LG: - return "LG"; - case CEC_VENDOR_PANASONIC: - return "Panasonic"; - case CEC_VENDOR_PIONEER: - return "Pioneer"; - case CEC_VENDOR_ONKYO: - return "Onkyo"; - case CEC_VENDOR_YAMAHA: - return "Yamaha"; - case CEC_VENDOR_PHILIPS: - return "Philips"; - default: - return "Unknown"; - } + return m_busDevices->At(GetLogicalAddress()); } -void *CCECBusScan::Process(void) +cec_logical_address CCECProcessor::GetLogicalAddress(void) { - CCECBusDevice *device(NULL); - int iCount(0); - while (!IsStopped()) - { - if (iCount == 0) - { - for (unsigned int iPtr = 0; iPtr < 15 && !IsStopped(); iPtr++) - { - device = m_processor->m_busDevices[iPtr]; - if (device && device->GetStatus(true) == CEC_DEVICE_STATUS_PRESENT) - { - if (!IsStopped()) - device->GetVendorId(); - Sleep(5); - } - } - } + cec_logical_addresses addresses = GetLogicalAddresses(); + return addresses.primary; +} - if (++iCount > 60) - iCount = 0; - Sleep(1000); - } - return NULL; +cec_logical_addresses CCECProcessor::GetLogicalAddresses(void) +{ + CLockObject lock(m_mutex); + cec_logical_addresses addresses; + addresses.Clear(); + for (map::const_iterator client = m_clients.begin(); client != m_clients.end(); client++) + addresses.Set(client->first); + + return addresses; } -bool CCECProcessor::StartBootloader(void) +bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const { - return m_communication->StartBootloader(); + CCECBusDevice *device = GetDevice(address); + return device && device->IsHandledByLibCEC(); } -bool CCECProcessor::PingAdapter(void) +bool CCECProcessor::IsRunningLatestFirmware(void) { - return m_communication->PingAdapter(); + return m_communication && m_communication->IsOpen() ? + m_communication->IsRunningLatestFirmware() : + true; }