diff --git a/configure.ac b/configure.ac index 081cb2be..7445ffc2 100644 --- a/configure.ac +++ b/configure.ac @@ -50,6 +50,19 @@ PKG_CHECK_MODULES([DBUS], [dbus-1]) AC_CHECK_LIB(gthread-2.0, g_thread_init) +# Thunder COM-RPC plugin support +AC_ARG_ENABLE([thunder-plugin], + AS_HELP_STRING([--enable-thunder-plugin], [Enable Thunder COM-RPC plugin support (default: no)]), + [enable_thunder_plugin=$enableval], + [enable_thunder_plugin=no]) + +AM_CONDITIONAL([USE_THUNDER_PLUGIN], [test "x$enable_thunder_plugin" = "xyes"]) + +AS_IF([test "x$enable_thunder_plugin" = "xyes"], + [AC_DEFINE([USE_WPE_THUNDER_PLUGIN], [1], [Define to 1 to enable Thunder COM-RPC plugin support]) + AC_MSG_NOTICE([Thunder COM-RPC plugin support enabled])], + [AC_MSG_NOTICE([Thunder COM-RPC plugin support disabled])]) + # Checks for typedefs, structures, and compiler characteristics. AC_TYPE_PID_T AC_TYPE_SIZE_T diff --git a/cov_build.sh b/cov_build.sh index 22e8d7d4..1f1a6463 100644 --- a/cov_build.sh +++ b/cov_build.sh @@ -66,8 +66,10 @@ cd ${RDK_SOURCE_PATH} export STANDALONE_BUILD_ENABLED=y export DS_MGRS=$WORKDIR +export USE_WPE_THUNDER_PLUGIN=y + find $WORKDIR -iname "*.o" -exec rm -v {} \; find $WORKDIR -iname "*.so*" -exec rm -v {} \; echo "##### Triggering make" -make CFLAGS+='-fPIC -DDSMGR_LOGGER_ENABLED=ON -DRDK_DSHAL_NAME=\"libdshal.so\" -I${DS_IF_PATH}/include -I${DS_HAL_PATH} -I${DS_MGRS}/stubs -I${IARMBUS_PATH}/core -I${IARMBUS_PATH}/core/include -I${IARM_MGRS}/sysmgr/include -I${DS_MGRS}/ds/include -I${DS_MGRS}/rpc/include -I${POWER_IF_PATH}/include/ -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I${IARM_MGRS}/mfr/include/ -I${IARM_MGRS}/mfr/common -I${DEEPSLEEP_IF_PATH}/include -I${IARM_MGRS}/hal/include -I${IARM_MGRS}/power -I${IARM_MGRS}/power/include' LDFLAGS="-L/usr/lib/x86_64-linux-gnu/ -L/usr/local/include -lglib-2.0 -lIARMBus -lWPEFrameworkPowerController -ldshal" \ No newline at end of file +make CFLAGS+='-fPIC -DDSMGR_LOGGER_ENABLED=ON -DRDK_DSHAL_NAME=\"libdshal.so\" -I${DS_IF_PATH}/include -I${DS_HAL_PATH} -I${DS_MGRS}/stubs -I${IARMBUS_PATH}/core -I${IARMBUS_PATH}/core/include -I${IARM_MGRS}/sysmgr/include -I${DS_MGRS}/ds/include -I${DS_MGRS}/rpc/include -I${POWER_IF_PATH}/include/ -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I${IARM_MGRS}/mfr/include/ -I${IARM_MGRS}/mfr/common -I${DEEPSLEEP_IF_PATH}/include -I${IARM_MGRS}/hal/include -I${IARM_MGRS}/power -I${IARM_MGRS}/power/include' LDFLAGS="-L/usr/lib/x86_64-linux-gnu/ -L/usr/local/include -lglib-2.0 -lIARMBus -lWPEFrameworkPowerController -ldshal" diff --git a/rpc/cli/Makefile b/rpc/cli/Makefile index ab24c14f..a10f6551 100644 --- a/rpc/cli/Makefile +++ b/rpc/cli/Makefile @@ -21,8 +21,20 @@ CFLAGS += -g -fPIC -D_REENTRANT -Wall LIBNAME := dshalcli LIBNAMEFULL := lib$(LIBNAME).so INSTALL := $(PWD)/install -OBJS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) -OBJS += $(patsubst %.c,%.o,$(wildcard *.c)) + +# Conditional compilation: Thunder vs IARM +ifdef USE_WPE_THUNDER_PLUGIN + # Thunder mode - use *-com.cpp files, exclude legacy .c implementations + OBJS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) + OBJS += $(patsubst %.c,%.o,$(filter-out dsFPD.c dsHdmiIn.c,$(wildcard *.c))) +else + # IARM mode - use legacy .c files, exclude Thunder -com.cpp implementations + OBJS := $(patsubst %.cpp,%.o,$(filter-out dsFPD-com.cpp dsHdmiIn-com.cpp dsConnectionManager.cpp,$(wildcard *.cpp))) + OBJS += $(patsubst %.c,%.o,$(wildcard *.c)) +endif + +#OBJS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) +#OBJS += $(patsubst %.c,%.o,$(wildcard *.c)) INCLUDE := -I$(PWD) \ -I$(PWD)/hal/include \ -I$(PWD)/rpc/include @@ -31,13 +43,19 @@ INCLUDE += $(HAL_INCLUDE) CFLAGS += $(INCLUDE) +# Conditional linking flags +ifdef USE_WPE_THUNDER_PLUGIN + LDLIBS := -lWPEFrameworkCore -lWPEFrameworkCOM +else + LDLIBS := -lIARMBus +endif all: install @echo "Build Finished...." library: $(OBJS) @echo "Building $(LIBNAMEFULL) ...." - $(CXX) $(OBJS) $(CFLAGS) -lIARMBus -shared -o $(LIBNAMEFULL) + $(CXX) $(OBJS) $(CFLAGS) $(LDLIBS) -shared -o $(LIBNAMEFULL) %.o: %.cpp @echo "Building $@ ...." diff --git a/rpc/cli/Makefile.am b/rpc/cli/Makefile.am index fe48bb37..0b98e994 100644 --- a/rpc/cli/Makefile.am +++ b/rpc/cli/Makefile.am @@ -28,4 +28,21 @@ INCLUDE_FILES = -I=$(includedir)/rdk/halif/ds-hal \ lib_LTLIBRARIES = libdshalcli.la libdshalcli_la_CPPFLAGS = $(INCLUDE_FILES) libdshalcli_la_CFLAGS = -g -fPIC -D_REENTRANT -Wall -libdshalcli_la_SOURCES = dsAudio.c dsclientlogger.c dsDisplay.c dsFPD.c dsHost.cpp dsVideoDevice.c dsVideoPort.c + +# Conditional compilation for Thunder COM-RPC +if USE_THUNDER_PLUGIN + FPD_SOURCE = dsFPD-com.cpp + HDMIIN_SOURCE = dsHdmiIn-com.cpp + COMPOSITEIN_SOURCE = + THUNDER_SOURCES = dsConnectionManager.cpp + THUNDER_LIBS = -lWPEFrameworkCore -lWPEFrameworkCOM +else + FPD_SOURCE = dsFPD.c + HDMIIN_SOURCE = dsHdmiIn.c + COMPOSITEIN_SOURCE = dsCompositeIn.c + THUNDER_SOURCES = + THUNDER_LIBS = +endif + +libdshalcli_la_SOURCES = dsAudio.c dsclientlogger.c dsDisplay.c $(FPD_SOURCE) $(HDMIIN_SOURCE) $(COMPOSITEIN_SOURCE) $(THUNDER_SOURCES) dsHost.cpp dsVideoDevice.c dsVideoPort.c +libdshalcli_la_LIBADD = $(THUNDER_LIBS) diff --git a/rpc/cli/dsConnectionManager.cpp b/rpc/cli/dsConnectionManager.cpp new file mode 100644 index 00000000..df5170c9 --- /dev/null +++ b/rpc/cli/dsConnectionManager.cpp @@ -0,0 +1,239 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifdef USE_WPE_THUNDER_PLUGIN + +#include +#include +#include +#include "dsConnectionManager.h" + +using namespace WPEFramework; + +namespace DeviceSettingsClient { + +// Thunder callsign for DeviceSettings plugin +static constexpr const TCHAR callSign[] = _T("org.rdk.DeviceSettings"); + +// Static member initialization +ConnectionManager* ConnectionManager::_instance = nullptr; +Core::CriticalSection ConnectionManager::_dsConnectionManagerlock; + +ConnectionManager::ConnectionManager() + : BaseClass() + , _fpdInterface(nullptr) + , _hdmiInInterface(nullptr) + , _connected(false) + , _shutdown(false) +{ + printf("[dsConnectionManager] Initializing centralized connection manager\n"); + (void)Connect(); +} + +ConnectionManager::~ConnectionManager() +{ + printf("[dsConnectionManager] Destroying connection manager\n"); + _shutdown = true; + + // Release all component interfaces before closing base connection + if (_hdmiInInterface) { + _hdmiInInterface->Release(); + _hdmiInInterface = nullptr; + } + + if (_fpdInterface) { + _fpdInterface->Release(); + _fpdInterface = nullptr; + } + + BaseClass::Close(Core::infinite); +} + +void ConnectionManager::Operational(const bool upAndRunning) +{ + _dsConnectionManagerlock.Lock(); + + if (!_shutdown) { + printf("[dsConnectionManager] Operational callback: %s\n", upAndRunning ? "UP" : "DOWN"); + } + + if (upAndRunning) { + // Communicator opened && DeviceSettings is Activated + if (nullptr == _fpdInterface) { + printf("[dsConnectionManager] Plugin activated, acquiring primary FPD interface\n"); + _fpdInterface = BaseClass::Interface(); + + if (_fpdInterface != nullptr) { + printf("[dsConnectionManager] Successfully established COM-RPC connection with DeviceSettings plugin\n"); + + // Acquire secondary interfaces via QueryInterface + if (nullptr == _hdmiInInterface) { + _hdmiInInterface = _fpdInterface->QueryInterface(); + if (_hdmiInInterface != nullptr) { + printf("[dsConnectionManager] Successfully acquired HDMIIn interface via QueryInterface\n"); + } else { + fprintf(stderr, "[dsConnectionManager] Failed to acquire HDMIIn interface via QueryInterface\n"); + } + } + + // Add more component interfaces here as needed: + // if (nullptr == _compositeInInterface) { + // _compositeInInterface = _fpdInterface->QueryInterface(); + // } + + } else { + fprintf(stderr, "[dsConnectionManager] Failed to get FPD interface - plugin implementation may have failed to load\n"); + } + } + } else { + // DeviceSettings is Deactivated || Communicator closed + printf("[dsConnectionManager] Plugin deactivated, releasing all interfaces\n"); + + if (_hdmiInInterface != nullptr) { + _hdmiInInterface->Release(); + _hdmiInInterface = nullptr; + } + + if (_fpdInterface != nullptr) { + _fpdInterface->Release(); + _fpdInterface = nullptr; + } + } + + _dsConnectionManagerlock.Unlock(); +} + +void ConnectionManager::Init() +{ + _dsConnectionManagerlock.Lock(); + if (nullptr == _instance) { + _instance = new ConnectionManager(); + } + _dsConnectionManagerlock.Unlock(); +} + +void ConnectionManager::Term() +{ + _dsConnectionManagerlock.Lock(); + if (nullptr != _instance) { + delete _instance; + _instance = nullptr; + } + _dsConnectionManagerlock.Unlock(); +} + +ConnectionManager* ConnectionManager::Instance() +{ + return _instance; +} + +bool ConnectionManager::IsOperational() const +{ + _dsConnectionManagerlock.Lock(); + bool result = (isConnected() && (nullptr != _fpdInterface)); + _dsConnectionManagerlock.Unlock(); + return result; +} + +bool ConnectionManager::WaitForOperational(uint32_t timeoutMs) const +{ + const uint32_t pollIntervalMs = 100; + uint32_t elapsedMs = 0; + + while (elapsedMs < timeoutMs) { + if (IsOperational()) { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(pollIntervalMs)); + elapsedMs += pollIntervalMs; + } + return false; +} + +uint32_t ConnectionManager::Connect() +{ + uint32_t status = Core::ERROR_NONE; + + _dsConnectionManagerlock.Lock(); + + if (!isConnected()) { + printf("[dsConnectionManager] Attempting to connect to Thunder with callsign: %s\n", callSign); + uint32_t res = BaseClass::Open(RPC::CommunicationTimeOut, BaseClass::Connector(), callSign); + if (Core::ERROR_NONE == res) { + _connected = true; + printf("[dsConnectionManager] Successfully opened RPC connection to Thunder\n"); + } else { + fprintf(stderr, "[dsConnectionManager] Failed to open RPC connection, error: %u. Is Thunder running?\n", res); + status = Core::ERROR_UNAVAILABLE; + } + } else { + printf("[dsConnectionManager] Already connected\n"); + } + + if (nullptr == _fpdInterface) { + status = Core::ERROR_NOT_EXIST; + printf("[dsConnectionManager] DeviceSettings plugin not yet operational, waiting for Operational() callback\n"); + } + + _dsConnectionManagerlock.Unlock(); + + return status; +} + +uint32_t ConnectionManager::Disconnect() +{ + uint32_t status = Core::ERROR_GENERAL; + bool close = false; + + _dsConnectionManagerlock.Lock(); + + if (isConnected()) { + close = true; + _connected = false; + } + + _dsConnectionManagerlock.Unlock(); + + if (close) { + status = BaseClass::Close(Core::infinite); + printf("[dsConnectionManager] Disconnected from Thunder\n"); + } + + return status; +} + +Exchange::IDeviceSettingsFPD* ConnectionManager::GetFPDInterface() +{ + _dsConnectionManagerlock.Lock(); + Exchange::IDeviceSettingsFPD* interface = _fpdInterface; + _dsConnectionManagerlock.Unlock(); + return interface; +} + +Exchange::IDeviceSettingsHDMIIn* ConnectionManager::GetHDMIInInterface() +{ + _dsConnectionManagerlock.Lock(); + Exchange::IDeviceSettingsHDMIIn* interface = _hdmiInInterface; + _dsConnectionManagerlock.Unlock(); + return interface; +} + +} // namespace DeviceSettingsClient + +#endif // USE_WPE_THUNDER_PLUGIN diff --git a/rpc/cli/dsConnectionManager.h b/rpc/cli/dsConnectionManager.h new file mode 100644 index 00000000..e553fab6 --- /dev/null +++ b/rpc/cli/dsConnectionManager.h @@ -0,0 +1,142 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +#ifndef __DS_CONNECTION_MANAGER_H__ +#define __DS_CONNECTION_MANAGER_H__ + +#ifdef USE_WPE_THUNDER_PLUGIN + +#ifndef MODULE_NAME +#define MODULE_NAME DeviceSettings_ConnectionManager +#endif + +#include +#include +#include +#include +#include + +namespace DeviceSettingsClient { + +/** + * @brief Centralized Connection Manager for DeviceSettings plugin + * + * This class manages a single Thunder COM-RPC connection to the DeviceSettings plugin + * and provides access to multiple component interfaces (FPD, HDMIIn, etc.) through + * QueryInterface pattern. + */ +class ConnectionManager : public WPEFramework::RPC::SmartInterfaceType { +private: + using BaseClass = WPEFramework::RPC::SmartInterfaceType; + + // Component interfaces obtained via QueryInterface + WPEFramework::Exchange::IDeviceSettingsFPD* _fpdInterface; + WPEFramework::Exchange::IDeviceSettingsHDMIIn* _hdmiInInterface; + // Add more component interfaces here as needed + // WPEFramework::Exchange::IDeviceSettingsCompositeIn* _compositeInInterface; + // WPEFramework::Exchange::IDeviceSettingsHost* _hostInterface; + + static ConnectionManager* _instance; + static WPEFramework::Core::CriticalSection _dsConnectionManagerlock; + + bool _connected; + bool _shutdown; + + // Private constructor for singleton + ConnectionManager(); + + // Private destructor + ~ConnectionManager(); + + // Operational callback from Thunder + virtual void Operational(const bool upAndRunning) override; + + inline bool isConnected() const { return _connected; } + inline bool IsActivatedLocked() const { return (nullptr != _fpdInterface); } + +public: + // Delete copy/move constructors and assignment operators + ConnectionManager(const ConnectionManager&) = delete; + ConnectionManager& operator=(const ConnectionManager&) = delete; + ConnectionManager(ConnectionManager&&) = delete; + ConnectionManager& operator=(ConnectionManager&&) = delete; + + /** + * @brief Initialize the connection manager singleton + */ + static void Init(); + + /** + * @brief Terminate the connection manager singleton + */ + static void Term(); + + /** + * @brief Get the singleton instance + */ + static ConnectionManager* Instance(); + + /** + * @brief Check if plugin is operational (connected and activated) + */ + bool IsOperational() const; + + /** + * @brief Wait for plugin to become operational with timeout + */ + bool WaitForOperational(uint32_t timeoutMs = 5000) const; + + /** + * @brief Connect to Thunder DeviceSettings plugin + */ + uint32_t Connect(); + + /** + * @brief Disconnect from Thunder DeviceSettings plugin + */ + uint32_t Disconnect(); + + /** + * @brief Get FPD interface pointer + * @return FPD interface or nullptr if not available + */ + WPEFramework::Exchange::IDeviceSettingsFPD* GetFPDInterface(); + + /** + * @brief Get HDMIIn interface pointer + * @return HDMIIn interface or nullptr if not available + */ + WPEFramework::Exchange::IDeviceSettingsHDMIIn* GetHDMIInInterface(); + + /** + * @brief Lock the API mutex for thread-safe operations + */ + static void Lock() { _dsConnectionManagerlock.Lock(); } + + /** + * @brief Unlock the API mutex + */ + static void Unlock() { _dsConnectionManagerlock.Unlock(); } +}; + +} // namespace DeviceSettingsClient + +#endif // USE_WPE_THUNDER_PLUGIN + +#endif // __DS_CONNECTION_MANAGER_H__ diff --git a/rpc/cli/dsFPD-com.cpp b/rpc/cli/dsFPD-com.cpp new file mode 100644 index 00000000..69b89f1a --- /dev/null +++ b/rpc/cli/dsFPD-com.cpp @@ -0,0 +1,517 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2016 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +/** +* @defgroup devicesettings +* @{ +* @defgroup rpc +* @{ +**/ + +#ifdef USE_WPE_THUNDER_PLUGIN + +#include +#include + +#include "dsFPD.h" +#include "dsError.h" +#include "dsclientlogger.h" +#include "dsConnectionManager.h" + +// Thunder COM-RPC includes +#ifndef MODULE_NAME +#define MODULE_NAME DeviceSettings_FPD_Client +#endif + +#include + +using namespace WPEFramework; +using namespace DeviceSettingsClient; + +/** + * @brief Convert Thunder error code to dsError_t + */ +static dsError_t ConvertThunderError(uint32_t thunderError) +{ + if (thunderError == WPEFramework::Core::ERROR_NONE) { + return dsERR_NONE; + } else if (thunderError == WPEFramework::Core::ERROR_UNAVAILABLE) { + return dsERR_OPERATION_NOT_SUPPORTED; + } else if (thunderError == WPEFramework::Core::ERROR_BAD_REQUEST) { + return dsERR_INVALID_PARAM; + } else { + return dsERR_GENERAL; + } +} + +// C API implementations using Thunder COM-RPC via ConnectionManager + +extern "C" { + +// Forward declarations +dsError_t dsSetFPDBrightness(dsFPDIndicator_t eIndicator, dsFPDBrightness_t eBrightness, bool toPersist); +dsError_t dsSetFPDColor(dsFPDIndicator_t eIndicator, dsFPDColor_t eColor, bool toPersist); + +dsError_t dsFPInit(void) +{ + printf("<<<<< Front Panel is initialized in Thunder Mode (using ConnectionManager) >>>>>>>>\r\n"); + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr) { + ConnectionManager::Init(); + connMgr = ConnectionManager::Instance(); + } + + if (!connMgr) { + fprintf(stderr, "[dsFPD-com] Failed to create ConnectionManager instance\n"); + return dsERR_GENERAL; + } + + // Wait for plugin to become operational + if (!connMgr->WaitForOperational(5000)) { + fprintf(stderr, "[dsFPD-com] DeviceSettings plugin not operational after 5 seconds\n"); + return dsERR_GENERAL; + } + + // Thunder interface doesn't have explicit Init method - connection is sufficient + return dsERR_NONE; +} + +dsError_t dsFPTerm(void) +{ + // Note: Don't terminate ConnectionManager here as other components may be using it + // ConnectionManager will be terminated by dsConnectionTerm() or at process exit + return dsERR_NONE; +} + +dsError_t dsSetFPText(const char* pszChars) +{ + if (pszChars == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: pszChars is NULL\n"); + return dsERR_INVALID_PARAM; + } + + // Thunder interface doesn't support text display + fprintf(stderr, "[dsFPD-com] SetFPText not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsSetFPTime(dsFPDTimeFormat_t eTime, const unsigned int uHour, const unsigned int uMinutes) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat = + static_cast(eTime); + + ConnectionManager::Lock(); + // Note: Interface expects minutes and seconds, but API provides hour and minutes + // Converting: treating uMinutes as seconds for interface compatibility + uint32_t result = fpdInterface->SetFPDTime(timeFormat, uHour, uMinutes); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsSetFPScroll(unsigned int nScrollHoldOnDur, unsigned int nHorzScrollIterations, unsigned int nVertScrollIterations) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDScroll(nScrollHoldOnDur, nHorzScrollIterations, nVertScrollIterations); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsSetFPBlink(dsFPDIndicator_t eIndicator, unsigned int nBlinkDuration, unsigned int nBlinkIterations) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDBlink(indicator, nBlinkDuration, nBlinkIterations); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetFPBrightness(dsFPDIndicator_t eIndicator, dsFPDBrightness_t *pBrightness) +{ + if (pBrightness == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: pBrightness is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t brightness = 0; + ConnectionManager::Lock(); + uint32_t result = fpdInterface->GetFPDBrightness(indicator, brightness); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *pBrightness = static_cast(brightness); + } + + return ConvertThunderError(result); +} + +dsError_t dsGetFPDBrightness(dsFPDIndicator_t eIndicator, dsFPDBrightness_t *pBrightness, bool persist) +{ + // Note: persist parameter may not be used in GET operation for Thunder + return dsGetFPBrightness(eIndicator, pBrightness); +} + +dsError_t dsSetFPBrightness(dsFPDIndicator_t eIndicator, dsFPDBrightness_t eBrightness) +{ + return dsSetFPDBrightness(eIndicator, eBrightness, true); +} + +dsError_t dsSetFPDBrightness(dsFPDIndicator_t eIndicator, dsFPDBrightness_t eBrightness, bool toPersist) +{ + if (eIndicator >= dsFPD_INDICATOR_MAX || eBrightness > 100) { + fprintf(stderr, "[dsFPD-com] Invalid parameter\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDBrightness(indicator, static_cast(eBrightness), toPersist); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetFPTextBrightness(dsFPDTextDisplay_t eIndicator, dsFPDBrightness_t *pBrightness) +{ + if (pBrightness == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: pBrightness is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator = + static_cast(eIndicator); + + uint32_t brightness = 0; + ConnectionManager::Lock(); + uint32_t result = fpdInterface->GetFPDTextBrightness(indicator, brightness); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *pBrightness = static_cast(brightness); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetFPTextBrightness(dsFPDTextDisplay_t eIndicator, dsFPDBrightness_t eBrightness) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator = + static_cast(eIndicator); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDTextBrightness(indicator, static_cast(eBrightness)); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetFPColor(dsFPDIndicator_t eIndicator, dsFPDColor_t *pColor) +{ + if (pColor == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: pColor is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t color = 0; + ConnectionManager::Lock(); + uint32_t result = fpdInterface->GetFPDColor(indicator, color); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *pColor = static_cast(color); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetFPColor(dsFPDIndicator_t eIndicator, dsFPDColor_t eColor) +{ + return dsSetFPDColor(eIndicator, eColor, true); +} + +dsError_t dsSetFPDColor(dsFPDIndicator_t eIndicator, dsFPDColor_t eColor, bool toPersist) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + // Thunder interface doesn't support persist flag - ignore it + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDColor(indicator, static_cast(eColor)); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsFPEnableCLockDisplay(int enable) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->EnableFPDClockDisplay(enable != 0); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsSetFPState(dsFPDIndicator_t eIndicator, dsFPDState_t state) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + Exchange::IDeviceSettingsFPD::FPDState fpdState = + static_cast(state); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDState(indicator, fpdState); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetFPState(dsFPDIndicator_t eIndicator, dsFPDState_t* state) +{ + if (state == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: state is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + Exchange::IDeviceSettingsFPD::FPDState fpdState; + ConnectionManager::Lock(); + uint32_t result = fpdInterface->GetFPDState(indicator, fpdState); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *state = static_cast(fpdState); + } + + return ConvertThunderError(result); +} + +dsError_t dsGetFPTimeFormat(dsFPDTimeFormat_t *pTimeFormat) +{ + if (pTimeFormat == NULL) { + fprintf(stderr, "[dsFPD-com] Invalid parameter: pTimeFormat is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat; + ConnectionManager::Lock(); + uint32_t result = fpdInterface->GetFPDTimeFormat(timeFormat); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *pTimeFormat = static_cast(timeFormat); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetFPTimeFormat(dsFPDTimeFormat_t eTimeFormat) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat = + static_cast(eTimeFormat); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDTimeFormat(timeFormat); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsSetFPDMode(dsFPDMode_t eMode) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = connMgr->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDMode mode = + static_cast(eMode); + + ConnectionManager::Lock(); + uint32_t result = fpdInterface->SetFPDMode(mode); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +} // extern "C" + +#endif // USE_WPE_THUNDER_PLUGIN + +/** @} */ +/** @} */ diff --git a/rpc/cli/dsHdmiIn-com.cpp b/rpc/cli/dsHdmiIn-com.cpp new file mode 100644 index 00000000..aee2ce4f --- /dev/null +++ b/rpc/cli/dsHdmiIn-com.cpp @@ -0,0 +1,666 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2025 RDK Management + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. +*/ + +/** +* @defgroup devicesettings +* @{ +* @defgroup rpc +* @{ +**/ + +#ifdef USE_WPE_THUNDER_PLUGIN + +#include +#include + +#include "dsHdmiIn.h" +#include "dsError.h" +#include "dsclientlogger.h" +#include "dsConnectionManager.h" + +// Thunder COM-RPC includes +#ifndef MODULE_NAME +#define MODULE_NAME DeviceSettings_HDMIIn_Client +#endif + +#include + +using namespace WPEFramework; +using namespace DeviceSettingsClient; + +/** + * @brief Convert Thunder error code to dsError_t + */ +static dsError_t ConvertThunderError(uint32_t thunderError) +{ + if (thunderError == WPEFramework::Core::ERROR_NONE) { + return dsERR_NONE; + } else if (thunderError == WPEFramework::Core::ERROR_UNAVAILABLE) { + return dsERR_OPERATION_NOT_SUPPORTED; + } else if (thunderError == WPEFramework::Core::ERROR_BAD_REQUEST) { + return dsERR_INVALID_PARAM; + } else { + return dsERR_GENERAL; + } +} + +// C API implementations using Thunder COM-RPC via ConnectionManager + +extern "C" { + +dsError_t dsHdmiInInit(void) +{ + printf("<<<<< HDMI In is initialized in Thunder Mode (using ConnectionManager) >>>>>>>>\r\n"); + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr) { + ConnectionManager::Init(); + connMgr = ConnectionManager::Instance(); + } + + if (!connMgr) { + fprintf(stderr, "[dsHdmiIn-com] Failed to create ConnectionManager instance\n"); + return dsERR_GENERAL; + } + + // Wait for plugin to become operational + if (!connMgr->WaitForOperational(5000)) { + fprintf(stderr, "[dsHdmiIn-com] DeviceSettings plugin not operational after 5 seconds\n"); + return dsERR_GENERAL; + } + + return dsERR_NONE; +} + +dsError_t dsHdmiInTerm(void) +{ + // Note: Don't terminate ConnectionManager here as other components may be using it + // ConnectionManager will be terminated by dsConnectionTerm() or at process exit + return dsERR_NONE; +} + +dsError_t dsHdmiInGetNumberOfInputs(uint8_t* pNumberOfInputs) +{ + if (pNumberOfInputs == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: pNumberOfInputs is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + int32_t count = 0; + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIInNumbefOfInputs(count); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *pNumberOfInputs = static_cast(count); + } + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInGetStatus(dsHdmiInStatus_t* pStatus) +{ + if (pStatus == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: pStatus is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInStatus hdmiStatus; + Exchange::IDeviceSettingsHDMIIn::IHDMIInPortConnectionStatusIterator* portConnectionStatus = nullptr; + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIInStatus(hdmiStatus, portConnectionStatus); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + pStatus->isPresented = hdmiStatus.isPresented; + pStatus->activePort = static_cast(hdmiStatus.activePort); + + // TODO: Handle portConnectionStatus iterator if needed + if (portConnectionStatus) { + portConnectionStatus->Release(); + } + } + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInSelectPort(dsHdmiInPort_t ePort, bool audioMix, dsVideoPlaneType_t videoPlaneType, bool topMost) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(ePort); + Exchange::IDeviceSettingsHDMIIn::HDMIVideoPlaneType planeType = + static_cast(videoPlaneType); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->SelectHDMIInPort(port, audioMix, topMost, planeType); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInScaleVideo(int32_t x, int32_t y, int32_t width, int32_t height) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInVideoRectangle videoPosition; + videoPosition.x = x; + videoPosition.y = y; + videoPosition.width = width; + videoPosition.height = height; + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->ScaleHDMIInVideo(videoPosition); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInSelectZoomMode(dsVideoZoom_t zoomMode) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInVideoZoom zoom = + static_cast(zoomMode); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->SelectHDMIZoomMode(zoom); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInGetCurrentVideoMode(dsVideoPortResolution_t* resolution) +{ + if (resolution == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: resolution is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIVideoPortResolution videoPortResolution; + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIVideoMode(videoPortResolution); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + strncpy(resolution->name, videoPortResolution.name.c_str(), sizeof(resolution->name) - 1); + resolution->name[sizeof(resolution->name) - 1] = '\0'; + resolution->pixelResolution = static_cast(videoPortResolution.pixelResolution); + resolution->aspectRatio = static_cast(videoPortResolution.aspectRatio); + resolution->stereoScopicMode = static_cast(videoPortResolution.stereoScopicMode); + resolution->frameRate = static_cast(videoPortResolution.frameRate); + resolution->interlaced = videoPortResolution.interlaced; + } + + return ConvertThunderError(result); +} + +dsError_t dsGetEDIDBytesInfo(dsHdmiInPort_t iHdmiPort, unsigned char* edid, int* length) +{ + if (edid == NULL || length == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetEdidBytes(port, static_cast(*length), edid); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetHDMISPDInfo(dsHdmiInPort_t iHdmiPort, unsigned char* data) +{ + if (data == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: data is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + // Assuming SPD info frame size (adjust as needed) + const uint16_t spdBytesLength = 32; + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMISPDInformation(port, spdBytesLength, data); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetEdidVersion(dsHdmiInPort_t iHdmiPort, tv_hdmi_edid_version_t* edidVersion) +{ + if (edidVersion == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: edidVersion is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + Exchange::IDeviceSettingsHDMIIn::HDMIInEdidVersion version; + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIEdidVersion(port, version); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *edidVersion = static_cast(version); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetEdidVersion(dsHdmiInPort_t iHdmiPort, tv_hdmi_edid_version_t edidVersion) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + Exchange::IDeviceSettingsHDMIIn::HDMIInEdidVersion version = + static_cast(edidVersion); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->SetHDMIEdidVersion(port, version); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetAllmStatus(dsHdmiInPort_t iHdmiPort, bool* allmStatus) +{ + if (allmStatus == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: allmStatus is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIInAllmStatus(port, *allmStatus); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetEdid2AllmSupport(dsHdmiInPort_t iHdmiPort, bool* allmSupport) +{ + if (allmSupport == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: allmSupport is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIInEdid2AllmSupport(port, *allmSupport); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsSetEdid2AllmSupport(dsHdmiInPort_t iHdmiPort, bool allmSupport) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->SetHDMIInEdid2AllmSupport(port, allmSupport); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsGetAVLatency(int* audio_latency, int* video_latency) +{ + if (audio_latency == NULL || video_latency == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + uint32_t videoLatency = 0; + uint32_t audioLatency = 0; + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIInAVLatency(videoLatency, audioLatency); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *audio_latency = static_cast(audioLatency); + *video_latency = static_cast(videoLatency); + } + + return ConvertThunderError(result); +} + +dsError_t dsGetHdmiVersion(dsHdmiInPort_t iHdmiPort, dsHdmiMaxCapabilityVersion_t* capversion) +{ + if (capversion == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: capversion is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + Exchange::IDeviceSettingsHDMIIn::HDMIInCapabilityVersion capabilityVersion; + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetHDMIVersion(port, capabilityVersion); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + *capversion = static_cast(capabilityVersion); + } + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInSetVRRSupport(dsHdmiInPort_t iHdmiPort, bool vrrSupport) +{ + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->SetVRRSupport(port, vrrSupport); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInGetVRRSupport(dsHdmiInPort_t iHdmiPort, bool* vrrSupport) +{ + if (vrrSupport == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: vrrSupport is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetVRRSupport(port, *vrrSupport); + ConnectionManager::Unlock(); + + return ConvertThunderError(result); +} + +dsError_t dsHdmiInGetVRRStatus(dsHdmiInPort_t iHdmiPort, dsHdmiInVrrStatus_t* vrrStatus) +{ + if (vrrStatus == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: vrrStatus is NULL\n"); + return dsERR_INVALID_PARAM; + } + + ConnectionManager* connMgr = ConnectionManager::Instance(); + if (!connMgr || !connMgr->IsOperational()) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn* hdmiInInterface = connMgr->GetHDMIInInterface(); + if (!hdmiInInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsHDMIIn::HDMIInPort port = + static_cast(iHdmiPort); + Exchange::IDeviceSettingsHDMIIn::HDMIInVRRStatus status; + + ConnectionManager::Lock(); + uint32_t result = hdmiInInterface->GetVRRStatus(port, status); + ConnectionManager::Unlock(); + + if (result == WPEFramework::Core::ERROR_NONE) { + vrrStatus->vrrType = static_cast(status.vrrType); + vrrStatus->vrrAmdfreesyncFramerate_Hz = status.vrrFreeSyncFramerateHz; + } + + return ConvertThunderError(result); +} + +dsError_t dsGetSupportedGameFeaturesList(dsSupportedGameFeatureList_t* feature) +{ + if (feature == NULL) { + fprintf(stderr, "[dsHdmiIn-com] Invalid parameter: feature is NULL\n"); + return dsERR_INVALID_PARAM; + } + + // This function is not yet implemented in Thunder COM-RPC mode + // For now, return empty list + feature->gameFeatureCount = 0; + feature->gameFeatureList[0] = '\0'; + fprintf(stderr, "[dsHdmiIn-com] dsGetSupportedGameFeaturesList not fully implemented in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +// Placeholder for unsupported APIs in Thunder mode +dsError_t dsHdmiInRegisterConnectCB(dsHdmiInConnectCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterConnectCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterSignalChangeCB(dsHdmiInSignalChangeCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterSignalChangeCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterStatusChangeCB(dsHdmiInStatusChangeCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterStatusChangeCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterVideoModeUpdateCB(dsHdmiInVideoModeUpdateCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterVideoModeUpdateCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterAllmChangeCB(dsHdmiInAllmChangeCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterAllmChangeCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterAVLatencyChangeCB(dsAVLatencyChangeCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterAVLatencyChangeCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +dsError_t dsHdmiInRegisterAviContentTypeChangeCB(dsHdmiInAviContentTypeChangeCB_t CBFunc) +{ + fprintf(stderr, "[dsHdmiIn-com] dsHdmiInRegisterAviContentTypeChangeCB not supported in Thunder mode\n"); + return dsERR_OPERATION_NOT_SUPPORTED; +} + +} // extern "C" + +#endif // USE_WPE_THUNDER_PLUGIN + +/** @} */ +/** @} */