From c0152c0940ee81c79150dbafafd6621f576c3ccb Mon Sep 17 00:00:00 2001 From: Lars Op den Kamp Date: Mon, 14 May 2012 10:25:21 +0200 Subject: [PATCH] cec: cleanups, documentation and some fixes for the last commits --- src/lib/CECClient.cpp | 923 +++++++++++------- src/lib/CECClient.h | 142 ++- src/lib/CECProcessor.cpp | 269 +++-- src/lib/CECProcessor.h | 18 +- src/lib/LibCEC.cpp | 194 ++-- src/lib/LibCEC.h | 3 +- src/lib/adapter/AdapterCommunication.h | 4 +- src/lib/adapter/USBCECAdapterCommands.cpp | 22 +- src/lib/adapter/USBCECAdapterCommands.h | 4 +- .../adapter/USBCECAdapterCommunication.cpp | 4 +- src/lib/adapter/USBCECAdapterCommunication.h | 4 +- src/lib/devices/CECBusDevice.cpp | 6 + src/lib/devices/CECBusDevice.h | 1 + src/lib/implementations/ANCommandHandler.cpp | 2 +- src/lib/implementations/CECCommandHandler.cpp | 27 +- 15 files changed, 1000 insertions(+), 623 deletions(-) diff --git a/src/lib/CECClient.cpp b/src/lib/CECClient.cpp index 9e69d0d..93b9916 100644 --- a/src/lib/CECClient.cpp +++ b/src/lib/CECClient.cpp @@ -43,19 +43,21 @@ using namespace PLATFORM; #define LIB_CEC m_processor->GetLib() #define ToString(x) LIB_CEC->ToString(x) -CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration *configuration) : +CCECClient::CCECClient(CCECProcessor *processor, const libcec_configuration &configuration) : m_processor(processor), m_bInitialised(false), m_bRegistered(false), m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN), m_buttontime(0) { + // set the initial configuration SetConfiguration(configuration); } CCECClient::~CCECClient(void) { - if (m_processor) + // unregister the client + if (m_processor && IsRegistered()) m_processor->UnregisterClient(this); } @@ -83,44 +85,51 @@ void CCECClient::SetRegistered(bool bSetTo) m_bRegistered = bSetTo; } -bool CCECClient::Initialise(void) +bool CCECClient::OnRegister(void) { + // return false if already initialised if (IsInitialised()) return true; - //TODO do the same for the other devices - CCECBusDevice *primary = m_processor->GetDevice(m_configuration.logicalAddresses.primary); - if (!primary) + // get all device we control + CECDEVICEVEC devices; + m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + + // return false when no devices were found + if (devices.empty()) { - LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", m_configuration.logicalAddresses.primary); + LIB_CEC->AddLog(CEC_LOG_WARNING, "cannot find the primary device (logical address %x)", GetPrimaryLogicalAdddress()); return false; } - /* only set our OSD name for the primary device */ - primary->SetOSDName(m_configuration.strDeviceName); - - /* set the default menu language for devices we control */ - primary->SetMenuLanguage(m_configuration.strDeviceLanguage); + // mark as initialised + SetInitialised(true); - if (CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress)) - { - primary->SetPhysicalAddress(m_configuration.iPhysicalAddress); - primary->TransmitPhysicalAddress(); - } - else + // configure all devices + for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) { - SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort, true); + // only set our OSD name for the primary device + if ((*it)->GetLogicalAddress() == GetPrimaryLogicalAdddress()) + (*it)->SetOSDName(m_configuration.strDeviceName); + + // set the default menu language for devices we control + (*it)->SetMenuLanguage(m_configuration.strDeviceLanguage); } - /* make the primary device the active source if the option is set */ + // set the physical address + SetPhysicalAddress(m_configuration); + + // ensure that we know the vendor id of the TV, so we are using the correct handler + m_processor->GetTV()->GetVendorId(GetPrimaryLogicalAdddress()); + + // make the primary device the active source if the option is set if (m_configuration.bActivateSource == 1) - primary->ActivateSource(); + GetPrimaryDevice()->ActivateSource(); - SetInitialised(true); return true; } -bool CCECClient::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */) +bool CCECClient::SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce /* = false */) { bool bReturn(false); @@ -129,22 +138,26 @@ bool CCECClient::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, boo iPort > CEC_MAX_HDMI_PORTNUMBER) return bReturn; + LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); + + // update the configuration { CLockObject lock(m_mutex); m_configuration.baseDevice = iBaseDevice; m_configuration.iHDMIPort = iPort; } + // don't continue if the connection isn't opened if (!m_processor->IsRunning() && !bForce) return true; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice); - + // get the PA of the base device uint16_t iPhysicalAddress(CEC_INVALID_PHYSICAL_ADDRESS); CCECBusDevice *baseDevice = m_processor->GetDevice(iBaseDevice); if (baseDevice) - iPhysicalAddress = baseDevice->GetPhysicalAddress(m_configuration.logicalAddresses.primary); + iPhysicalAddress = baseDevice->GetPhysicalAddress(GetPrimaryLogicalAdddress()); + // add our port number if (iPhysicalAddress <= CEC_MAX_PHYSICAL_ADDRESS) { if (iPhysicalAddress == 0) @@ -159,98 +172,117 @@ bool CCECClient::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, boo bReturn = true; } + // set the default address when something went wrong if (!bReturn) { LIB_CEC->AddLog(CEC_LOG_WARNING, "failed to set the physical address to %04X, setting it to the default value %04X", iPhysicalAddress, CEC_DEFAULT_PHYSICAL_ADDRESS); iPhysicalAddress = CEC_DEFAULT_PHYSICAL_ADDRESS; } - SetPhysicalAddress(iPhysicalAddress); + // and set the address + SetDevicePhysicalAddress(iPhysicalAddress); + + ConfigurationChanged(m_configuration); return bReturn; } -bool CCECClient::SetPhysicalAddress(uint16_t iPhysicalAddress) +void CCECClient::ResetPhysicalAddress(void) { - bool bSendActiveView(false); - bool bReturn(false); - bool bSendUpdate = m_processor->CECInitialised(); + SetPhysicalAddress(m_configuration); +} - CECDEVICEVEC sendUpdatesTo; - { - CLockObject lock(m_mutex); - m_configuration.iPhysicalAddress = iPhysicalAddress; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress); +void CCECClient::SetPhysicalAddress(const libcec_configuration &configuration) +{ + // try to autodetect the address + bool bPASet(false); + if (m_processor->IsRunning() && configuration.bAutodetectAddress == 1) + bPASet = AutodetectPhysicalAddress(); - bool bWasActiveSource(false); - CECDEVICEVEC devices; - // TODO - m_processor->GetDevices()->GetLibCECControlled(devices); + // try to use physical address setting + if (!bPASet && CLibCEC::IsValidPhysicalAddress(configuration.iPhysicalAddress)) + bPASet = SetPhysicalAddress(configuration.iPhysicalAddress); - for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) - { - bWasActiveSource |= (*it)->IsActiveSource(); - (*it)->MarkAsInactiveSource(); - (*it)->SetPhysicalAddress(iPhysicalAddress); - if (bSendUpdate) - sendUpdatesTo.push_back(*it); - } + // use the base device + hdmi port settings + if (!bPASet) + bPASet = SetHDMIPort(configuration.baseDevice, configuration.iHDMIPort); - bSendActiveView = bWasActiveSource && bSendUpdate; - bReturn = true; + // reset to defaults if something went wrong + if (!bPASet) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__); + m_configuration.baseDevice = CECDEVICE_UNKNOWN; + m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE; } +} - for (CECDEVICEVEC::iterator it = sendUpdatesTo.begin(); it != sendUpdatesTo.end(); it++) +bool CCECClient::SetPhysicalAddress(const uint16_t iPhysicalAddress) +{ + // update the configuration { - (*it)->TransmitPhysicalAddress(); - if (bSendActiveView && m_configuration.logicalAddresses.primary == (*it)->GetLogicalAddress()) + CLockObject lock(m_mutex); + if (m_configuration.iPhysicalAddress == iPhysicalAddress) { - (*it)->MarkAsActiveSource(); - if ((*it)->HasValidPhysicalAddress()) - (*it)->ActivateSource(); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "physical address unchanged (%04X)", iPhysicalAddress); + return true; + } + else + { + m_configuration.iPhysicalAddress = iPhysicalAddress; + LIB_CEC->AddLog(CEC_LOG_DEBUG, "setting physical address to '%04X'", iPhysicalAddress); } } - if (bReturn) - { - m_processor->PersistConfiguration(&m_configuration); - ConfigurationChanged(m_configuration); - } + // persist the new configuration + if (m_processor->IsRunning()) + m_processor->PersistConfiguration(m_configuration); - return bReturn; + // set the physical address for each device + SetDevicePhysicalAddress(iPhysicalAddress); + + // and send back the updated configuration + ConfigurationChanged(m_configuration); + + return true; } -bool CCECClient::FindLogicalAddresses(void) +bool CCECClient::AllocateLogicalAddresses(void) { + // reset all previous LAs that were set m_configuration.logicalAddresses.Clear(); + // display an error if no device types are set if (m_configuration.deviceTypes.IsEmpty()) { LIB_CEC->AddLog(CEC_LOG_ERROR, "no device types given"); return false; } + // check each entry of the list for (uint8_t iPtr = 0; iPtr < 5; iPtr++) { if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) continue; + // find an LA for this type cec_logical_address address(CECDEVICE_UNKNOWN); if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE) - address = FindLogicalAddressRecordingDevice(); + address = AllocateLogicalAddressRecordingDevice(); if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_TUNER) - address = FindLogicalAddressTuner(); + address = AllocateLogicalAddressTuner(); if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE) - address = FindLogicalAddressPlaybackDevice(); + address = AllocateLogicalAddressPlaybackDevice(); if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM) - address = FindLogicalAddressAudioSystem(); + address = AllocateLogicalAddressAudioSystem(); + // display an error if no LA could be allocated if (address == CECDEVICE_UNKNOWN) { LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - failed to allocate device '%d', type '%s'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr])); return false; } + // display the registered LA LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - device '%d', type '%s', LA '%X'", __FUNCTION__, iPtr, ToString(m_configuration.deviceTypes.types[iPtr]), address); m_configuration.logicalAddresses.Set(address); } @@ -258,7 +290,7 @@ bool CCECClient::FindLogicalAddresses(void) return true; } -cec_logical_address CCECClient::FindLogicalAddressRecordingDevice(void) +cec_logical_address CCECClient::AllocateLogicalAddressRecordingDevice(void) { cec_logical_address retVal(CECDEVICE_UNKNOWN); @@ -273,7 +305,7 @@ cec_logical_address CCECClient::FindLogicalAddressRecordingDevice(void) return retVal; } -cec_logical_address CCECClient::FindLogicalAddressTuner(void) +cec_logical_address CCECClient::AllocateLogicalAddressTuner(void) { cec_logical_address retVal(CECDEVICE_UNKNOWN); @@ -290,7 +322,7 @@ cec_logical_address CCECClient::FindLogicalAddressTuner(void) return retVal; } -cec_logical_address CCECClient::FindLogicalAddressPlaybackDevice(void) +cec_logical_address CCECClient::AllocateLogicalAddressPlaybackDevice(void) { cec_logical_address retVal(CECDEVICE_UNKNOWN); @@ -305,7 +337,7 @@ cec_logical_address CCECClient::FindLogicalAddressPlaybackDevice(void) return retVal; } -cec_logical_address CCECClient::FindLogicalAddressAudioSystem(void) +cec_logical_address CCECClient::AllocateLogicalAddressAudioSystem(void) { cec_logical_address retVal(CECDEVICE_UNKNOWN); @@ -330,22 +362,19 @@ CCECBusDevice *CCECClient::GetDeviceByType(const cec_device_type type) const *devices.begin(); } -bool CCECClient::ChangeDeviceType(cec_device_type from, cec_device_type to) +bool CCECClient::ChangeDeviceType(const cec_device_type from, const cec_device_type to) { - bool bChanged(false); - LIB_CEC->AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to)); CLockObject lock(m_mutex); + // get the previous device that was allocated CCECBusDevice *previousDevice = GetDeviceByType(from); if (!previousDevice) return false; - m_processor->UnregisterClient(this); - - m_configuration.logicalAddresses.primary = CECDEVICE_UNREGISTERED; - + // change the type in the device type list + bool bChanged(false); for (uint8_t iPtr = 0; iPtr < 5; iPtr++) { if (m_configuration.deviceTypes.types[iPtr] == CEC_DEVICE_TYPE_RESERVED) @@ -358,53 +387,29 @@ bool CCECClient::ChangeDeviceType(cec_device_type from, cec_device_type to) } else if (m_configuration.deviceTypes.types[iPtr] == to && bChanged) { + // ensure that dupes are removed m_configuration.deviceTypes.types[iPtr] = CEC_DEVICE_TYPE_RESERVED; } } - if (bChanged) - { - // re-register the client to set the new ackmask - if (!m_processor->RegisterClient(this)) - return false; - - // copy the data from the previous device - CCECBusDevice *newDevice = GetDeviceByType(to); - if (previousDevice && newDevice) - { - newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC); - newDevice->SetCecVersion(previousDevice->GetCecVersion(m_configuration.logicalAddresses.primary, false)); - newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(m_configuration.logicalAddresses.primary, false)); - newDevice->SetMenuState(previousDevice->GetMenuState(m_configuration.logicalAddresses.primary)); - newDevice->SetOSDName(previousDevice->GetOSDName(m_configuration.logicalAddresses.primary, false)); - newDevice->SetPhysicalAddress(previousDevice->GetCurrentPhysicalAddress()); - newDevice->SetPowerStatus(previousDevice->GetPowerStatus(m_configuration.logicalAddresses.primary, false)); - newDevice->SetVendorId(previousDevice->GetVendorId(m_configuration.logicalAddresses.primary, false)); - - 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)) - { - newDevice->AsPlaybackDevice()->SetDeckControlMode(previousDevice->AsPlaybackDevice()->GetDeckControlMode(m_configuration.logicalAddresses.primary)); - newDevice->AsPlaybackDevice()->SetDeckStatus(previousDevice->AsPlaybackDevice()->GetDeckStatus(m_configuration.logicalAddresses.primary)); - } - } - - // and reset the previous device to the initial state - if (previousDevice) - previousDevice->ResetDeviceStatus(); - } + // re-register the client to set the new ackmask + if (!m_processor->RegisterClient(this)) + return false; return true; } -bool CCECClient::SetLogicalAddress(cec_logical_address iLogicalAddress) +bool CCECClient::SetLogicalAddress(const cec_logical_address iLogicalAddress) { CLockObject lock(m_mutex); - if (m_configuration.logicalAddresses.primary != iLogicalAddress) + if (GetPrimaryLogicalAdddress() != iLogicalAddress) { - LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress); - m_configuration.logicalAddresses.primary = iLogicalAddress; - m_configuration.logicalAddresses.Set(iLogicalAddress); + { + CLockObject lock(m_mutex); + LIB_CEC->AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress); + m_configuration.logicalAddresses.primary = iLogicalAddress; + m_configuration.logicalAddresses.Set(iLogicalAddress); + } return m_processor->RegisterClient(this); } @@ -416,122 +421,135 @@ bool CCECClient::Transmit(const cec_command &data) return m_processor ? m_processor->Transmit(data) : false; } -bool CCECClient::SendPowerOnDevices(cec_logical_address address /* = CECDEVICE_TV */) +bool CCECClient::SendPowerOnDevices(const cec_logical_address address /* = CECDEVICE_TV */) { + // if the broadcast address if set as destination and the client version >=1.5.0, read the wakeDevices setting if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0) { CECDEVICEVEC devices; m_processor->GetDevices()->GetWakeDevices(m_configuration, devices); - return m_processor->PowerOnDevices(m_configuration.logicalAddresses.primary, devices); + return m_processor->PowerOnDevices(GetPrimaryLogicalAdddress(), devices); } - return m_processor->PowerOnDevice(m_configuration.logicalAddresses.primary, address); + return m_processor->PowerOnDevice(GetPrimaryLogicalAdddress(), address); } -bool CCECClient::SendStandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */) +bool CCECClient::SendStandbyDevices(const cec_logical_address address /* = CECDEVICE_BROADCAST */) { + // if the broadcast address if set as destination and the client version >=1.5.0, read the standbyDevices setting if (address == CECDEVICE_BROADCAST && m_configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_0) { CECDEVICEVEC devices; m_processor->GetDevices()->GetPowerOffDevices(m_configuration, devices); - return m_processor->StandbyDevices(m_configuration.logicalAddresses.primary, devices); + return m_processor->StandbyDevices(GetPrimaryLogicalAdddress(), devices); } - return m_processor->StandbyDevice(m_configuration.logicalAddresses.primary, address); + return m_processor->StandbyDevice(GetPrimaryLogicalAdddress(), address); } -bool CCECClient::SendSetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */) +bool CCECClient::SendSetActiveSource(const cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */) { - bool bReturn(false); - - CCECBusDevice *device(NULL); + // get the devices that are controlled by us CECDEVICEVEC devices; m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + // filter out the device that matches the given type if (type != CEC_DEVICE_TYPE_RESERVED) CCECDeviceMap::FilterType(type, devices); - // no devices left + // no devices left, re-fetch the list of devices that are controlled by us if (devices.empty()) m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); if (!devices.empty()) - device = *devices.begin(); - - if (device) { - bReturn = true; - if (m_processor->IsRunning() && device->HasValidPhysicalAddress()) - bReturn = device->ActivateSource(); + // get the first device from the list + CCECBusDevice *device = *devices.begin(); + + // and activate it + if (!m_processor->IsRunning()) + device->MarkAsActiveSource(); + else if (device->HasValidPhysicalAddress()) + return device->ActivateSource(); } - return bReturn; + return false; } CCECPlaybackDevice *CCECClient::GetPlaybackDevice(void) { CCECPlaybackDevice *device(NULL); CECDEVICEVEC devices; + + // get the playback devices m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE, devices); - // no devices left + // no matches, get the recording devices if (devices.empty()) { m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); CCECDeviceMap::FilterType(CEC_DEVICE_TYPE_RECORDING_DEVICE, devices); } + // get the first device that matches, and cast it to CCECPlaybackDevice if (!devices.empty()) device = (*devices.begin())->AsPlaybackDevice(); return device; } -CCECBusDevice *CCECClient::GetPrimaryDevice(void) +cec_logical_address CCECClient::GetPrimaryLogicalAdddress(void) { - return m_processor->GetDevice(m_configuration.logicalAddresses.primary); + CLockObject lock(m_mutex); + return m_configuration.logicalAddresses.primary; } -bool CCECClient::SendSetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */) +CCECBusDevice *CCECClient::GetPrimaryDevice(void) { - bool bReturn(false); + return m_processor->GetDevice(GetPrimaryLogicalAdddress()); +} - CCECBusDevice *device = GetPlaybackDevice(); +bool CCECClient::SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate /* = true */) +{ + // find a playback device that we control + CCECPlaybackDevice *device = GetPlaybackDevice(); if (device) { - device->AsPlaybackDevice()->SetDeckControlMode(mode); + // and set the deck control mode if there is a match + device->SetDeckControlMode(mode); if (bSendUpdate) - bReturn = device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV); - else - bReturn = true; + return device->TransmitDeckStatus(CECDEVICE_TV); + return true; } + // no match return false; } -bool CCECClient::SendSetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */) +bool CCECClient::SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate /* = true */) { - bool bReturn(false); - - CCECBusDevice *device = GetPlaybackDevice(); + // find a playback device that we control + CCECPlaybackDevice *device = GetPlaybackDevice(); if (device) { - device->AsPlaybackDevice()->SetDeckStatus(info); + // and set the deck status if there is a match + device->SetDeckStatus(info); if (bSendUpdate) - bReturn = device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV); - else - bReturn = true; + return device->AsPlaybackDevice()->TransmitDeckStatus(CECDEVICE_TV); + return true; } + // no match return false; } -bool CCECClient::SendSetMenuState(cec_menu_state state, bool bSendUpdate /* = true */) +bool CCECClient::SendSetMenuState(const cec_menu_state state, bool bSendUpdate /* = true */) { CECDEVICEVEC devices; - m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + // set the menu state for all devices that are controlled by us + m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) { (*it)->SetMenuState(state); @@ -544,16 +562,23 @@ bool CCECClient::SendSetMenuState(cec_menu_state state, bool bSendUpdate /* = tr bool CCECClient::SendSetInactiveView(void) { - CCECBusDevice *primary = GetPrimaryDevice(); - if (primary) + CECDEVICEVEC devices; + + // mark all devices that are controlled by us as inactive source + m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) { - primary->MarkAsInactiveSource(); - return primary->TransmitInactiveSource(); + if ((*it)->IsActiveSource()) + { + (*it)->MarkAsInactiveSource(); + return (*it)->TransmitInactiveSource(); + } } - return false; + + return true; } -bool CCECClient::SendSetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage) +bool CCECClient::SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage) { CCECBusDevice *primary = GetPrimaryDevice(); if (primary) @@ -562,26 +587,26 @@ bool CCECClient::SendSetOSDString(cec_logical_address iLogicalAddress, cec_displ return false; } -cec_version CCECClient::GetDeviceCecVersion(cec_logical_address iAddress) +cec_version CCECClient::GetDeviceCecVersion(const cec_logical_address iAddress) { CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) - return device->GetCecVersion(m_configuration.logicalAddresses.primary); + return device->GetCecVersion(GetPrimaryLogicalAdddress()); return CEC_VERSION_UNKNOWN; } -bool CCECClient::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language) +bool CCECClient::GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language) { CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) { - *language = device->GetMenuLanguage(m_configuration.logicalAddresses.primary); - return (strcmp(language->language, "???") != 0); + language = device->GetMenuLanguage(GetPrimaryLogicalAdddress()); + return (strcmp(language.language, "???") != 0); } return false; } -cec_osd_name CCECClient::GetDeviceOSDName(cec_logical_address iAddress) +cec_osd_name CCECClient::GetDeviceOSDName(const cec_logical_address iAddress) { cec_osd_name retVal; retVal.device = iAddress; @@ -590,7 +615,7 @@ cec_osd_name CCECClient::GetDeviceOSDName(cec_logical_address iAddress) CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) { - CStdString strOSDName = device->GetOSDName(m_configuration.logicalAddresses.primary); + CStdString strOSDName = device->GetOSDName(GetPrimaryLogicalAdddress()); snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str()); retVal.device = iAddress; } @@ -598,27 +623,27 @@ cec_osd_name CCECClient::GetDeviceOSDName(cec_logical_address iAddress) return retVal; } -uint16_t CCECClient::GetDevicePhysicalAddress(cec_logical_address iAddress) +uint16_t CCECClient::GetDevicePhysicalAddress(const cec_logical_address iAddress) { CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) - return device->GetPhysicalAddress(m_configuration.logicalAddresses.primary); + return device->GetPhysicalAddress(GetPrimaryLogicalAdddress()); return CEC_INVALID_PHYSICAL_ADDRESS; } -cec_power_status CCECClient::GetDevicePowerStatus(cec_logical_address iAddress) +cec_power_status CCECClient::GetDevicePowerStatus(const cec_logical_address iAddress) { CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) - return device->GetPowerStatus(m_configuration.logicalAddresses.primary); + return device->GetPowerStatus(GetPrimaryLogicalAdddress()); return CEC_POWER_STATUS_UNKNOWN; } -uint64_t CCECClient::GetDeviceVendorId(cec_logical_address iAddress) +uint64_t CCECClient::GetDeviceVendorId(const cec_logical_address iAddress) { CCECBusDevice *device = m_processor->GetDevice(iAddress); if (device) - return device->GetVendorId(m_configuration.logicalAddresses.primary); + return device->GetVendorId(GetPrimaryLogicalAdddress()); return CEC_VENDOR_UNKNOWN; } @@ -652,232 +677,165 @@ uint8_t CCECClient::SendMuteAudio(void) (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN; } -bool CCECClient::SendKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */) +bool CCECClient::SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait /* = true */) { CCECBusDevice *device = GetPrimaryDevice(); CCECBusDevice *dest = m_processor->GetDevice(iDestination); return device && dest ? - device->TransmitKeypress(m_configuration.logicalAddresses.primary, key, bWait) : + device->TransmitKeypress(GetPrimaryLogicalAdddress(), key, bWait) : false; } -bool CCECClient::SendKeyRelease(cec_logical_address iDestination, bool bWait /* = true */) +bool CCECClient::SendKeyRelease(const cec_logical_address iDestination, bool bWait /* = true */) { CCECBusDevice *device = GetPrimaryDevice(); CCECBusDevice *dest = m_processor->GetDevice(iDestination); return device && dest ? - device->TransmitKeyRelease(m_configuration.logicalAddresses.primary, bWait) : + device->TransmitKeyRelease(GetPrimaryLogicalAdddress(), bWait) : false; } -bool CCECClient::GetCurrentConfiguration(libcec_configuration *configuration) +bool CCECClient::GetCurrentConfiguration(libcec_configuration &configuration) { + CLockObject lock(m_mutex); + // 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; + 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; + 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; + 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) + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0) { - configuration->iFirmwareVersion = m_configuration.iFirmwareVersion; - configuration->bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby; - configuration->bShutdownOnStandby = m_configuration.bShutdownOnStandby; + configuration.iFirmwareVersion = m_configuration.iFirmwareVersion; + configuration.bPowerOffDevicesOnStandby = m_configuration.bPowerOffDevicesOnStandby; + configuration.bShutdownOnStandby = m_configuration.bShutdownOnStandby; } // client version 1.6.2 - if (configuration->clientVersion >= CEC_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; + memcpy(configuration.strDeviceLanguage, m_configuration.strDeviceLanguage, 3); + configuration.iFirmwareBuildDate = m_configuration.iFirmwareBuildDate; } return true; } -bool CCECClient::SetConfiguration(const libcec_configuration *configuration) +bool CCECClient::SetConfiguration(const libcec_configuration &configuration) { - bool bReinit(false); bool bIsRunning(m_processor && m_processor->IsRunning()); - - if (configuration->callbacks) - { - m_configuration.callbacks = configuration->callbacks; - m_configuration.callbackParam = configuration->callbackParam; - } - - //TODO CCECBusDevice *primary = bIsRunning ? GetPrimaryDevice() : NULL; - cec_device_type oldPrimaryType = primary ? primary->GetType() : CEC_DEVICE_TYPE_RECORDING_DEVICE; + uint16_t iPA = primary ? primary->GetCurrentPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS; - m_configuration.clientVersion = configuration->clientVersion; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString((cec_client_version)configuration->clientVersion)); + // update the callbacks + if (configuration.callbacks) + EnableCallbacks(configuration.callbackParam, configuration.callbacks); - // client version 1.5.0 + // update the client version + SetClientVersion((cec_client_version)configuration.clientVersion); - // device types - bool bDeviceTypeChanged = bIsRunning && m_configuration.deviceTypes != configuration->deviceTypes; - m_configuration.deviceTypes = configuration->deviceTypes; - if (bDeviceTypeChanged) - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(configuration->deviceTypes[0])); + // update the OSD name + CStdString strOSDName(configuration.strDeviceName); + SetOSDName(strOSDName); - bool bPhysicalAddressChanged(false); + // update the TV vendor override + SetTVVendorOverride((cec_vendor_id)configuration.tvVendor); - // autodetect address - bool bPhysicalAutodetected(false); - if (bIsRunning && configuration->bAutodetectAddress == 1) + // just copy these { - uint16_t iPhysicalAddress = m_processor->GetDetectedPhysicalAddress(); - if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress)) + CLockObject lock(m_mutex); + m_configuration.bUseTVMenuLanguage = configuration.bUseTVMenuLanguage; + m_configuration.bActivateSource = configuration.bActivateSource; + m_configuration.bGetSettingsFromROM = configuration.bGetSettingsFromROM; + m_configuration.wakeDevices = configuration.wakeDevices; + m_configuration.powerOffDevices = configuration.powerOffDevices; + m_configuration.bPowerOffScreensaver = configuration.bPowerOffScreensaver; + m_configuration.bPowerOffOnStandby = configuration.bPowerOffOnStandby; + + // client version 1.5.1 + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_5_1) + m_configuration.bSendInactiveSource = configuration.bSendInactiveSource; + + // client version 1.6.0 + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_0) { - if (bIsRunning) - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress); - else - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using physical address '%04X'", __FUNCTION__, iPhysicalAddress); - bPhysicalAddressChanged = (m_configuration.iPhysicalAddress != iPhysicalAddress); - m_configuration.iPhysicalAddress = iPhysicalAddress; - m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE; - m_configuration.baseDevice = CECDEVICE_UNKNOWN; - bPhysicalAutodetected = true; + m_configuration.bPowerOffDevicesOnStandby = configuration.bPowerOffDevicesOnStandby; + m_configuration.bShutdownOnStandby = configuration.bShutdownOnStandby; } - } - // physical address - if (!bPhysicalAutodetected) - { - uint16_t iPhysicalAddress(CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress) ? configuration->iPhysicalAddress : CEC_PHYSICAL_ADDRESS_TV); - bPhysicalAddressChanged = bIsRunning && m_configuration.iPhysicalAddress != iPhysicalAddress; - if (bPhysicalAddressChanged) + // client version 1.6.2 + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_2) { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address '%04X'", __FUNCTION__, iPhysicalAddress); - m_configuration.iPhysicalAddress = iPhysicalAddress; + memcpy(m_configuration.strDeviceLanguage, configuration.strDeviceLanguage, 3); } - } - - bool bHdmiPortChanged(false); - if (!bPhysicalAutodetected && !CLibCEC::IsValidPhysicalAddress(configuration->iPhysicalAddress)) - { - // base device - bHdmiPortChanged = bIsRunning && m_configuration.baseDevice != configuration->baseDevice; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using base device '%x'", __FUNCTION__, (int)configuration->baseDevice); - m_configuration.baseDevice = configuration->baseDevice; - // hdmi port - bHdmiPortChanged |= bIsRunning && m_configuration.iHDMIPort != configuration->iHDMIPort; - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using HDMI port '%d'", __FUNCTION__, configuration->iHDMIPort); - m_configuration.iHDMIPort = configuration->iHDMIPort; - } - else - { - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - resetting HDMI port and base device to defaults", __FUNCTION__); - m_configuration.baseDevice = CECDEVICE_UNKNOWN; - m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE; - } - - bReinit = bPhysicalAddressChanged || bHdmiPortChanged || bDeviceTypeChanged; - - // device name - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, configuration->strDeviceName); - snprintf(m_configuration.strDeviceName, 13, "%s", configuration->strDeviceName); - if (primary && !primary->GetOSDName(m_configuration.logicalAddresses.primary, false).Equals(m_configuration.strDeviceName)) - { - primary->SetOSDName(m_configuration.strDeviceName); - if (!bReinit && bIsRunning) - primary->TransmitOSDName(CECDEVICE_TV); - } - - // tv vendor id override - LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString((cec_vendor_id)configuration->tvVendor)); - if (m_processor && m_configuration.tvVendor != configuration->tvVendor) - { - m_configuration.tvVendor= configuration->tvVendor; - m_processor->GetTV()->SetVendorId((uint64_t)m_configuration.tvVendor); - } - - // wake CEC devices - if (m_configuration.wakeDevices != configuration->wakeDevices) - { - m_configuration.wakeDevices = configuration->wakeDevices; - if (!bReinit && bIsRunning) - SendPowerOnDevices(); + // ensure that there is at least 1 device type set + if (m_configuration.deviceTypes.IsEmpty()) + m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); } - // 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; - - // client version 1.5.1 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_5_1) - m_configuration.bSendInactiveSource = configuration->bSendInactiveSource; + bool bNeedReinit(false); - // client version 1.6.0 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_0) + // device types + if (SetDeviceTypes(configuration.deviceTypes)) { - m_configuration.bPowerOffDevicesOnStandby = configuration->bPowerOffDevicesOnStandby; - m_configuration.bShutdownOnStandby = configuration->bShutdownOnStandby; + // the device type changed. just copy the rest, and re-register + { + CLockObject lock(m_mutex); + m_configuration.iPhysicalAddress = configuration.iPhysicalAddress; + m_configuration.baseDevice = configuration.baseDevice; + m_configuration.iHDMIPort = configuration.iHDMIPort; + bNeedReinit = true; + } } - - // client version 1.6.2 - if (configuration->clientVersion >= CEC_CLIENT_VERSION_1_6_2) + else { - memcpy(m_configuration.strDeviceLanguage, configuration->strDeviceLanguage, 3); + // set the physical address + SetPhysicalAddress(configuration); } - // ensure that there is at least 1 device type set - if (m_configuration.deviceTypes.IsEmpty()) - m_configuration.deviceTypes.Add(CEC_DEVICE_TYPE_RECORDING_DEVICE); - if (bIsRunning) - m_processor->GetTV()->ReplaceHandler(false); + m_processor->PersistConfiguration(m_configuration); - bool bReturn(true); - if (bReinit || m_configuration.logicalAddresses.IsEmpty()) + if (!primary) + primary = GetPrimaryDevice(); + + if (bNeedReinit || !primary || primary->GetCurrentPhysicalAddress() != iPA) { - if (bDeviceTypeChanged) - bReturn = ChangeDeviceType(oldPrimaryType, m_configuration.deviceTypes[0]); - else if (CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress)) - bReturn = SetPhysicalAddress(m_configuration.iPhysicalAddress); - else if (m_configuration.baseDevice != CECDEVICE_UNKNOWN && m_configuration.iHDMIPort != CEC_HDMI_PORTNUMBER_NONE) - bReturn = SetHDMIPort(m_configuration.baseDevice, m_configuration.iHDMIPort); + // PA or device type changed + m_processor->RegisterClient(this); } - else if (m_configuration.bActivateSource == 1 && bIsRunning && !m_processor->IsActiveSource(m_configuration.logicalAddresses.primary)) + else if (primary && configuration.bActivateSource == 1 && bIsRunning && !primary->IsActiveSource()) { // activate the source if we're not already the active source - SendSetActiveSource(m_configuration.deviceTypes.types[0]); + primary->ActivateSource(); } - // persist the configuration - if (bIsRunning) - m_processor->PersistConfiguration(&m_configuration); - - return bReturn; + return true; } void CCECClient::AddCommand(const cec_command &command) @@ -962,10 +920,9 @@ void CCECClient::AddKey(const cec_keypress &key) m_buttontime = key.duration > 0 ? 0 : GetTimeMs(); } -void CCECClient::SetCurrentButton(cec_user_control_code iButtonCode) +void CCECClient::SetCurrentButton(const cec_user_control_code iButtonCode) { - /* push keypress to the keybuffer with 0 duration. - push another press to the keybuffer with the duration set when the button is released */ + // push a keypress to the buffer with 0 duration and another with the duration set when released cec_keypress key; key.duration = 0; key.keycode = iButtonCode; @@ -1001,6 +958,11 @@ bool CCECClient::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) return true; } +bool CCECClient::PingAdapter(void) +{ + return m_processor ? m_processor->PingAdapter() : false; +} + bool CCECClient::GetNextLogMessage(cec_log_message *message) { return (m_logBuffer.Pop(*message)); @@ -1015,3 +977,292 @@ bool CCECClient::GetNextCommand(cec_command *command) { return m_commandBuffer.Pop(*command); } + +CStdString CCECClient::GetConnectionInfo(void) +{ + CStdString strLog; + strLog.Format("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 != CEC_FW_BUILD_UNKNOWN) + { + 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"); + } + + // log the addresses that are being used + if (!m_configuration.logicalAddresses.IsEmpty()) + { + strLog.append(", logical address(es) = "); + CECDEVICEVEC devices; + m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) + strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress()); + } + + if (!CLibCEC::IsValidPhysicalAddress(m_configuration.iPhysicalAddress)) + strLog.AppendFormat(", base device: %s (%X), HDMI port number: %d", ToString(m_configuration.baseDevice), m_configuration.baseDevice, m_configuration.iHDMIPort); + else + strLog.AppendFormat(", physical address: %04x", m_configuration.iPhysicalAddress); + + return strLog; +} + +void CCECClient::SetTVVendorOverride(const cec_vendor_id id) +{ + { + CLockObject lock(m_mutex); + m_configuration.tvVendor = id; + } + + if (id != CEC_VENDOR_UNKNOWN) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vendor id '%s'", __FUNCTION__, ToString(id)); + + CCECBusDevice *tv = m_processor ? m_processor->GetTV() : NULL; + if (tv) + tv->SetVendorId((uint64_t)id); + } +} + +cec_vendor_id CCECClient::GetTVVendorOverride(void) +{ + CLockObject lock(m_mutex); + return (cec_vendor_id)m_configuration.tvVendor; +} + +void CCECClient::SetOSDName(const CStdString &strDeviceName) +{ + { + CLockObject lock(m_mutex); + snprintf(m_configuration.strDeviceName, 13, "%s", strDeviceName.c_str()); + } + + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using OSD name '%s'", __FUNCTION__, strDeviceName.c_str()); + + CCECBusDevice *primary = GetPrimaryDevice(); + if (primary && !primary->GetCurrentOSDName().Equals(strDeviceName)) + { + primary->SetOSDName(strDeviceName); + if (m_processor && m_processor->IsRunning()) + primary->TransmitOSDName(CECDEVICE_TV); + } +} + +CStdString CCECClient::GetOSDName(void) +{ + CLockObject lock(m_mutex); + CStdString strOSDName(m_configuration.strDeviceName); + return strOSDName; +} + +void CCECClient::SetWakeDevices(const cec_logical_addresses &addresses) +{ + CLockObject lock(m_mutex); + m_configuration.wakeDevices = addresses; +} + +cec_logical_addresses CCECClient::GetWakeDevices(void) +{ + CLockObject lock(m_mutex); + return m_configuration.wakeDevices; +} + +bool CCECClient::AutodetectPhysicalAddress(void) +{ + bool bPhysicalAutodetected(false); + uint16_t iPhysicalAddress = m_processor ? m_processor->GetDetectedPhysicalAddress() : CEC_INVALID_PHYSICAL_ADDRESS; + + if (CLibCEC::IsValidPhysicalAddress(iPhysicalAddress)) + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - autodetected physical address '%04X'", __FUNCTION__, iPhysicalAddress); + + CLockObject lock(m_mutex); + m_configuration.iPhysicalAddress = iPhysicalAddress; + m_configuration.iHDMIPort = CEC_HDMI_PORTNUMBER_NONE; + m_configuration.baseDevice = CECDEVICE_UNKNOWN; + bPhysicalAutodetected = true; + } + + SetDevicePhysicalAddress(iPhysicalAddress); + + return bPhysicalAutodetected; +} + +void CCECClient::SetClientVersion(const cec_client_version version) +{ + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using client version '%s'", __FUNCTION__, ToString(version)); + + CLockObject lock(m_mutex); + m_configuration.clientVersion = (uint32_t)version; +} + +cec_client_version CCECClient::GetClientVersion(void) +{ + CLockObject lock(m_mutex); + return (cec_client_version)m_configuration.clientVersion; +} + +bool CCECClient::SetDeviceTypes(const cec_device_type_list &deviceTypes) +{ + bool bNeedReinit(false); + + { + CLockObject lock(m_mutex); + bNeedReinit = m_processor && m_processor->IsRunning() && + (m_configuration.deviceTypes != deviceTypes); + m_configuration.deviceTypes = deviceTypes; + } + + if (bNeedReinit) + LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - using primary device type '%s'", __FUNCTION__, ToString(deviceTypes[0])); + + return bNeedReinit; +} + +cec_device_type_list CCECClient::GetDeviceTypes(void) +{ + cec_device_type_list retVal; + CLockObject lock(m_mutex); + retVal = m_configuration.deviceTypes; + return retVal; +} + +bool CCECClient::SetDevicePhysicalAddress(const uint16_t iPhysicalAddress) +{ + if (!CLibCEC::IsValidPhysicalAddress(iPhysicalAddress)) + return false; + + // reconfigure all devices + cec_logical_address reactivateSource(CECDEVICE_UNKNOWN); + CECDEVICEVEC devices; + m_processor->GetDevices()->GetByLogicalAddresses(devices, m_configuration.logicalAddresses); + for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) + { + // if this device was the active source, reactivate it afterwards + if ((*it)->IsActiveSource()) + reactivateSource = (*it)->GetLogicalAddress(); + + // mark the device as inactive source + if (IsInitialised()) + (*it)->MarkAsInactiveSource(); + + // set the new physical address + (*it)->SetPhysicalAddress(iPhysicalAddress); + + // and transmit it + if (IsInitialised()) + (*it)->TransmitPhysicalAddress(); + } + + // reactivate the previous active source + if (reactivateSource != CECDEVICE_UNKNOWN && + m_processor->IsRunning() && + IsInitialised()) + { + CCECBusDevice *device = m_processor->GetDevice(reactivateSource); + if (device) + device->ActivateSource(); + } + + return true; +} + +bool CCECClient::SwitchMonitoring(bool bEnable) +{ + return m_processor ? m_processor->SwitchMonitoring(bEnable) : false; +} + +bool CCECClient::PollDevice(const cec_logical_address iAddress) +{ + // try to find the primary device + CCECBusDevice *primary = GetPrimaryDevice(); + // poll the destination, with the primary as source + if (primary) + return primary->TransmitPoll(iAddress); + + return m_processor ? m_processor->PollDevice(iAddress) : false; +} + +cec_logical_addresses CCECClient::GetActiveDevices(void) +{ + CECDEVICEVEC activeDevices; + if (m_processor) + m_processor->GetDevices()->GetActive(activeDevices); + return CCECDeviceMap::ToLogicalAddresses(activeDevices); +} + +bool CCECClient::IsActiveDevice(const cec_logical_address iAddress) +{ + cec_logical_addresses activeDevices = GetActiveDevices(); + return activeDevices.IsSet(iAddress); +} + +bool CCECClient::IsActiveDeviceType(const cec_device_type type) +{ + CECDEVICEVEC activeDevices; + if (m_processor) + m_processor->GetDevices()->GetActive(activeDevices); + CCECDeviceMap::FilterType(type, activeDevices); + return !activeDevices.empty(); +} + +cec_logical_address CCECClient::GetActiveSource(void) +{ + return m_processor ? m_processor->GetActiveSource() : CECDEVICE_UNKNOWN; +} + +bool CCECClient::IsActiveSource(const cec_logical_address iAddress) +{ + return m_processor ? m_processor->IsActiveSource(iAddress) : false; +} + +bool CCECClient::SetStreamPath(const cec_logical_address iAddress) +{ + uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress); + if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS) + return SetStreamPath(iPhysicalAddress); + return false; +} + +bool CCECClient::SetStreamPath(const uint16_t iPhysicalAddress) +{ + return m_processor ? m_processor->SetStreamPath(iPhysicalAddress) : false; +} + +cec_logical_addresses CCECClient::GetLogicalAddresses(void) +{ + cec_logical_addresses addresses; + CLockObject lock(m_mutex); + addresses = m_configuration.logicalAddresses; + return addresses; +} + +bool CCECClient::CanPersistConfiguration(void) +{ + return m_processor ? m_processor->CanPersistConfiguration() : false; +} + +bool CCECClient::PersistConfiguration(const libcec_configuration &configuration) +{ + return m_processor ? m_processor->PersistConfiguration(configuration) : false; +} + +void CCECClient::RescanActiveDevices(void) +{ + if (m_processor) + m_processor->RescanActiveDevices(); +} + +bool CCECClient::IsLibCECActiveSource(void) +{ + bool bReturn(false); + if (m_processor) + { + cec_logical_address activeSource = m_processor->GetActiveSource(); + CCECBusDevice *device = m_processor->GetDevice(activeSource); + if (device) + bReturn = device->IsHandledByLibCEC(); + } + return bReturn; +} diff --git a/src/lib/CECClient.h b/src/lib/CECClient.h index 0ea111f..bd4099a 100644 --- a/src/lib/CECClient.h +++ b/src/lib/CECClient.h @@ -44,72 +44,106 @@ namespace CEC class CCECClient { public: - CCECClient(CCECProcessor *processor, const libcec_configuration *configuration); + CCECClient(CCECProcessor *processor, const libcec_configuration &configuration); virtual ~CCECClient(void); // methods for registration in CCECProcessor - bool Initialise(void); - void OnUnregister(void) { SetRegistered(false); SetInitialised(false); } - bool IsInitialised(void); - void SetInitialised(bool bSetTo); - bool IsRegistered(void); - void SetRegistered(bool bSetTo); - CCECBusDevice * GetPrimaryDevice(void); - CCECPlaybackDevice *GetPlaybackDevice(void); - bool FindLogicalAddresses(void); - bool ChangeDeviceType(cec_device_type from, cec_device_type to); - CCECBusDevice * GetDeviceByType(const cec_device_type type) const; + bool OnRegister(void); + void OnUnregister(void) { SetRegistered(false); SetInitialised(false); } + bool IsInitialised(void); + void SetInitialised(bool bSetTo); + bool IsRegistered(void); + void SetRegistered(bool bSetTo); + cec_logical_address GetPrimaryLogicalAdddress(void); + + // device specific methods + CCECBusDevice * GetPrimaryDevice(void); + CCECPlaybackDevice * GetPlaybackDevice(void); + bool AllocateLogicalAddresses(void); + bool ChangeDeviceType(const cec_device_type from, const cec_device_type to); + CCECBusDevice * GetDeviceByType(const cec_device_type type) const; + void ResetPhysicalAddress(void); + CStdString GetConnectionInfo(void); + void SetTVVendorOverride(const cec_vendor_id id); + cec_vendor_id GetTVVendorOverride(void); + void SetOSDName(const CStdString &strDeviceName); + CStdString GetOSDName(void); + void SetWakeDevices(const cec_logical_addresses &addresses); + cec_logical_addresses GetWakeDevices(void); + bool AutodetectPhysicalAddress(void); + void SetClientVersion(const cec_client_version version); + cec_client_version GetClientVersion(void); + bool SetDeviceTypes(const cec_device_type_list &deviceTypes); + cec_device_type_list GetDeviceTypes(void); // client-specific part of ICECAdapter - bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks); - bool GetNextLogMessage(cec_log_message *message); - bool GetNextKeypress(cec_keypress *key); - bool GetNextCommand(cec_command *command); - bool Transmit(const cec_command &data); - bool SetLogicalAddress(cec_logical_address iLogicalAddress); - bool SetPhysicalAddress(uint16_t iPhysicalAddress); - bool SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce = false); - bool SendPowerOnDevices(cec_logical_address address = CECDEVICE_TV); - bool SendStandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST); - bool SendSetActiveSource(cec_device_type type = CEC_DEVICE_TYPE_RESERVED); - bool SendSetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate = true); - bool SendSetDeckInfo(cec_deck_info info, bool bSendUpdate = true); - bool SendSetInactiveView(void); - bool SendSetMenuState(cec_menu_state state, bool bSendUpdate = true); - bool SendSetOSDString(cec_logical_address iLogicalAddress, cec_display_control duration, const char *strMessage); - cec_version GetDeviceCecVersion(cec_logical_address iAddress); - bool GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language); - uint64_t GetDeviceVendorId(cec_logical_address iAddress); - cec_power_status GetDevicePowerStatus(cec_logical_address iAddress); - uint16_t GetDevicePhysicalAddress(cec_logical_address iAddress); - uint8_t SendVolumeUp(bool bSendRelease = true); - uint8_t SendVolumeDown(bool bSendRelease = true); - uint8_t SendMuteAudio(void); - bool SendKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait = true); - bool SendKeyRelease(cec_logical_address iDestination, bool bWait = true); - cec_osd_name GetDeviceOSDName(cec_logical_address iAddress); + bool EnableCallbacks(void *cbParam, ICECCallbacks *callbacks); + bool PingAdapter(void); + bool GetNextLogMessage(cec_log_message *message); /**< @deprecated will be removed in v2.0 */ + bool GetNextKeypress(cec_keypress *key); /**< @deprecated will be removed in v2.0 */ + bool GetNextCommand(cec_command *command); /**< @deprecated will be removed in v2.0 */ + bool Transmit(const cec_command &data); + bool SetLogicalAddress(const cec_logical_address iLogicalAddress); + bool SetPhysicalAddress(const uint16_t iPhysicalAddress); + bool SetHDMIPort(const cec_logical_address iBaseDevice, const uint8_t iPort, bool bForce = false); + bool SendPowerOnDevices(const cec_logical_address address = CECDEVICE_TV); + bool SendStandbyDevices(const cec_logical_address address = CECDEVICE_BROADCAST); + bool SendSetActiveSource(const cec_device_type type = CEC_DEVICE_TYPE_RESERVED); + bool SendSetDeckControlMode(const cec_deck_control_mode mode, bool bSendUpdate = true); + bool SendSetDeckInfo(const cec_deck_info info, bool bSendUpdate = true); + bool SendSetInactiveView(void); + bool SendSetMenuState(const cec_menu_state state, bool bSendUpdate = true); + bool SendSetOSDString(const cec_logical_address iLogicalAddress, const cec_display_control duration, const char *strMessage); + bool SwitchMonitoring(bool bEnable); + cec_version GetDeviceCecVersion(const cec_logical_address iAddress); + bool GetDeviceMenuLanguage(const cec_logical_address iAddress, cec_menu_language &language); + uint64_t GetDeviceVendorId(const cec_logical_address iAddress); + cec_power_status GetDevicePowerStatus(const cec_logical_address iAddress); + uint16_t GetDevicePhysicalAddress(const cec_logical_address iAddress); + bool PollDevice(const cec_logical_address iAddress); + cec_logical_addresses GetActiveDevices(void); + bool IsActiveDevice(const cec_logical_address iAddress); + bool IsActiveDeviceType(const cec_device_type type); + uint8_t SendVolumeUp(bool bSendRelease = true); + uint8_t SendVolumeDown(bool bSendRelease = true); + uint8_t SendMuteAudio(void); + bool SendKeypress(const cec_logical_address iDestination, const cec_user_control_code key, bool bWait = true); + bool SendKeyRelease(const cec_logical_address iDestination, bool bWait = true); + cec_osd_name GetDeviceOSDName(const cec_logical_address iAddress); + cec_logical_address GetActiveSource(void); + bool IsActiveSource(const cec_logical_address iAddress); + bool SetStreamPath(const cec_logical_address iAddress); + bool SetStreamPath(const uint16_t iPhysicalAddress); + cec_logical_addresses GetLogicalAddresses(void); + void RescanActiveDevices(void); + bool IsLibCECActiveSource(void); // configuration libcec_configuration *GetConfiguration(void) { return &m_configuration; } - bool GetCurrentConfiguration(libcec_configuration *configuration); - bool SetConfiguration(const libcec_configuration *configuration); + bool GetCurrentConfiguration(libcec_configuration &configuration); + bool SetConfiguration(const libcec_configuration &configuration); + bool CanPersistConfiguration(void); + bool PersistConfiguration(const libcec_configuration &configuration); + void SetPhysicalAddress(const libcec_configuration &configuration); // callbacks - void AddCommand(const cec_command &command); - int MenuStateChanged(const cec_menu_state newState); - void Alert(const libcec_alert type, const libcec_parameter ¶m); - void AddLog(const cec_log_message &message); - void AddKey(void); - void AddKey(const cec_keypress &key); - void SetCurrentButton(cec_user_control_code iButtonCode); - void CheckKeypressTimeout(void); - void ConfigurationChanged(const libcec_configuration &config); + void AddCommand(const cec_command &command); + int MenuStateChanged(const cec_menu_state newState); + void Alert(const libcec_alert type, const libcec_parameter ¶m); + void AddLog(const cec_log_message &message); + void AddKey(void); + void AddKey(const cec_keypress &key); + void SetCurrentButton(const cec_user_control_code iButtonCode); + void CheckKeypressTimeout(void); + void ConfigurationChanged(const libcec_configuration &config); protected: - cec_logical_address FindLogicalAddressRecordingDevice(void); - cec_logical_address FindLogicalAddressTuner(void); - cec_logical_address FindLogicalAddressPlaybackDevice(void); - cec_logical_address FindLogicalAddressAudioSystem(void); + cec_logical_address AllocateLogicalAddressRecordingDevice(void); + cec_logical_address AllocateLogicalAddressTuner(void); + cec_logical_address AllocateLogicalAddressPlaybackDevice(void); + cec_logical_address AllocateLogicalAddressAudioSystem(void); + + bool SetDevicePhysicalAddress(const uint16_t iPhysicalAddress); CCECProcessor * m_processor; libcec_configuration m_configuration; diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index bce22c0..0714d2a 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -74,10 +74,11 @@ CCECProcessor::~CCECProcessor(void) bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { CLockObject lock(m_mutex); + // open a connection if (!OpenConnection(strPort, iBaudRate, iTimeoutMs)) return false; - /* create the processor thread */ + // create the processor thread if (!IsRunning()) { if (CreateThread()) @@ -89,6 +90,7 @@ bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIA } } + // mark as initialised SetCECInitialised(true); return true; @@ -96,15 +98,30 @@ bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIA void CCECProcessor::Close(void) { + // mark as uninitialised SetCECInitialised(false); + + // stop the processor StopThread(); + // close the connection if (m_communication) { delete m_communication; m_communication = NULL; } +} +void CCECProcessor::ResetMembers(void) +{ + // close the connection + if (m_communication) + { + delete m_communication; + m_communication = NULL; + } + + // reset the other members to the initial state m_bMonitor = false; m_iPreviousAckMask = 0; m_iStandardLineTimeout = 3; @@ -116,22 +133,25 @@ void CCECProcessor::Close(void) 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); + + // ensure that a previous connection is closed Close(); + // reset all member to the initial state + ResetMembers(); + + // check whether the Close() method deleted any previous connection + if (m_communication) { - CLockObject lock(m_mutex); - if (m_communication && m_communication->IsOpen()) - { - m_libcec->AddLog(CEC_LOG_ERROR, "connection already opened"); - return true; - } - else if (!m_communication) - m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); + m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed"); + return bReturn; } - CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); + // create a new connection + m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); - /* open a new connection */ + // open a new connection unsigned iConnectTry(0); while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false) { @@ -153,20 +173,25 @@ bool CCECProcessor::CECInitialised(void) void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */) { - CLockObject lock(m_mutex); - m_bInitialised = bSetTo; + { + CLockObject lock(m_mutex); + m_bInitialised = bSetTo; + } if (!bSetTo) UnregisterClients(); } bool CCECProcessor::TryLogicalAddress(cec_logical_address address) { + // find the device CCECBusDevice *device = m_busDevices->At(address); if (device) { + // 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(); } @@ -179,6 +204,7 @@ void CCECProcessor::ReplaceHandlers(void) if (!CECInitialised()) return; + // check each device for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) it->second->ReplaceHandler(true); } @@ -193,29 +219,34 @@ void *CCECProcessor::Process(void) m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); cec_command command; - command.Clear(); + // as long as we're not being stopped and the connection is open while (!IsStopped() && m_communication->IsOpen()) { + // wait for a new incoming command, and process it if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME)) - ParseCommand(command); + ProcessCommand(command); if (CECInitialised()) { - ReplaceHandlers(); - + // check clients for keypress timeouts m_libcec->CheckKeypressTimeout(); + + // check if we need to replace handlers + ReplaceHandlers(); } } return NULL; } -bool CCECProcessor::SetActiveSource(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) bReturn = device->ActivateSource(); else @@ -230,12 +261,24 @@ void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout) m_iStandardLineTimeout = iTimeout; } +uint8_t CCECProcessor::GetStandardLineTimeout(void) +{ + CLockObject lock(m_mutex); + return m_iStandardLineTimeout; +} + void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout) { CLockObject lock(m_mutex); m_iRetryLineTimeout = iTimeout; } +uint8_t CCECProcessor::GetRetryLineTimeout(void) +{ + CLockObject lock(m_mutex); + return m_iRetryLineTimeout; +} + bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) { CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress); @@ -245,12 +288,19 @@ bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) 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]); + + // and log it m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); } @@ -260,26 +310,34 @@ bool CCECProcessor::SwitchMonitoring(bool bEnable) { CLockObject lock(m_mutex); + // switch to monitoring mode, which will stop processing of incoming messages m_bMonitor = bEnable; + // and store the current ackmask m_iPreviousAckMask = m_communication->GetAckMask(); } + // set the mask to 0 when enabling monitor mode if (bEnable) return SetAckMask(0); + // and restore the previous mask otherwise else return SetAckMask(m_iPreviousAckMask); } bool CCECProcessor::PollDevice(cec_logical_address iAddress) { - CCECBusDevice *device = m_busDevices->At(iAddress); + // try to find the primary device CCECBusDevice *primary = GetPrimaryDevice(); + // poll the destination, with the primary as source + if (primary) + return primary->TransmitPoll(iAddress); + + // try to find the destination + CCECBusDevice *device = m_busDevices->At(iAddress); + // and poll the destination, with the same LA as source if (device) - { - return primary ? - primary->TransmitPoll(iAddress) : - device->TransmitPoll(iAddress); - } + return device->TransmitPoll(iAddress); + return false; } @@ -327,6 +385,19 @@ bool CCECProcessor::IsActiveSource(cec_logical_address iAddress) bool CCECProcessor::Transmit(const cec_command &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; + + LogOutput(data); + + // find the initiator device CCECBusDevice *initiator = m_busDevices->At(data.initiator); if (!initiator) { @@ -334,42 +405,35 @@ bool CCECProcessor::Transmit(const cec_command &data) return false; } + // find the destination device, if it's not the broadcast address if (data.destination != CECDEVICE_BROADCAST) { + // check if the device is marked as handled by libCEC CCECBusDevice *destination = m_busDevices->At(data.destination); if (destination && destination->IsHandledByLibCEC()) { + // 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; } } - uint8_t iMaxTries(0); { CLockObject lock(m_mutex); - if (IsStopped()) - return false; - LogOutput(data); m_iLastTransmission = GetTimeMs(); - if (!m_communication || !m_communication->IsOpen()) - { - m_libcec->AddLog(CEC_LOG_ERROR, "cannot transmit command: connection closed"); - return false; - } + // set the number of tries iMaxTries = 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; - + // and try to send the command while (bRetry && ++iTries < iMaxTries) { if (initiator->IsUnsupportedFeature(data.opcode)) return false; - adapterState = m_communication->Write(data, bRetry, iLineTimeout); + adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ? + m_communication->Write(data, bRetry, iLineTimeout) : + ADAPTER_MESSAGE_STATE_ERROR; iLineTimeout = m_iRetryLineTimeout; } @@ -388,8 +452,9 @@ void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_addres Transmit(command); } -void CCECProcessor::ParseCommand(const cec_command &command) +void CCECProcessor::ProcessCommand(const cec_command &command) { + // log the command CStdString dataStr; dataStr.Format(">> %1x%1x", command.initiator, command.destination); if (command.opcode_set == 1) @@ -398,9 +463,12 @@ void CCECProcessor::ParseCommand(const cec_command &command) dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]); m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str()); + // if we're not in monitor mode if (!m_bMonitor) { + // find the initiator CCECBusDevice *device = m_busDevices->At(command.initiator); + // and "handle" the command if (device) device->HandleCommand(command); } @@ -461,6 +529,7 @@ bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logic bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */) { bool bReturn(false); + // open a connection if no connection has been opened if (!m_communication && strPort) { IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort); @@ -518,7 +587,7 @@ bool CCECProcessor::CanPersistConfiguration(void) return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false; } -bool CCECProcessor::PersistConfiguration(libcec_configuration *configuration) +bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration) { return m_communication ? m_communication->PersistConfiguration(configuration) : false; } @@ -579,17 +648,25 @@ bool CCECProcessor::RegisterClient(CCECClient *client) if (!client) return false; + // unregister the client first if it's already been marked as registered + if (client->IsRegistered()) + UnregisterClient(client); + + // get the configuration from the client libcec_configuration &configuration = *client->GetConfiguration(); m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion)); + // mark as uninitialised and unregistered client->SetRegistered(false); client->SetInitialised(false); + // 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->FindLogicalAddresses()) + if (!client->AllocateLogicalAddresses()) { + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types"); SetAckMask(iPreviousMask); return false; } @@ -599,17 +676,17 @@ bool CCECProcessor::RegisterClient(CCECClient *client) m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) { + // replace a previous client CLockObject lock(m_mutex); m_clients.erase((*it)->GetLogicalAddress()); m_clients.insert(make_pair((*it)->GetLogicalAddress(), client)); - client->SetRegistered(true); } // get the settings from the rom if (configuration.bGetSettingsFromROM == 1) { libcec_configuration config; - m_communication->GetConfiguration(&config); + m_communication->GetConfiguration(config); CLockObject lock(m_mutex); if (!config.deviceTypes.IsEmpty()) @@ -619,43 +696,26 @@ bool CCECProcessor::RegisterClient(CCECClient *client) snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName); } - // set the new ack mask - bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) && - client->Initialise(); - // set the firmware version and build date configuration.serverVersion = LIBCEC_VERSION_CURRENT; configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); - CStdString strLog; - if (bReturn) - strLog = "CEC client registered."; - else - strLog = "failed to register the CEC client."; - strLog.AppendFormat(" libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)configuration.serverVersion), ToString((cec_client_version)configuration.clientVersion), configuration.iFirmwareVersion); - if (configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN) - { - time_t buildTime = (time_t)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"); - } + // mark the client as registered + client->SetRegistered(true); - m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog); + // set the new ack mask + bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) && + // and initialise the client + client->OnRegister(); - if (bReturn) - { - strLog = "Using logical address(es): "; - CECDEVICEVEC devices; - m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); - for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) - strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress()); - m_libcec->AddLog(CEC_LOG_NOTICE, strLog); - } + // 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 (!m_communication->IsRunningLatestFirmware()) + if (bReturn && !IsRunningLatestFirmware()) { const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information."; m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage); @@ -663,38 +723,51 @@ bool CCECProcessor::RegisterClient(CCECClient *client) param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING; client->Alert(CEC_ALERT_SERVICE_DEVICE, param); } - else - { - m_libcec->AddLog(CEC_LOG_DEBUG, "the adapter is using the latest (known) firmware version"); - } - - if (bReturn) - { - /* get the vendor id from the TV, so we are using the correct handler */ - GetTV()->GetVendorId(configuration.logicalAddresses.primary); - } return bReturn; } void CCECProcessor::UnregisterClient(CCECClient *client) { - CLockObject lock(m_mutex); - CECDEVICEVEC devices; - m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses); - for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + if (!client) + return; + + m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str()); + + // notify the client that it will be unregistered + client->OnUnregister(); + { - map::iterator entry = m_clients.find((*it)->GetLogicalAddress()); - if (entry != m_clients.end()) - m_clients.erase(entry); + 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 + SetAckMask(GetLogicalAddresses().AckMask()); } void CCECProcessor::UnregisterClients(void) { + 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); - for (map::iterator client = m_clients.begin(); client != m_clients.end(); client++) - client->second->OnUnregister(); m_clients.clear(); } @@ -716,19 +789,20 @@ CCECClient *CCECProcessor::GetPrimaryClient(void) return NULL; } -CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const +CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) { return m_busDevices->At(GetLogicalAddress()); } -cec_logical_address CCECProcessor::GetLogicalAddress(void) const +cec_logical_address CCECProcessor::GetLogicalAddress(void) { cec_logical_addresses addresses = GetLogicalAddresses(); return addresses.primary; } -cec_logical_addresses CCECProcessor::GetLogicalAddresses(void) const +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++) @@ -742,3 +816,10 @@ bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const CCECBusDevice *device = GetDevice(address); return device && device->IsHandledByLibCEC(); } + +bool CCECProcessor::IsRunningLatestFirmware(void) +{ + return m_communication && m_communication->IsOpen() ? + m_communication->IsRunningLatestFirmware() : + true; +} diff --git a/src/lib/CECProcessor.h b/src/lib/CECProcessor.h index 48154fe..e859ab0 100644 --- a/src/lib/CECProcessor.h +++ b/src/lib/CECProcessor.h @@ -80,9 +80,9 @@ namespace CEC CCECTV * GetTV(void) const; CCECBusDevice * GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bSuppressUpdate = true); - CCECBusDevice * GetPrimaryDevice(void) const; - cec_logical_address GetLogicalAddress(void) const; - cec_logical_addresses GetLogicalAddresses(void) const; + CCECBusDevice * GetPrimaryDevice(void); + cec_logical_address GetLogicalAddress(void); + cec_logical_addresses GetLogicalAddresses(void); bool IsPresentDevice(cec_logical_address address); bool IsPresentDeviceType(cec_device_type type); uint16_t GetDetectedPhysicalAddress(void) const; @@ -98,13 +98,15 @@ namespace CEC bool PowerOnDevice(const cec_logical_address initiator, cec_logical_address address); bool SetDeckInfo(cec_deck_info info, bool bSendUpdate = true); - bool SetActiveSource(uint16_t iStreamPath); + bool ActivateSource(uint16_t iStreamPath); bool SwitchMonitoring(bool bEnable); bool PollDevice(cec_logical_address iAddress); void SetStandardLineTimeout(uint8_t iTimeout); + uint8_t GetStandardLineTimeout(void); void SetRetryLineTimeout(uint8_t iTimeout); + uint8_t GetRetryLineTimeout(void); bool CanPersistConfiguration(void); - bool PersistConfiguration(libcec_configuration *configuration); + bool PersistConfiguration(const libcec_configuration &configuration); void RescanActiveDevices(void); bool SetLineTimeout(uint8_t iTimeout); @@ -127,6 +129,8 @@ namespace CEC bool IsHandledByLibCEC(const cec_logical_address address) const; bool TryLogicalAddress(cec_logical_address address); + + bool IsRunningLatestFirmware(void); private: bool OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening = true); void SetCECInitialised(bool bSetTo = true); @@ -136,7 +140,9 @@ namespace CEC bool SetAckMask(uint16_t iMask); void LogOutput(const cec_command &data); - void ParseCommand(const cec_command &command); + void ProcessCommand(const cec_command &command); + + void ResetMembers(void); bool m_bInitialised; PLATFORM::CMutex m_mutex; diff --git a/src/lib/LibCEC.cpp b/src/lib/LibCEC.cpp index 1646041..5f22f9a 100644 --- a/src/lib/LibCEC.cpp +++ b/src/lib/LibCEC.cpp @@ -64,29 +64,27 @@ CLibCEC::CLibCEC(libcec_configuration *UNUSED(configuration)) : CLibCEC::~CLibCEC(void) { - delete m_client; - m_client = NULL; + // unregister all clients client + UnregisterClients(); + + // delete the adapter connection delete m_cec; m_cec = NULL; } bool CLibCEC::Open(const char *strPort, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { - if (!m_cec) + if (!m_cec || !strPort) return false; - if (m_cec->IsRunning()) - { - AddLog(CEC_LOG_ERROR, "connection already open"); - return false; - } - + // open a new connection if (!m_cec->Start(strPort, CEC_SERIAL_DEFAULT_BAUDRATE, iTimeoutMs)) { AddLog(CEC_LOG_ERROR, "could not start CEC communications"); return false; } + // register all clients for (vector::iterator it = m_clients.begin(); it != m_clients.end(); it++) { if (!m_cec->RegisterClient(*it)) @@ -101,14 +99,12 @@ bool CLibCEC::Open(const char *strPort, uint32_t iTimeoutMs /* = CEC_DEFAULT_CON void CLibCEC::Close(void) { + // unregister all clients + UnregisterClients(); + + // close the connection if (m_cec) m_cec->Close(); - - for (vector::iterator it = m_clients.begin(); it != m_clients.end(); it++) - { - (*it)->SetRegistered(false); - (*it)->SetInitialised(false); - } } int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const char *strDevicePath /* = NULL */) @@ -116,113 +112,50 @@ int8_t CLibCEC::FindAdapters(cec_adapter *deviceList, uint8_t iBufSize, const ch return CUSBCECAdapterDetection::FindAdapters(deviceList, iBufSize, strDevicePath); } -bool CLibCEC::PingAdapter(void) -{ - return m_cec ? m_cec->PingAdapter() : false; -} - bool CLibCEC::StartBootloader(void) { return m_cec ? m_cec->StartBootloader() : false; } -bool CLibCEC::SwitchMonitoring(bool bEnable) -{ - return m_cec ? m_cec->SwitchMonitoring(bEnable) : false; -} - -cec_logical_address CLibCEC::GetActiveSource(void) -{ - return m_cec ? m_cec->GetActiveSource() : CECDEVICE_UNKNOWN; -} - -bool CLibCEC::IsActiveSource(cec_logical_address iAddress) -{ - return m_cec ? m_cec->IsActiveSource(iAddress) : false; -} - -bool CLibCEC::PollDevice(cec_logical_address iAddress) -{ - return m_cec ? m_cec->PollDevice(iAddress) : false; -} - -cec_logical_addresses CLibCEC::GetActiveDevices(void) -{ - CECDEVICEVEC activeDevices; - if (m_cec) - m_cec->GetDevices()->GetActive(activeDevices); - return CCECDeviceMap::ToLogicalAddresses(activeDevices); -} - -bool CLibCEC::IsActiveDevice(cec_logical_address iAddress) +bool CLibCEC::PingAdapter(void) { - cec_logical_addresses activeDevices = GetActiveDevices(); - return activeDevices.IsSet(iAddress); + return m_client ? m_client->PingAdapter() : false; } -bool CLibCEC::IsActiveDeviceType(cec_device_type type) -{ - CECDEVICEVEC activeDevices; - if (m_cec) - m_cec->GetDevices()->GetActive(activeDevices); - CCECDeviceMap::FilterType(type, activeDevices); - return !activeDevices.empty(); -} - -bool CLibCEC::SetStreamPath(cec_logical_address iAddress) +bool CLibCEC::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) { - uint16_t iPhysicalAddress = GetDevicePhysicalAddress(iAddress); - if (iPhysicalAddress != CEC_INVALID_PHYSICAL_ADDRESS) - return SetStreamPath(iPhysicalAddress); - return false; + return m_client ? m_client->EnableCallbacks(cbParam, callbacks) : false; } -bool CLibCEC::SetStreamPath(uint16_t iPhysicalAddress) +bool CLibCEC::GetCurrentConfiguration(libcec_configuration *configuration) { - return m_cec->SetStreamPath(iPhysicalAddress); + return m_client ? m_client->GetCurrentConfiguration(*configuration) : false; } -bool CLibCEC::IsLibCECActiveSource(void) +bool CLibCEC::SetConfiguration(const libcec_configuration *configuration) { - bool bReturn(false); - if (m_cec) - { - cec_logical_address activeSource = m_cec->GetActiveSource(); - CCECBusDevice *device = m_cec->GetDevice(activeSource); - if (device) - bReturn = device->IsHandledByLibCEC(); - } - return bReturn; + return m_client ? m_client->SetConfiguration(*configuration) : false; } bool CLibCEC::CanPersistConfiguration(void) { - return m_cec->CanPersistConfiguration(); + return m_client ? m_client->CanPersistConfiguration() : false; } bool CLibCEC::PersistConfiguration(libcec_configuration *configuration) { - return m_cec->PersistConfiguration(configuration); + return m_client ? m_client->PersistConfiguration(*configuration) : false; } void CLibCEC::RescanActiveDevices(void) { - return m_cec->RescanActiveDevices(); -} - -bool CLibCEC::EnableCallbacks(void *cbParam, ICECCallbacks *callbacks) -{ - return m_client ? m_client->EnableCallbacks(cbParam, callbacks) : false; -} - -bool CLibCEC::GetCurrentConfiguration(libcec_configuration *configuration) -{ - return m_client ? m_client->GetCurrentConfiguration(configuration) : false; + if (m_client) + m_client->RescanActiveDevices(); } -bool CLibCEC::SetConfiguration(const libcec_configuration *configuration) +bool CLibCEC::IsLibCECActiveSource(void) { - return m_client ? m_client->SetConfiguration(configuration) : false; + return m_client ? m_client->IsLibCECActiveSource() : false; } bool CLibCEC::Transmit(const cec_command &data) @@ -285,6 +218,11 @@ bool CLibCEC::SetOSDString(cec_logical_address iLogicalAddress, cec_display_cont return m_client ? m_client->SendSetOSDString(iLogicalAddress, duration, strMessage) : false; } +bool CLibCEC::SwitchMonitoring(bool bEnable) +{ + return m_client ? m_client->SwitchMonitoring(bEnable) : false; +} + cec_version CLibCEC::GetDeviceCecVersion(cec_logical_address iAddress) { return m_client ? m_client->GetDeviceCecVersion(iAddress) : CEC_VERSION_UNKNOWN; @@ -292,7 +230,7 @@ cec_version CLibCEC::GetDeviceCecVersion(cec_logical_address iAddress) bool CLibCEC::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language) { - return m_client ? m_client->GetDeviceMenuLanguage(iAddress, language) : false; + return m_client ? m_client->GetDeviceMenuLanguage(iAddress, *language) : false; } uint64_t CLibCEC::GetDeviceVendorId(cec_logical_address iAddress) @@ -310,6 +248,30 @@ cec_power_status CLibCEC::GetDevicePowerStatus(cec_logical_address iAddress) return m_client ? m_client->GetDevicePowerStatus(iAddress) : CEC_POWER_STATUS_UNKNOWN; } +bool CLibCEC::PollDevice(cec_logical_address iAddress) +{ + return m_client ? m_client->PollDevice(iAddress) : false; +} + +cec_logical_addresses CLibCEC::GetActiveDevices(void) +{ + cec_logical_addresses addresses; + addresses.Clear(); + if (m_client) + addresses = m_client->GetActiveDevices(); + return addresses; +} + +bool CLibCEC::IsActiveDevice(cec_logical_address iAddress) +{ + return m_client ? m_client->IsActiveDevice(iAddress) : false; +} + +bool CLibCEC::IsActiveDeviceType(cec_device_type type) +{ + return m_client ? m_client->IsActiveDeviceType(type) : false; +} + uint8_t CLibCEC::VolumeUp(bool bSendRelease /* = true */) { return m_client ? m_client->SendVolumeUp(bSendRelease) : (uint8_t)CEC_AUDIO_VOLUME_STATUS_UNKNOWN; @@ -346,12 +308,31 @@ cec_osd_name CLibCEC::GetDeviceOSDName(cec_logical_address iAddress) return retVal; } +cec_logical_address CLibCEC::GetActiveSource(void) +{ + return m_client ? m_client->GetActiveSource() : CECDEVICE_UNKNOWN; +} + +bool CLibCEC::IsActiveSource(cec_logical_address iAddress) +{ + return m_client ? m_client->IsActiveSource(iAddress) : false; +} +bool CLibCEC::SetStreamPath(cec_logical_address iAddress) +{ + return m_client ? m_client->SetStreamPath(iAddress) : false; +} + +bool CLibCEC::SetStreamPath(uint16_t iPhysicalAddress) +{ + return m_client ? m_client->SetStreamPath(iPhysicalAddress) : false; +} + cec_logical_addresses CLibCEC::GetLogicalAddresses(void) { cec_logical_addresses addresses; addresses.Clear(); - if (m_cec) - addresses = m_cec->GetLogicalAddresses(); + if (m_client) + m_client->GetLogicalAddresses(); return addresses; } @@ -883,6 +864,7 @@ void CLibCEC::AddLog(const cec_log_level level, const char *strFormat, ...) { CStdString strLog; + // format the message va_list argList; va_start(argList, strFormat); strLog.FormatV(strFormat, argList); @@ -917,19 +899,22 @@ bool CLibCEC::EnablePhysicalAddressDetection(void) return true; } -CCECClient *CLibCEC::RegisterClient(libcec_configuration *configuration) +CCECClient *CLibCEC::RegisterClient(libcec_configuration &configuration) { if (!m_cec) return NULL; + // create a new client instance CCECClient *newClient = new CCECClient(m_cec, configuration); if (!newClient) return NULL; - m_clients.push_back(newClient); + + // if the default client isn't set, set it if (!m_client) m_client = newClient; + // register the new client if (m_cec->IsRunning()) m_cec->RegisterClient(newClient); @@ -938,7 +923,11 @@ CCECClient *CLibCEC::RegisterClient(libcec_configuration *configuration) void CLibCEC::UnregisterClients(void) { + if (m_cec) + m_cec->UnregisterClients(); + m_clients.clear(); + delete m_client; m_client = NULL; } @@ -948,13 +937,17 @@ void * CECInitialise(libcec_configuration *configuration) if (!configuration) return NULL; + // create a new libCEC instance CLibCEC *lib = new CLibCEC(NULL); + + // register a new client CCECClient *client(NULL); - if (lib) - client = lib->RegisterClient(configuration); + if (lib && configuration) + client = lib->RegisterClient(*configuration); + // update the current configuration if (client) - client->GetCurrentConfiguration(configuration); + client->GetCurrentConfiguration(*configuration); // ensure that the correct server version is set configuration->serverVersion = LIBCEC_VERSION_CURRENT; @@ -1010,6 +1003,7 @@ bool CLibCEC::GetDeviceInformation(const char *strPort, libcec_configuration *co return m_cec->GetDeviceInformation(strPort, config, iTimeoutMs); } +// no longer being used void CLibCEC::AddKey(const cec_keypress &UNUSED(key)) {} void CLibCEC::AddCommand(const cec_command &UNUSED(command)) {} void CLibCEC::ConfigurationChanged(const libcec_configuration &UNUSED(config)) {} diff --git a/src/lib/LibCEC.h b/src/lib/LibCEC.h index 673b525..0ef3e83 100644 --- a/src/lib/LibCEC.h +++ b/src/lib/LibCEC.h @@ -143,8 +143,9 @@ namespace CEC static void SetInstance(CLibCEC *instance); //UNUSED static bool IsValidPhysicalAddress(uint16_t iPhysicalAddress); - CCECClient *RegisterClient(libcec_configuration *configuration); + CCECClient *RegisterClient(libcec_configuration &configuration); void UnregisterClients(void); + std::vector GetClients(void) { return m_clients; }; CCECProcessor * m_cec; diff --git a/src/lib/adapter/AdapterCommunication.h b/src/lib/adapter/AdapterCommunication.h index 889c87b..11af5e7 100644 --- a/src/lib/adapter/AdapterCommunication.h +++ b/src/lib/adapter/AdapterCommunication.h @@ -163,14 +163,14 @@ namespace CEC * @brief The configuration to store. * @return True when the configuration was persisted, false otherwise. */ - virtual bool PersistConfiguration(libcec_configuration *configuration) = 0; + virtual bool PersistConfiguration(const libcec_configuration &configuration) = 0; /*! * @brief Get the persisted configuration from the adapter (if supported) * @param configuration The updated configuration. * @return True when the configuration was updated, false otherwise. */ - virtual bool GetConfiguration(libcec_configuration *configuration) = 0; + virtual bool GetConfiguration(libcec_configuration &configuration) = 0; /*! * @return The name of the port diff --git a/src/lib/adapter/USBCECAdapterCommands.cpp b/src/lib/adapter/USBCECAdapterCommands.cpp index 62a011a..bdc2412 100644 --- a/src/lib/adapter/USBCECAdapterCommands.cpp +++ b/src/lib/adapter/USBCECAdapterCommands.cpp @@ -413,7 +413,7 @@ bool CUSBCECAdapterCommands::WriteEEPROM(void) return m_bNeedsWrite; } -bool CUSBCECAdapterCommands::PersistConfiguration(libcec_configuration *configuration) +bool CUSBCECAdapterCommands::PersistConfiguration(const libcec_configuration &configuration) { if (m_persistedConfiguration.iFirmwareVersion < 2) return false; @@ -423,12 +423,12 @@ bool CUSBCECAdapterCommands::PersistConfiguration(libcec_configuration *configur bool bReturn(true); bReturn &= SetSettingAutoEnabled(true); - bReturn &= SetSettingDeviceType(CLibCEC::GetType(configuration->logicalAddresses.primary)); - bReturn &= SetSettingDefaultLogicalAddress(configuration->logicalAddresses.primary); - bReturn &= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration->logicalAddresses.primary)); - bReturn &= SetSettingPhysicalAddress(configuration->iPhysicalAddress); + bReturn &= SetSettingDeviceType(CLibCEC::GetType(configuration.logicalAddresses.primary)); + bReturn &= SetSettingDefaultLogicalAddress(configuration.logicalAddresses.primary); + bReturn &= SetSettingLogicalAddressMask(CLibCEC::GetMaskForType(configuration.logicalAddresses.primary)); + bReturn &= SetSettingPhysicalAddress(configuration.iPhysicalAddress); bReturn &= SetSettingCECVersion(CEC_VERSION_1_3A); - bReturn &= SetSettingOSDName(configuration->strDeviceName); + bReturn &= SetSettingOSDName(configuration.strDeviceName); bReturn &= WriteEEPROM(); return bReturn; } @@ -477,17 +477,17 @@ bool CUSBCECAdapterCommands::RequestSettings(void) return bReturn; } -bool CUSBCECAdapterCommands::GetConfiguration(libcec_configuration *configuration) +bool CUSBCECAdapterCommands::GetConfiguration(libcec_configuration &configuration) { // get the settings from the eeprom if needed if (!RequestSettings()) return false; // copy the settings - configuration->iFirmwareVersion = m_persistedConfiguration.iFirmwareVersion; - configuration->deviceTypes = m_persistedConfiguration.deviceTypes; - configuration->iPhysicalAddress = m_persistedConfiguration.iPhysicalAddress; - snprintf(configuration->strDeviceName, 13, "%s", m_persistedConfiguration.strDeviceName); + configuration.iFirmwareVersion = m_persistedConfiguration.iFirmwareVersion; + configuration.deviceTypes = m_persistedConfiguration.deviceTypes; + configuration.iPhysicalAddress = m_persistedConfiguration.iPhysicalAddress; + snprintf(configuration.strDeviceName, 13, "%s", m_persistedConfiguration.strDeviceName); return true; } diff --git a/src/lib/adapter/USBCECAdapterCommands.h b/src/lib/adapter/USBCECAdapterCommands.h index 389aa5f..d961240 100644 --- a/src/lib/adapter/USBCECAdapterCommands.h +++ b/src/lib/adapter/USBCECAdapterCommands.h @@ -57,14 +57,14 @@ namespace CEC * @param configuration The configuration to persist. * @return True when persisted, false otherwise. */ - bool PersistConfiguration(libcec_configuration *configuration); + bool PersistConfiguration(const libcec_configuration &configuration); /*! * @brief Get the persisted configuration from the EEPROM. * @param configuration The persisted configuration. * @return True when retrieved, false otherwise. */ - bool GetConfiguration(libcec_configuration *configuration); + bool GetConfiguration(libcec_configuration &configuration); /*! * @brief Send a ping command to the adapter. diff --git a/src/lib/adapter/USBCECAdapterCommunication.cpp b/src/lib/adapter/USBCECAdapterCommunication.cpp index 5c2653a..3e3ba08 100644 --- a/src/lib/adapter/USBCECAdapterCommunication.cpp +++ b/src/lib/adapter/USBCECAdapterCommunication.cpp @@ -523,12 +523,12 @@ bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void) GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE; } -bool CUSBCECAdapterCommunication::PersistConfiguration(libcec_configuration *configuration) +bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration) { return m_port->IsOpen() ? m_commands->PersistConfiguration(configuration) : false; } -bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration *configuration) +bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration) { return m_port->IsOpen() ? m_commands->GetConfiguration(configuration) : false; } diff --git a/src/lib/adapter/USBCECAdapterCommunication.h b/src/lib/adapter/USBCECAdapterCommunication.h index 8d8476a..7409e86 100644 --- a/src/lib/adapter/USBCECAdapterCommunication.h +++ b/src/lib/adapter/USBCECAdapterCommunication.h @@ -79,8 +79,8 @@ namespace CEC uint16_t GetFirmwareVersion(void); uint32_t GetFirmwareBuildDate(void); bool IsRunningLatestFirmware(void); - bool PersistConfiguration(libcec_configuration *configuration); - bool GetConfiguration(libcec_configuration *configuration); + bool PersistConfiguration(const libcec_configuration &configuration); + bool GetConfiguration(libcec_configuration &configuration); CStdString GetPortName(void); uint16_t GetPhysicalAddress(void) { return 0; } bool SetControlledMode(bool controlled); diff --git a/src/lib/devices/CECBusDevice.cpp b/src/lib/devices/CECBusDevice.cpp index a0d20a8..9ce80fb 100644 --- a/src/lib/devices/CECBusDevice.cpp +++ b/src/lib/devices/CECBusDevice.cpp @@ -404,6 +404,12 @@ bool CCECBusDevice::TransmitOSDString(const cec_logical_address destination, cec return bReturn; } +CStdString CCECBusDevice::GetCurrentOSDName(void) +{ + CLockObject lock(m_mutex); + return m_strDeviceName; +} + CStdString CCECBusDevice::GetOSDName(const cec_logical_address initiator, bool bUpdate /* = false */) { bool bIsPresent(GetStatus() == CEC_DEVICE_STATUS_PRESENT); diff --git a/src/lib/devices/CECBusDevice.h b/src/lib/devices/CECBusDevice.h index e93f1d3..0e11d42 100644 --- a/src/lib/devices/CECBusDevice.h +++ b/src/lib/devices/CECBusDevice.h @@ -85,6 +85,7 @@ namespace CEC virtual bool TransmitOSDString(const cec_logical_address destination, cec_display_control duration, const char *strMessage); + virtual CStdString GetCurrentOSDName(void); virtual CStdString GetOSDName(const cec_logical_address initiator, bool bUpdate = false); virtual void SetOSDName(CStdString strName); virtual bool RequestOSDName(const cec_logical_address source, bool bWaitForResponse = true); diff --git a/src/lib/implementations/ANCommandHandler.cpp b/src/lib/implementations/ANCommandHandler.cpp index 2a4dc0d..ed4218e 100644 --- a/src/lib/implementations/ANCommandHandler.cpp +++ b/src/lib/implementations/ANCommandHandler.cpp @@ -61,7 +61,7 @@ bool CANCommandHandler::HandleVendorRemoteButtonDown(const cec_command &command) switch (command.parameters[0]) { case CEC_USER_CONTROL_CODE_AN_RETURN: - key.keycode = client && client->GetConfiguration()->clientVersion >= CEC_CLIENT_VERSION_1_5_0 ? + key.keycode = client && client->GetClientVersion() >= CEC_CLIENT_VERSION_1_5_0 ? CEC_USER_CONTROL_CODE_AN_RETURN : CEC_USER_CONTROL_CODE_EXIT; break; diff --git a/src/lib/implementations/CECCommandHandler.cpp b/src/lib/implementations/CECCommandHandler.cpp index ad4349e..813b416 100644 --- a/src/lib/implementations/CECCommandHandler.cpp +++ b/src/lib/implementations/CECCommandHandler.cpp @@ -738,21 +738,24 @@ void CCECCommandHandler::SetPhysicalAddress(cec_logical_address iAddress, uint16 { if (!m_processor->IsHandledByLibCEC(iAddress)) { - CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress); - bool bOurAddress(device && device->GetLogicalAddress() != m_busDevice->GetLogicalAddress() && - device->IsHandledByLibCEC()); - - GetDevice(iAddress)->SetPhysicalAddress(iNewAddress); + CCECBusDevice *otherDevice = m_processor->GetDeviceByPhysicalAddress(iNewAddress); + CCECClient *client = otherDevice ? otherDevice->GetClient() : NULL; - if (bOurAddress) + CCECBusDevice *device = m_processor->GetDevice(iAddress); + if (device) + device->SetPhysicalAddress(iNewAddress); + else { - /* another device reported the same physical address as ours - * since we don't have physical address detection yet, we'll just use the - * given address, increased by 0x100 for now */ - CCECClient *client = device->GetClient(); - if (client) - client->SetPhysicalAddress(iNewAddress + 0x100); + LIB_CEC->AddLog(CEC_LOG_DEBUG, "device with logical address %X not found", iAddress); } + + /* another device reported the same physical address as ours */ + if (client) + client->ResetPhysicalAddress(); + } + else + { + LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignore physical address report for device %s (%X) because it's marked as handled by libCEC", ToString(iAddress), iAddress); } } -- 2.34.1