diff --git a/Makefile.am b/Makefile.am index 22cfac37..d7945d96 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,3 +38,15 @@ package_ipk: rm -rf $(IPK_GEN_PATH)/data.tar.gz rm -rf $(IPK_GEN_PATH)/control.tar.gz rm -rf $(IPK_GEN_STAGING_DIR) + +# DSApp - Device Settings Test Application +.PHONY: dsapp dsapp-clean + +dsapp: + @echo "Building DSApp..." + $(MAKE) -C sample dsapp + @echo "DSApp executable: $(abs_top_builddir)/DSApp" + +dsapp-clean: + @echo "Cleaning DSApp..." + $(MAKE) -C sample dsapp-clean diff --git a/configure.ac b/configure.ac index 081cb2be..edf9676e 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_WPE_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/ds/Makefile b/ds/Makefile index 3000c0f2..df057235 100644 --- a/ds/Makefile +++ b/ds/Makefile @@ -59,8 +59,8 @@ all: install library: $(OBJS) @echo "Building $(LIBNAMEFULL) ...." - $(CXX) $(OBJS) $(CFLAGS) $(DSHAL_LDFLAGS) -L$(INSTALL)/lib -lIARMBus -ldshalcli -shared -o $(LIBNAMEFULL) - $(CXX) $(OBJS) $(CFLAGS) -L$(INSTALL)/lib -lIARMBus -ldshalcli -shared -o $(LIBNAMECLI) + $(CXX) $(OBJS) $(CFLAGS) $(DSHAL_LDFLAGS) -L$(INSTALL)/lib -lIARMBus -Wl,--no-as-needed -ldshalcli -Wl,--as-needed -shared -o $(LIBNAMEFULL) + $(CXX) $(OBJS) $(CFLAGS) -L$(INSTALL)/lib -lIARMBus -Wl,--no-as-needed -ldshalcli -Wl,--as-needed -shared -o $(LIBNAMECLI) %.o: %.cpp @echo "Building $@ ...." diff --git a/ds/Makefile.am b/ds/Makefile.am index 90c9bea4..c8a8b2c4 100644 --- a/ds/Makefile.am +++ b/ds/Makefile.am @@ -63,3 +63,5 @@ libds_la_SOURCES = aspectRatio.cpp \ host.cpp \ manager.cpp \ videoDFC.cpp + +libds_la_LIBADD = ../rpc/cli/libdshalcli.la diff --git a/ds/include/dsController-com.h b/ds/include/dsController-com.h new file mode 100644 index 00000000..94bbe509 --- /dev/null +++ b/ds/include/dsController-com.h @@ -0,0 +1,261 @@ +/* + * 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. +*/ + +/** +* @file DeviceSettingsController.h +* @brief Central controller for all DeviceSettings COM-RPC connections +* +* This controller manages a single RPC connection to the Thunder DeviceSettings plugin +* and provides access to all component interfaces (FPD, HDMIIn, Audio, etc.) through +* QueryInterface on the main IDeviceSettings interface. +*/ + +#pragma once + +#ifdef USE_WPE_THUNDER_PLUGIN + +#ifndef MODULE_NAME +#define MODULE_NAME DeviceSettings_Client +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WPEFramework { + +/** + * @brief Component types supported by DeviceSettings + */ +enum class DeviceSettingsComponent : uint8_t { + FPD, + HDMIIn, + Audio, + Display, + VideoPort, + VideoDevice, + Host, + CompositeIn +}; + +/** + * @class DeviceSettingsController + * @brief Singleton controller managing all DeviceSettings component interfaces + * + * This class provides: + * - Single RPC connection to Thunder DeviceSettings plugin + * - Thread-safe access to all component interfaces + * - Automatic lifecycle management + * - Connection status monitoring + * - Reconnection support + */ +class DeviceSettingsController : public RPC::SmartInterfaceType { +private: + using BaseClass = RPC::SmartInterfaceType; + + // Main interface + Exchange::IDeviceSettings* _deviceSettings; + + // Component interfaces obtained via QueryInterface + Exchange::IDeviceSettingsFPD* _fpdInterface; + Exchange::IDeviceSettingsHDMIIn* _hdmiInInterface; + Exchange::IDeviceSettingsAudio* _audioInterface; + Exchange::IDeviceSettingsDisplay* _displayInterface; + Exchange::IDeviceSettingsVideoPort* _videoPortInterface; + Exchange::IDeviceSettingsVideoDevice* _videoDeviceInterface; + Exchange::IDeviceSettingsHost* _hostInterface; + Exchange::IDeviceSettingsCompositeIn* _compositeInInterface; + + // Singleton instance + static DeviceSettingsController* _instance; + static Core::CriticalSection _lock; + + // Connection state + bool _connected; + bool _shutdown; + + // Thunder callsign + static constexpr const TCHAR _callSign[] = _T("org.rdk.DeviceSettings"); + + // Connection retry thread management + static pthread_t _connectionThreadID; + static std::atomic _isConnecting; + static uint32_t _connectionRetryCount; + + #define DS_CONTROLLER_CONNECT_WAIT_TIME_MS 300000 // 300ms in microseconds + + // Helper methods + static void EstablishThunderConnection(); + static void* RetryThunderConnectionThread(void* arg); + static void FetchAndInitializeInterfaces(); + + /** + * @brief Private constructor for singleton pattern + */ + DeviceSettingsController(); + + /** + * @brief Private destructor + */ + ~DeviceSettingsController(); + + /** + * @brief Operational callback from SmartInterfaceType + * @param upAndRunning true if plugin is operational, false otherwise + */ + virtual void Operational(const bool upAndRunning) override; + + /** + * @brief Check if connected (without lock) + */ + inline bool isConnected() const { return _connected; } + + /** + * @brief Query and cache a component interface + * @tparam T Component interface type + * @param interfacePtr Reference to cached interface pointer + * @param componentName Name for logging + * @return true if interface obtained successfully + */ + template + bool QueryComponentInterface(T*& interfacePtr, const char* componentName); + +public: + // Delete copy constructor and assignment operator + DeviceSettingsController(const DeviceSettingsController&) = delete; + DeviceSettingsController& operator=(const DeviceSettingsController&) = delete; + + /** + * @brief Get singleton instance + * @return Pointer to singleton instance + */ + static DeviceSettingsController* GetInstance(); + + /** + * @brief Initialize the controller + * @return Core::ERROR_NONE on success, error code otherwise + */ + static uint32_t Initialize(); + + /** + * @brief Terminate the controller and release all resources + */ + static void Terminate(); + + /** + * @brief Connect to Thunder DeviceSettings plugin + * @return Core::ERROR_NONE on success, error code otherwise + */ + uint32_t Connect(); + + /** + * @brief Disconnect from Thunder DeviceSettings plugin + * @return Core::ERROR_NONE on success, error code otherwise + */ + uint32_t Disconnect(); + + /** + * @brief Check if the main interface is operational + * @return true if operational, false otherwise + */ + bool IsOperational() const; + + /** + * @brief Check if a specific component interface is available + * @param component Component type to check + * @return true if available, false otherwise + */ + bool IsComponentAvailable(DeviceSettingsComponent component) const; + + /** + * @brief Wait for the main interface to become operational + * @param timeoutMs Timeout in milliseconds + * @return true if operational within timeout, false otherwise + */ + bool WaitForOperational(uint32_t timeoutMs = 5000) const; + + /** + * @brief Wait for a specific component to become available + * @param component Component type to wait for + * @param timeoutMs Timeout in milliseconds + * @return true if available within timeout, false otherwise + */ + bool WaitForComponent(DeviceSettingsComponent component, uint32_t timeoutMs = 5000) const; + + /** + * @brief Get FPD component interface + * @return Pointer to IDeviceSettingsFPD interface or nullptr + */ + Exchange::IDeviceSettingsFPD* GetFPDInterface(); + + /** + * @brief Get HDMIIn component interface + * @return Pointer to IDeviceSettingsHDMIIn interface or nullptr + */ + Exchange::IDeviceSettingsHDMIIn* GetHDMIInInterface(); + + /** + * @brief Get Audio component interface + * @return Pointer to IDeviceSettingsAudio interface or nullptr + */ + Exchange::IDeviceSettingsAudio* GetAudioInterface(); + + /** + * @brief Get Display component interface + * @return Pointer to IDeviceSettingsDisplay interface or nullptr + */ + Exchange::IDeviceSettingsDisplay* GetDisplayInterface(); + + /** + * @brief Get VideoPort component interface + * @return Pointer to IDeviceSettingsVideoPort interface or nullptr + */ + Exchange::IDeviceSettingsVideoPort* GetVideoPortInterface(); + + /** + * @brief Get VideoDevice component interface + * @return Pointer to IDeviceSettingsVideoDevice interface or nullptr + */ + Exchange::IDeviceSettingsVideoDevice* GetVideoDeviceInterface(); + + /** + * @brief Get Host component interface + * @return Pointer to IDeviceSettingsHost interface or nullptr + */ + Exchange::IDeviceSettingsHost* GetHostInterface(); + + /** + * @brief Get CompositeIn component interface + * @return Pointer to IDeviceSettingsCompositeIn interface or nullptr + */ + Exchange::IDeviceSettingsCompositeIn* GetCompositeInInterface(); +}; + +} // namespace WPEFramework + +#endif // USE_WPE_THUNDER_PLUGIN diff --git a/ds/manager.cpp b/ds/manager.cpp index f94b9da1..889ecd64 100644 --- a/ds/manager.cpp +++ b/ds/manager.cpp @@ -45,6 +45,10 @@ #include #include +#ifdef USE_WPE_THUNDER_PLUGIN +#include "dsController-com.h" +#endif + /** * @file manager.cpp * @brief RDK Device Settings module is a cross-platform device for controlling the following hardware configurations: @@ -156,6 +160,15 @@ void Manager::Initialize() if (needInit) { dsError_t err = dsERR_GENERAL; + #ifdef USE_WPE_THUNDER_PLUGIN + // For Thunder COM-RPC mode, initialize the DeviceSettingsController + if (WPEFramework::DeviceSettingsController::Initialize() != WPEFramework::Core::ERROR_NONE) { + fprintf(stderr, "[Manager] Failed to initialize DeviceSettingsController\n"); + throw std::runtime_error("DeviceSettingsController initialization failed"); + } + printf("[Manager] DeviceSettingsController initialized successfully\n"); + #endif + err = initializeFunctionWithRetry("dsDisplayInit", dsDisplayInit); CHECK_RET_VAL(err); diff --git a/rpc/cli/Makefile b/rpc/cli/Makefile index ab24c14f..e9448399 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 dsFPD-com.cpp and dsController-com.cpp, exclude dsFPD.c + OBJS := $(patsubst %.cpp,%.o,$(wildcard *.cpp)) + OBJS += $(patsubst %.c,%.o,$(filter-out dsFPD.c,$(wildcard *.c))) +else + # IARM mode - use dsFPD.c, exclude both dsFPD-com.cpp and dsController-com.cpp + OBJS := $(patsubst %.cpp,%.o,$(filter-out dsFPD-com.cpp dsController-com.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..5e240587 100644 --- a/rpc/cli/Makefile.am +++ b/rpc/cli/Makefile.am @@ -28,4 +28,15 @@ 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_WPE_THUNDER_PLUGIN + FPD_SOURCE = dsController-com.cpp dsFPD-com.cpp + THUNDER_LIBS = -lWPEFrameworkCore -lWPEFrameworkCOM +else + FPD_SOURCE = dsFPD.c + THUNDER_LIBS = +endif + +libdshalcli_la_SOURCES = dsAudio.c dsclientlogger.c dsDisplay.c $(FPD_SOURCE) dsHost.cpp dsVideoDevice.c dsVideoPort.c +libdshalcli_la_LIBADD = $(THUNDER_LIBS) diff --git a/rpc/cli/dsController-com.cpp b/rpc/cli/dsController-com.cpp new file mode 100644 index 00000000..7c668c1a --- /dev/null +++ b/rpc/cli/dsController-com.cpp @@ -0,0 +1,590 @@ +/* + * 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. +*/ + +/** +* @file dsController-com.cpp +* @brief Implementation of central controller for DeviceSettings COM-RPC connections +*/ + +#ifdef USE_WPE_THUNDER_PLUGIN + +#include "dsController-com.h" +#include +#include +#include + +namespace WPEFramework { + +// Static member initialization +DeviceSettingsController* DeviceSettingsController::_instance = nullptr; +Core::CriticalSection DeviceSettingsController::_lock; + +// Define the static constexpr member +constexpr const TCHAR DeviceSettingsController::_callSign[]; + +pthread_t DeviceSettingsController::_connectionThreadID = 0; +std::atomic DeviceSettingsController::_isConnecting(false); +uint32_t DeviceSettingsController::_connectionRetryCount = 0; + +DeviceSettingsController::DeviceSettingsController() + : BaseClass() + , _deviceSettings(nullptr) + , _fpdInterface(nullptr) +// , _hdmiInInterface(nullptr) +// , _audioInterface(nullptr) +// , _displayInterface(nullptr) +// , _videoPortInterface(nullptr) +// , _videoDeviceInterface(nullptr) +// , _hostInterface(nullptr) +// , _compositeInInterface(nullptr) + , _connected(false) + , _shutdown(false) +{ + printf("[DeviceSettingsController] Constructor called\n"); +} + +DeviceSettingsController::~DeviceSettingsController() +{ + printf("[DeviceSettingsController] Destructor called\n"); + _shutdown = true; + + // Release all component interfaces + _lock.Lock(); + +/* if (_compositeInInterface) { + _compositeInInterface->Release(); + _compositeInInterface = nullptr; + } + if (_hostInterface) { + _hostInterface->Release(); + _hostInterface = nullptr; + } + if (_videoDeviceInterface) { + _videoDeviceInterface->Release(); + _videoDeviceInterface = nullptr; + } + if (_videoPortInterface) { + _videoPortInterface->Release(); + _videoPortInterface = nullptr; + } + if (_displayInterface) { + _displayInterface->Release(); + _displayInterface = nullptr; + } + if (_audioInterface) { + _audioInterface->Release(); + _audioInterface = nullptr; + } + if (_hdmiInInterface) { + _hdmiInInterface->Release(); + _hdmiInInterface = nullptr; + } +*/ + if (_fpdInterface) { + _fpdInterface->Release(); + _fpdInterface = nullptr; + } + if (_deviceSettings) { + _deviceSettings->Release(); + _deviceSettings = nullptr; + } + + _lock.Unlock(); + + BaseClass::Close(Core::infinite); +} + +void DeviceSettingsController::Operational(const bool upAndRunning) +{ + _lock.Lock(); + + if (upAndRunning) { + printf("[DeviceSettingsController] Plugin is operational\n"); + + // Get main IDeviceSettings interface + if (nullptr == _deviceSettings) { + _deviceSettings = BaseClass::Interface(); + if (_deviceSettings != nullptr) { + printf("[DeviceSettingsController] Successfully obtained IDeviceSettings interface\n"); + } else { + fprintf(stderr, "[DeviceSettingsController] Failed to get IDeviceSettings interface\n"); + } + } + } else { + printf("[DeviceSettingsController] Plugin is not operational - releasing all interfaces\n"); + + // Release all component interfaces + /* if (_compositeInInterface) { + _compositeInInterface->Release(); + _compositeInInterface = nullptr; + } + if (_hostInterface) { + _hostInterface->Release(); + _hostInterface = nullptr; + } + if (_videoDeviceInterface) { + _videoDeviceInterface->Release(); + _videoDeviceInterface = nullptr; + } + if (_videoPortInterface) { + _videoPortInterface->Release(); + _videoPortInterface = nullptr; + } + if (_displayInterface) { + _displayInterface->Release(); + _displayInterface = nullptr; + } + if (_audioInterface) { + _audioInterface->Release(); + _audioInterface = nullptr; + } + if (_hdmiInInterface) { + _hdmiInInterface->Release(); + _hdmiInInterface = nullptr; + } + */ + if (_fpdInterface) { + _fpdInterface->Release(); + _fpdInterface = nullptr; + } + if (_deviceSettings) { + _deviceSettings->Release(); + _deviceSettings = nullptr; + } + } + + _lock.Unlock(); +} + +template +bool DeviceSettingsController::QueryComponentInterface(T*& interfacePtr, const char* componentName) +{ + if (interfacePtr != nullptr) { + // Already cached + return true; + } + + if (_deviceSettings == nullptr) { + fprintf(stderr, "[DeviceSettingsController] Cannot query %s - main interface not available\n", componentName); + return false; + } + + interfacePtr = _deviceSettings->QueryInterface(); + if (interfacePtr != nullptr) { + printf("[DeviceSettingsController] Successfully obtained %s interface\n", componentName); + return true; + } else { + fprintf(stderr, "[DeviceSettingsController] Failed to get %s interface\n", componentName); + return false; + } +} + +DeviceSettingsController* DeviceSettingsController::GetInstance() +{ + return _instance; +} + +uint32_t DeviceSettingsController::Initialize() +{ + printf("[DeviceSettingsController] Initialize entry\n"); + _lock.Lock(); + + if (_instance != nullptr) { + printf("[DeviceSettingsController] Already initialized\n"); + _lock.Unlock(); + return Core::ERROR_NONE; + } + + // Create the controller instance (NOT connected yet) + _instance = new DeviceSettingsController(); + + if (_instance == nullptr) { + fprintf(stderr, "[DeviceSettingsController] Failed to create instance\n"); + _lock.Unlock(); + return Core::ERROR_GENERAL; + } + + _lock.Unlock(); + + // Start Thunder connection retry thread (non-blocking, like dsMgrPwrCtrlEstablishConnection) + printf("[DeviceSettingsController] Starting Thunder COM-RPC connection thread\n"); + EstablishThunderConnection(); + + return Core::ERROR_NONE; +} + +void DeviceSettingsController::Terminate() +{ + printf("[DeviceSettingsController] Terminate entry\n"); + + _lock.Lock(); + + if (_instance != nullptr) { + delete _instance; + _instance = nullptr; + printf("[DeviceSettingsController] Instance terminated\n"); + } + + _lock.Unlock(); + + // Note: Connection retry thread is detached and will exit on its own + // when it successfully connects or if the process is shutting down +} + +void DeviceSettingsController::EstablishThunderConnection() +{ + printf("[DeviceSettingsController] Entering EstablishThunderConnection\n"); + + if (_isConnecting.load()) { + printf("[DeviceSettingsController] Connection already in progress\n"); + return; + } + + _isConnecting = true; + _connectionRetryCount = 0; + + // Create detached thread for retry logic (matches dsMgrPwrCtrlEstablishConnection pattern) + if (pthread_create(&_connectionThreadID, NULL, RetryThunderConnectionThread, NULL) == 0) { + if (pthread_detach(_connectionThreadID) != 0) { + fprintf(stderr, "[DeviceSettingsController] Thread detach failed\n"); + _isConnecting = false; + } + else { + printf("[DeviceSettingsController] Connection retry thread started successfully\n"); + } + } + else { + fprintf(stderr, "[DeviceSettingsController] Thread creation failed\n"); + _isConnecting = false; + } +} + +void* DeviceSettingsController::RetryThunderConnectionThread(void* arg) +{ + printf("[DeviceSettingsController] RetryThunderConnectionThread entry\n"); + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (controller == nullptr) { + fprintf(stderr, "[DeviceSettingsController] Controller instance is null\n"); + _isConnecting = false; + return arg; + } + + // Infinite retry loop (matches dsMgrPwrRetryEstablishConnThread pattern) + while (1) + { + _connectionRetryCount++; + + uint32_t result = controller->Connect(); + + if (result == Core::ERROR_NONE) { + printf("[DeviceSettingsController] Thunder connection SUCCESS after %u attempts\n", + _connectionRetryCount); + + // Post-connection initialization + FetchAndInitializeInterfaces(); + + _isConnecting = false; + printf("[DeviceSettingsController] RetryThunderConnectionThread completed successfully\n"); + break; + } + else { + if (_connectionRetryCount % 10 == 0) { // Log every 3 seconds + printf("[DeviceSettingsController] Connection attempt %u failed, retrying...\n", + _connectionRetryCount); + } + + // 300ms wait before retry (matches DSMGR_PWR_CNTRL_CONNECT_WAIT_TIME_MS) + usleep(DS_CONTROLLER_CONNECT_WAIT_TIME_MS); + } + } + + printf("[DeviceSettingsController] RetryThunderConnectionThread exit\n"); + return arg; +} + +void DeviceSettingsController::FetchAndInitializeInterfaces() +{ + printf("[DeviceSettingsController] FetchAndInitializeInterfaces entry\n"); + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (controller == nullptr || !controller->IsOperational()) { + fprintf(stderr, "[DeviceSettingsController] Controller not operational\n"); + return; + } + + // Pre-query all component interfaces to verify availability + uint32_t loadedCount = 0; + + // Query each interface (lazy loading will cache them) + if (controller->GetFPDInterface() != nullptr) { + printf("[DeviceSettingsController] FPD interface available\n"); + loadedCount++; + } + +/* if (controller->GetAudioInterface() != nullptr) { + printf("[DeviceSettingsController] Audio interface available\n"); + loadedCount++; + } + + if (controller->GetDisplayInterface() != nullptr) { + printf("[DeviceSettingsController] Display interface available\n"); + loadedCount++; + } + + if (controller->GetVideoDeviceInterface() != nullptr) { + printf("[DeviceSettingsController] VideoDevice interface available\n"); + loadedCount++; + } + + if (controller->GetVideoPortInterface() != nullptr) { + printf("[DeviceSettingsController] VideoPort interface available\n"); + loadedCount++; + } + + if (controller->GetHostInterface() != nullptr) { + printf("[DeviceSettingsController] Host interface available\n"); + loadedCount++; + } + + if (controller->GetHDMIInInterface() != nullptr) { + printf("[DeviceSettingsController] HDMIIn interface available\n"); + loadedCount++; + } + + if (controller->GetCompositeInInterface() != nullptr) { + printf("[DeviceSettingsController] CompositeIn interface available\n"); + loadedCount++; + } +*/ + printf("[DeviceSettingsController] Interface initialization complete: %u interfaces loaded\n", + loadedCount); +} + +uint32_t DeviceSettingsController::Connect() +{ + uint32_t status = Core::ERROR_NONE; + + _lock.Lock(); + + if (!isConnected()) { + printf("[DeviceSettingsController] 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("[DeviceSettingsController] Successfully opened RPC connection to Thunder\n"); + } else { + fprintf(stderr, "[DeviceSettingsController] Failed to open RPC connection, error: %u. Is Thunder running?\n", res); + status = Core::ERROR_UNAVAILABLE; + } + } + + if (nullptr == _deviceSettings) { + status = Core::ERROR_NOT_EXIST; + printf("[DeviceSettingsController] DeviceSettings plugin not yet operational\n"); + } + + _lock.Unlock(); + + return status; +} + +uint32_t DeviceSettingsController::Disconnect() +{ + uint32_t status = Core::ERROR_GENERAL; + bool close = false; + + _lock.Lock(); + + if (isConnected()) { + close = true; + _connected = false; + } + + _lock.Unlock(); + + if (close) { + status = BaseClass::Close(Core::infinite); + } + + return status; +} + +bool DeviceSettingsController::IsOperational() const +{ + _lock.Lock(); + bool result = (isConnected() && (_deviceSettings != nullptr)); + _lock.Unlock(); + return result; +} + +bool DeviceSettingsController::IsComponentAvailable(DeviceSettingsComponent component) const +{ + _lock.Lock(); + + bool available = false; + switch (component) { + case DeviceSettingsComponent::FPD: + available = (_fpdInterface != nullptr); + break; + /* case DeviceSettingsComponent::HDMIIn: + available = (_hdmiInInterface != nullptr); + break; + case DeviceSettingsComponent::Audio: + available = (_audioInterface != nullptr); + break; + case DeviceSettingsComponent::Display: + available = (_displayInterface != nullptr); + break; + case DeviceSettingsComponent::VideoPort: + available = (_videoPortInterface != nullptr); + break; + case DeviceSettingsComponent::VideoDevice: + available = (_videoDeviceInterface != nullptr); + break; + case DeviceSettingsComponent::Host: + available = (_hostInterface != nullptr); + break; + case DeviceSettingsComponent::CompositeIn: + available = (_compositeInInterface != nullptr); + break; + */ + } + + _lock.Unlock(); + return available; +} + +bool DeviceSettingsController::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; +} + +bool DeviceSettingsController::WaitForComponent(DeviceSettingsComponent component, uint32_t timeoutMs) const +{ + const uint32_t pollIntervalMs = 100; + uint32_t elapsedMs = 0; + + while (elapsedMs < timeoutMs) { + if (IsComponentAvailable(component)) { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(pollIntervalMs)); + elapsedMs += pollIntervalMs; + } + + return false; +} + +Exchange::IDeviceSettingsFPD* DeviceSettingsController::GetFPDInterface() +{ + _lock.Lock(); + QueryComponentInterface(_fpdInterface, "IDeviceSettingsFPD"); + Exchange::IDeviceSettingsFPD* result = _fpdInterface; + _lock.Unlock(); + return result; +} + +Exchange::IDeviceSettingsHDMIIn* DeviceSettingsController::GetHDMIInInterface() +{ +/* _lock.Lock(); + QueryComponentInterface(_hdmiInInterface, "IDeviceSettingsHDMIIn"); + Exchange::IDeviceSettingsHDMIIn* result = _hdmiInInterface; + _lock.Unlock(); + return result; +*/ +} + +Exchange::IDeviceSettingsAudio* DeviceSettingsController::GetAudioInterface() +{ +/* _lock.Lock(); + QueryComponentInterface(_audioInterface, "IDeviceSettingsAudio"); + Exchange::IDeviceSettingsAudio* result = _audioInterface; + _lock.Unlock(); + return result; +*/ +} + +Exchange::IDeviceSettingsDisplay* DeviceSettingsController::GetDisplayInterface() +{ +/* _lock.Lock(); + QueryComponentInterface(_displayInterface, "IDeviceSettingsDisplay"); + Exchange::IDeviceSettingsDisplay* result = _displayInterface; + _lock.Unlock(); + return result; +*/} + +Exchange::IDeviceSettingsVideoPort* DeviceSettingsController::GetVideoPortInterface() +{ +/* + _lock.Lock(); + QueryComponentInterface(_videoPortInterface, "IDeviceSettingsVideoPort"); + Exchange::IDeviceSettingsVideoPort* result = _videoPortInterface; + _lock.Unlock(); + return result; +*/ +} + +Exchange::IDeviceSettingsVideoDevice* DeviceSettingsController::GetVideoDeviceInterface() +{ +/* + _lock.Lock(); + QueryComponentInterface(_videoDeviceInterface, "IDeviceSettingsVideoDevice"); + Exchange::IDeviceSettingsVideoDevice* result = _videoDeviceInterface; + _lock.Unlock(); + return result; +*/ +} + +Exchange::IDeviceSettingsHost* DeviceSettingsController::GetHostInterface() +{ +/* + _lock.Lock(); + QueryComponentInterface(_hostInterface, "IDeviceSettingsHost"); + Exchange::IDeviceSettingsHost* result = _hostInterface; + _lock.Unlock(); + return result; +*/ +} + +Exchange::IDeviceSettingsCompositeIn* DeviceSettingsController::GetCompositeInInterface() +{ +/* + _lock.Lock(); + QueryComponentInterface(_compositeInInterface, "IDeviceSettingsCompositeIn"); + Exchange::IDeviceSettingsCompositeIn* result = _compositeInInterface; + _lock.Unlock(); + return result; +*/ +} + +} // namespace WPEFramework + +#endif // USE_WPE_THUNDER_PLUGIN diff --git a/rpc/cli/dsController-com.h b/rpc/cli/dsController-com.h new file mode 100644 index 00000000..94bbe509 --- /dev/null +++ b/rpc/cli/dsController-com.h @@ -0,0 +1,261 @@ +/* + * 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. +*/ + +/** +* @file DeviceSettingsController.h +* @brief Central controller for all DeviceSettings COM-RPC connections +* +* This controller manages a single RPC connection to the Thunder DeviceSettings plugin +* and provides access to all component interfaces (FPD, HDMIIn, Audio, etc.) through +* QueryInterface on the main IDeviceSettings interface. +*/ + +#pragma once + +#ifdef USE_WPE_THUNDER_PLUGIN + +#ifndef MODULE_NAME +#define MODULE_NAME DeviceSettings_Client +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WPEFramework { + +/** + * @brief Component types supported by DeviceSettings + */ +enum class DeviceSettingsComponent : uint8_t { + FPD, + HDMIIn, + Audio, + Display, + VideoPort, + VideoDevice, + Host, + CompositeIn +}; + +/** + * @class DeviceSettingsController + * @brief Singleton controller managing all DeviceSettings component interfaces + * + * This class provides: + * - Single RPC connection to Thunder DeviceSettings plugin + * - Thread-safe access to all component interfaces + * - Automatic lifecycle management + * - Connection status monitoring + * - Reconnection support + */ +class DeviceSettingsController : public RPC::SmartInterfaceType { +private: + using BaseClass = RPC::SmartInterfaceType; + + // Main interface + Exchange::IDeviceSettings* _deviceSettings; + + // Component interfaces obtained via QueryInterface + Exchange::IDeviceSettingsFPD* _fpdInterface; + Exchange::IDeviceSettingsHDMIIn* _hdmiInInterface; + Exchange::IDeviceSettingsAudio* _audioInterface; + Exchange::IDeviceSettingsDisplay* _displayInterface; + Exchange::IDeviceSettingsVideoPort* _videoPortInterface; + Exchange::IDeviceSettingsVideoDevice* _videoDeviceInterface; + Exchange::IDeviceSettingsHost* _hostInterface; + Exchange::IDeviceSettingsCompositeIn* _compositeInInterface; + + // Singleton instance + static DeviceSettingsController* _instance; + static Core::CriticalSection _lock; + + // Connection state + bool _connected; + bool _shutdown; + + // Thunder callsign + static constexpr const TCHAR _callSign[] = _T("org.rdk.DeviceSettings"); + + // Connection retry thread management + static pthread_t _connectionThreadID; + static std::atomic _isConnecting; + static uint32_t _connectionRetryCount; + + #define DS_CONTROLLER_CONNECT_WAIT_TIME_MS 300000 // 300ms in microseconds + + // Helper methods + static void EstablishThunderConnection(); + static void* RetryThunderConnectionThread(void* arg); + static void FetchAndInitializeInterfaces(); + + /** + * @brief Private constructor for singleton pattern + */ + DeviceSettingsController(); + + /** + * @brief Private destructor + */ + ~DeviceSettingsController(); + + /** + * @brief Operational callback from SmartInterfaceType + * @param upAndRunning true if plugin is operational, false otherwise + */ + virtual void Operational(const bool upAndRunning) override; + + /** + * @brief Check if connected (without lock) + */ + inline bool isConnected() const { return _connected; } + + /** + * @brief Query and cache a component interface + * @tparam T Component interface type + * @param interfacePtr Reference to cached interface pointer + * @param componentName Name for logging + * @return true if interface obtained successfully + */ + template + bool QueryComponentInterface(T*& interfacePtr, const char* componentName); + +public: + // Delete copy constructor and assignment operator + DeviceSettingsController(const DeviceSettingsController&) = delete; + DeviceSettingsController& operator=(const DeviceSettingsController&) = delete; + + /** + * @brief Get singleton instance + * @return Pointer to singleton instance + */ + static DeviceSettingsController* GetInstance(); + + /** + * @brief Initialize the controller + * @return Core::ERROR_NONE on success, error code otherwise + */ + static uint32_t Initialize(); + + /** + * @brief Terminate the controller and release all resources + */ + static void Terminate(); + + /** + * @brief Connect to Thunder DeviceSettings plugin + * @return Core::ERROR_NONE on success, error code otherwise + */ + uint32_t Connect(); + + /** + * @brief Disconnect from Thunder DeviceSettings plugin + * @return Core::ERROR_NONE on success, error code otherwise + */ + uint32_t Disconnect(); + + /** + * @brief Check if the main interface is operational + * @return true if operational, false otherwise + */ + bool IsOperational() const; + + /** + * @brief Check if a specific component interface is available + * @param component Component type to check + * @return true if available, false otherwise + */ + bool IsComponentAvailable(DeviceSettingsComponent component) const; + + /** + * @brief Wait for the main interface to become operational + * @param timeoutMs Timeout in milliseconds + * @return true if operational within timeout, false otherwise + */ + bool WaitForOperational(uint32_t timeoutMs = 5000) const; + + /** + * @brief Wait for a specific component to become available + * @param component Component type to wait for + * @param timeoutMs Timeout in milliseconds + * @return true if available within timeout, false otherwise + */ + bool WaitForComponent(DeviceSettingsComponent component, uint32_t timeoutMs = 5000) const; + + /** + * @brief Get FPD component interface + * @return Pointer to IDeviceSettingsFPD interface or nullptr + */ + Exchange::IDeviceSettingsFPD* GetFPDInterface(); + + /** + * @brief Get HDMIIn component interface + * @return Pointer to IDeviceSettingsHDMIIn interface or nullptr + */ + Exchange::IDeviceSettingsHDMIIn* GetHDMIInInterface(); + + /** + * @brief Get Audio component interface + * @return Pointer to IDeviceSettingsAudio interface or nullptr + */ + Exchange::IDeviceSettingsAudio* GetAudioInterface(); + + /** + * @brief Get Display component interface + * @return Pointer to IDeviceSettingsDisplay interface or nullptr + */ + Exchange::IDeviceSettingsDisplay* GetDisplayInterface(); + + /** + * @brief Get VideoPort component interface + * @return Pointer to IDeviceSettingsVideoPort interface or nullptr + */ + Exchange::IDeviceSettingsVideoPort* GetVideoPortInterface(); + + /** + * @brief Get VideoDevice component interface + * @return Pointer to IDeviceSettingsVideoDevice interface or nullptr + */ + Exchange::IDeviceSettingsVideoDevice* GetVideoDeviceInterface(); + + /** + * @brief Get Host component interface + * @return Pointer to IDeviceSettingsHost interface or nullptr + */ + Exchange::IDeviceSettingsHost* GetHostInterface(); + + /** + * @brief Get CompositeIn component interface + * @return Pointer to IDeviceSettingsCompositeIn interface or nullptr + */ + Exchange::IDeviceSettingsCompositeIn* GetCompositeInInterface(); +}; + +} // namespace WPEFramework + +#endif // USE_WPE_THUNDER_PLUGIN diff --git a/rpc/cli/dsFPD-com.cpp b/rpc/cli/dsFPD-com.cpp new file mode 100644 index 00000000..858a176f --- /dev/null +++ b/rpc/cli/dsFPD-com.cpp @@ -0,0 +1,620 @@ +/* + * 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 +#include + +#include "dsFPD.h" +#include "dsError.h" +#include "dsclientlogger.h" +#include "dsController-com.h" + +using namespace WPEFramework; + +// Thread safety lock for FPD interface calls +static Core::CriticalSection _fpdLock; + +/** + * @brief Convert Thunder error code to dsError_t + */ +static dsError_t ConvertThunderError(uint32_t thunderError) +{ + if (thunderError == Core::ERROR_NONE) { + return dsERR_NONE; + } else if (thunderError == Core::ERROR_UNAVAILABLE) { + return dsERR_OPERATION_NOT_SUPPORTED; + } else if (thunderError == Core::ERROR_BAD_REQUEST) { + return dsERR_INVALID_PARAM; + } else { + return dsERR_GENERAL; + } +} + +/** + * @brief Thread-safe wrapper functions for FPD interface calls + */ +namespace FPDWrapper { + +inline uint32_t SetFPDTime(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat, + uint32_t hour, uint32_t minutes) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDTime(timeFormat, hour, minutes); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDScroll(Exchange::IDeviceSettingsFPD* fpdInterface, + uint32_t scrollHoldOnDur, uint32_t horzScrollIterations, + uint32_t vertScrollIterations) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDScroll(scrollHoldOnDur, horzScrollIterations, vertScrollIterations); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDBlink(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + uint32_t blinkDuration, uint32_t blinkIterations) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDBlink(indicator, blinkDuration, blinkIterations); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t GetFPDBrightness(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + uint32_t& brightness) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->GetFPDBrightness(indicator, brightness); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDBrightness(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + uint32_t brightness, bool persist) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDBrightness(indicator, brightness, persist); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t GetFPDTextBrightness(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator, + uint32_t& brightness) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->GetFPDTextBrightness(indicator, brightness); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDTextBrightness(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator, + uint32_t brightness) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDTextBrightness(indicator, brightness); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t GetFPDColor(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + uint32_t& color) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->GetFPDColor(indicator, color); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDColor(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + uint32_t color) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDColor(indicator, color); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t EnableFPDClockDisplay(Exchange::IDeviceSettingsFPD* fpdInterface, bool enable) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->EnableFPDClockDisplay(enable); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDState(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + Exchange::IDeviceSettingsFPD::FPDState state) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDState(indicator, state); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t GetFPDState(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDIndicator indicator, + Exchange::IDeviceSettingsFPD::FPDState& state) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->GetFPDState(indicator, state); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t GetFPDTimeFormat(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDTimeFormat& timeFormat) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->GetFPDTimeFormat(timeFormat); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDTimeFormat(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDTimeFormat(timeFormat); + _fpdLock.Unlock(); + return result; +} + +inline uint32_t SetFPDMode(Exchange::IDeviceSettingsFPD* fpdInterface, + Exchange::IDeviceSettingsFPD::FPDMode mode) +{ + _fpdLock.Lock(); + uint32_t result = fpdInterface->SetFPDMode(mode); + _fpdLock.Unlock(); + return result; +} + +} // namespace FPDWrapper + +// C API implementations using Thunder COM-RPC + +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 >>>>>>>>\r\n"); + + //DeviceSettingsController is initialized by manager.cpp + // Just verify we have FPD interface access + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + fprintf(stderr, "[dsFPD-com] DeviceSettingsController not initialized\n"); + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + fprintf(stderr, "[dsFPD-com] FPD interface not available\n"); + return dsERR_GENERAL; + } + + return dsERR_NONE; +} + +dsError_t dsFPTerm(void) +{ + // DeviceSettingsController cleanup handled by manager.cpp + // No component-specific cleanup needed + 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) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat = + static_cast(eTime); + + uint32_t result = FPDWrapper::SetFPDTime(fpdInterface, timeFormat, uHour, uMinutes); + return ConvertThunderError(result); +} + +dsError_t dsSetFPScroll(unsigned int nScrollHoldOnDur, unsigned int nHorzScrollIterations, unsigned int nVertScrollIterations) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + uint32_t result = FPDWrapper::SetFPDScroll(fpdInterface, nScrollHoldOnDur, nHorzScrollIterations, nVertScrollIterations); + return ConvertThunderError(result); +} + +dsError_t dsSetFPBlink(dsFPDIndicator_t eIndicator, unsigned int nBlinkDuration, unsigned int nBlinkIterations) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t result = FPDWrapper::SetFPDBlink(fpdInterface, indicator, nBlinkDuration, nBlinkIterations); + 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t brightness = 0; + uint32_t result = FPDWrapper::GetFPDBrightness(fpdInterface, indicator, brightness); + + if (result == 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t result = FPDWrapper::SetFPDBrightness(fpdInterface, indicator, static_cast(eBrightness), toPersist); + 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator = + static_cast(eIndicator); + + uint32_t brightness = 0; + uint32_t result = FPDWrapper::GetFPDTextBrightness(fpdInterface, indicator, brightness); + + if (result == Core::ERROR_NONE) { + *pBrightness = static_cast(brightness); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetFPTextBrightness(dsFPDTextDisplay_t eIndicator, dsFPDBrightness_t eBrightness) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTextDisplay indicator = + static_cast(eIndicator); + + uint32_t result = FPDWrapper::SetFPDTextBrightness(fpdInterface, indicator, static_cast(eBrightness)); + 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + uint32_t color = 0; + uint32_t result = FPDWrapper::GetFPDColor(fpdInterface, indicator, color); + + if (result == 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) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + // Thunder interface doesn't support persist flag - ignore it + uint32_t result = FPDWrapper::SetFPDColor(fpdInterface, indicator, static_cast(eColor)); + return ConvertThunderError(result); +} + +dsError_t dsFPEnableCLockDisplay(int enable) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + uint32_t result = FPDWrapper::EnableFPDClockDisplay(fpdInterface, enable != 0); + return ConvertThunderError(result); +} + +dsError_t dsSetFPState(dsFPDIndicator_t eIndicator, dsFPDState_t state) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + Exchange::IDeviceSettingsFPD::FPDState fpdState = + static_cast(state); + + uint32_t result = FPDWrapper::SetFPDState(fpdInterface, indicator, fpdState); + 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDIndicator indicator = + static_cast(eIndicator); + + Exchange::IDeviceSettingsFPD::FPDState fpdState; + uint32_t result = FPDWrapper::GetFPDState(fpdInterface, indicator, fpdState); + + if (result == 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; + } + + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat; + uint32_t result = FPDWrapper::GetFPDTimeFormat(fpdInterface, timeFormat); + + if (result == Core::ERROR_NONE) { + *pTimeFormat = static_cast(timeFormat); + } + + return ConvertThunderError(result); +} + +dsError_t dsSetFPTimeFormat(dsFPDTimeFormat_t eTimeFormat) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDTimeFormat timeFormat = + static_cast(eTimeFormat); + + uint32_t result = FPDWrapper::SetFPDTimeFormat(fpdInterface, timeFormat); + return ConvertThunderError(result); +} + +dsError_t dsSetFPDMode(dsFPDMode_t eMode) +{ + DeviceSettingsController* controller = DeviceSettingsController::GetInstance(); + if (!controller) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD* fpdInterface = controller->GetFPDInterface(); + if (!fpdInterface) { + return dsERR_GENERAL; + } + + Exchange::IDeviceSettingsFPD::FPDMode mode = + static_cast(eMode); + + uint32_t result = FPDWrapper::SetFPDMode(fpdInterface, mode); + return ConvertThunderError(result); +} + +} // extern "C" + +#endif // USE_WPE_THUNDER_PLUGIN + +/** @} */ +/** @} */ diff --git a/sample/DSCliLibStandaloneApp.cpp b/sample/DSCliLibStandaloneApp.cpp new file mode 100644 index 00000000..00a699df --- /dev/null +++ b/sample/DSCliLibStandaloneApp.cpp @@ -0,0 +1,775 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// RDK specific includes - libds.so API +#include "manager.hpp" // Device Settings Manager from libds.so +#include "host.hpp" // Host class from libds.so +#include "frontPanelConfig.hpp" // FrontPanelConfig class from libds.so +#include "frontPanelIndicator.hpp" // FrontPanelIndicator class from libds.so +#include "frontPanelTextDisplay.hpp" // FrontPanelTextDisplay class from libds.so +// #include "libIBus.h" // Uncomment if using IARM Bus +// #include "libIBusDaemon.h" // Uncomment if using IARM Bus daemon + +using namespace std; +using namespace device; // For libds.so classes + +// RDK/Yocto specific constants +#define MAX_DEBUG_LOG_BUFF_SIZE 8192 +#define DEVICE_SETTINGS_ERROR_NONE 0 +#define DEVICE_SETTINGS_ERROR_GENERAL 1 +#define DEVICE_SETTINGS_ERROR_UNAVAILABLE 2 +#define DEVICE_SETTINGS_ERROR_NOT_EXIST 43 + +// Define logging macros +#define LOGI(fmt, ...) printf("[INFO] " fmt "\n", ##__VA_ARGS__) +#define LOGE(fmt, ...) printf("[ERROR] " fmt "\n", ##__VA_ARGS__) +#define LOGD(fmt, ...) printf("[DEBUG] " fmt "\n", ##__VA_ARGS__) + +// Global variables +static pthread_mutex_t gCurlInitMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t gDSAppMutex = PTHREAD_MUTEX_INITIALIZER; +CURLSH* mCurlShared = nullptr; +char* gDebugPrintBuffer = nullptr; + +// RDK specific globals +static bool gAppInitialized = false; +static bool gSignalHandlersSet = false; +static device::List fpIndicators; + +// Function declarations (libds.so functions) +void libds_Initialize(); +void libds_Terminate(); +int setup_signals(); +int breakpad_ExceptionHandler(); +void cleanup_resources(); + +// Menu system functions +void showMainMenu(); +void handleFPDModule(); +void handleAudioPortsModule(); +void handleVideoPortsModule(); +void handleHDMIInModule(); +void handleCompositeInModule(); +int getUserChoice(); +void clearScreen(); + + +int main(int argc, char **argv) +{ + // Initialize syslog for RDK environment + openlog("DSApp", LOG_PID | LOG_CONS, LOG_DAEMON); + + printf("\n=== DeviceSettings Interactive Test Application ===\n"); + printf("Build: %s %s\n", __DATE__, __TIME__); + + // Parse command line arguments + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + printf("Usage: %s [options]\n", argv[0]); + printf("Options:\n"); + printf(" -h, --help Show this help message\n"); + printf(" --version Show version information\n"); + return 0; + } else if (strcmp(argv[i], "--version") == 0) { + printf("DSApp version 1.0.0 (RDK/Yocto build)\n"); + return 0; + } + } + + // Set up signal handlers early + if (setup_signals() != 0) { + LOGE("Failed to setup signal handlers"); + return -1; + } + + // Allocate debug buffer with error checking + gDebugPrintBuffer = new(std::nothrow) char[MAX_DEBUG_LOG_BUFF_SIZE]; + if (!gDebugPrintBuffer) { + LOGE("Failed to allocate debug buffer of size %d", MAX_DEBUG_LOG_BUFF_SIZE); + return -1; + } + memset(gDebugPrintBuffer, 0, MAX_DEBUG_LOG_BUFF_SIZE); + + // Initialize curl for network operations + CURLcode curl_result = curl_global_init(CURL_GLOBAL_ALL); + if (curl_result != CURLE_OK) { + LOGE("Failed to initialize curl: %s", curl_easy_strerror(curl_result)); + cleanup_resources(); + return -1; + } + + // Initialize breakpad for crash reporting + if (breakpad_ExceptionHandler() != 0) { + LOGE("Failed to initialize exception handler"); + // Continue anyway, not critical + } + + // Initialize libds.so (Device Settings library) + printf("\nInitializing libds.so (Device Settings library)...\n"); + libds_Initialize(); + printf("libds.so initialized successfully!\n"); + + // Set application as initialized + pthread_mutex_lock(&gDSAppMutex); + gAppInitialized = true; + pthread_mutex_unlock(&gDSAppMutex); + + // Main interactive menu loop + int result = 0; + try { + showMainMenu(); + printf("\nApplication completed successfully\n"); + } catch (const std::exception& e) { + LOGE("Application exception: %s", e.what()); + result = -1; + } catch (...) { + LOGE("Unknown application exception"); + result = -1; + } + + // Cleanup before exit + cleanup_resources(); + + printf("\nDevice Settings Application exiting with code %d\n", result); + closelog(); + return result; +} + +// Initialize libds.so Device Settings library +void libds_Initialize() +{ + try { + LOGI("Initializing Device Settings Manager from libds.so..."); + + // Initialize the Device Settings Manager + // This will internally call dsFPInit() and other init functions from libdshalcli.so + // device::Manager::Initialize(); As of now FPD only communicate through COM-RPC + + LOGI("Initializing Front Panel from libds.so..."); + LOGI("Front panel init"); + fpIndicators = device::FrontPanelConfig::getInstance().getIndicators(); + + for (size_t i = 0; i < fpIndicators.size(); i++) { + std::string IndicatorName = fpIndicators.at(i).getName(); + LOGI("Initializing Front Panel Indicator: %s", IndicatorName.c_str()); + } + + LOGI("Device Settings FPD initialized successfully"); + + LOGI("Device Settings Manager initialized successfully"); + } catch (const std::exception& e) { + LOGE("Failed to initialize Device Settings Manager: %s", e.what()); + throw; + } +} + +// Terminate libds.so Device Settings library +void libds_Terminate() +{ + try { + LOGI("Terminating Device Settings Manager from libds.so..."); + device::Manager::DeInitialize(); + LOGI("Device Settings Manager terminated successfully"); + } catch (const std::exception& e) { + LOGE("Error during Device Settings Manager termination: %s", e.what()); + } +} + +// Signal handler for graceful shutdown +static void signal_handler(int signum) { + const char* signal_name = "UNKNOWN"; + switch (signum) { + case SIGTERM: signal_name = "SIGTERM"; break; + case SIGINT: signal_name = "SIGINT"; break; + case SIGHUP: signal_name = "SIGHUP"; break; + case SIGUSR1: signal_name = "SIGUSR1"; break; + case SIGUSR2: signal_name = "SIGUSR2"; break; + } + + syslog(LOG_INFO, "[DSApp] Received signal %s (%d), initiating graceful shutdown", signal_name, signum); + + // Mark application as not initialized to trigger cleanup + pthread_mutex_lock(&gDSAppMutex); + gAppInitialized = false; + pthread_mutex_unlock(&gDSAppMutex); + + // Cleanup and exit + cleanup_resources(); + closelog(); + exit(0); +} + +int setup_signals() { + LOGI("Setting up signal handlers for RDK environment..."); + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = signal_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + // Handle common termination signals + if (sigaction(SIGTERM, &sa, nullptr) == -1) { + LOGE("Failed to set SIGTERM handler: %s", strerror(errno)); + return -1; + } + + if (sigaction(SIGINT, &sa, nullptr) == -1) { + LOGE("Failed to set SIGINT handler: %s", strerror(errno)); + return -1; + } + + if (sigaction(SIGHUP, &sa, nullptr) == -1) { + LOGE("Failed to set SIGHUP handler: %s", strerror(errno)); + return -1; + } + + // Handle user-defined signals for debugging + if (sigaction(SIGUSR1, &sa, nullptr) == -1) { + LOGE("Failed to set SIGUSR1 handler: %s", strerror(errno)); + return -1; + } + + if (sigaction(SIGUSR2, &sa, nullptr) == -1) { + LOGE("Failed to set SIGUSR2 handler: %s", strerror(errno)); + return -1; + } + + // Ignore SIGPIPE to handle broken pipe gracefully + signal(SIGPIPE, SIG_IGN); + + gSignalHandlersSet = true; + LOGI("Signal handlers configured successfully"); + return 0; +} + +int breakpad_ExceptionHandler() { + LOGI("Initializing crash reporting for RDK environment..."); + + // Set core dump parameters for RDK debugging + struct rlimit core_limit; + core_limit.rlim_cur = RLIM_INFINITY; + core_limit.rlim_max = RLIM_INFINITY; + + if (setrlimit(RLIMIT_CORE, &core_limit) != 0) { + LOGE("Failed to set core dump limit: %s", strerror(errno)); + } else { + LOGI("Core dump enabled for crash analysis"); + } + + // Set up crash dump directory (typical RDK location) + const char* crash_dir = "/opt/logs/crashes"; + if (access(crash_dir, W_OK) == 0) { + LOGI("Crash dumps will be written to: %s", crash_dir); + } else { + LOGD("Crash directory %s not accessible, using default location", crash_dir); + } + + // TODO: Initialize actual breakpad/crashpad when available + // For now, rely on system core dumps and signal handlers + + LOGI("Crash reporting initialization complete"); + return 0; +} + +// DeviceSettings_Init() is now implemented in device_settings.cpp + +// DeviceSettings_IsOperational() is now implemented in device_settings.cpp + +// DeviceSettings_Connect() is now implemented in device_settings.cpp + +void cleanup_resources() { + LOGI("Cleaning up application resources..."); + + // Mark application as shutting down + pthread_mutex_lock(&gDSAppMutex); + gAppInitialized = false; + pthread_mutex_unlock(&gDSAppMutex); + + // Clean up curl resources + if (mCurlShared) { + curl_share_cleanup(mCurlShared); + mCurlShared = nullptr; + LOGD("CURL shared handle cleaned up"); + } + + curl_global_cleanup(); + LOGD("CURL global cleanup completed"); + + // Free debug buffer + if (gDebugPrintBuffer) { + delete[] gDebugPrintBuffer; + gDebugPrintBuffer = nullptr; + LOGD("Debug buffer freed"); + } + + // Clean up RDK-specific resources + LOGD("Terminating libds.so Device Settings..."); + libds_Terminate(); // Call libds.so cleanup + + // TODO: Add IARM Bus cleanup if used + // IARM_Bus_Disconnect(); + // IARM_Bus_Term(); + + // Destroy mutexes + pthread_mutex_destroy(&gDSAppMutex); + pthread_mutex_destroy(&gCurlInitMutex); + LOGD("Mutexes destroyed"); + + LOGI("Resource cleanup complete"); +} + +// Menu system implementation +void clearScreen() { + printf("\033[2J\033[H"); // ANSI escape codes to clear screen +} + +int getUserChoice() { + int choice; + printf("\nEnter your choice: "); + if (scanf("%d", &choice) != 1) { + // Clear input buffer on invalid input + int c; + while ((c = getchar()) != '\n' && c != EOF); + return -1; + } + return choice; +} + +void showMainMenu() { + int choice; + + do { + clearScreen(); + printf("\n=== DeviceSettings Module Test Menu ===\n"); + printf("1. FPD (Front Panel Display)\n"); + printf("2. Audio Ports\n"); + printf("3. Video Ports\n"); + printf("4. HDMI Input\n"); + printf("5. Composite Input\n"); + printf("0. Exit\n"); + printf("========================================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: + handleFPDModule(); + break; + case 2: + handleAudioPortsModule(); + break; + case 3: + handleVideoPortsModule(); + break; + case 4: + handleHDMIInModule(); + break; + case 5: + handleCompositeInModule(); + break; + case 0: + printf("\nExiting...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); // consume newline + getchar(); // wait for Enter + break; + } + } while (choice != 0); +} + +void handleFPDModule() { + int choice; + + do { + clearScreen(); + printf("\n=== FPD (Front Panel Display) Module ===\n"); + printf("Using libds.so → libdshalcli.so → COM-RPC → DeviceSettings plugin\n"); + printf("1. GetFPDBrightness (Power LED)\n"); + printf("2. SetFPDBrightness (Power LED)\n"); + printf("3. GetFPDState (Power LED)\n"); + printf("4. SetFPDState (Power LED)\n"); + printf("5. GetFPDColor (Power LED)\n"); + printf("6. SetFPDColor (Power LED)\n"); + printf("7. Set Blink (Power LED)\n"); + printf("8. Test All Indicators\n"); + printf("0. Back to Main Menu\n"); + printf("=========================================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: { + try { + printf("\nCalling libds.so FrontPanelIndicator::getBrightness()...\n"); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Get brightness - this will call dsGetFPDBrightness from libdshalcli.so + int brightness = powerLED.getBrightness(); + + printf("SUCCESS: Power LED Brightness: %d\n", brightness); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 2: { + int brightness; + printf("\nEnter brightness level (0-100): "); + if (scanf("%d", &brightness) == 1) { + try { + printf("Calling libds.so FrontPanelIndicator::setBrightness(%d)...\n", brightness); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Set brightness - this will call dsSetFPDBrightness from libdshalcli.so + powerLED.setBrightness(brightness, true); + + printf("SUCCESS: Power LED brightness set to %d\n", brightness); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + } else { + printf("Invalid input!\n"); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 3: { + try { + printf("\nCalling libds.so FrontPanelIndicator::getState()...\n"); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Get state - this will call dsGetFPDState from libdshalcli.so + bool state = powerLED.getState(); + + printf("SUCCESS: Power LED State: %s\n", state ? "ON" : "OFF"); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 4: { + int state; + printf("\nEnter FPD state (0=Off, 1=On): "); + if (scanf("%d", &state) == 1) { + try { + printf("Calling libds.so FrontPanelIndicator::setState(%d)...\n", state); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Set state - this will call dsSetFPDState from libdshalcli.so + powerLED.setState(state != 0); + + printf("SUCCESS: Power LED state set to %s\n", state ? "ON" : "OFF"); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + } else { + printf("Invalid input!\n"); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 5: { + try { + printf("\nCalling libds.so FrontPanelIndicator::getColor()...\n"); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Get color - this will call dsGetFPColor from libdshalcli.so + uint32_t color = powerLED.getColor(); + + printf("SUCCESS: Power LED Color: 0x%08X\n", color); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 6: { + unsigned int color; + printf("\nEnter FPD color (hex, e.g., 0xFF0000 for red): 0x"); + if (scanf("%x", &color) == 1) { + try { + printf("Calling libds.so FrontPanelIndicator::setColor(0x%08X)...\n", color); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Set color - this will call dsSetFPDColor from libdshalcli.so + powerLED.setColor(color, true); + + printf("SUCCESS: Power LED color set to 0x%08X\n", color); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + } else { + printf("Invalid input!\n"); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 7: { + int interval, iterations; + printf("\nEnter blink interval (ms): "); + if (scanf("%d", &interval) != 1) { + printf("Invalid input!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + printf("Enter blink iterations: "); + if (scanf("%d", &iterations) == 1) { + try { + printf("Calling libds.so FrontPanelIndicator::setBlink(%d, %d)...\n", interval, iterations); + + // Get the Power LED indicator from libds.so + FrontPanelIndicator& powerLED = FrontPanelIndicator::getInstance(FrontPanelIndicator::kPower); + + // Set blink - this will call dsSetFPBlink from libdshalcli.so + FrontPanelIndicator::Blink blink(interval, iterations); + powerLED.setBlink(blink); + + printf("SUCCESS: Power LED blink set (interval=%d ms, iterations=%d)\n", interval, iterations); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + } else { + printf("Invalid input!\n"); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 8: { + printf("\nTesting all FPD indicators...\n"); + try { + const char* indicatorNames[] = {"Message", "Power", "Record", "Remote", "RFBypass"}; + int indicatorIds[] = { + FrontPanelIndicator::kMessage, + FrontPanelIndicator::kPower, + FrontPanelIndicator::kRecord, + FrontPanelIndicator::kRemote, + FrontPanelIndicator::kRFBypass + }; + + for (int i = 0; i < 5; i++) { + try { + FrontPanelIndicator& indicator = FrontPanelIndicator::getInstance(indicatorIds[i]); + int brightness = indicator.getBrightness(); + bool state = indicator.getState(); + printf(" %s LED: Brightness=%d, State=%s\n", + indicatorNames[i], brightness, state ? "ON" : "OFF"); + } catch (const std::exception& e) { + printf(" %s LED: ERROR - %s\n", indicatorNames[i], e.what()); + } + } + printf("Test completed!\n"); + } catch (const std::exception& e) { + printf("ERROR: %s\n", e.what()); + } + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + case 0: + printf("\nReturning to main menu...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + } while (choice != 0); +} + +void handleAudioPortsModule() { + int choice; + + do { + clearScreen(); + printf("\n=== Audio Ports Module ===\n"); + printf("1. DeviceSettings_GetAudioPort\n"); + printf("2. DeviceSettings_SetAudioPort\n"); + printf("3. DeviceSettings_GetAudioFormat\n"); + printf("4. DeviceSettings_SetAudioFormat\n"); + printf("5. DeviceSettings_GetAudioCompression\n"); + printf("6. DeviceSettings_SetAudioCompression\n"); + printf("7. DeviceSettings_GetDialogEnhancement\n"); + printf("8. DeviceSettings_SetDialogEnhancement\n"); + printf("9. DeviceSettings_GetDolbyVolumeMode\n"); + printf("10. DeviceSettings_SetDolbyVolumeMode\n"); + printf("0. Back to Main Menu\n"); + printf("===============================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: + printf("\nCalling DeviceSettings_GetAudioPort()...\n"); + // TODO: Add actual function call + printf("Function called successfully!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + case 2: + printf("\nCalling DeviceSettings_SetAudioPort()...\n"); + // TODO: Add actual function call + printf("Function called successfully!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + // Add more cases as needed + case 0: + printf("\nReturning to main menu...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + } while (choice != 0); +} + +void handleVideoPortsModule() { + int choice; + + do { + clearScreen(); + printf("\n=== Video Ports Module ===\n"); + printf("1. DeviceSettings_GetVideoPort\n"); + printf("2. DeviceSettings_SetVideoPort\n"); + printf("3. DeviceSettings_GetVideoFormat\n"); + printf("4. DeviceSettings_SetVideoFormat\n"); + printf("5. DeviceSettings_GetVideoResolution\n"); + printf("6. DeviceSettings_SetVideoResolution\n"); + printf("7. DeviceSettings_GetVideoFrameRate\n"); + printf("8. DeviceSettings_SetVideoFrameRate\n"); + printf("0. Back to Main Menu\n"); + printf("===============================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: + printf("\nCalling DeviceSettings_GetVideoPort()...\n"); + // TODO: Add actual function call + printf("Function called successfully!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + // Add more cases as needed + case 0: + printf("\nReturning to main menu...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + } while (choice != 0); +} + +void handleHDMIInModule() { + int choice; + + do { + clearScreen(); + printf("\n=== HDMI Input Module ===\n"); + printf("1. DeviceSettings_GetHDMIInput\n"); + printf("2. DeviceSettings_SetHDMIInput\n"); + printf("3. DeviceSettings_GetHDMIInputStatus\n"); + printf("4. DeviceSettings_GetHDMIInputSignalStatus\n"); + printf("0. Back to Main Menu\n"); + printf("==========================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: + printf("\nCalling DeviceSettings_GetHDMIInput()...\n"); + // TODO: Add actual function call + printf("Function called successfully!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + // Add more cases as needed + case 0: + printf("\nReturning to main menu...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + } while (choice != 0); +} + +void handleCompositeInModule() { + int choice; + + do { + clearScreen(); + printf("\n=== Composite Input Module ===\n"); + printf("1. DeviceSettings_GetCompositeInput\n"); + printf("2. DeviceSettings_SetCompositeInput\n"); + printf("3. DeviceSettings_GetCompositeInputStatus\n"); + printf("0. Back to Main Menu\n"); + printf("===============================\n"); + + choice = getUserChoice(); + + switch (choice) { + case 1: + printf("\nCalling DeviceSettings_GetCompositeInput()...\n"); + // TODO: Add actual function call + printf("Function called successfully!\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + // Add more cases as needed + case 0: + printf("\nReturning to main menu...\n"); + break; + default: + printf("\nInvalid choice! Please try again.\n"); + printf("Press Enter to continue..."); + getchar(); getchar(); + break; + } + } while (choice != 0); +} diff --git a/sample/Makefile b/sample/Makefile index 80b1a2dc..5bb61c31 100644 --- a/sample/Makefile +++ b/sample/Makefile @@ -45,12 +45,13 @@ CFLAGS += -g -fPIC -D_REENTRANT -Wall $(INCLUDE) .PHONY: $(OUTPUT) -BINARIES := $(patsubst %.cpp,%,$(wildcard *.cpp)) +# Exclude DSCliLibStandaloneApp.cpp from the automatic build pattern +BINARIES := $(patsubst %.cpp,%,$(filter-out DSCliLibStandaloneApp.cpp,$(wildcard *.cpp))) UNINSTALL := $(patsubst %,$(PWD)/install/bin/%, $(BINARIES)) #all: $(BINARIES) install -all: $(BINARIES) - @echo "Build Finished...." +all: $(BINARIES) dsapp + @echo "Build Finished and Installed (including DSApp) ..." #frontPanelTest: $(BINARIES): @@ -58,9 +59,22 @@ $(BINARIES): $(CXX) $(CFLAGS) $@.cpp -o $@ $(LDFLAGS) # @cp -f $@ $(INSTALL)/bin -install: +install: $(BINARIES) dsapp + @echo "INSTALL=$(INSTALL)" + @echo "DSApp install path: $(INSTALL)/bin/DSApp" + @mkdir -p $(INSTALL)/bin @echo "Copying the binaries to bin install folder..." - @cp $(BINARIES) $(INSTALL)/bin + @if [ -n "$(BINARIES)" ]; then \ + @cp $(BINARIES) $(INSTALL)/bin + else \ + echo "No sample BINARIES to install"; \ + fi + @if [ -f ../DSApp ]; then \ + echo "Installing DSApp to $(INSTALL)/bin/..."; \ + cp ../DSApp $(INSTALL)/bin/; \ + else \ + echo "DSApp not found at ../DSApp (skipping)"; \ + fi uninstall: @echo "Removing bin from install folder..." @@ -69,6 +83,23 @@ uninstall: clean: @echo "Cleaning the directory..." @$(RM) $(BINARIES) + @$(RM) DSApp + +# DSApp - Device Settings Test Application +.PHONY: dsapp dsapp-clean + +dsapp: + @if [ -f DSCliLibStandaloneApp.cpp ]; then \ + echo "Building DSApp..."; \ + $(CXX) $(CFLAGS) -std=c++11 DSCliLibStandaloneApp.cpp -o ../DSApp -L$(INSTALL)/lib -lds -Wl,--no-as-needed -ldshalcli -Wl,--as-needed -lcurl -lpthread $(LDFLAGS); \ + echo "DSApp built successfully at devicesettings/DSApp"; \ + else \ + echo "DSCliLibStandaloneApp.cpp not found, skipping DSApp build"; \ + fi + +dsapp-clean: + @echo "Cleaning DSApp..." + @$(RM) ../DSApp