X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Flib%2FCECProcessor.cpp;h=63e481551457682c0e58545b37272028a56979f0;hb=f7539eaf1ed0a488c0a93998c9b178d435014c51;hp=a1603e8243a26b92cbed4d2080639eaa044fdf28;hpb=5734016c1b1e932c19a85bb671211c4967399f8c;p=deb_libcec.git diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index a1603e8..63e4815 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-2012 Pulse-Eight Limited. All rights reserved. + * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved. * libCEC(R) is an original work, containing original code. * * libCEC(R) is a trademark of Pulse-Eight Limited. @@ -30,9 +30,10 @@ * http://www.pulse-eight.net/ */ +#include "env.h" #include "CECProcessor.h" -#include "adapter/USBCECAdapterCommunication.h" +#include "adapter/AdapterFactory.h" #include "devices/CECBusDevice.h" #include "devices/CECAudioSystem.h" #include "devices/CECPlaybackDevice.h" @@ -41,438 +42,198 @@ #include "devices/CECTV.h" #include "implementations/CECCommandHandler.h" #include "LibCEC.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, libcec_configuration *configuration) : - m_bConnectionOpened(false), - m_bInitialised(false), - m_communication(NULL), - m_controller(controller), - m_bMonitor(false), - m_iStandardLineTimeout(3), - m_iRetryLineTimeout(3), - m_iLastTransmission(0) -{ - CreateBusDevices(); - m_configuration.Clear(); - m_configuration.serverVersion = CEC_SERVER_VERSION_1_6_2; - SetConfiguration(configuration); - - if (m_configuration.tvVendor != CEC_VENDOR_UNKNOWN) - m_busDevices[CECDEVICE_TV]->ReplaceHandler(false); -} - -CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types, uint16_t iPhysicalAddress) : - m_bConnectionOpened(false), - m_bInitialised(false), - m_communication(NULL), - m_controller(controller), - m_bMonitor(false), - m_iStandardLineTimeout(3), - m_iRetryLineTimeout(3), - m_iLastTransmission(0) -{ - m_configuration.Clear(); - m_configuration.serverVersion = CEC_SERVER_VERSION_1_6_2; +#define CEC_PROCESSOR_SIGNAL_WAIT_TIME 1000 +#define ACTIVE_SOURCE_CHECK_INTERVAL 500 +#define TV_PRESENT_CHECK_INTERVAL 30000 - // client version < 1.5.0 - m_configuration.clientVersion = (uint32_t)CEC_CLIENT_VERSION_PRE_1_5; - snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName); - m_configuration.deviceTypes = types; - m_configuration.iPhysicalAddress = iPhysicalAddress; - m_configuration.baseDevice = (cec_logical_address)CEC_DEFAULT_BASE_DEVICE; - m_configuration.iHDMIPort = CEC_DEFAULT_HDMI_PORT; +#define ToString(x) CCECTypeUtils::ToString(x) - if (m_configuration.deviceTypes.IsEmpty()) - m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); - CreateBusDevices(); -} +CCECStandbyProtection::CCECStandbyProtection(CCECProcessor* processor) : + m_processor(processor) {} +CCECStandbyProtection::~CCECStandbyProtection(void) {} -void CCECProcessor::CreateBusDevices(void) +void* CCECStandbyProtection::Process(void) { - for (int iPtr = 0; iPtr < 16; iPtr++) + int64_t last = GetTimeMs(); + int64_t next; + while (!IsStopped()) { - switch(iPtr) + PLATFORM::CEvent::Sleep(1000); + + next = GetTimeMs(); + + // reset the connection if the clock changed + if (next < last || next - last > 10000) { - 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); + 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(void) +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) { - Close(); - - for (unsigned int iPtr = 0; iPtr < 16; iPtr++) - { - delete m_busDevices[iPtr]; - m_busDevices[iPtr] = NULL; - } + m_busDevices = new CCECDeviceMap(this); } -void CCECProcessor::Close(void) +CCECProcessor::~CCECProcessor(void) { - StopThread(false); - SetInitialised(false); - StopThread(); - - bool bClose(false); - { - CLockObject lock(m_mutex); - bClose = m_bConnectionOpened; - m_bConnectionOpened = false; - } - - if (bClose && m_communication) - { - delete m_communication; - m_communication = NULL; - } + m_bStallCommunication = false; + DELETE_AND_NULL(m_addrAllocator); + Close(); + DELETE_AND_NULL(m_busDevices); } -bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */) +bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { - bool bReturn(false); - Close(); + CLockObject lock(m_mutex); + // open a connection + if (!OpenConnection(strPort, iBaudRate, iTimeoutMs)) + return false; + // create the processor thread + if (!IsRunning()) { - CLockObject lock(m_mutex); - if (m_bConnectionOpened) + if (!CreateThread()) { - CLibCEC::AddLog(CEC_LOG_ERROR, "connection already opened"); + m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); return false; } - m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); - m_bConnectionOpened = (m_communication != NULL); - } - - CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); - - /* open a new connection */ - unsigned iConnectTry(0); - while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry); - m_communication->Close(); - CEvent::Sleep(1000); - } - - if (bReturn) - { - m_configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); - m_configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); - CStdString strLog; - strLog.Format("connected to the CEC adapter. libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)m_configuration.serverVersion), ToString((cec_client_version)m_configuration.clientVersion), m_configuration.iFirmwareVersion); - if (m_configuration.iFirmwareBuildDate > 0) - { - time_t buildTime = (time_t)m_configuration.iFirmwareBuildDate; - strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime))); - strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime - strLog.append(" +0000"); - } - CLibCEC::AddLog(CEC_LOG_NOTICE, strLog); - } - - if (m_configuration.bGetSettingsFromROM == 1) - { - libcec_configuration config; - config.Clear(); - m_communication->GetConfiguration(&config); - - CLockObject lock(m_mutex); - if (!config.deviceTypes.IsEmpty()) - m_configuration.deviceTypes = config.deviceTypes; - if (config.iPhysicalAddress > 0) - m_configuration.iPhysicalAddress = config.iPhysicalAddress; - snprintf(m_configuration.strDeviceName, 13, "%s", config.strDeviceName); } - return bReturn; + return true; } -bool CCECProcessor::IsInitialised(void) +void CCECProcessor::Close(void) { - CLockObject lock(m_threadMutex); - return m_bInitialised; -} + // mark as uninitialised + SetCECInitialised(false); -void CCECProcessor::SetInitialised(bool bSetTo /* = true */) -{ - CLockObject lock(m_mutex); - m_bInitialised = bSetTo; + // stop the processor + DELETE_AND_NULL(m_connCheck); + StopThread(-1); + m_inBuffer.Broadcast(); + StopThread(); + + // close the connection + DELETE_AND_NULL(m_communication); } -bool CCECProcessor::Initialise(void) +void CCECProcessor::ResetMembers(void) { - bool bReturn(false); - { - CLockObject lock(m_mutex); - if (!m_configuration.logicalAddresses.IsEmpty()) - m_configuration.logicalAddresses.Clear(); - - if (!FindLogicalAddresses()) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not detect our logical addresses"); - return bReturn; - } - - /* only set our OSD name for the primary device */ - m_busDevices[m_configuration.logicalAddresses.primary]->m_strDeviceName = m_configuration.strDeviceName; - - /* make the primary device the active source if the option is set */ - if (m_configuration.bActivateSource == 1) - m_busDevices[m_configuration.logicalAddresses.primary]->m_bActiveSource = true; + // close the connection + DELETE_AND_NULL(m_communication); - /* set the default menu language for devices we control */ - cec_menu_language language; - language.device = m_configuration.logicalAddresses.primary; - memcpy(language.language, m_configuration.strDeviceLanguage, 3); - language.language[3] = 0; - - for (uint8_t iPtr = 0; iPtr < 16; iPtr++) - { - if (m_configuration.logicalAddresses[iPtr]) - { - language.device = (cec_logical_address) iPtr; - m_busDevices[iPtr]->SetMenuLanguage(language); - } - } - } - - /* get the vendor id from the TV, so we are using the correct handler */ - m_busDevices[CECDEVICE_TV]->GetVendorId(); - - if (m_configuration.iPhysicalAddress != 0) - { - CLibCEC::AddLog(CEC_LOG_NOTICE, "setting the physical address to %4x", m_configuration.iPhysicalAddress); - m_busDevices[m_configuration.logicalAddresses.primary]->m_iPhysicalAddress = m_configuration.iPhysicalAddress; - if ((bReturn = m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPhysicalAddress()) == false) - CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set the physical address to %4x", m_configuration.iPhysicalAddress); - } - else if (m_configuration.iPhysicalAddress == 0 && (bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true)) == false) - CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set HDMI port %d on %s (%x)", m_configuration.iHDMIPort, ToString(m_configuration.baseDevice), (uint8_t)m_configuration.baseDevice); - - if (bReturn && m_configuration.bActivateSource == 1) - m_busDevices[m_configuration.logicalAddresses.primary]->ActivateSource(); - - SetInitialised(bReturn); - if (bReturn) - CLibCEC::ConfigurationChanged(m_configuration); - - return bReturn; + // reset the other members to the initial state + m_iStandardLineTimeout = 3; + m_iRetryLineTimeout = 3; + m_iLastTransmission = 0; + m_busDevices->ResetDeviceStatus(); } -bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */) +bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */) { bool bReturn(false); + CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); - { - CLockObject lock(m_mutex); - if (!OpenConnection(strPort, iBaudRate, iTimeoutMs)) - return bReturn; + // ensure that a previous connection is closed + Close(); - /* create the processor thread */ - if (!CreateThread()) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread"); - return bReturn; - } - } + // reset all member to the initial state + ResetMembers(); - if ((bReturn = Initialise()) == false) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread"); - StopThread(true); - } - else + // check whether the Close() method deleted any previous connection + if (m_communication) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started"); + m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed"); + return bReturn; } - return bReturn; -} + // create a new connection + m_communication = CAdapterFactory(this->m_libcec).GetInstance(strPort, iBaudRate); -bool CCECProcessor::TryLogicalAddress(cec_logical_address address) -{ - if (m_busDevices[address]->TryLogicalAddress()) + // open a new connection + unsigned iConnectTry(0); + while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false) { - m_configuration.logicalAddresses.Set(address); - return true; + m_libcec->AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry); + m_communication->Close(); + CEvent::Sleep(CEC_DEFAULT_CONNECT_RETRY_WAIT); } - return false; -} + m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened"); -bool CCECProcessor::FindLogicalAddressRecordingDevice(void) -{ - CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'"); - return TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1) || - TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2) || - TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3); -} + // mark as initialised + SetCECInitialised(true); -bool CCECProcessor::FindLogicalAddressTuner(void) -{ - CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'"); - return TryLogicalAddress(CECDEVICE_TUNER1) || - TryLogicalAddress(CECDEVICE_TUNER2) || - TryLogicalAddress(CECDEVICE_TUNER3) || - TryLogicalAddress(CECDEVICE_TUNER4); -} - -bool CCECProcessor::FindLogicalAddressPlaybackDevice(void) -{ - CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'"); - return TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1) || - TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2) || - TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3); + return bReturn; } -bool CCECProcessor::FindLogicalAddressAudioSystem(void) +bool CCECProcessor::CECInitialised(void) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audio'"); - return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM); + CLockObject lock(m_threadMutex); + return m_bInitialised; } -bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to) +void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */) { - bool bChanged(false); - - CLibCEC::AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to)); - - CLockObject lock(m_mutex); - CCECBusDevice *previousDevice = GetDeviceByType(from); - m_configuration.logicalAddresses.primary = CECDEVICE_UNKNOWN; - - for (unsigned int iPtr = 0; iPtr < 5; iPtr++) { - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) - continue; - - if (m_configuration.deviceTypes.types[iPtr] == from) - { - bChanged = true; - m_configuration.deviceTypes.types[iPtr] = to; - } - else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged) - { - m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED; - } - } - - if (bChanged) - { - FindLogicalAddresses(); - - CCECBusDevice *newDevice = GetDeviceByType(to); - if (previousDevice && newDevice) - { - newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC); - previousDevice->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT); - - newDevice->SetCecVersion(previousDevice->GetCecVersion(false)); - previousDevice->SetCecVersion(CEC_VERSION_UNKNOWN); - - newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(false)); - - newDevice->SetMenuState(previousDevice->GetMenuState()); - previousDevice->SetMenuState(CEC_MENU_STATE_DEACTIVATED); - - newDevice->SetOSDName(previousDevice->GetOSDName(false)); - previousDevice->SetOSDName(ToString(previousDevice->GetLogicalAddress())); - - newDevice->SetPhysicalAddress(previousDevice->GetPhysicalAddress()); - previousDevice->SetPhysicalAddress(0xFFFF); - - newDevice->SetPowerStatus(previousDevice->GetPowerStatus(false)); - previousDevice->SetPowerStatus(CEC_POWER_STATUS_UNKNOWN); - - newDevice->SetVendorId(previousDevice->GetVendorId(false)); - previousDevice->SetVendorId(CEC_VENDOR_UNKNOWN); - - if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) && - (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE)) - { - ((CCECPlaybackDevice *) newDevice)->SetDeckControlMode(((CCECPlaybackDevice *) previousDevice)->GetDeckControlMode()); - ((CCECPlaybackDevice *) previousDevice)->SetDeckControlMode(CEC_DECK_CONTROL_MODE_STOP); - - ((CCECPlaybackDevice *) newDevice)->SetDeckStatus(((CCECPlaybackDevice *) previousDevice)->GetDeckStatus()); - ((CCECPlaybackDevice *) previousDevice)->SetDeckStatus(CEC_DECK_INFO_STOP); - } - } + CLockObject lock(m_mutex); + m_bInitialised = bSetTo; } - - return true; + if (!bSetTo) + UnregisterClients(); } -bool CCECProcessor::FindLogicalAddresses(void) +bool CCECProcessor::TryLogicalAddress(cec_logical_address address, cec_version libCECSpecVersion /* = CEC_VERSION_1_4 */) { - bool bReturn(true); - m_configuration.logicalAddresses.Clear(); - - if (m_configuration.deviceTypes.IsEmpty()) + // find the device + CCECBusDevice *device = m_busDevices->At(address); + if (device) { - CLibCEC::AddLog(CEC_LOG_ERROR, "no device types set"); - return false; - } + // check if it's already marked as present or used + if (device->IsPresent() || device->IsHandledByLibCEC()) + return false; - for (unsigned int iPtr = 0; iPtr < 5; iPtr++) - { - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) - continue; - - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - device %d: type %d", __FUNCTION__, iPtr, m_configuration.deviceTypes.types[iPtr]); - - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE) - bReturn &= FindLogicalAddressRecordingDevice(); - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER) - bReturn &= FindLogicalAddressTuner(); - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE) - bReturn &= FindLogicalAddressPlaybackDevice(); - if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM) - bReturn &= FindLogicalAddressAudioSystem(); + // poll the LA if not + return device->TryLogicalAddress(libCECSpecVersion); } - if (bReturn) - SetAckMask(m_configuration.logicalAddresses.AckMask()); - - return bReturn; + return false; } void CCECProcessor::ReplaceHandlers(void) { - if (!IsInitialised()) + if (!CECInitialised()) return; - for (uint8_t iPtr = 0; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++) - m_busDevices[iPtr]->ReplaceHandler(m_bInitialised); + + // check each device + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + it->second->ReplaceHandler(true); } bool CCECProcessor::OnCommandReceived(const cec_command &command) @@ -482,1306 +243,823 @@ bool CCECProcessor::OnCommandReceived(const cec_command &command) void *CCECProcessor::Process(void) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started"); + m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); - cec_command command; - command.Clear(); + 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); + // as long as we're not being stopped and the connection is open while (!IsStopped() && m_communication->IsOpen()) { - if (m_inBuffer.Pop(command, 500)) - ParseCommand(command); + // wait for a new incoming command, and process it + if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME)) + ProcessCommand(command); - if (IsInitialised()) + if (CECInitialised() && !IsStopped()) { - ReplaceHandlers(); - - m_controller->CheckKeypressTimeout(); - } - } - - return NULL; -} - -bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */) -{ - bool bReturn(false); + // check clients for keypress timeouts + m_libcec->CheckKeypressTimeout(); - if (!IsRunning()) - return bReturn; + // check if we need to replace handlers + ReplaceHandlers(); - cec_logical_address addr = m_configuration.logicalAddresses.primary; + // check whether we need to activate a source, if it failed before + if (activeSourceCheck.TimeLeft() == 0) + { + if (CECInitialised()) + TransmitPendingActiveSourceCommands(); + activeSourceCheck.Init(ACTIVE_SOURCE_CHECK_INTERVAL); + } - if (type != CEC_DEVICE_TYPE_RESERVED) - { - for (uint8_t iPtr = 0; iPtr <= 11; iPtr++) - { - if (m_configuration.logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type) + // check whether the TV is present and responding + if (tvPresentCheck.TimeLeft() == 0) { - addr = (cec_logical_address) iPtr; - break; + CCECClient *primary = GetPrimaryClient(); + // only check whether the tv responds to polls when a client is connected and not in monitoring mode + 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); } } } - m_busDevices[addr]->SetActiveSource(); - if (m_busDevices[addr]->GetPhysicalAddress() != 0xFFFF) - bReturn = m_busDevices[addr]->ActivateSource(); - - return bReturn; + return NULL; } -bool CCECProcessor::SetActiveSource(uint16_t iStreamPath) +bool CCECProcessor::ActivateSource(uint16_t iStreamPath) { bool bReturn(false); - // suppress polls when searching for a device + // find the device with the given PA CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath); + // and make it the active source when found if (device) - { - device->SetActiveSource(); - bReturn = true; - } + bReturn = device->ActivateSource(); else - { - CLibCEC::AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath); - } + m_libcec->AddLog(CEC_LOG_DEBUG, "device with PA '%04x' not found", iStreamPath); return bReturn; } -void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout) +void CCECProcessor::SetActiveSource(bool bSetTo, bool bClientUnregistered) { - CLockObject lock(m_mutex); - m_iStandardLineTimeout = iTimeout; + if (m_communication) + m_communication->SetActiveSource(bSetTo, bClientUnregistered); } -void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout) +void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout) { CLockObject lock(m_mutex); - m_iRetryLineTimeout = iTimeout; -} - -bool CCECProcessor::SetActiveView(void) -{ - CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__); - return SetActiveSource(m_configuration.deviceTypes.IsEmpty() ? CEC_DEVICE_TYPE_RESERVED : m_configuration.deviceTypes[0]); + m_iStandardLineTimeout = iTimeout; } -bool CCECProcessor::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */) +uint8_t CCECProcessor::GetStandardLineTimeout(void) { - bool bReturn(false); - - CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE); - if (device) - { - ((CCECPlaybackDevice *) device)->SetDeckControlMode(mode); - if (bSendUpdate) - ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV); - bReturn = true; - } - - return bReturn; + CLockObject lock(m_mutex); + return m_iStandardLineTimeout; } -bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */) +void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout) { - 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; + CLockObject lock(m_mutex); + m_iRetryLineTimeout = iTimeout; } -bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */) +uint8_t CCECProcessor::GetRetryLineTimeout(void) { - bool bReturn(false); - - // limit the HDMI port range to 1-15 - if (iPort < 1) - iPort = 1; - if (iPort > 15) - iPort = 15; - - { - CLockObject lock(m_mutex); - m_configuration.baseDevice = iBaseDevice; - m_configuration.iHDMIPort = iPort; - } - - if (!IsRunning() && !bForce) - return true; - - CLibCEC::AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); - - uint16_t iPhysicalAddress(0); - if (iBaseDevice > CECDEVICE_TV) - { - iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress(); - } - - if (iPhysicalAddress < 0xffff) - { - if (iPhysicalAddress == 0) - iPhysicalAddress += 0x1000 * iPort; - else if (iPhysicalAddress % 0x1000 == 0) - iPhysicalAddress += 0x100 * iPort; - else if (iPhysicalAddress % 0x100 == 0) - iPhysicalAddress += 0x10 * iPort; - else if (iPhysicalAddress % 0x10 == 0) - iPhysicalAddress += iPort; - - bReturn = true; - } - - if (!bReturn) - CLibCEC::AddLog(CEC_LOG_ERROR, "failed to set the physical address"); - else - SetPhysicalAddress(iPhysicalAddress); - - return bReturn; + CLockObject lock(m_mutex); + return m_iRetryLineTimeout; } bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) { - for (unsigned int iPtr = 0; iPtr < 15; iPtr++) - { - if (m_busDevices[iPtr]->GetPhysicalAddress() == iPhysicalAddress) - return true; - } - return false; -} - -bool CCECProcessor::TransmitInactiveSource(void) -{ - if (!IsRunning()) - return false; - - if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary]) - return m_busDevices[m_configuration.logicalAddresses.primary]->TransmitInactiveSource(); - return false; + 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]); - CLibCEC::AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); -} -bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress) -{ - CLockObject lock(m_mutex); - if (m_configuration.logicalAddresses.primary != iLogicalAddress) - { - CLibCEC::AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress); - m_configuration.logicalAddresses.primary = iLogicalAddress; - m_configuration.logicalAddresses.Set(iLogicalAddress); - return SetAckMask(m_configuration.logicalAddresses.AckMask()); - } - - return true; -} - -bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */) -{ - for (uint8_t iPtr = 0; iPtr < 16; iPtr++) - { - if (m_configuration.logicalAddresses[iPtr]) - m_busDevices[iPtr]->SetMenuState(state); - } - - if (bSendUpdate) - m_busDevices[m_configuration.logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV); - - return true; -} - -bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */) -{ - bool bSendActiveView(false); - bool bReturn(false); - cec_logical_addresses sendUpdatesTo; - sendUpdatesTo.Clear(); - - { - CLockObject lock(m_mutex); - m_configuration.iPhysicalAddress = iPhysicalAddress; - CLibCEC::AddLog(CEC_LOG_DEBUG, "setting physical address to '%4x'", iPhysicalAddress); - - if (!m_configuration.logicalAddresses.IsEmpty()) - { - bool bWasActiveSource(false); - for (uint8_t iPtr = 0; iPtr < 15; iPtr++) - if (m_configuration.logicalAddresses[iPtr]) - { - bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource(); - m_busDevices[iPtr]->SetInactiveSource(); - m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress); - if (bSendUpdate) - sendUpdatesTo.Set((cec_logical_address)iPtr); - } - - bSendActiveView = bWasActiveSource && bSendUpdate; - bReturn = true; - } - } - - for (uint8_t iPtr = 0; iPtr < 15; iPtr++) - if (sendUpdatesTo[iPtr]) - m_busDevices[iPtr]->TransmitPhysicalAddress(); - - if (bSendActiveView) - SetActiveView(); - - if (bReturn) - { - libcec_configuration config; - { - CLockObject lock(m_mutex); - config = m_configuration; - } - - PersistConfiguration(&config); - CLibCEC::ConfigurationChanged(config); - } - - return bReturn; -} - -bool CCECProcessor::SwitchMonitoring(bool bEnable) -{ - CLibCEC::AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling"); - - { - CLockObject lock(m_mutex); - m_bMonitor = bEnable; - } - - if (bEnable) - return SetAckMask(0); - else - return SetAckMask(m_configuration.logicalAddresses.AckMask()); + // and log it + m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); } bool CCECProcessor::PollDevice(cec_logical_address iAddress) { - if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress]) - { - return m_configuration.logicalAddresses.primary == CECDEVICE_UNKNOWN ? - m_busDevices[iAddress]->TransmitPoll(iAddress) : - m_busDevices[m_configuration.logicalAddresses.primary]->TransmitPoll(iAddress); - } - return false; -} - -uint8_t CCECProcessor::VolumeUp(bool bSendRelease /* = true */) -{ - uint8_t status = 0; - if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeUp(bSendRelease); - - return status; -} - -uint8_t CCECProcessor::VolumeDown(bool bSendRelease /* = true */) -{ - uint8_t status = 0; - if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeDown(bSendRelease); + // try to find the primary device + CCECBusDevice *primary = GetPrimaryDevice(); + // poll the destination, with the primary as source + if (primary) + return primary->TransmitPoll(iAddress, true); - return status; -} - -uint8_t CCECProcessor::MuteAudio(bool bSendRelease /* = true */) -{ - uint8_t status = 0; - if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM)) - status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->MuteAudio(bSendRelease); - - return status; -} - -CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */) -{ - CCECBusDevice *device(NULL); - - // invalid PA - if (iPhysicalAddress == 0xFFFF) - return device; - - // check each device until we found a match - for (unsigned int iPtr = 0; !device && iPtr < 16; iPtr++) - { - if (m_busDevices[iPtr]->GetPhysicalAddress(bSuppressUpdate) == iPhysicalAddress) - device = m_busDevices[iPtr]; - } - - return device; -} - -CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const -{ - CCECBusDevice *device = NULL; - - for (uint8_t iPtr = 0; iPtr < 16; iPtr++) - { - if (m_busDevices[iPtr]->m_type == type && m_configuration.logicalAddresses[iPtr]) - { - device = m_busDevices[iPtr]; - break; - } - } - - return device; -} - -CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const -{ - CCECBusDevice *device(NULL); - cec_logical_address primary = m_configuration.logicalAddresses.primary; - if (primary != CECDEVICE_UNKNOWN) - device = m_busDevices[primary]; - return device; -} - -cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress) -{ - return m_busDevices[iAddress]->GetCecVersion(); -} - -cec_osd_name CCECProcessor::GetDeviceOSDName(cec_logical_address iAddress) -{ - CStdString strOSDName = m_busDevices[iAddress]->GetOSDName(); - cec_osd_name retVal; - - snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str()); - retVal.device = iAddress; - - return retVal; -} - -bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language) -{ - if (m_busDevices[iAddress]) - { - *language = m_busDevices[iAddress]->GetMenuLanguage(); - return (strcmp(language->language, "???") != 0); - } - return false; -} - -CStdString CCECProcessor::GetDeviceName(void) const -{ - CStdString strName; - strName = m_configuration.strDeviceName; - return strName; -} + CCECBusDevice *device = m_busDevices->At(CECDEVICE_UNREGISTERED); + if (device) + return device->TransmitPoll(iAddress, true); -uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress) -{ - if (m_busDevices[iAddress]) - return m_busDevices[iAddress]->GetVendorId(); return false; } -uint16_t CCECProcessor::GetDevicePhysicalAddress(cec_logical_address iAddress) +CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate /* = true */) { - if (m_busDevices[iAddress]) - return m_busDevices[iAddress]->GetPhysicalAddress(false); - return false; + return m_busDevices ? + m_busDevices->GetDeviceByPhysicalAddress(iPhysicalAddress, bSuppressUpdate) : + NULL; } -cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress) +CCECBusDevice *CCECProcessor::GetDevice(cec_logical_address address) const { - if (m_busDevices[iAddress]) - return m_busDevices[iAddress]->GetPowerStatus(); - return CEC_POWER_STATUS_UNKNOWN; + return m_busDevices ? + m_busDevices->At(address) : + NULL; } cec_logical_address CCECProcessor::GetActiveSource(bool bRequestActiveSource /* = true */) { - for (uint8_t iPtr = 0; iPtr <= 11; iPtr++) - { - if (m_busDevices[iPtr]->IsActiveSource()) - return (cec_logical_address)iPtr; - } + // 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 && m_configuration.logicalAddresses.primary != CECDEVICE_UNKNOWN) + if (bRequestActiveSource) { - CCECBusDevice *primary = m_busDevices[m_configuration.logicalAddresses.primary]; + // request the active source from the bus + CCECBusDevice *primary = GetPrimaryDevice(); if (primary) + { primary->RequestActiveSource(); - - return GetActiveSource(false); + return GetActiveSource(false); + } } + // unknown or none return CECDEVICE_UNKNOWN; } bool CCECProcessor::IsActiveSource(cec_logical_address iAddress) { - return iAddress > CECDEVICE_TV && iAddress < CECDEVICE_BROADCAST ? - m_busDevices[iAddress]->IsActiveSource() : - false; + CCECBusDevice *device = m_busDevices->At(iAddress); + return device && device->IsActiveSource(); } -bool CCECProcessor::Transmit(const cec_command &data) +bool CCECProcessor::Transmit(const cec_command &data, bool bIsReply) { - if (m_configuration.logicalAddresses[(uint8_t)data.destination]) + cec_command transmitData(data); + uint8_t iMaxTries(0); + bool bRetry(true); + uint8_t iTries(0); + + // get the current timeout setting + uint8_t iLineTimeout(GetStandardLineTimeout()); + + // reset the state of this message to 'unknown' + cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; + + 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); + + // find the initiator device + CCECBusDevice *initiator = m_busDevices->At(transmitData.initiator); + if (!initiator) { - CLibCEC::AddLog(CEC_LOG_WARNING, "not sending data to myself!"); + m_libcec->AddLog(CEC_LOG_WARNING, "invalid initiator"); return false; } - uint8_t iMaxTries(0); + // find the destination device, if it's not the broadcast address + if (transmitData.destination != CECDEVICE_BROADCAST) { - CLockObject lock(m_mutex); - if (IsStopped()) - return false; - LogOutput(data); - m_iLastTransmission = GetTimeMs(); - if (!m_communication || !m_communication->IsOpen()) + // check if the device is marked as handled by libCEC + CCECBusDevice *destination = m_busDevices->At(transmitData.destination); + if (destination && destination->IsHandledByLibCEC()) { - CLibCEC::AddLog(CEC_LOG_ERROR, "cannot transmit command: connection closed"); + // 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; } - iMaxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1; } - bool bRetry(true); - uint8_t iTries(0); - uint8_t iLineTimeout = m_iStandardLineTimeout; - cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; + // wait until we finished allocating a new LA if it got lost + while (m_bStallCommunication) Sleep(5); + + { + CLockObject lock(m_mutex); + m_iLastTransmission = GetTimeMs(); + // set the number of tries + iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; + initiator->MarkHandlerReady(); + } + // and try to send the command while (bRetry && ++iTries < iMaxTries) { - if (m_busDevices[data.initiator]->IsUnsupportedFeature(data.opcode)) + if (initiator->IsUnsupportedFeature(transmitData.opcode)) return false; - adapterState = m_communication->Write(data, bRetry, iLineTimeout); + adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ? + m_communication->Write(transmitData, bRetry, iLineTimeout, bIsReply) : + ADAPTER_MESSAGE_STATE_ERROR; iLineTimeout = m_iRetryLineTimeout; } - return adapterState == ADAPTER_MESSAGE_STATE_SENT_ACKED; + 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 address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) +void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "<< transmitting abort message"); + m_libcec->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message"); cec_command command; - // TODO - cec_command::Format(command, m_configuration.logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT); + cec_command::Format(command, source, destination, CEC_OPCODE_FEATURE_ABORT); command.parameters.PushBack((uint8_t)opcode); command.parameters.PushBack((uint8_t)reason); - Transmit(command); + Transmit(command, true); } -void CCECProcessor::ParseCommand(const cec_command &command) +void CCECProcessor::ProcessCommand(const cec_command &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]); - CLibCEC::AddLog(CEC_LOG_TRAFFIC, dataStr.c_str()); + // log the command + m_libcec->AddLog(CEC_LOG_TRAFFIC, ToString(command).c_str()); - if (!m_bMonitor && command.initiator >= CECDEVICE_TV && command.initiator <= CECDEVICE_BROADCAST) - m_busDevices[(uint8_t)command.initiator]->HandleCommand(command); -} + // find the initiator + CCECBusDevice *device = m_busDevices->At(command.initiator); -cec_logical_addresses CCECProcessor::GetActiveDevices(void) -{ - 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; + if (device) + device->HandleCommand(command); } bool CCECProcessor::IsPresentDevice(cec_logical_address address) { - return m_busDevices[address]->GetStatus() == CEC_DEVICE_STATUS_PRESENT; + CCECBusDevice *device = m_busDevices->At(address); + return device && device->GetStatus() == CEC_DEVICE_STATUS_PRESENT; } bool CCECProcessor::IsPresentDeviceType(cec_device_type type) { - for (unsigned int iPtr = 0; iPtr < 15; iPtr++) - { - if (m_busDevices[iPtr]->GetType() == type && m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT) - return true; - } - - return false; + CECDEVICEVEC devices; + m_busDevices->GetByType(type, devices); + CCECDeviceMap::FilterActive(devices); + return !devices.empty(); } -uint16_t CCECProcessor::GetPhysicalAddress(void) const +uint16_t CCECProcessor::GetDetectedPhysicalAddress(void) const { - if (!m_configuration.logicalAddresses.IsEmpty() && m_busDevices[m_configuration.logicalAddresses.primary]) - return m_busDevices[m_configuration.logicalAddresses.primary]->GetPhysicalAddress(); - return false; + return m_communication ? m_communication->GetPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS; } -bool CCECProcessor::SetAckMask(uint16_t iMask) +bool CCECProcessor::ClearLogicalAddresses(void) { - return m_communication ? m_communication->SetAckMask(iMask) : false; + cec_logical_addresses addresses; addresses.Clear(); + return SetLogicalAddresses(addresses); } -bool CCECProcessor::TransmitKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */) +bool CCECProcessor::SetLogicalAddresses(const cec_logical_addresses &addresses) { - return m_busDevices[iDestination]->TransmitKeypress(key, bWait); + return m_communication ? m_communication->SetLogicalAddresses(addresses) : false; } -bool CCECProcessor::TransmitKeyRelease(cec_logical_address iDestination, bool bWait /* = true */) +bool CCECProcessor::StandbyDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices) { - return m_busDevices[iDestination]->TransmitKeyRelease(bWait); + bool bReturn(true); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + bReturn &= (*it)->Standby(initiator); + return bReturn; } -bool CCECProcessor::EnablePhysicalAddressDetection(void) +bool CCECProcessor::StandbyDevice(const cec_logical_address initiator, cec_logical_address address) { - CLibCEC::AddLog(CEC_LOG_WARNING, "deprecated method %s called", __FUNCTION__); - uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress(); - if (iPhysicalAddress != 0) - { - m_configuration.bAutodetectAddress = 1; - m_configuration.iPhysicalAddress = iPhysicalAddress; - m_configuration.baseDevice = CECDEVICE_UNKNOWN; - m_configuration.iHDMIPort = 0; - return SetPhysicalAddress(iPhysicalAddress); - } - return false; + CCECBusDevice *device = m_busDevices->At(address); + return device ? device->Standby(initiator) : false; } -bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +bool CCECProcessor::PowerOnDevices(const cec_logical_address initiator, const CECDEVICEVEC &devices) { - if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0) - { - bool bReturn(true); - for (uint8_t iPtr = 0; iPtr <= 0xF; iPtr++) - { - if (m_configuration.powerOffDevices[iPtr]) - { - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - putting '%s' in standby mode", __FUNCTION__, ToString((cec_logical_address)iPtr)); - bReturn &= m_busDevices[iPtr]->Standby(); - } - } - return bReturn; - } + bool bReturn(true); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + bReturn &= (*it)->PowerOn(initiator); + return bReturn; +} - return m_busDevices[address]->Standby(); +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::PowerOnDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */) { - if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0) + bool bReturn(false); + // open a connection if no connection has been opened + if (!m_communication && strPort) { - bool bReturn(true); - for (uint8_t iPtr = 0; iPtr <= 0xF; iPtr++) + 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) { - if (m_configuration.wakeDevices[iPtr]) - { - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - powering on '%s'", __FUNCTION__, ToString((cec_logical_address)iPtr)); - bReturn &= m_busDevices[iPtr]->PowerOn(); - } + 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(); + DELETE_AND_NULL(comm); } return bReturn; } + else + { + m_communication->StartBootloader(); + Close(); + bReturn = true; + } - return m_busDevices[address]->PowerOn(); + return bReturn; } -const char *CCECProcessor::ToString(const cec_device_type type) +bool CCECProcessor::PingAdapter(void) { - switch (type) - { - case CEC_DEVICE_TYPE_AUDIO_SYSTEM: - return "audio system"; - case CEC_DEVICE_TYPE_PLAYBACK_DEVICE: - return "playback device"; - case CEC_DEVICE_TYPE_RECORDING_DEVICE: - return "recording device"; - case CEC_DEVICE_TYPE_RESERVED: - return "reserved"; - case CEC_DEVICE_TYPE_TUNER: - return "tuner"; - case CEC_DEVICE_TYPE_TV: - return "TV"; - default: - return "unknown"; - } + return m_communication->PingAdapter(); } -const char *CCECProcessor::ToString(const cec_menu_state state) +void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination) { - switch (state) - { - case CEC_MENU_STATE_ACTIVATED: - return "activated"; - case CEC_MENU_STATE_DEACTIVATED: - return "deactivated"; - default: - return "unknown"; - } + CCECBusDevice *device = m_busDevices->At(destination); + if (device) + device->HandlePollFrom(initiator); } -const char *CCECProcessor::ToString(const cec_version version) +bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator) { - switch (version) - { - 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"; - } + CCECBusDevice *device = m_busDevices->At(initiator); + return !device || !device->HandleReceiveFailed(); } -const char *CCECProcessor::ToString(const cec_power_status status) +bool CCECProcessor::CanPersistConfiguration(void) { - switch (status) - { - 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"; - } + return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false; } -const char *CCECProcessor::ToString(const cec_logical_address address) +bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration) { - switch(address) + libcec_configuration persistConfiguration = configuration; + if (!CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress)) { - 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"; + CCECBusDevice *device = GetPrimaryDevice(); + if (device) + persistConfiguration.iPhysicalAddress = device->GetCurrentPhysicalAddress(); } + + return m_communication ? m_communication->PersistConfiguration(persistConfiguration) : false; } -const char *CCECProcessor::ToString(const cec_deck_control_mode mode) +void CCECProcessor::RescanActiveDevices(void) { - switch (mode) - { - 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"; - } + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + it->second->GetStatus(true); } -const char *CCECProcessor::ToString(const cec_deck_info status) +bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { - switch (status) - { - 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"; - } + 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; } -const char *CCECProcessor::ToString(const cec_opcode opcode) +bool CCECProcessor::TransmitPendingActiveSourceCommands(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"; - case CEC_OPCODE_NONE: - return "poll"; - default: - return "UNKNOWN"; - } + bool bReturn(true); + for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) + bReturn &= it->second->TransmitPendingActiveSourceCommands(); + return bReturn; } -const char *CCECProcessor::ToString(const cec_system_audio_status mode) +CCECTV *CCECProcessor::GetTV(void) const { - switch(mode) - { - case CEC_SYSTEM_AUDIO_STATUS_ON: - return "on"; - case CEC_SYSTEM_AUDIO_STATUS_OFF: - return "off"; - default: - return "unknown"; - } + return CCECBusDevice::AsTV(m_busDevices->At(CECDEVICE_TV)); } -const char *CCECProcessor::ToString(const cec_audio_status UNUSED(status)) +CCECAudioSystem *CCECProcessor::GetAudioSystem(void) const { - // TODO this is a mask - return "TODO"; + return CCECBusDevice::AsAudioSystem(m_busDevices->At(CECDEVICE_AUDIOSYSTEM)); } -const char *CCECProcessor::ToString(const cec_vendor_id vendor) +CCECPlaybackDevice *CCECProcessor::GetPlaybackDevice(cec_logical_address address) const { - 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"; - case CEC_VENDOR_SONY: - return "Sony"; - case CEC_VENDOR_TOSHIBA: - return "Toshiba"; - default: - return "Unknown"; - } + return CCECBusDevice::AsPlaybackDevice(m_busDevices->At(address)); } -const char *CCECProcessor::ToString(const cec_client_version version) +CCECRecordingDevice *CCECProcessor::GetRecordingDevice(cec_logical_address address) const { - switch (version) - { - case CEC_CLIENT_VERSION_PRE_1_5: - return "pre-1.5"; - case CEC_CLIENT_VERSION_1_5_0: - return "1.5.0"; - case CEC_CLIENT_VERSION_1_5_1: - return "1.5.1"; - case CEC_CLIENT_VERSION_1_5_2: - return "1.5.2"; - case CEC_CLIENT_VERSION_1_5_3: - return "1.5.3"; - case CEC_CLIENT_VERSION_1_6_0: - return "1.6.0"; - case CEC_CLIENT_VERSION_1_6_1: - return "1.6.1"; - case CEC_CLIENT_VERSION_1_6_2: - return "1.6.2"; - default: - return "Unknown"; - } + return CCECBusDevice::AsRecordingDevice(m_busDevices->At(address)); } -const char *CCECProcessor::ToString(const cec_server_version version) +CCECTuner *CCECProcessor::GetTuner(cec_logical_address address) const { - switch (version) - { - case CEC_SERVER_VERSION_PRE_1_5: - return "pre-1.5"; - case CEC_SERVER_VERSION_1_5_0: - return "1.5.0"; - case CEC_SERVER_VERSION_1_5_1: - return "1.5.1"; - case CEC_SERVER_VERSION_1_5_2: - return "1.5.2"; - case CEC_SERVER_VERSION_1_5_3: - return "1.5.3"; - case CEC_SERVER_VERSION_1_6_0: - return "1.6.0"; - case CEC_SERVER_VERSION_1_6_1: - return "1.6.1"; - case CEC_SERVER_VERSION_1_6_2: - return "1.6.2"; - default: - return "Unknown"; - } + return CCECBusDevice::AsTuner(m_busDevices->At(address)); } -bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */) +bool CCECProcessor::AllocateLogicalAddresses(CCECClient* client) { - if (!m_communication && strPort) + libcec_configuration &configuration = *client->GetConfiguration(); + + // mark as unregistered + client->SetRegistered(false); + + // unregister this client from the old addresses + CECDEVICEVEC devices; + m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) { - bool bReturn(false); - IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort); - CTimeout timeout(10000); - int iConnectTry(0); - while (timeout.TimeLeft() > 0 && (bReturn = comm->Open(timeout.TimeLeft() / CEC_CONNECT_TRIES, true)) == false) - { - CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection (try %d)", ++iConnectTry); - comm->Close(); - Sleep(500); - } - if (comm->IsOpen()) - { - bReturn = comm->StartBootloader(); - delete comm; - } - return bReturn; + // remove client entry + CLockObject lock(m_mutex); + m_clients.erase((*it)->GetLogicalAddress()); } - else + + // find logical addresses for this client + if (!client->AllocateLogicalAddresses()) { - return m_communication->StartBootloader(); + m_libcec->AddLog(CEC_LOG_ERROR, "failed to find a free logical address for the client"); + return false; } -} -bool CCECProcessor::PingAdapter(void) -{ - return m_communication->PingAdapter(); -} + // register this client on the new addresses + devices.clear(); + m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + { + // set the physical address of the device at this LA + if (CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress)) + (*it)->SetPhysicalAddress(configuration.iPhysicalAddress); -void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination) -{ - if (destination < CECDEVICE_BROADCAST) - m_busDevices[destination]->HandlePollFrom(initiator); -} + // replace a previous client + CLockObject lock(m_mutex); + m_clients.erase((*it)->GetLogicalAddress()); + m_clients.insert(make_pair((*it)->GetLogicalAddress(), client)); + } -bool CCECProcessor::HandleReceiveFailed(cec_logical_address initiator) -{ - return !m_busDevices[initiator]->HandleReceiveFailed(); + // set the new ackmask + SetLogicalAddresses(GetLogicalAddresses()); + + // resume outgoing communication + m_bStallCommunication = false; + + return true; } -bool CCECProcessor::SetStreamPath(uint16_t iPhysicalAddress) +uint16_t CCECProcessor::GetPhysicalAddressFromEeprom(void) { - // stream path changes are sent by the TV - return m_busDevices[CECDEVICE_TV]->GetHandler()->TransmitSetStreamPath(iPhysicalAddress); + libcec_configuration config; config.Clear(); + if (m_communication) + m_communication->GetConfiguration(config); + return config.iPhysicalAddress; } -bool CCECProcessor::SetConfiguration(const libcec_configuration *configuration) +bool CCECProcessor::RegisterClient(CCECClient *client) { - bool bReinit(false); - CCECBusDevice *primary = IsRunning() ? GetPrimaryDevice() : NULL; - cec_device_type oldPrimaryType = primary ? primary->GetType() : CEC_DEVICE_TYPE_RECORDING_DEVICE; - m_configuration.clientVersion = configuration->clientVersion; - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion)); + if (!client) + return false; - // client version 1.5.0 + libcec_configuration &configuration = *client->GetConfiguration(); - // device types - bool bDeviceTypeChanged = IsRunning () && m_configuration.deviceTypes != configuration->deviceTypes; - m_configuration.deviceTypes = configuration->deviceTypes; - if (bDeviceTypeChanged) - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0])); + if (configuration.clientVersion < CEC_CLIENT_VERSION_2_0_0) + { + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: client version %s is no longer supported", ToString((cec_client_version)configuration.clientVersion)); + return false; + } - bool bPhysicalAddressChanged(false); + if (configuration.bMonitorOnly == 1) + return true; - // autodetect address - bool bPhysicalAutodetected(false); - if (IsRunning() && configuration->bAutodetectAddress == 1) + if (!CECInitialised()) { - uint16_t iPhysicalAddress = m_communication->GetPhysicalAddress(); - if (iPhysicalAddress != 0) - { - if (IsRunning()) - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%4x'", __FUNCTION__, iPhysicalAddress); - else - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using physical address '%x'", __FUNCTION__, iPhysicalAddress); - bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress); - m_configuration.iPhysicalAddress = iPhysicalAddress; - m_configuration.iHDMIPort = 0; - m_configuration.baseDevice = CECDEVICE_UNKNOWN; - bPhysicalAutodetected = true; - } + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised"); + return false; } - // physical address - if (!bPhysicalAutodetected) + // unregister the client first if it's already been marked as registered + if (client->IsRegistered()) + UnregisterClient(client); + + // ensure that controlled mode is enabled + m_communication->SetControlledMode(true); + m_bMonitor = false; + + // source logical address for requests + cec_logical_address sourceAddress(CECDEVICE_UNREGISTERED); + if (!m_communication->SupportsSourceLogicalAddress(CECDEVICE_UNREGISTERED)) { - if (configuration->iPhysicalAddress != 0) - bPhysicalAddressChanged = IsRunning() && m_configuration.iPhysicalAddress != configuration->iPhysicalAddress; - if (bPhysicalAddressChanged) + if (m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE)) + sourceAddress = CECDEVICE_FREEUSE; + else { - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - physical address '%x'", __FUNCTION__, configuration->iPhysicalAddress); - m_configuration.iPhysicalAddress = configuration->iPhysicalAddress; + 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; } } - bool bHdmiPortChanged(false); - if (!bPhysicalAutodetected && !bPhysicalAddressChanged) - { - // base device - bHdmiPortChanged = IsRunning() && m_configuration.baseDevice != configuration->baseDevice; - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice); - m_configuration.baseDevice = configuration->baseDevice; - - // hdmi port - bHdmiPortChanged |= IsRunning() && m_configuration.iHDMIPort != configuration->iHDMIPort; - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort); - m_configuration.iHDMIPort = configuration->iHDMIPort; - } - else + // ensure that we know the vendor id of the TV + CCECBusDevice *tv = GetTV(); + cec_vendor_id tvVendor(tv->GetVendorId(sourceAddress)); + + // wait until the handler is replaced, to avoid double registrations + if (tvVendor != CEC_VENDOR_UNKNOWN && + CCECCommandHandler::HasSpecificHandler(tvVendor)) { - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__); - m_configuration.baseDevice = CECDEVICE_UNKNOWN; - m_configuration.iHDMIPort = 0; + while (!tv->ReplaceHandler(false)) + CEvent::Sleep(5); } - bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged; + // get the configuration from the client + m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion)); - // device name - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName); - snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName); - if (primary && !primary->GetOSDName().Equals(m_configuration.strDeviceName)) - { - primary->SetOSDName(m_configuration.strDeviceName); - if (!bReinit && IsRunning()) - primary->TransmitOSDName(CECDEVICE_TV); - } + // get the current ackmask, so we can restore it if polling fails + cec_logical_addresses previousMask = GetLogicalAddresses(); - // tv vendor id override - CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor)); - if (m_configuration.tvVendor != configuration->tvVendor) + // mark as uninitialised + client->SetInitialised(false); + + // find logical addresses for this client + if (!AllocateLogicalAddresses(client)) { - m_configuration.tvVendor= configuration->tvVendor; - m_busDevices[CECDEVICE_TV]->SetVendorId((uint64_t)m_configuration.tvVendor); + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types"); + SetLogicalAddresses(previousMask); + return false; } - // wake CEC devices - if (m_configuration.wakeDevices != configuration->wakeDevices) + // get the settings from the rom + if (configuration.bGetSettingsFromROM == 1) { - m_configuration.wakeDevices = configuration->wakeDevices; - if (!bReinit && IsRunning()) - PowerOnDevices(); + libcec_configuration config; config.Clear(); + 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); } - // just copy these - m_configuration.bUseTVMenuLanguage = configuration->bUseTVMenuLanguage; - m_configuration.bActivateSource = configuration->bActivateSource; - m_configuration.bGetSettingsFromROM = configuration->bGetSettingsFromROM; - m_configuration.powerOffDevices = configuration->powerOffDevices; - m_configuration.bPowerOffScreensaver = configuration->bPowerOffScreensaver; - m_configuration.bPowerOffOnStandby = configuration->bPowerOffOnStandby; + // set the firmware version and build date + configuration.serverVersion = LIBCEC_VERSION_CURRENT; + configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); + configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); + configuration.adapterType = m_communication->GetAdapterType(); + + // mark the client as registered + client->SetRegistered(true); - // client version 1.5.1 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1) - m_configuration.bSendInactiveSource = configuration->bSendInactiveSource; + sourceAddress = client->GetPrimaryLogicalAdddress(); - // client version 1.6.0 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0) + // initialise the client + bool bReturn = 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()) { - m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby; - m_configuration.bShutdownOnStandby = configuration->bShutdownOnStandby; + 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); } - // client version 1.6.2 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2) + // ensure that the command handler for the TV is initialised + if (bReturn) { - memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3); + CCECCommandHandler *handler = GetTV()->GetHandler(); + if (handler) + handler->InitHandler(); + GetTV()->MarkHandlerReady(); } - // ensure that there is at least 1 device type set - if (m_configuration.deviceTypes.IsEmpty()) - m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); + // report our OSD name to the TV, since some TVs don't request it + client->GetPrimaryDevice()->TransmitOSDName(CECDEVICE_TV, false); + + // request the power status of the TV + tv->RequestPowerStatus(sourceAddress, true, true); - // persist the configuration - if (IsRunning()) - m_communication->PersistConfiguration(&m_configuration); + return bReturn; +} + +bool CCECProcessor::UnregisterClient(CCECClient *client) +{ + 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(); - if (bReinit || m_configuration.logicalAddresses.IsEmpty()) { - if (bDeviceTypeChanged) - return ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]); - else if (bPhysicalAddressChanged) - return SetPhysicalAddress(m_configuration.iPhysicalAddress); - else - return SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort); + 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(true); + } } - else if (m_configuration.bActivateSource == 1 && IsRunning() && !IsActiveSource(m_configuration.logicalAddresses.primary)) + + // set the new ackmask + cec_logical_addresses addresses = GetLogicalAddresses(); + if (SetLogicalAddresses(addresses)) { - // activate the source if we're not already the active source - SetActiveSource(m_configuration.deviceTypes.types[0]); + // no more clients left, disable controlled mode + if (addresses.IsEmpty() && !m_bMonitor) + m_communication->SetControlledMode(false); + + return true; } - return true; + return false; } -bool CCECProcessor::GetCurrentConfiguration(libcec_configuration *configuration) -{ - // client version 1.5.0 - snprintf(configuration->strDeviceName, 13, "%s", m_configuration.strDeviceName); - configuration->deviceTypes = m_configuration.deviceTypes; - configuration->bAutodetectAddress = m_configuration.bAutodetectAddress; - configuration->iPhysicalAddress = m_configuration.iPhysicalAddress; - configuration->baseDevice = m_configuration.baseDevice; - configuration->iHDMIPort = m_configuration.iHDMIPort; - configuration->clientVersion = m_configuration.clientVersion; - configuration->serverVersion = m_configuration.serverVersion; - configuration->tvVendor = m_configuration.tvVendor; - - configuration->bGetSettingsFromROM = m_configuration.bGetSettingsFromROM; - configuration->bUseTVMenuLanguage = m_configuration.bUseTVMenuLanguage; - configuration->bActivateSource = m_configuration.bActivateSource; - configuration->wakeDevices = m_configuration.wakeDevices; - configuration->powerOffDevices = m_configuration.powerOffDevices; - configuration->bPowerOffScreensaver = m_configuration.bPowerOffScreensaver; - configuration->bPowerOffOnStandby = m_configuration.bPowerOffOnStandby; - - // client version 1.5.1 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1) - configuration->bSendInactiveSource = m_configuration.bSendInactiveSource; - - // client version 1.5.3 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_3) - configuration->logicalAddresses = m_configuration.logicalAddresses; - - // client version 1.6.0 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0) - { - configuration->iFirmwareVersion = m_configuration.iFirmwareVersion; - configuration->bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby; - configuration->bShutdownOnStandby = m_configuration.bShutdownOnStandby; - } +void CCECProcessor::UnregisterClients(void) +{ + m_libcec->AddLog(CEC_LOG_DEBUG, "unregistering all CEC clients"); - // client version 1.6.2 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2) - { - memcpy(configuration->strDeviceLanguage, m_configuration.strDeviceLanguage, 3); - configuration->iFirmwareBuildDate = m_configuration.iFirmwareBuildDate; - } - return true; + vector clients = m_libcec->GetClients(); + for (vector::iterator client = clients.begin(); client != clients.end(); client++) + UnregisterClient(*client); + + CLockObject lock(m_mutex); + m_clients.clear(); } -bool CCECProcessor::CanPersistConfiguration(void) +CCECClient *CCECProcessor::GetClient(const cec_logical_address address) { - return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false; + CLockObject lock(m_mutex); + map::const_iterator client = m_clients.find(address); + if (client != m_clients.end()) + return client->second; + return NULL; } -bool CCECProcessor::PersistConfiguration(libcec_configuration *configuration) +CCECClient *CCECProcessor::GetPrimaryClient(void) { - return m_communication ? m_communication->PersistConfiguration(configuration) : false; + CLockObject lock(m_mutex); + map::const_iterator client = m_clients.begin(); + if (client != m_clients.end()) + return client->second; + return NULL; } -void CCECProcessor::RescanActiveDevices(void) +CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) { - for (unsigned int iPtr = 0; iPtr < CECDEVICE_BROADCAST; iPtr++) - m_busDevices[iPtr]->GetStatus(true); + return m_busDevices->At(GetLogicalAddress()); } -bool CCECProcessor::GetDeviceInformation(const char *strPort, libcec_configuration *config, uint32_t iTimeoutMs /* = 10000 */) +cec_logical_address CCECProcessor::GetLogicalAddress(void) { - if (!OpenConnection(strPort, 38400, iTimeoutMs, false)) - return false; + cec_logical_addresses addresses = GetLogicalAddresses(); + return addresses.primary; +} - config->iFirmwareVersion = m_communication->GetFirmwareVersion(); - config->iPhysicalAddress = m_communication->GetPhysicalAddress(); +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); - delete m_communication; - m_communication = NULL; - return true; + return addresses; } -bool CCECProcessor::TransmitPendingActiveSourceCommands(void) +bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const { - bool bReturn(true); - for (unsigned int iPtr = 0; iPtr < CECDEVICE_BROADCAST; iPtr++) - bReturn &= m_busDevices[iPtr]->TransmitPendingActiveSourceCommands(); - return bReturn; + 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) +{ + // stall outgoing messages until we know our new LA + m_bStallCommunication = true; + + m_libcec->AddLog(CEC_LOG_NOTICE, "logical address %x was taken by another device, allocating a new address", oldAddress); + CCECClient* 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) +{ + m_libcec->AddLog(CEC_LOG_NOTICE, "physical address changed to %04x", iNewAddress); + CCECClient* 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, CCECClient* client) : + m_processor(processor), + m_client(client) { } + +void* CCECAllocateLogicalAddress::Process(void) +{ + m_processor->AllocateLogicalAddresses(m_client); + return NULL; }