diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..ed44621 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @rdkcentral/entservices-maintainers diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..147a412 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,21 @@ +### Review Comment Linking Guidelines + +When writing review comments based on custom instructions located in .github/instructions/**.instructions.md, include a direct GitHub link to the exact violated guideline in the respective instruction file. Use the following format: + + Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/.instructions.md#guideline-section-name + +## Examples + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugin.instructions.md#interface-implementation + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginlifecycle.instructions.md#deactivated + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginimplementation.instructions.md#inter-plugin-communication + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginmodule.instructions.md#module-name-convention + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginconfig.instructions.md#plugin-configuration + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugincmake.instructions.md#namespace-usage + +Refer: https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/PluginOnboardingCompliance.instructions.md#coverity-scan-inclusion-and-test-workflow-updates-for-new-plugins diff --git a/.github/instructions/Plugin.instructions.md b/.github/instructions/Plugin.instructions.md new file mode 100644 index 0000000..e92d726 --- /dev/null +++ b/.github/instructions/Plugin.instructions.md @@ -0,0 +1,203 @@ +--- +description: Guidelines for C++ files and header files that share the same name as their parent folder. +applyTo: "**/*.cpp,**/*.h" +--- + +# Instructions summary + 1. [Interface Implementation](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugin.instructions.md#interface-implementation) + 2. [Service Registration](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugin.instructions.md#service-registration) + 3. [JSON-RPC Stub Registration](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugin.instructions.md#json-rpc-stub-registration) + 4. [Handling Out-of-Process Plugin Failures](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Plugin.instructions.md#handling-out-of-process-plugin-failures) + +### Interface Implementation + +### Requirement + +Each plugin must implement the appropriate Thunder interfaces. + +-> PluginHost::IPlugin – Mandatory for all plugins. + +-> PluginHost::IDispatcher or derive from PluginHost::JSONRPC – Mandatory If the plugin handles JSON-RPC. + +-> Custom interfaces (like IHdcpProfile for HdcpProfile plugin) must be added to ThunderInterfaces for RPC. + +-> PluginHost::IWeb – If the plugin handles web requests. + + +### Example + +```cpp +BEGIN_INTERFACE_MAP(HdcpProfile) + INTERFACE_ENTRY(PluginHost::IPlugin) + INTERFACE_ENTRY(PluginHost::IDispatcher) + INTERFACE_AGGREGATE(Exchange::IHdcpProfile, _hdcpProfile) +END_INTERFACE_MAP +``` + +### Service Registration + +### Requirement + +All Thunder services must be registered using the SERVICE_REGISTRATION macro with name, major, minor and patch versions of service. Register the service using the following macro: + +``` +SERVICE_REGISTRATION(ServiceName, MAJOR, MINOR, PATCH) +``` + +For better readability, it is always good to define the following plugin metadata which is not mandatory: + +- **Precondition** - List of Thunder subsystems that must be active in order for the plugin to activate. This can also be set in Plugin.conf.in file. + +- **Terminations** - List of Thunder subsystems that will cause the plugin to deactivate if they are marked inactive whilst the plugin is running. + +- **Controls** - List of the subsystems that are controlled by the plugin. + +### Example + +```cpp +namespace WPEFramework { + namespace { + static Plugin::Metadata metadata( + API_VERSION_NUMBER_MAJOR, + API_VERSION_NUMBER_MINOR, + API_VERSION_NUMBER_PATCH, + {}, // Preconditions + {}, // Terminations + {} // Controls + ); + } + + namespace Plugin { + // Register HdcpProfile service with Thunder + SERVICE_REGISTRATION(HdcpProfile,API_VERSION_NUMBER_MAJOR,API_VERSION_NUMBER_MINOR,API_VERSION_NUMBER_PATCH); + } +} +``` + +### JSON-RPC Stub Registration + +### Requirement + +If the plugin includes , , and and inherits from PluginHost::JsonRPC, then it provides JSON‑RPC support and uses autogenerated JSON‑RPC stubs. + +These autogenerated stubs are the Exchange::J* C++ classes (for example, Exchange::JHdcpProfile and JsonData_HdcpProfile.h) that are produced by the Thunder JSON‑RPC code generator from the IPluginName* interface headers; they expose the C++ interface over JSON‑RPC so you do not have to call Register() for each method manually. + +Plugins using autogenerated JSON-RPC stubs (Exchange::J* classes) must register and unregister them in Initialize() and Deinitialize() methods.It should not be done in constructor and destructor. + +In Initialize(): + +```cpp +Exchange::JHdcpProfile::Register(*this, _hdcpProfile); +``` + +In Deinitialize(): + +```cpp +Exchange::JHdcpProfile::Unregister(*this); +``` + +It is strongly recommended to use the autogenerated JSON-RPC stubs rather than registering the json-rpc methods manually as below. + +```cpp +RDKShell::RDKShell() + ... +{ + ..... + Register(RDKSHELL_METHOD_MOVE_TO_FRONT, &RDKShell::moveToFrontWrapper, this); + Register(RDKSHELL_METHOD_MOVE_TO_BACK, &RDKShell::moveToBackWrapper, this); + ... +} +``` + +### Handling Out-of-Process Plugin Failures + +### Requirement + +- If the plugin runs as out-of-process, then it should implement RPC::IRemoteConnection::INotification interface inside your plugin. + +### Example + +```cpp +class TestPlugin : public PluginHost::IPlugin, public PluginHost::JSONRPC { +private: + class Notification : public RPC::IRemoteConnection::INotification { + public: + explicit Notification(TestPlugin* parent) + : _parent(*parent) + { + ASSERT(parent != nullptr); + } + + ~Notification() override = default; + + Notification(Notification&&) = delete; + Notification(const Notification&) = delete; + Notification& operator=(Notification&&) = delete; + Notification& operator=(const Notification&) = delete; + + public: + void Activated(RPC::IRemoteConnection* /* connection */) override + { + } + void Deactivated(RPC::IRemoteConnection* connection) override + { + _parent.Deactivated(connection); + } + + BEGIN_INTERFACE_MAP(Notification) + INTERFACE_ENTRY(RPC::IRemoteConnection::INotification) + END_INTERFACE_MAP + + private: + TestPlugin& _parent; + }; + +public: + TestPlugin() + : _connectionId(0) + , _service(nullptr) + , _testPlugin(nullptr) + , _notification(this) + { + } + ~TestPlugin() override = default; + + TestPlugin(TestPlugin&&) = delete; + TestPlugin(const TestPlugin&) = delete; + TestPlugin& operator=(TestPlugin&&) = delete; + TestPlugin& operator=(const TestPlugin&) = delete; + + BEGIN_INTERFACE_MAP(TestPlugin) + INTERFACE_ENTRY(PluginHost::IPlugin) + INTERFACE_ENTRY(PluginHost::IDispatcher) + INTERFACE_AGGREGATE(Exchange::ITestPlugin, _testPlugin) + END_INTERFACE_MAP + +public: + // IPlugin methods + const string Initialize(PluginHost::IShell* service) override; + void Deinitialize(PluginHost::IShell* service) override; + string Information() const override; + +private: + void Deactivated(RPC::IRemoteConnection* connection); + +private: + uint32_t _connectionId; + PluginHost::IShell* _service; + Exchange::ITestPlugin* _testPlugin; + Core::Sink _notification; +}; +``` + +- It should be registered during Initialize() to get itself notified when the remote process connects or disconnects. + +### Example + +```cpp +const string TestPlugin::Initialize(PluginHost::IShell* service) +{ + // Register for COM-RPC connection/disconnection notifications + _service->Register(&_notification); +} +``` diff --git a/.github/instructions/PluginOnboardingCompliance.instructions.md b/.github/instructions/PluginOnboardingCompliance.instructions.md new file mode 100644 index 0000000..839ac4c --- /dev/null +++ b/.github/instructions/PluginOnboardingCompliance.instructions.md @@ -0,0 +1,67 @@ +--- +applyTo: "CMakeLists.txt" +--- + +## Requirement + +### Coverity Scan Inclusion and Test Workflow Updates for New Plugins + +When adding a new plugin in `CMakeLists.txt`, you **must** also update the following to guarantee the plugin is included in all required test and Coverity analysis workflows: + +- **CI Workflow Files:** + - `L1-tests.yml` + - `L2-tests.yml` + - `L2-tests-oop.yml` +- **Coverity Build Script:** + - `cov_build.sh` + +**Example:** + +1. **CMake Plugin Registration Example** + + If you add your plugin in `CMakeLists.txt` as: + ```cmake + if (PLUGIN_RESOURCEMANAGER) + add_subdirectory(ResourceManager) + endif() + if (PLUGIN_MY_NEW_PLUGIN) + add_subdirectory(MyNewPlugin) + endif() + ``` +2. **Update Coverity Build Script** + + Add your plugin’s flag in the build command in `cov_build.sh`: + ```bash + cmake \ + -DPLUGIN_CORE=ON \ + -DPLUGIN_LEGACY=ON \ + # <-- NEW PLUGIN FLAG + -DPLUGIN_MY_NEW_PLUGIN=ON \ + . + ``` + This ensures Coverity runs on your new plugin. + +3. **Update Test Workflow YAMLs** + + Ensure each test workflow references your new plugin using the **DPLUGIN_** CMake flag in their build/test step. For example, in `L1-tests.yml`: + ```yaml + jobs: + build-test: + runs-on: ubuntu-22.04 + steps: + - name: Configure with new plugin + run: | + cmake \ + -DPLUGIN_CORE=ON \ + -DPLUGIN_MY_NEW_PLUGIN=ON \ + . + - name: Run tests + run: | + ctest + ``` + Repeat similar additions in `L2-tests.yml` and `L2-tests-oop.yml`. + +**Summary:** +Whenever a new plugin is registered via `CMakeLists.txt`, always update: +- `cov_build.sh` (add plugin flag to Coverity scan build step) +- All test CI workflows (`L1-tests.yml`, `L2-tests.yml`, `L2-tests-oop.yml`) to include your plugin flag so that your plugin’s code quality and tests are assured! diff --git a/.github/instructions/Plugincmake.instructions.md b/.github/instructions/Plugincmake.instructions.md new file mode 100644 index 0000000..a0b9335 --- /dev/null +++ b/.github/instructions/Plugincmake.instructions.md @@ -0,0 +1,43 @@ +--- +applyTo: "**/CMakeLists.txt" +--- + +### NAMESPACE Usage + +### Requirement + +All CMake targets, install paths, export sets,find_package and references must use the ${NAMESPACE} variable instead of hardcoded framework names (e.g., WPEFrameworkCore, WPEFrameworkPlugins). +This ensures smooth upgrades (e.g., WPEFramework → Thunder) and prevents regressions. + +### Correct Example + +```cmake +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +find_package(${NAMESPACE}Plugins REQUIRED) + +find_package(${NAMESPACE}Definitions REQUIRED) + +target_link_libraries(${MODULE_NAME} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Definitions::${NAMESPACE}Definitions) +``` + + +### Incorrect Example + +```cmake +set(MODULE_NAME WPEFramework${PLUGIN_NAME}) + +find_package(WPEFrameworkPlugins REQUIRED) + +find_package(WPEFrameworkDefinitions REQUIRED) + +target_link_libraries(${MODULE_NAME} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + WPEFrameworkPlugins::WPEFrameworkPlugins + WPEFrameworkDefinitions::WPEFrameworkDefinitions) +``` diff --git a/.github/instructions/Pluginconfig.instructions.md b/.github/instructions/Pluginconfig.instructions.md new file mode 100644 index 0000000..fd7206a --- /dev/null +++ b/.github/instructions/Pluginconfig.instructions.md @@ -0,0 +1,49 @@ +--- +applyTo: "**/*.config,**/*.conf.in" +--- + +### Plugin Configuration + +### Requirement + +- Each plugin must define .conf.in file that includes the following mandatory properties: + + - **autostart**: Indicates whether the plugin should start automatically when the framework boots. This should be set to false by default. + + - **callsign**: A unique identifier used to reference the plugin within the framework. Every callsign must be defined with a prefix of org.rdk and it must be followed by the ENT Service name written in PascalCase (e.g., org.rdk.PersistentStore). + + - **Custom properties**: Any additional configuration parameters required by the plugin. These are passed during activation via PluginHost::IShell::ConfigLine(). The following structural configuration elements are commonly defined: + - startuporder - Specifies the order in which plugins are started, relative to others. + - precondition - If these aren't met, the plugin stays in the Preconditions state and activates automatically once they are satisfied. It is recommended to define the precondition if the plugin depends on other subsystems being active. + - mode - Defines the execution mode of the plugin. + +### Plugin Mode Determination + +If the plugin's mode is set to OFF, it is treated as in-process. + +If no mode is specified, the plugin defaults to in-process. + +If the mode is explicitly set to LOCAL, the plugin runs out-of-process. + +The plugin mode is configured in the plugin's CMakeLists.txt file. + +- **locator** - Update with the name of the library (.so) that contains the actual plugin Implementation code. + +### Example + +.conf.in + +``` +precondition = ["Platform"] +callsign = "org.rdk.HdcpProfile" +autostart = "@PLUGIN_HDCPPROFILE_AUTOSTART@" +startuporder = "@PLUGIN_HDCPPROFILE_STARTUPORDER@" + +configuration = JSON() +rootobject = JSON() + +rootobject.add("mode", "@PLUGIN_HDCPPROFILE_MODE@") +rootobject.add("locator", "lib@PLUGIN_IMPLEMENTATION@.so") + +configuration.add("root", rootobject) +``` diff --git a/.github/instructions/Pluginimplementation.instructions.md b/.github/instructions/Pluginimplementation.instructions.md new file mode 100644 index 0000000..967d9d6 --- /dev/null +++ b/.github/instructions/Pluginimplementation.instructions.md @@ -0,0 +1,256 @@ +--- +applyTo: "**/*Implementation.cpp,**/*Implementation.h,**/*.cpp,**/*.h" +--- + +# Instruction Summary + 1. [Inter-Plugin Communication](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginimplementation.instructions.md#inter-plugin-communication) + 2. [On-Demand Plugin Interface Acquisition](https://github.com/rdkcentral/entservices-devicesettings/blob/develop/.github/instructions/Pluginimplementation.instructions.md#on-demand-plugin-interface-acquisition) + +### Inter-Plugin Communication + +### Requirement + +Plugins should use COM-RPC (e.g., use QueryInterfaceByCallsign or QueryInterface) to access other plugins. + +### Example + +Telemetry Plugin accessing UserSettings(via COM-RPC) through the IShell Interface API **QueryInterfaceByCallsign()** exposed for each Plugin - (Refer https://github.com/rdkcentral/entservices-infra/blob/7988b8a719e594782f041309ce2d079cf6f52863/Telemetry/TelemetryImplementation.cpp#L160 ) + +```cpp +_userSettingsPlugin = _service->QueryInterfaceByCallsign(USERSETTINGS_CALLSIGN); +``` + +QueryInterface: + +```cpp +_userSettingsPlugin = _service->QueryInterface(); +``` + +should not use JSON-RPC or LinkType for inter-plugin communication, as they introduce unnecessary overhead. + +### Incorrect Example + +LinkType: +```cpp +_telemetry = Core::ProxyType::Create(_T("org.rdk.telemetry"), _T(""), "token=" + token); +``` + +JSON-RPC: +```cpp +uint32_t ret = m_SystemPluginObj->Invoke(THUNDER_RPC_TIMEOUT, _T("getFriendlyName"), params, Result); +``` + +Use COM-RPC for plugin event registration by passing a C++ callback interface pointer for low-latency communication. It is important to register for StateChange notifications to monitor the notifying plugin's lifecycle. This allows you to safely release the interface pointer upon deactivation and prevents accessing a non-existent service. + +### Example + +**1. Initialize the Listener and Start Monitoring** + +```cpp +// Assuming you have a list of all target callsigns you want to monitor +const std::vector MonitoredCallsigns = { + "AudioTargetPlugin", + "NetworkTargetPlugin", + "InputTargetPlugin" +}; + +void Initialize(PluginHost::IShell* service) override { + + _service = service; + _service->AddRef(); + + // 1. Tell the Framework to send ALL state changes to *this* object + // This enables the StateChange() method to work for ALL plugins. + _service->Register(this); + + // 2. Check if the target plugins are ALREADY running (First-Time check) + for (const std::string& callsign : MonitoredCallsigns) { + + // Query the framework for the current instance of the target plugin + PluginHost::IShell* target = _service->QueryInterfaceByCallsign(callsign.c_str()); + + if (target != nullptr) { + // If the plugin is found and ACTIVATED, register immediately + if (target->State() == PluginHost::IShell::ACTIVATED) { + printf("LOG: Initial check found %s active. Registering events.\n", callsign.c_str()); + + // Use the multi-target registration method + RegisterWithTarget(callsign, target); + } + + // Release the IShell pointer obtained from QueryInterfaceByCallsign + target->Release(); + } + } +} +``` + +**2. Handle Activation (The Re-registration Step)** + +Always use if (plugin->Callsign() == "YourTargetCallsign") as the initial gate in your StateChange method. This guarantees that all subsequent logs and re-registration/cleanup logic are executed only for the plugin you are actively monitoring. + +```cpp +// StateChange() called when TargetPlugin comes online +void StateChange(PluginHost::IShell* plugin) override { + + const string& callsign = plugin->Callsign(); + + // --- Step 1: Handle DEACTIVATED (Cleanup) --- + if (plugin->State() == PluginHost::IShell::DEACTIVATED) { + + // Find if this specific callsign is in our map (if we were connected) + auto it = _targetPlugins.find(callsign); + + if (it != _targetPlugins.end()) { + printf("LOG: %s DEACTIVATED. Releasing interface.\n", callsign.c_str()); + + // Unregister and Release the specific pointer for this callsign + it->second->Unregister(this->QueryInterface()); + it->second->Release(); + + // Remove the entry from the map + _targetPlugins.erase(it); + } + } + + // --- Step 2: Handle ACTIVATED (Re-registration) --- + else if (plugin->State() == PluginHost::IShell::ACTIVATED) { + + // Use a list/set of monitored callsigns (e.g., {"Audio", "Network", "Input"}) + // Assuming 'isMonitoredPlugin(callsign)' is a method that checks your watchlist + if (isMonitoredPlugin(callsign)) { + + // Check if we are already connected (not found in the map) + if (_targetPlugins.find(callsign) == _targetPlugins.end()) { + + printf("LOG: %s ACTIVATED. Establishing new COM-RPC link.\n", callsign.c_str()); + + // Call the helper method to get the new pointer and register + RegisterWithTarget(callsign, plugin); + } + } + } +} +``` + +**3. COM-RPC Subscription** + +```cpp +void RegisterWithTarget(const string& callsign, PluginHost::IShell* plugin) { + + // 1. Get the new, valid interface pointer + Exchange::IMyTargetPlugin* newPtr = plugin->QueryInterface(); + + if (newPtr != nullptr) { + // 2. Register the callback + newPtr->Register(this->QueryInterface()); + + // 3. Store the new pointer in the map, indexed by callsign + _targetPlugins[callsign] = newPtr; + } +} +``` + +If the notifying plugin supports only JSON-RPC, then use a specialized smart link type when subscribing to its events. This method allows the framework to efficiently handle Plugin statechange events. + +### Example + +```cpp +/** + * @file Network.cpp + * @brief Example implementation showing JSON-RPC SmartLinkType setup and event subscription. + */ + +#define NETWORK_MANAGER_CALLSIGN "org.rdk.NetworkManager" + +void Initialize(PluginHost::IShell* service) override { + + // ... other initialization code ... + + // This state check ensures the environment is ready for JSON-RPC access. + if(PluginHost::IShell::state::ACTIVATED == state) + { + Core::SystemInfo::SetEnvironment(_T("THUNDER_ACCESS"), (_T("127.0.0.1:9998"))); + + // **SMART LINK TYPE INSTANTIATION:** + // This creates an object that acts as a client proxy for the JSON-RPC-only service. + // It handles sending JSON-RPC requests and receiving/deserializing JSON-RPC events. + // The type arguments specify the JSON interface (org.rdk.Network) and the CallSign. + m_networkmanager = make_shared >( + _T(NETWORK_MANAGER_CALLSIGN), + _T("org.rdk.Network"), + query + ); + + subscribeToEvents(); + } +} + +void Network::subscribeToEvents(void) { + uint32_t errCode = Core::ERROR_GENERAL; + + // Check if the smart link object was successfully created. + if (m_networkmanager) { + + if (!m_subsIfaceStateChange) { + + // **SMART LINK EVENT SUBSCRIPTION:** + // Using the SmartLinkType's Subscribe method, which internally constructs and + // sends the required JSON-RPC "Controller.1.subscribe" request to the target plugin. + // It automatically registers the local C++ callback (&Network::onInterfaceStateChange) + // to receive and process the JSON event payload. + errCode = m_networkmanager->Subscribe( + 5000, + _T("onInterfaceStateChange"), + &Network::onInterfaceStateChange + ); + + if (Core::ERROR_NONE == errCode) { + m_subsIfaceStateChange = true; + } else { + NMLOG_ERROR ("Subscribe to onInterfaceStateChange failed, errCode: %u", errCode); + } + } + } +} +``` + +### On-Demand Plugin Interface Acquisition + +### Requirement + +When a Thunder plugin needs to communicate with another plugin (via JSON-RPC or COM-RPC), do not create and hold the other plugin's interface instance throughout the plugin lifecycle. +Instead, create the instance only when needed and release it immediately after use. If the other plugin gets deactivated, your stored interface becomes stale. Calling methods on a stale interface leads to undefined behavior, crashes, or deadlocks. Thunder does not automatically invalidate your pointer when the remote plugin goes down. + +### Example + +```cpp +void MyPlugin::setNumber() { + .... + WPEFramework::Exchange::IOtherPlugin* other = shell->QueryInterfaceByCallsign("org.rdk.OtherPlugin"); + + if (other != nullptr) { + other->PerformAction(); + other->Release(); // Release immediately after use + } +} +``` + +### Incorrect Example + +```cpp +void MyPlugin::Initialize() { + _otherPlugin = shell->QueryInterfaceByCallsign(); +} + +void MyPlugin::Deinitialize() { + if (_otherPlugin) { + _otherPlugin->Release(); + _otherPlugin = nullptr; + } +} + +void MyPlugin::DoSomething() { + _otherPlugin->PerformAction(); // Risky if other plugin is deactivated! +} +``` diff --git a/.github/instructions/Pluginlifecycle.instructions.md b/.github/instructions/Pluginlifecycle.instructions.md new file mode 100644 index 0000000..7763ac3 --- /dev/null +++ b/.github/instructions/Pluginlifecycle.instructions.md @@ -0,0 +1,260 @@ +--- +description: Guidelines for C++ files and header files that share the same name as their parent folder. +applyTo: "**/*.cpp,**/*.h" +--- + + +### Mandatory Lifecycle Methods + +Every plugin must implement: + +- Initialize(IShell* service) → Called when the plugin is activated. + +- Deinitialize(IShell* service) → Called when the plugin is deactivated. + +### Initialization + +### Requirement + +- Initialize() must handle all setup logic; constructors should remain minimal. +- It must validate inputs and acquire necessary references. + +### Example + +```cpp +const string HdcpProfile::Initialize(PluginHost::IShell* service) { + ..... + if (_hdcpProfile != nullptr) { + ... + Exchange::IConfiguration* configure = _hdcpProfile->QueryInterface(); + ... + } + .... +} +``` + +- Plugin should register your listener object twice: + + - Framework Service (_service): Use _service->Register(listener) to receive general plugin state change notifications (like ACTIVATED/DEACTIVATED). + + Example: _service->Register(&_hdcpProfileNotification); + + - Target Plugin Interface (_hdcpProfile): Use _hdcpProfile->Register(listener) to receive the plugin's specific custom events (e.g., onProfileChanged).This registration serves as the internal bridge that captures C++ events from the implementation, allowing the plugin to translate and broadcast them as JSON-RPC notifications to external subscribers. + + Example: _hdcpProfile->Register(&_hdcpProfileNotification); + +- It must return a non-empty string on failure with a clear error message. + +**Example:** + +```cpp +const string HdcpProfile::Initialize(PluginHost::IShell* service) { + ... + message = _T("HdcpProfile could not be configured"); + ... + message = _T("HdcpProfile implementation did not provide a configuration interface"); + ... + message = _T("HdcpProfile plugin could not be initialized"); + ... +} +``` + +- Threads or async tasks should be started here if needed, with proper tracking. + +**Example:** + +```cpp +Core::hresult NativeJSImplementation::Initialize(string waylandDisplay) +{ + std::cout << "initialize called on nativejs implementation " << std::endl; + mRenderThread = std::thread([=](std::string waylandDisplay) { + mNativeJSRenderer = std::make_shared(waylandDisplay); + mNativeJSRenderer->run(); + std::cout << "After launch application execution ... " << std::endl; + mNativeJSRenderer.reset(); + }, waylandDisplay); + return (Core::ERROR_NONE); +} +``` + +- Before executing Initialize, ensure all private member variables are in a reset state (either initialized by the constructor or cleared by a prior Deinitialize). Validate this by asserting their default values. + +**Example:** + +```cpp +const string HdcpProfile::Initialize(PluginHost::IShell *service) +{ + ASSERT(_server == nullptr); + ASSERT(_impl == nullptr); + ASSERT(_connectionId == 0); +} +``` + +- If a plugin needs to keep the `IShell` pointer beyond the scope of `Initialize()` (for example, by storing it in a member variable to access other plugins via COM-RPC or JSON-RPC throughout the plugin's lifecycle), then it **must** call `AddRef()` on the service instance before storing it, to increment its reference count. If the plugin only uses the `service` pointer within `Initialize()` and does not store it for later use, then `AddRef()` **must not** be called on the `IShell` instance. + +**Example:** + +```cpp +const string HdcpProfile::Initialize(PluginHost::IShell *service) +{ + ... + _service = service; + _service->AddRef(); + // _service will be used to access other plugins via COM-RPC or JSON-RPC in later methods. + ... +} +``` + +- Only one Initialize() method must exist — avoid overloads or split logic. + +### Deinitialize and Cleanup + +### Requirement + +- Deinitialize() must clean up all resources acquired during Initialize(). It must release resources in reverse order of initialization. +- Every pointer or instance must be checked for nullptr before cleanup. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + if (_service != nullptr) { + _service->Release(); + _service = nullptr; + } + ... +} +``` + +- All acquired interfaces must be explicitly Released(). + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + if (_hdcpProfile != nullptr) { + .... + // Release interface + RPC::IRemoteConnection* connection = service->RemoteConnection(_connectionId); + connection->Terminate(); + connection->Release(); + .... + } + ... +} +``` + +- Unregister your listener from both the Target Plugin interface and the Framework Shell before releasing the pointers. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + // 1. Unregister from the Target Plugin (stops custom events) + if (_hdcpProfile != nullptr) { + _hdcpProfile->Unregister(&_hdcpProfileNotification); + } + // 2. Unregister from the Framework Shell (stops state change events) + if (_service != nullptr) { + _service->Unregister(&_hdcpProfileNotification); + } + ... +} +``` + +- Remote connections must be terminated after releasing plugin references. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + if (_hdcpProfile != nullptr) { + .... + if (nullptr != connection) { + // Trigger the cleanup sequence for out-of-process code, + // which ensures that unresponsive processes are terminated + // if they do not stop gracefully. + connection->Terminate(); + connection->Release(); + } + .... + } +} +``` + +- Threads must be joined or safely terminated. + +**Example:** + +```cpp +Core::hresult NativeJSImplementation::Deinitialize() { + LOGINFO("deinitializing NativeJS process"); + if (mNativeJSRenderer) { + mNativeJSRenderer->terminate(); + if (mRenderThread.joinable()) { + mRenderThread.join(); + } + } + return (Core::ERROR_NONE); +} +``` + +- Internal state (e.g., _connectionId, _service) and private members should be reset to their default state. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + if (connection != nullptr) { + connection->Terminate(); + connection->Release(); + } + ... + if (_service != nullptr) { + _service->Release(); + _service = nullptr; + } +} +``` + +- If AddRef() was called on the IShell instance in Initialize(), then it should call Release() on the IShell instance to decrement its reference count. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + if (_service != nullptr) { + _service->Release(); + _service = nullptr; + } + ... +} +``` + +- All cleanup steps should be logged for traceability. + +**Example:** + +```cpp +void HdcpProfile::Deinitialize(PluginHost::IShell* service) { + ... + SYSLOG(Logging::Shutdown, (_T("HdcpProfile de-initialized"))); + ... +} +``` + + +### Deactivated + +Each plugin should implement the deactivated method. In Deactivated, it should be checked if remote connectionId matches your plugin's connectionId. If it matches your plugin's connectionId, the plugin should submit a deactivation job to handle the out-of-process failure gracefully. + +### Example + +```cpp +void XCast::Deactivated(RPC::IRemoteConnection *connection) diff --git a/.github/instructions/Pluginmodule.instructions.md b/.github/instructions/Pluginmodule.instructions.md new file mode 100644 index 0000000..b82510f --- /dev/null +++ b/.github/instructions/Pluginmodule.instructions.md @@ -0,0 +1,34 @@ +--- +applyTo: "**/Module.cpp,**/Module.h" +--- + + +### Module Name Convention + +### Requirement + +- Every plugin must define MODULE_NAME because Thunder uses it to identify the plugin. +- Every plugin must also define MODULE_NAME_DECLARATION() macro since it generates identifiers such as the module name string, SHA value, and version for the module, enabling the system to recognize and link it. +- The MODULE_NAME should always start with the prefix Plugin_. + +### Example + +1. In Module.h: + + ```cpp + // Rest of the code + #ifndef MODULE_NAME + #define MODULE_NAME Plugin_IOController + #endif + // Rest of the code + ``` + +2. In Module.cpp: + + ```cpp + #include "Module.h" + + MODULE_NAME_DECLARATION(BUILD_REFERENCE) + + // Rest of the code + ``` diff --git a/.github/workflows/L1-tests.yml b/.github/workflows/L1-tests.yml new file mode 100644 index 0000000..0053e4c --- /dev/null +++ b/.github/workflows/L1-tests.yml @@ -0,0 +1,782 @@ +permissions: + contents: read +name: L1-tests + +on: + workflow_call: + inputs: + caller_source: + description: "Specifies the source type (e.g., local or test framework) for the workflow." + required: true + type: string + secrets: + RDKCM_RDKE: + required: true + +env: + BUILD_TYPE: Debug + THUNDER_REF: "R4.4.1" + INTERFACES_REF: "feature/RDKEMW-6078_DeviceSettingsInterface" + AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME}} + AUTOMATICS_PASSCODE: ${{ secrets. AUTOMATICS_PASSCODE}} + +jobs: + L1-tests: + name: Build and run unit tests + runs-on: ubuntu-22.04 + strategy: + matrix: + compiler: [ gcc, clang ] + coverage: [ with-coverage, without-coverage ] + exclude: + - compiler: clang + coverage: with-coverage + - compiler: clang + coverage: without-coverage + - compiler: gcc + coverage: without-coverage + + steps: + - name: Set up cache + # Cache Thunder/ThunderInterfaces. + # https://github.com/actions/cache + # https://docs.github.com/en/rest/actions/cache + # Modify the key if changing the list. + if: ${{ !env.ACT }} + id: cache + uses: actions/cache@v3 + with: + path: | + build/Thunder + build/entservices-apis + build/entservices-helpers + build/ThunderTools + install + !install/etc/WPEFramework/plugins + !install/usr/bin/RdkServicesTest + !install/usr/include/gmock + !install/usr/include/gtest + !install/usr/lib/libgmockd.a + !install/usr/lib/libgmock_maind.a + !install/usr/lib/libgtestd.a + !install/usr/lib/libgtest_maind.a + !install/usr/lib/cmake/GTest + !install/usr/lib/pkgconfig/gmock.pc + !install/usr/lib/pkgconfig/gmock_main.pc + !install/usr/lib/pkgconfig/gtest.pc + !install/usr/lib/pkgconfig/gtest_main.pc + !install/usr/lib/wpeframework/plugins + key: ${{ runner.os }}-${{ env.THUNDER_REF }}-${{ env.INTERFACES_REF }}-5 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: ACK External Trigger + run: | + echo "Message: External Trigger Received for L1 Tests" + echo "Trigger Source: ${{ inputs.caller_source }}" + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + + - name: Install packages + run: > + sudo apt update + && + sudo apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libglib2.0-dev pkg-config + + - name: Install GStreamer + run: | + sudo apt update + sudo apt install -y libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + + - name: Build trower-base64 + run: | + if [ ! -d "trower-base64" ]; then + git clone https://github.com/xmidt-org/trower-base64.git + fi + cd trower-base64 + meson setup --warnlevel 3 --werror build + ninja -C build + sudo ninja -C build install + + - name: Checkout Thunder + uses: actions/checkout@v3 + with: + repository: rdkcentral/Thunder + path: Thunder + ref: ${{env.THUNDER_REF}} + + - name: Checkout ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: rdkcentral/ThunderTools + path: ThunderTools + ref: R4.4.3 + + - name: Checkout entservices-testframework + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-testframework + path: entservices-testframework + ref: 1.0.14 + + - name: Checkout rdk-halif-device_settings + uses: actions/checkout@v3 + with: + repository: rdkcentral/rdk-halif-device_settings + path: rdk-halif-device_settings + ref: main + + - name: Checkout devicesettings + uses: actions/checkout@v3 + with: + repository: rdkcentral/devicesettings + path: devicesettings + ref: main + + - name: Checkout iarmbus + uses: actions/checkout@v3 + with: + repository: rdkcentral/iarmbus + path: iarmbus + ref: develop + + - name: Checkout iarmmgrs + uses: actions/checkout@v3 + with: + repository: rdkcentral/iarmmgrs + path: iarmmgrs + ref: main + + - name: Checkout entservices-helpers + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-helpers + path: entservices-helpers + ref: DeviceSetting_Plugin + + - name: Create parent helpers compatibility path + run: > + if [ ! -e "$GITHUB_WORKSPACE/../entservices-helpers" ]; then ln -s "$GITHUB_WORKSPACE/entservices-helpers" "$GITHUB_WORKSPACE/../entservices-helpers"; fi + + - name: Checkout entservices-devicesettings + if: ${{ inputs.caller_source == 'local' }} + uses: actions/checkout@v3 + with: + path: entservices-devicesettings + + - name: Checkout entservices-devicesettings-testframework + if: ${{ inputs.caller_source == 'testframework' }} + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-devicesettings + path: entservices-devicesettings + ref: develop + + - name: Checkout googletest + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: google/googletest + path: googletest + ref: v1.15.0 + + - name: Apply patches ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd $GITHUB_WORKSPACE/ThunderTools + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch + cd - + + - name: Build ThunderTools + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/ThunderTools" + -B build/ThunderTools + -DEXCEPTIONS_ENABLE=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + && + cmake --build build/ThunderTools -j8 + && + cmake --install build/ThunderTools + + - name: Apply patches Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: | + cd $GITHUB_WORKSPACE/Thunder + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/error_code_R4_4.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/1004-Add-support-for-project-dir.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch + cd - + + - name: Build Thunder + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/Thunder" + -B build/Thunder + -DMESSAGING=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBINDING=127.0.0.1 + -DPORT=55555 + -DEXCEPTIONS_ENABLE=ON + && + cmake --build build/Thunder -j8 + && + cmake --install build/Thunder + + - name: Checkout entservices-apis + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-apis + path: entservices-apis + ref: ${{env.INTERFACES_REF}} + #token : ${{ secrets.RDKCM_RDKE }} + + - name: Remove DTV.json + run: rm -rf $GITHUB_WORKSPACE/entservices-apis/jsonrpc/DTV.json + + - name: Build entservices-apis + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/entservices-apis" + -B build/entservices-apis + -DEXCEPTIONS_ENABLE=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + && + cmake --build build/entservices-apis -j8 + && + cmake --install build/entservices-apis + + - name: Build entservices-helpers + run: > + mkdir -p "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus" + && + touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus/libIARM.h" + && + touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus/libIBus.h" + && + touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/iarm.h" + && + cmake -G Ninja + -S "$GITHUB_WORKSPACE/entservices-helpers" + -B build/entservices-helpers + -DEXCEPTIONS_ENABLE=ON + -DCOMCAST_CONFIG=OFF + -DPLUGIN_HELPERS=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + "-DCMAKE_CXX_FLAGS= + -I$GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I$GITHUB_WORKSPACE/entservices-testframework/Tests/mocks + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/tr181api.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Iarm.h" + && + cmake --build build/entservices-helpers -j8 + && + cmake --install build/entservices-helpers + + - name: Copy DeviceSettings interface headers + run: | + mkdir -p "$GITHUB_WORKSPACE/install/usr/include/WPEFramework/interfaces" + find "$GITHUB_WORKSPACE/entservices-apis/apis/DeviceSettings" -name "IDeviceSettings*.h" -exec cp {} "$GITHUB_WORKSPACE/install/usr/include/WPEFramework/interfaces/" \; 2>/dev/null || true + + - name: Generate external headers + # Empty headers to mute errors + run: > + cd "$GITHUB_WORKSPACE/entservices-testframework/Tests/" + && + mkdir -p + headers + headers/audiocapturemgr + headers/rdk/ds + headers/rdk/iarmbus + headers/rdk/iarmmgrs-hal + headers/rdk/halif/ + headers/rdk/halif/deepsleep-manager + headers/ccec/drivers + headers/network + headers/proc + && + cd headers + && + touch + audiocapturemgr/audiocapturemgr_iarm.h + ccec/drivers/CecIARMBusMgr.h + rdk/ds/audioOutputPort.hpp + rdk/ds/compositeIn.hpp + rdk/ds/dsDisplay.h + rdk/ds/dsError.h + rdk/ds/dsMgr.h + rdk/ds/dsTypes.h + rdk/ds/dsUtl.h + rdk/ds/dsAudio.h + rdk/ds/dsHdmiIn.h + rdk/ds/dsHost.h + rdk/ds/dsFPD.h + rdk/ds/dsFPDTypes.h + rdk/ds/dsCompositeIn.h + rdk/ds/dsCompositeInTypes.h + rdk/ds/dsAVDTypes.h + rdk/ds/dsHdmiInTypes.h + rdk/ds/dsHostTypes.h + rdk/ds/exception.hpp + rdk/ds/hdmiIn.hpp + rdk/ds/host.hpp + rdk/ds/list.hpp + rdk/ds/manager.hpp + rdk/ds/sleepMode.hpp + rdk/ds/videoDevice.hpp + rdk/ds/videoOutputPort.hpp + rdk/ds/videoOutputPortConfig.hpp + rdk/ds/videoOutputPortType.hpp + rdk/ds/videoResolution.hpp + rdk/ds/frontPanelIndicator.hpp + rdk/ds/frontPanelConfig.hpp + rdk/ds/frontPanelTextDisplay.hpp + rdk/ds/audioOutputPortType.hpp + rdk/ds/audioOutputPortConfig.hpp + rdk/ds/pixelResolution.hpp + rdk/iarmbus/libIARM.h + rdk/iarmbus/libIBus.h + rdk/iarmbus/libIBusDaemon.h + rdk/halif/deepsleep-manager/deepSleepMgr.h + rdk/iarmmgrs-hal/mfrMgr.h + rdk/iarmmgrs-hal/sysMgr.h + network/wifiSrvMgrIarmIf.h + network/netsrvmgrIarm.h + libudev.h + rfcapi.h + rbus.h + motionDetector.h + telemetry_busmessage_sender.h + maintenanceMGR.h + pkg.h + edid-parser.hpp + secure_wrapper.h + wpa_ctrl.h + proc/readproc.h + systemaudioplatform.h + gdialservice.h + gdialservicecommon.h + && + cp -r "$GITHUB_WORKSPACE/iarmmgrs/sysmgr/include/." rdk/iarmmgrs-hal/ + && + cp -r "$GITHUB_WORKSPACE/iarmmgrs/mfr/include/." rdk/iarmmgrs-hal/ + && + cp -r /usr/include/gstreamer-1.0/gst /usr/include/glib-2.0/* /usr/lib/x86_64-linux-gnu/glib-2.0/include/* /usr/local/include/trower-base64/base64.h /usr/include/libdrm/drm.h /usr/include/libdrm/drm_mode.h /usr/include/xf86drm.h . + + - name: Set clang toolchain + if: ${{ matrix.compiler == 'clang' }} + run: echo "TOOLCHAIN_FILE=$GITHUB_WORKSPACE/entservices-testframework/Tests/clang.cmake" >> $GITHUB_ENV + + - name: Set gcc/with-coverage toolchain + if: ${{ matrix.compiler == 'gcc' && matrix.coverage == 'with-coverage' && !env.ACT }} + run: echo "TOOLCHAIN_FILE=$GITHUB_WORKSPACE/entservices-testframework/Tests/gcc-with-coverage.cmake" >> $GITHUB_ENV + + - name: Build googletest + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/googletest" + -B build/googletest + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBUILD_GMOCK=ON + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + && + cmake --build build/googletest -j8 + && + cmake --install build/googletest + + - name: Build mocks + run: > + cmake + -S "$GITHUB_WORKSPACE/entservices-testframework/Tests/mocks" + -B build/mocks + -DBUILD_SHARED_LIBS=ON + -DRDK_SERVICES_L1_TEST=ON + -DUSE_THUNDER_R4=ON + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DCMAKE_CXX_FLAGS=" + -fprofile-arcs + -ftest-coverage + -DEXCEPTIONS_ENABLE=ON + -DUSE_THUNDER_R4=ON + -DTHUNDER_VERSION=4 + -DTHUNDER_VERSION_MAJOR=4 + -DTHUNDER_VERSION_MINOR=4 + -DRDK_SERVICES_L1_TEST + -I $GITHUB_WORKSPACE/iarmbus/core/include + -I $GITHUB_WORKSPACE/rdk-halif-device_settings/include + -I $GITHUB_WORKSPACE/devicesettings/rpc/include + -I $GITHUB_WORKSPACE/devicesettings/ds/include + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/audiocapturemgr + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/ds + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec/drivers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/network + -I $GITHUB_WORKSPACE/entservices-testframework/Tests + -I $GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers + -I $GITHUB_WORKSPACE/Thunder/Source + -I $GITHUB_WORKSPACE/Thunder/Source/core + -I $GITHUB_WORKSPACE/install/usr/include + -I ./usr/include/libdrm + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Rfc.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/RBus.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Telemetry.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Udev.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/maintenanceMGR.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/pkg.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/secure_wrappermock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/gdialservice.h + --coverage + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format= + -Wl,-wrap,system -Wl,-wrap,popen -Wl,-wrap,syslog -Wl,-wrap,v_secure_system -Wl,-wrap,v_secure_popen -Wl,-wrap,v_secure_pclose -Wl,-wrap,unlink + -DENABLE_TELEMETRY_LOGGING + -DUSE_IARMBUS + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK + -DENABLE_DEEP_SLEEP + -DENABLE_SET_WAKEUP_SRC_CONFIG + -DENABLE_THERMAL_PROTECTION + -DUSE_DRM_SCREENCAPTURE + -DHAS_API_SYSTEM + -DHAS_API_POWERSTATE + -DHAS_RBUS + -DENABLE_DEVICE_MANUFACTURER_INFO" + && + cmake --build build/mocks -j8 + && + cmake --install build/mocks + + - name: Build entservices-devicesettings + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/entservices-devicesettings" + -B build/entservices-devicesettings + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" + -fprofile-arcs + -ftest-coverage + -DEXCEPTIONS_ENABLE=ON + -DUSE_THUNDER_R4=ON + -DTHUNDER_VERSION=4 + -DTHUNDER_VERSION_MAJOR=4 + -DTHUNDER_VERSION_MINOR=4 + -DRDK_SERVICES_L1_TEST + -I $GITHUB_WORKSPACE/entservices-devicesettings/plugin + -I $GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers + -I $GITHUB_WORKSPACE/iarmbus/core/include + -I $GITHUB_WORKSPACE/rdk-halif-device_settings/include + -I $GITHUB_WORKSPACE/devicesettings/rpc/include + -I $GITHUB_WORKSPACE/devicesettings/ds/include + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/audiocapturemgr + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/ds + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec/drivers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/network + -I $GITHUB_WORKSPACE/entservices-testframework/Tests + -I $GITHUB_WORKSPACE/Thunder/Source + -I $GITHUB_WORKSPACE/Thunder/Source/core + -I $GITHUB_WORKSPACE/install/usr/include + -I $GITHUB_WORKSPACE/install/usr/include/WPEFramework + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Rfc.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/RBus.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Telemetry.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Udev.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/maintenanceMGR.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/pkg.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/secure_wrappermock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/gdialservice.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/MotionDetection.h + --coverage + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format= + -Wl,-wrap,system -Wl,-wrap,popen -Wl,-wrap,syslog -Wl,-wrap,v_secure_system -Wl,-wrap,v_secure_popen -Wl,-wrap,v_secure_pclose -Wl,-wrap,unlink + -DENABLE_TELEMETRY_LOGGING + -DUSE_IARMBUS + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK + -DENABLE_DEEP_SLEEP + -DENABLE_SET_WAKEUP_SRC_CONFIG + -DENABLE_THERMAL_PROTECTION + -DUSE_DRM_SCREENCAPTURE + -DHAS_API_SYSTEM + -DHAS_API_POWERSTATE + -DHAS_RBUS + -DCLOCK_BRIGHTNESS_ENABLED + -DUSE_DS + -DENABLE_DEVICE_MANUFACTURER_INFO" + -DCOMCAST_CONFIG=OFF + -DCMAKE_DISABLE_FIND_PACKAGE_DS=ON + -DCMAKE_DISABLE_FIND_PACKAGE_IARMBus=ON + -DCMAKE_DISABLE_FIND_PACKAGE_Udev=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RFC=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RBus=ON + -DCMAKE_BUILD_TYPE=Debug + -DDS_FOUND=ON + -DHAS_FRONT_PANEL=ON + -DPLUGIN_DEVICESETTINGS=ON + -DRDK_SERVICES_L1_TEST=ON + -DUSE_THUNDER_R4=ON + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + -DWPEFrameworkHelpers_INCLUDE_DIRS=$GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers + && + cmake --build build/entservices-devicesettings -j8 + && + cmake --install build/entservices-devicesettings + + - name: Build entservices-testframework + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/entservices-testframework" + -B build/entservices-testframework + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DCMAKE_CXX_FLAGS=" + -fprofile-arcs + -ftest-coverage + -DEXCEPTIONS_ENABLE=ON + -DUSE_THUNDER_R4=ON + -DTHUNDER_VERSION=4 + -DTHUNDER_VERSION_MAJOR=4 + -DTHUNDER_VERSION_MINOR=4 + -DRDK_SERVICES_L1_TEST + -I $GITHUB_WORKSPACE/iarmbus/core/include + -I $GITHUB_WORKSPACE/rdk-halif-device_settings/include + -I $GITHUB_WORKSPACE/devicesettings/rpc/include + -I $GITHUB_WORKSPACE/devicesettings/ds/include + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/audiocapturemgr + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/ds + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec/drivers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/network + -I $GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests + -I $GITHUB_WORKSPACE/Thunder/Source + -I $GITHUB_WORKSPACE/Thunder/Source/core + -I $GITHUB_WORKSPACE/install/usr/include + -I $GITHUB_WORKSPACE/install/usr/include/WPEFramework + -I ./usr/include/libdrm + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Rfc.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/RBus.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Telemetry.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Udev.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/maintenanceMGR.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/pkg.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/secure_wrappermock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/gdialservice.h + --coverage + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format= + -Wl,-wrap,system -Wl,-wrap,popen -Wl,-wrap,syslog -Wl,--no-as-needed + -DENABLE_TELEMETRY_LOGGING + -DUSE_IARMBUS + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK + -DENABLE_DEEP_SLEEP + -DENABLE_SET_WAKEUP_SRC_CONFIG + -DENABLE_THERMAL_PROTECTION + -DUSE_DRM_SCREENCAPTURE + -DHAS_API_SYSTEM + -DHAS_API_POWERSTATE + -DHAS_RBUS + -DCLOCK_BRIGHTNESS_ENABLED + -DUSE_DS + -DENABLE_DEVICE_MANUFACTURER_INFO" + -DCOMCAST_CONFIG=OFF + -DCMAKE_DISABLE_FIND_PACKAGE_DS=ON + -DCMAKE_DISABLE_FIND_PACKAGE_IARMBus=ON + -DCMAKE_DISABLE_FIND_PACKAGE_Udev=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RFC=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RBus=ON + -DCMAKE_BUILD_TYPE=Debug + -DDS_FOUND=ON + -DHAS_FRONT_PANEL=ON + -DPLUGIN_DEVICESETTINGS=ON + -DRDK_SERVICES_L1_TEST=ON + -DUSE_THUNDER_R4=ON + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + && + cmake --build build/entservices-testframework -j8 + && + cmake --install build/entservices-testframework + + - name: Set up files + run: > + sudo mkdir -p -m 777 + /tmp/test/testApp/etc/apps + /opt/persistent + /opt/secure + /opt/secure/reboot + /opt/secure/persistent + /opt/secure/persistent/System + /opt/logs + /lib/rdk + /run/media/sda1/logs/PreviousLogs + /run/sda1/UsbTestFWUpdate + /run/sda1/UsbProdFWUpdate + /run/sda2 + /var/run/wpa_supplicant + /tmp/bus/usb/devices/100-123 + /tmp/bus/usb/devices/101-124 + /tmp/block/sda/device + /tmp/block/sdb/device + /dev/disk/by-id + /dev + && + if [ ! -f mknod /dev/sda c 240 0 ]; then mknod /dev/sda c 240 0; fi && + if [ ! -f mknod /dev/sda1 c 240 0 ]; then mknod /dev/sda1 c 240 0; fi && + if [ ! -f mknod /dev/sda2 c 240 0 ]; then mknod /dev/sda2 c 240 0; fi && + if [ ! -f mknod /dev/sdb c 240 0 ]; then mknod /dev/sdb c 240 0; fi && + if [ ! -f mknod /dev/sdb1 c 240 0 ]; then mknod /dev/sdb1 c 240 0; fi && + if [ ! -f mknod /dev/sdb2 c 240 0 ]; then mknod /dev/sdb2 c 240 0; fi + && + sudo touch + /tmp/test/testApp/etc/apps/testApp_package.json + /opt/rdk_maintenance.conf + /opt/persistent/timeZoneDST + /opt/standbyReason.txt + /opt/tmtryoptout + /opt/fwdnldstatus.txt + /opt/dcm.properties + /etc/device.properties + /etc/dcm.properties + /etc/authService.conf + /version.txt + /run/media/sda1/logs/PreviousLogs/logFile.txt + /run/sda1/HSTP11MWR_5.11p5s1_VBN_sdy.bin + /run/sda1/UsbTestFWUpdate/HSTP11MWR_3.11p5s1_VBN_sdy.bin + /run/sda1/UsbProdFWUpdate/HSTP11MWR_4.11p5s1_VBN_sdy.bin + /lib/rdk/getMaintenanceStartTime.sh + /tmp/opkg.conf + /tmp/bus/usb/devices/100-123/serial + /tmp/bus/usb/devices/101-124/serial + /tmp/block/sda/device/vendor + /tmp/block/sda/device/model + /tmp/block/sdb/device/vendor + /tmp/block/sdb/device/model + && + sudo chmod -R 777 + /opt/rdk_maintenance.conf + /opt/persistent/timeZoneDST + /opt/standbyReason.txt + /opt/tmtryoptout + /opt/fwdnldstatus.txt + /opt/dcm.properties + /etc/device.properties + /etc/dcm.properties + /etc/authService.conf + /version.txt + /lib/rdk/getMaintenanceStartTime.sh + /tmp/opkg.conf + /tmp/bus/usb/devices/100-123/serial + /tmp/block/sda/device/vendor + /tmp/block/sda/device/model + /tmp/bus/usb/devices/101-124/serial + /tmp/block/sdb/device/vendor + /tmp/block/sdb/device/model + && + cd /dev/disk/by-id/ + && + sudo ln -s ../../sda /dev/disk/by-id/usb-Generic_Flash_Disk_B32FD507-0 + && + sudo ln -s ../../sdb /dev/disk/by-id/usb-JetFlash_Transcend_16GB_UEUIRCXT-0 + && + ls -l /dev/disk/by-id/usb-Generic_Flash_Disk_B32FD507-0 + && + ls -l /dev/disk/by-id/usb-JetFlash_Transcend_16GB_UEUIRCXT-0 + + - name: Run unit tests without valgrind + run: > + PATH=$GITHUB_WORKSPACE/install/usr/bin:${PATH} + LD_LIBRARY_PATH=$GITHUB_WORKSPACE/install/usr/lib:$GITHUB_WORKSPACE/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + GTEST_OUTPUT="json:$(pwd)/rdkL1TestResults.json" + RdkServicesL1Test && + cp -rf $(pwd)/rdkL1TestResults.json $GITHUB_WORKSPACE/rdkL1TestResultsWithoutValgrind.json && + rm -rf $(pwd)/rdkL1TestResults.json + + - name: Run unit tests with valgrind + if: ${{ !env.ACT }} + run: > + PATH=$GITHUB_WORKSPACE/install/usr/bin:${PATH} + LD_LIBRARY_PATH=$GITHUB_WORKSPACE/install/usr/lib:$GITHUB_WORKSPACE/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + GTEST_OUTPUT="json:$(pwd)/rdkL1TestResults.json" + valgrind + --tool=memcheck + --log-file=valgrind_log + --leak-check=yes + --show-reachable=yes + --track-fds=yes + --fair-sched=try + RdkServicesL1Test && + cp -rf $(pwd)/rdkL1TestResults.json $GITHUB_WORKSPACE/rdkL1TestResultsWithValgrind.json && + rm -rf $(pwd)/rdkL1TestResults.json + + - name: Generate coverage + if: ${{ matrix.coverage == 'with-coverage' && !env.ACT }} + run: > + cp $GITHUB_WORKSPACE/entservices-testframework/Tests/L1Tests/.lcovrc_l1 ~/.lcovrc + && + lcov -c + -o coverage.info + -d build/entservices-devicesettings + -d build/mocks + -d build/entservices-testframework + -d $GITHUB_WORKSPACE + && + lcov + -r coverage.info + '/usr/include/*' + '*/build/entservices-devicesettings/_deps/*' + '*/install/usr/include/*' + '*/Tests/headers/*' + '*/Tests/mocks/*' + '*/Tests/L1Tests/tests/*' + '*/Thunder/*' + -o filtered_coverage.info + && + genhtml + -o coverage + -t "entservices-devicesettings coverage" + filtered_coverage.info + + - name: Upload artifacts + if: ${{ !env.ACT }} + uses: actions/upload-artifact@v4 + with: + name: artifacts-L1-devicesettings + path: | + coverage/ + valgrind_log + rdkL1TestResultsWithoutValgrind.json + rdkL1TestResultsWithValgrind.json + if-no-files-found: warn diff --git a/.github/workflows/L2-tests.yml b/.github/workflows/L2-tests.yml new file mode 100644 index 0000000..d3adcad --- /dev/null +++ b/.github/workflows/L2-tests.yml @@ -0,0 +1,651 @@ +name: L2-tests + +on: + workflow_call: + inputs: + caller_source: + description: "Specifies the source type (e.g., local or test framework) for the workflow." + required: true + type: string + secrets: + RDKCM_RDKE: + required: true + +env: + BUILD_TYPE: Debug + THUNDER_REF: "R4.4.1" + INTERFACES_REF: "develop" + AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME}} + AUTOMATICS_PASSCODE: ${{ secrets. AUTOMATICS_PASSCODE}} + RDK_SERVICE_L2_TEST: "OFF" + +jobs: + L2-tests: + name: Build and run L2 tests + runs-on: ubuntu-22.04 + strategy: + matrix: + compiler: [ gcc, clang ] + coverage: [ with-coverage, without-coverage ] + exclude: + - compiler: clang + coverage: with-coverage + - compiler: clang + coverage: without-coverage + - compiler: gcc + coverage: without-coverage + + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - run: pip install jsonref + + - name: ACK External Trigger + run: | + echo "Message: External Trigger Received for L2 Tests" + echo "Trigger Source: ${{ inputs.caller_source }}" + + - name: Set up CMake + uses: jwlawson/actions-setup-cmake@v1.13 + with: + cmake-version: '3.16.x' + + - name: Install packages + run: > + sudo apt update + && + sudo apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libdbus-1-dev + + - name: Install GStreamer + run: | + sudo apt update + sudo apt install -y libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev + + - name: Build trower-base64 + run: | + if [ ! -d "trower-base64" ]; then + git clone https://github.com/xmidt-org/trower-base64.git + fi + cd trower-base64 + meson setup --warnlevel 3 --werror build + ninja -C build + sudo ninja -C build install + + - name: Checkout Thunder + uses: actions/checkout@v3 + with: + repository: rdkcentral/Thunder + path: Thunder + ref: ${{env.THUNDER_REF}} + + - name: Checkout ThunderTools + uses: actions/checkout@v3 + with: + repository: rdkcentral/ThunderTools + path: ThunderTools + ref: R4.4.3 + + - name: Checkout entservices-devicesettings + if: ${{ inputs.caller_source == 'local' }} + uses: actions/checkout@v3 + with: + path: entservices-devicesettings + + - name: Checkout entservices-devicesettings-testframework + if: ${{ inputs.caller_source == 'testframework' }} + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-devicesettings + path: entservices-devicesettings + ref: develop + + - name: Checkout entservices-testframework + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-testframework + path: entservices-testframework + ref: 1.0.1 + + - name: Checkout googletest + if: steps.cache.outputs.cache-hit != 'true' + uses: actions/checkout@v3 + with: + repository: google/googletest + path: googletest + ref: v1.15.0 + + - name: Apply patches ThunderTools + run: | + cd $GITHUB_WORKSPACE/ThunderTools + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch + cd - + + - name: Build ThunderTools + run: > + cmake + -S "$GITHUB_WORKSPACE/ThunderTools" + -B build/ThunderTools + -DEXCEPTIONS_ENABLE=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + && + cmake --build build/ThunderTools -j8 + && + cmake --install build/ThunderTools + + - name: Apply patches Thunder + run: | + cd $GITHUB_WORKSPACE/Thunder + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/error_code_R4_4.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/1004-Add-support-for-project-dir.patch + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch + cd - + + - name: Build Thunder + run: > + cmake + -S "$GITHUB_WORKSPACE/Thunder" + -B build/Thunder + -DMESSAGING=ON + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DBUILD_TYPE=${{env.BUILD_TYPE}} + -DBINDING=127.0.0.1 + -DPORT=9998 + -DEXCEPTIONS_ENABLE=ON + && + cmake --build build/Thunder -j8 + && + cmake --install build/Thunder + + - name: Checkout entservices-apis + uses: actions/checkout@v3 + with: + repository: rdkcentral/entservices-apis + path: entservices-apis + ref: ${{env.INTERFACES_REF}} + run: rm -rf $GITHUB_WORKSPACE/entservices-apis/jsonrpc/DTV.json + + - name: Apply patches entservices-apis + run: | + cd $GITHUB_WORKSPACE/entservices-apis + patch -p1 < $GITHUB_WORKSPACE/entservices-testframework/patches/RDKEMW-1007.patch + cd - + + - name: Build entservices-apis + run: > + cmake + -S "$GITHUB_WORKSPACE/entservices-apis" + -B build/entservices-apis + -DEXCEPTIONS_ENABLE=ON + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + && + cmake --build build/entservices-apis -j8 + && + cmake --install build/entservices-apis + + - name: Generate external headers + # Empty headers to mute errors + run: > + cd "$GITHUB_WORKSPACE/entservices-testframework/Tests/" + && + mkdir -p + headers + headers/audiocapturemgr + headers/rdk/ds + headers/rdk/iarmbus + headers/rdk/iarmmgrs-hal + headers/rdk/halif/ + headers/rdk/halif/deepsleep-manager + headers/ccec/drivers + headers/network + headers/proc + && + cd headers + && + touch + audiocapturemgr/audiocapturemgr_iarm.h + ccec/drivers/CecIARMBusMgr.h + rdk/ds/audioOutputPort.hpp + rdk/ds/compositeIn.hpp + rdk/ds/dsDisplay.h + rdk/ds/dsError.h + rdk/ds/dsMgr.h + rdk/ds/dsTypes.h + rdk/ds/dsUtl.h + rdk/ds/exception.hpp + rdk/ds/hdmiIn.hpp + rdk/ds/host.hpp + rdk/ds/list.hpp + rdk/ds/manager.hpp + rdk/ds/sleepMode.hpp + rdk/ds/videoDevice.hpp + rdk/ds/videoOutputPort.hpp + rdk/ds/videoOutputPortConfig.hpp + rdk/ds/videoOutputPortType.hpp + rdk/ds/videoResolution.hpp + rdk/ds/frontPanelIndicator.hpp + rdk/ds/frontPanelConfig.hpp + rdk/ds/frontPanelTextDisplay.hpp + rdk/ds/audioOutputPortType.hpp + rdk/ds/audioOutputPortConfig.hpp + rdk/ds/pixelResolution.hpp + rdk/iarmbus/libIARM.h + rdk/iarmbus/libIBus.h + rdk/iarmbus/libIBusDaemon.h + rdk/halif/deepsleep-manager/deepSleepMgr.h + rdk/iarmmgrs-hal/mfrMgr.h + rdk/iarmmgrs-hal/sysMgr.h + network/wifiSrvMgrIarmIf.h + network/netsrvmgrIarm.h + libudev.h + rfcapi.h + rbus.h + motionDetector.h + telemetry_busmessage_sender.h + maintenanceMGR.h + pkg.h + edid-parser.hpp + secure_wrapper.h + wpa_ctrl.h + proc/readproc.h + systemaudioplatform.h + gdialservice.h + gdialservicecommon.h + rdk/ds/audioOutputPort.hpp + rdk/ds/audioOutputPortType.hpp + rdk/ds/AudioStereoMode.hpp + rdk/ds/VideoDFC.hpp + && + cp -r /usr/include/gstreamer-1.0/gst /usr/include/glib-2.0/* /usr/lib/x86_64-linux-gnu/glib-2.0/include/* /usr/local/include/trower-base64/base64.h /usr/include/libdrm/drm.h /usr/include/libdrm/drm_mode.h /usr/include/xf86drm.h . + + - name: Set clang toolchain + if: ${{ matrix.compiler == 'clang' }} + run: echo "TOOLCHAIN_FILE=$GITHUB_WORKSPACE/entservices-testframework/Tests/clang.cmake" >> $GITHUB_ENV + + - name: Set gcc/with-coverage toolchain + if: ${{ matrix.compiler == 'gcc' && matrix.coverage == 'with-coverage' && !env.ACT }} + run: echo "TOOLCHAIN_FILE=$GITHUB_WORKSPACE/entservices-testframework/Tests/gcc-with-coverage.cmake" >> $GITHUB_ENV + + - name: Build googletest + if: steps.cache.outputs.cache-hit != 'true' + run: > + cmake -G Ninja + -S "$GITHUB_WORKSPACE/googletest" + -B build/googletest + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DBUILD_TYPE=Debug + -DBUILD_GMOCK=ON + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_POSITION_INDEPENDENT_CODE=ON + && + cmake --build build/googletest -j8 + && + cmake --install build/googletest + + - name: Build mocks + run: > + cmake + -S "$GITHUB_WORKSPACE/entservices-testframework/Tests/mocks" + -B build/mocks + -DBUILD_SHARED_LIBS=ON + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DCMAKE_CXX_FLAGS=" + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/install/usr/include" + && + cmake --build build/mocks -j8 + && + cmake --install build/mocks + + - name: Build entservices-devicesettings + run: > + cmake + -S "$GITHUB_WORKSPACE/entservices-devicesettings" + -B build/entservices-devicesettings + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + -DCMAKE_CXX_FLAGS=" + -DEXCEPTIONS_ENABLE=ON + -fprofile-arcs + -ftest-coverage + -DUSE_THUNDER_R4=ON + -DTHUNDER_VERSION=4 + -DTHUNDER_VERSION_MAJOR=4 + -DTHUNDER_VERSION_MINOR=4 + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/audiocapturemgr + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/ds + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec/drivers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/network + -I $GITHUB_WORKSPACE/entservices-testframework/Tests + -I $GITHUB_WORKSPACE/install/usr/include + -I $GITHUB_WORKSPACE/install/usr/include/WPEFramework + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/devicesettings.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Iarm.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Rfc.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/RBus.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Telemetry.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Udev.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/maintenanceMGR.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/pkg.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/secure_wrappermock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/readprocMockInterface.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/gdialservice.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/MotionDetection.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/dsFPD.h + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format= + -DUSE_IARMBUS + -DRDK_SERVICE_L2_TEST + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK + -DENABLE_DEEP_SLEEP + -DENABLE_SET_WAKEUP_SRC_CONFIG + -DENABLE_THERMAL_PROTECTION + -DUSE_DRM_SCREENCAPTURE + -DHAS_API_SYSTEM + -DHAS_API_POWERSTATE + -DHAS_RBUS + -DCLOCK_BRIGHTNESS_ENABLED + -DUSE_DS + -DENABLE_DEVICE_MANUFACTURER_INFO" + -DCOMCAST_CONFIG=OFF + -DCMAKE_DISABLE_FIND_PACKAGE_DS=ON + -DCMAKE_DISABLE_FIND_PACKAGE_IARMBus=ON + -DCMAKE_DISABLE_FIND_PACKAGE_Udev=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RFC=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RBus=ON + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DDS_FOUND=ON + -DHAS_FRONT_PANEL=ON + -DPLUGIN_LEDCONTROL=ON + -DPLUGIN_FRONTPANEL=OFF + -DPLUGIN_MOTION_DETECTION=ON + -DRDK_SERVICE_L2_TEST=${{env.RDK_SERVICE_L2_TEST}} + -DPLUGIN_L2Tests=OFF + -DUSE_THUNDER_R4=ON + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + && + cmake --build build/entservices-devicesettings -j8 + && + cmake --install build/entservices-devicesettings + + - name: Build entservices-testframework + run: > + cmake + -S "$GITHUB_WORKSPACE/entservices-testframework" + -B build/entservices-testframework + -DCMAKE_TOOLCHAIN_FILE="${{ env.TOOLCHAIN_FILE }}" + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + -DCMAKE_CXX_FLAGS=" + -DEXCEPTIONS_ENABLE=ON + -fprofile-arcs + -ftest-coverage + -DUSE_THUNDER_R4=ON + -DTHUNDER_VERSION=4 + -DTHUNDER_VERSION_MAJOR=4 + -DTHUNDER_VERSION_MINOR=4 + -DRDK_SERVICE_L2_TEST + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/audiocapturemgr + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/ds + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec/drivers + -I $GITHUB_WORKSPACE/entservices-testframework/Tests/headers/network + -I $GITHUB_WORKSPACE/entservices-devicesettings/helpers + -I $GITHUB_WORKSPACE/install/usr/include + -I $GITHUB_WORKSPACE/install/usr/include/WPEFramework + -I ./usr/include/libdrm + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/devicesettings.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Iarm.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Rfc.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/RBus.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Telemetry.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Udev.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/maintenanceMGR.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/pkg.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/secure_wrappermock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/readprocMockInterface.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/gdialservice.h + -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/dsFPD.h + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format= + -Wl,-wrap,system -Wl,-wrap,syslog -Wl,--no-as-needed + -DENABLE_TELEMETRY_LOGGING + -DUSE_IARMBUS + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK + -DENABLE_DEEP_SLEEP + -DENABLE_SET_WAKEUP_SRC_CONFIG + -DENABLE_THERMAL_PROTECTION + -DUSE_DRM_SCREENCAPTURE + -DHAS_API_SYSTEM + -DHAS_API_POWERSTATE + -DHAS_RBUS + -DCLOCK_BRIGHTNESS_ENABLED + -DUSE_DS + -DENABLE_DEVICE_MANUFACTURER_INFO" + -DCOMCAST_CONFIG=OFF + -DCMAKE_DISABLE_FIND_PACKAGE_DS=ON + -DCMAKE_DISABLE_FIND_PACKAGE_IARMBus=ON + -DCMAKE_DISABLE_FIND_PACKAGE_Udev=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RFC=ON + -DCMAKE_DISABLE_FIND_PACKAGE_RBus=ON + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + -DDS_FOUND=ON + -DHAS_FRONT_PANEL=ON + -DPLUGIN_LEDCONTROL=ON + -DPLUGIN_FRONTPANEL=OFF + -DPLUGIN_MOTION_DETECTION=ON + -DRDK_SERVICE_L2_TEST=${{env.RDK_SERVICE_L2_TEST}} + -DPLUGIN_L2Tests=OFF + -DUSE_THUNDER_R4=ON + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF + && + cmake --build build/entservices-testframework -j8 + && + cmake --install build/entservices-testframework + + - name: Set up files + run: > + sudo mkdir -p -m 777 + /tmp/test/testApp/etc/apps + /opt/persistent + /opt/secure + /opt/secure/reboot + /opt/secure/persistent + /opt/secure/persistent/System + /opt/logs + /lib/rdk + /run/media/sda1/logs/PreviousLogs + /run/sda1/UsbTestFWUpdate + /run/sda1/UsbProdFWUpdate + /run/sda2 + /var/run/wpa_supplicant + /tmp/bus/usb/devices/100-123 + /tmp/bus/usb/devices/101-124 + /tmp/block/sda/device + /tmp/block/sdb/device + /dev/disk/by-id + /dev + && + if [ ! -f mknod /dev/sda c 240 0 ]; then mknod /dev/sda c 240 0; fi && + if [ ! -f mknod /dev/sda1 c 240 0 ]; then mknod /dev/sda1 c 240 0; fi && + if [ ! -f mknod /dev/sda2 c 240 0 ]; then mknod /dev/sda2 c 240 0; fi && + if [ ! -f mknod /dev/sdb c 240 0 ]; then mknod /dev/sdb c 240 0; fi && + if [ ! -f mknod /dev/sdb1 c 240 0 ]; then mknod /dev/sdb1 c 240 0; fi && + if [ ! -f mknod /dev/sdb2 c 240 0 ]; then mknod /dev/sdb2 c 240 0; fi + && + sudo touch + /tmp/test/testApp/etc/apps/testApp_package.json + /opt/rdk_maintenance.conf + /opt/persistent/timeZoneDST + /opt/standbyReason.txt + /opt/tmtryoptout + /opt/fwdnldstatus.txt + /opt/dcm.properties + /etc/device.properties + /etc/dcm.properties + /etc/authService.conf + /version.txt + /run/media/sda1/logs/PreviousLogs/logFile.txt + /run/sda1/HSTP11MWR_5.11p5s1_VBN_sdy.bin + /run/sda1/UsbTestFWUpdate/HSTP11MWR_3.11p5s1_VBN_sdy.bin + /run/sda1/UsbProdFWUpdate/HSTP11MWR_4.11p5s1_VBN_sdy.bin + /lib/rdk/getMaintenanceStartTime.sh + /tmp/opkg.conf + /tmp/bus/usb/devices/100-123/serial + /tmp/bus/usb/devices/101-124/serial + /tmp/block/sda/device/vendor + /tmp/block/sda/device/model + /tmp/block/sdb/device/vendor + /tmp/block/sdb/device/model + && + sudo chmod -R 777 + /opt/rdk_maintenance.conf + /opt/persistent/timeZoneDST + /opt/standbyReason.txt + /opt/tmtryoptout + /opt/fwdnldstatus.txt + /opt/dcm.properties + /etc/device.properties + /etc/dcm.properties + /etc/authService.conf + /version.txt + /lib/rdk/getMaintenanceStartTime.sh + /tmp/opkg.conf + /tmp/bus/usb/devices/100-123/serial + /tmp/block/sda/device/vendor + /tmp/block/sda/device/model + /tmp/bus/usb/devices/101-124/serial + /tmp/block/sdb/device/vendor + /tmp/block/sdb/device/model + && + cd /dev/disk/by-id/ + && + sudo ln -s ../../sda /dev/disk/by-id/usb-Generic_Flash_Disk_B32FD507-0 + && + sudo ln -s ../../sdb /dev/disk/by-id/usb-JetFlash_Transcend_16GB_UEUIRCXT-0 + && + ls -l /dev/disk/by-id/usb-Generic_Flash_Disk_B32FD507-0 + && + ls -l /dev/disk/by-id/usb-JetFlash_Transcend_16GB_UEUIRCXT-0 + + - name: Download pact_verifier_cli + run: | + export PATH="$GITHUB_WORKSPACE/install/usr/bin:${PATH}" + $GITHUB_WORKSPACE/entservices-testframework/Tests/L2Tests/pact/install-verifier-cli.sh + + - name: Run unit tests without valgrind + if: ${{ env.RDK_SERVICE_L2_TEST == 'ON' }} + run: | + PATH=$GITHUB_WORKSPACE/install/usr/bin:${PATH} + LD_LIBRARY_PATH=$GITHUB_WORKSPACE/install/usr/lib:$GITHUB_WORKSPACE/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + GTEST_OUTPUT="json:$(pwd)/rdkL2TestResults.json" + RdkServicesL2Test && + cp -rf $(pwd)/rdkL2TestResults.json $GITHUB_WORKSPACE/rdkL2TestResultsWithoutValgrind.json && + rm -rf $(pwd)/rdkL2TestResults.json + + - name: Run unit tests with valgrind + if: ${{ !env.ACT && env.RDK_SERVICE_L2_TEST == 'ON'}} + run: > + PATH=$GITHUB_WORKSPACE/install/usr/bin:${PATH} + LD_LIBRARY_PATH=$GITHUB_WORKSPACE/install/usr/lib:$GITHUB_WORKSPACE/install/usr/lib/wpeframework/plugins:${LD_LIBRARY_PATH} + GTEST_OUTPUT="json:$(pwd)/rdkL2TestResults.json" + valgrind + --tool=memcheck + --log-file=valgrind_log + --leak-check=yes + --show-reachable=yes + --track-fds=yes + --fair-sched=try + RdkServicesL2Test && + cp -rf $(pwd)/rdkL2TestResults.json $GITHUB_WORKSPACE/rdkL2TestResultsWithValgrind.json && + rm -rf $(pwd)/rdkL2TestResults.json + + - name: Generate coverage + if: ${{ matrix.coverage == 'with-coverage' && !env.ACT && env.RDK_SERVICE_L2_TEST == 'ON'}} + run: > + cp $GITHUB_WORKSPACE/entservices-testframework/Tests/L2Tests/.lcovrc_l2 ~/.lcovrc + && + lcov -c + -o coverage.info + -d build/entservices-devicesettings + && + lcov + -r coverage.info + '/usr/include/*' + '*/build/entservices-devicesettings/_deps/*' + '*/build/entservices-entservices-testframework/_deps/*' + '*/install/usr/include/*' + '*/Tests/headers/*' + '*/Tests/mocks/*' + '*/Tests/L2Tests/*' + '*/googlemock/*' + '*/googletest/*' + '*/sqlite/*' + -o filtered_coverage.info + && + genhtml + -o coverage + -t "entservices-devicesettings coverage" + filtered_coverage.info + + - name: Upload artifacts + if: ${{ !env.ACT && env.RDK_SERVICE_L2_TEST == 'ON'}} + uses: actions/upload-artifact@v4 + with: + name: artifacts-L2-frontpanel + path: | + coverage/ + valgrind_log + rdkL2TestResultsWithoutValgrind.json + rdkL2TestResultsWithValgrind.json + if-no-files-found: warn + + - name: Generate external headers + # Empty headers to mute errors + run: > + cd "$GITHUB_WORKSPACE/entservices-testframework/Tests/" + && + mkdir -p + headers + headers/audiocapturemgr + headers/rdk/ds + headers/rdk/iarmbus + headers/rdk/iarmmgrs-hal + headers/rdk/halif/ + headers/rdk/halif/deepsleep-manager + headers/ccec/drivers + headers/network + headers/proc + && + cd headers + && + touch + audiocapturemgr/audiocapturemgr_iarm.h + ccec/drivers/CecIARMBusMgr.h + rdk/ds/audioOutputPort.hpp + rdk/ds/compositeIn.hpp + rdk/ds/dsDisplay.h + rdk/ds/dsError.h + rdk/ds/dsMgr.h + rdk/ds/dsTypes.h diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index c58b1b0..93872e4 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -1,5 +1,25 @@ name: "CLA" +permissions: + contents: read + pull-requests: write + actions: write + statuses: write + +on: + issue_comment: + types: [created] + pull_request_target: + types: [opened, closed, synchronize] + +jobs: + CLA-Lite: + name: "Signature" + uses: rdkcentral/cmf-actions/.github/workflows/cla.yml@v1 + secrets: + PERSONAL_ACCESS_TOKEN: ${{ secrets.CLA_ASSISTANT }} +name: "CLA" + permissions: contents: read pull-requests: write diff --git a/.github/workflows/component-release.yml b/.github/workflows/component-release.yml new file mode 100644 index 0000000..21a0a3b --- /dev/null +++ b/.github/workflows/component-release.yml @@ -0,0 +1,124 @@ +name: Component Release + +permissions: + contents: write + +on: + pull_request: + types: [opened, edited, ready_for_review, closed] + branches: + - develop + +jobs: + validate-version: + if: ${{ github.event.action == 'opened' || github.event.action == 'edited' || github.event.action == 'ready_for_review' }} + runs-on: ubuntu-latest + steps: + - name: Validate PR description for version field + env: + PR_DESC: ${{ github.event.pull_request.body }} + run: | + if ! echo "$PR_DESC" | grep -qiE 'version[[:space:]]*:[[:space:]]*(major|minor|patch)'; then + echo "ERROR: PR description must include a version field in the format 'version: major|minor|patch' (case-insensitive). Example: version: minor" + exit 1 + fi + echo "Validation passed: version field found." + release: + if: github.event.pull_request.merged == true + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Git + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "187267378+rdkcm-rdke@users.noreply.github.com" + + - name: Install git-flow and auto-changelog + run: | + sudo apt-get update + sudo apt-get install -y git-flow + npm install -g auto-changelog + + - name: Clone the project and start release + run: | + set -e + git clone https://x-access-token:${{ secrets.RDKCM_RDKE }}@github.com/${{ github.repository }} project + cd project + git fetch --all + git checkout main || git checkout -b main origin/main + git checkout develop || git checkout -b develop origin/develop + + git config gitflow.branch.master main + git config gitflow.branch.develop develop + git config gitflow.prefix.feature feature/ + git config gitflow.prefix.bugfix bugfix/ + git config gitflow.prefix.release release/ + git config gitflow.prefix.hotfix hotfix/ + git config gitflow.prefix.support support/ + git config gitflow.prefix.versiontag '' + + echo "git config completed" + # Extract version from PR description + PR_DESC="${{ github.event.pull_request.body }}" + # Get top tag from CHANGELOG.md + TOP_TAG=$(grep -m 1 -oP '^#### \[\K[^\]]+' CHANGELOG.md) + if [[ -z "$TOP_TAG" ]]; then + echo "No version found in CHANGELOG.md!" + exit 1 + fi + # Validate TOP_TAG format (semantic versioning: major.minor.patch) + if [[ ! "$TOP_TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid version format in CHANGELOG.md: $TOP_TAG. Expected format: major.minor.patch" + exit 1 + fi + IFS='.' read -r major minor patch <<< "$TOP_TAG" + VERSION_TYPE=$(echo "$PR_DESC" | grep -oiP 'version\s*:\s*\K(major|minor|patch)' | tr '[:upper:]' '[:lower:]') + if [[ -z "$VERSION_TYPE" ]]; then + echo "No version type found in PR description, defaulting to PATCH increment." + patch=$((patch + 1)) + elif [[ "$VERSION_TYPE" == "major" ]]; then + major=$((major + 1)) + minor=0 + patch=0 + elif [[ "$VERSION_TYPE" == "minor" ]]; then + minor=$((minor + 1)) + patch=0 + elif [[ "$VERSION_TYPE" == "patch" ]]; then + patch=$((patch + 1)) + else + echo "Invalid version type in PR description: $VERSION_TYPE" + exit 1 + fi + RELEASE_VERSION="$major.$minor.$patch" + echo "Using calculated version: $RELEASE_VERSION" + echo "RELEASE_VERSION=$RELEASE_VERSION" + echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV + # Check if tag already exists + if git rev-parse "refs/tags/$RELEASE_VERSION" >/dev/null 2>&1; then + echo "Tag $RELEASE_VERSION already exists. Skipping release." + exit 0 + fi + git flow release start $RELEASE_VERSION + auto-changelog -v $RELEASE_VERSION + git add CHANGELOG.md + git commit -m "$RELEASE_VERSION release changelog updates" + git flow release publish + + - name: Finish release and push (default git-flow messages) + run: | + set -e + cd project + git flow release finish -m "$RELEASE_VERSION release" $RELEASE_VERSION + git push origin main + git push origin --tags + git push origin develop + + - name: Cleanup tag if workflow fails + if: failure() + run: | + cd project + git tag -d $RELEASE_VERSION || true + git push origin :refs/tags/$RELEASE_VERSION || true diff --git a/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml b/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml new file mode 100644 index 0000000..7b8c1cb --- /dev/null +++ b/.github/workflows/fossid_integration_stateless_diffscan_target_repo.yml @@ -0,0 +1,19 @@ +name: Fossid Stateless Diff Scan + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: read + pull-requests: read + +jobs: + call-fossid-workflow: + if: ${{ ! github.event.pull_request.head.repo.fork }} + uses: rdkcentral/build_tools_workflows/.github/workflows/fossid_integration_stateless_diffscan.yml@1.0.0 + secrets: + FOSSID_CONTAINER_USERNAME: ${{ secrets.FOSSID_CONTAINER_USERNAME }} + FOSSID_CONTAINER_PASSWORD: ${{ secrets.FOSSID_CONTAINER_PASSWORD }} + FOSSID_HOST_USERNAME: ${{ secrets.FOSSID_HOST_USERNAME }} + FOSSID_HOST_TOKEN: ${{ secrets.FOSSID_HOST_TOKEN }} diff --git a/.github/workflows/manual-ci.yml b/.github/workflows/manual-ci.yml new file mode 100644 index 0000000..7081556 --- /dev/null +++ b/.github/workflows/manual-ci.yml @@ -0,0 +1,32 @@ +# This is a basic workflow that is manually triggered + +name: Manual workflow + +# Controls when the action will run. Workflow runs when manually triggered using the UI +# or API. +on: + workflow_dispatch: + # Inputs the workflow accepts. + inputs: + name: + # Friendly description to be shown in the UI instead of 'name' + description: 'Type of test : [Sanity, Quick, L1, L2]' + # Default value if no value is explicitly provided + default: 'Sanity' + # Input has to be provided for the workflow to run + required: true + # The data type of the input + type: string + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "greet" + greet: + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Runs a single command using the runners shell + - name: Run CI tests + run: echo "Executing ${{ inputs.name }}" diff --git a/.github/workflows/native_full_build.yml b/.github/workflows/native_full_build.yml new file mode 100644 index 0000000..0a2997b --- /dev/null +++ b/.github/workflows/native_full_build.yml @@ -0,0 +1,25 @@ +name: Build Component in Native Environment + +on: + push: + branches: [ main, 'sprint/**', 'release/**', develop ] + pull_request: + branches: [ main, 'sprint/**', 'release/**', topic/RDK*, develop ] + +jobs: + build-entservices-on-pr: + name: Build entservices-devicesettings component in github rdkcentral + runs-on: ubuntu-latest + container: + image: ghcr.io/rdkcentral/docker-rdk-ci:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: native build + run: | + sh -x build_dependencies.sh + sh -x cov_build.sh + env: + GITHUB_TOKEN: ${{ secrets.RDKCM_RDKE }} diff --git a/.github/workflows/tests-trigger.yml b/.github/workflows/tests-trigger.yml new file mode 100644 index 0000000..bb3de6a --- /dev/null +++ b/.github/workflows/tests-trigger.yml @@ -0,0 +1,24 @@ +permissions: + contents: read +name: main-workflow + +on: + push: + branches: [ main, develop, 'sprint/**', 'release/**' ] + pull_request: + branches: [ main, develop, 'sprint/**', 'release/**' ] + +jobs: + trigger-L1: + uses: ./.github/workflows/L1-tests.yml + with: + caller_source: local + secrets: + RDKCM_RDKE: ${{ secrets.RDKCM_RDKE }} + + trigger-L2: + uses: ./.github/workflows/L2-tests.yml + with: + caller_source: local + secrets: + RDKCM_RDKE: ${{ secrets.RDKCM_RDKE }} diff --git a/.github/workflows/update-changelog-and-api-version.yml b/.github/workflows/update-changelog-and-api-version.yml new file mode 100644 index 0000000..61fdb94 --- /dev/null +++ b/.github/workflows/update-changelog-and-api-version.yml @@ -0,0 +1,31 @@ +name: update changelog and api version + +on: + push: + branches: [ main, 'release/**' ] + paths-ignore: ['docs/**', 'Tests/**', 'Tools/**', '.github/**'] + + pull_request: + branches: [ main, 'release/**' ] + paths-ignore: ['docs/**', 'Tests/**', 'Tools/**', '.github/**'] + + +jobs: + build: + runs-on: ubuntu-latest # windows-latest | macos-latest + name: Check if changelog and api version were updated + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # OR "2" -> To retrieve the preceding commit. + + - name: Get changed files using defaults + id: changed-files + uses: rdkcentral/tj-actions_changed-files@v19 + + - name: Run step when a CHANGELOG.md didn't change + uses: actions/github-script@v3 + if: ${{ !contains(steps.changed-files.outputs.all_changed_files, 'CHANGELOG.md') }} + with: + script: | + core.setFailed('CHANGELOG.md should be modified') diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..ecac6c5 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,48 @@ +# RDK EntServices DeviceSettings - Architecture + +## Overview + +The DeviceSettings component is a Thunder plugin that exposes device settings and front-panel related functionality through the WPEFramework service model. + +## System Architecture + +```text +Client Applications + -> JSON-RPC / COM-RPC + -> Thunder Core + -> DeviceSettings plugin layer + -> DeviceSettings implementation layer + -> Helper / DS / IARM / HAL layer + -> Hardware and system services +``` + +## Core Components + +### Plugin Layer + +- `plugin/DeviceSettings/DeviceSettings.cpp` owns activation, deactivation, and external interface acquisition. +- `plugin/DeviceSettings/Module.cpp` and `Module.h` define the Thunder module identity. + +### Implementation Layer + +- `plugin/DeviceSettings/DeviceSettingsImplementation.cpp` owns the `Exchange::IDeviceSettings` contract. +- The component-specific implementation files delegate to the lower-level helpers and HAL adapters. + +### Helper Layer + +- `plugin/DeviceSettings/Audio.cpp`, `Display.cpp`, `Host.cpp`, `VideoPort.cpp`, `VideoDevice.cpp`, `HdmiIn.cpp`, and `CompositeIn.cpp` provide the device-specific logic. +- `DSController.cpp` and `DSPwrEventListener.cpp` coordinate system and power-state behavior. + +## Build Model + +The repository is structured as separate build targets for the Thunder shell and the implementation library, with the plugin configured from the repository root. + +## Integration Points + +- Thunder plugin lifecycle and service registration. +- COM-RPC access to the `Exchange::IDeviceSettings` interface and its subinterfaces. +- DS and IARM integration for hardware-backed operations. + +## Testing + +The component should be validated with the same layered approach used by the other entservices repositories: unit-style coverage for helper logic and integration coverage for the plugin entry point. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..b5b5f51 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,60 @@ +### +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 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. +### + +cmake_minimum_required(VERSION 3.3) + +find_package(WPEFramework) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") + +option(COMCAST_CONFIG "Comcast services configuration" ON) +if(COMCAST_CONFIG) + include(services.cmake) +endif() + +string(TOLOWER ${NAMESPACE} STORAGE_DIRECTORY) + +include(CmakeHelperFunctions) + +if(PLUGIN_DEVICESETTINGS) + add_subdirectory(plugin) +endif() + +if(RDK_SERVICES_L1_TEST) + add_subdirectory(Tests/L1Tests) +endif() + +if(RDK_SERVICE_L2_TEST) + add_subdirectory(Tests/L2Tests) +endif() + +if(WPEFRAMEWORK_CREATE_IPKG_TARGETS) + set(CPACK_GENERATOR "DEB") + set(CPACK_DEB_COMPONENT_INSTALL ON) + set(CPACK_COMPONENTS_GROUPING IGNORE) + + set(CPACK_DEBIAN_PACKAGE_NAME "${WPEFRAMEWORK_PLUGINS_OPKG_NAME}") + set(CPACK_DEBIAN_PACKAGE_VERSION "${WPEFRAMEWORK_PLUGINS_OPKG_VERSION}") + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${WPEFRAMEWORK_PLUGINS_OPKG_ARCHITECTURE}") + set(CPACK_DEBIAN_PACKAGE_MAINTAINER "${WPEFRAMEWORK_PLUGINS_OPKG_MAINTAINER}") + set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${WPEFRAMEWORK_PLUGINS_OPKG_DESCRIPTION}") + set(CPACK_PACKAGE_FILE_NAME "${WPEFRAMEWORK_PLUGINS_OPKG_FILE_NAME}") + + include(CPack) +endif() diff --git a/PRODUCT.md b/PRODUCT.md new file mode 100644 index 0000000..c0c063f --- /dev/null +++ b/PRODUCT.md @@ -0,0 +1,21 @@ +# RDK EntServices DeviceSettings - Product Functionality + +## Product Overview + +The DeviceSettings plugin provides a common service interface for device-level audio, display, host, video, HDMI-in, and front-panel configuration. + +## Core Functionality + +- Audio port and audio output control. +- Display and video-port configuration. +- HDMI-in and composite-in settings. +- Host and power-related device settings. +- Front-panel style indicator control where supported by the platform. + +## API Surface + +The primary public surface is exposed through the Thunder `Exchange::IDeviceSettings` interface and its related subinterfaces. + +## Deployment + +The plugin is packaged and loaded through the Thunder service model and follows the repository-level build configuration. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..a6d3b1b --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# entservices-devicesettings + +This repository contains the Thunder plugin for `DeviceSettings`. + +## Layout + +- `plugin/DeviceSettings/` contains the Thunder shell, the implementation library, and the DS helper classes. +- `cmake/` contains the local find-modules needed by the component build. +- `build_dependencies.sh` bootstraps the local Thunder build dependencies used by this repository. +- `cov_build.sh` runs the coverage-oriented configuration used by CI. + +## Build Flow + +The repository follows the same split as the frontpanel component architecture: + +1. The Thunder plugin layer owns activation, service registration, and JSON-RPC wiring. +2. The implementation layer exposes the `Exchange::IDeviceSettings` surface. +3. The helper layer wraps the DS / IARM / HAL-specific logic. + +## Notes + +- The component is built from the repository root through the top-level `CMakeLists.txt`. +- New plugin flags or workflow changes should be mirrored in the build scripts when the component matrix changes. \ No newline at end of file diff --git a/Tests/L1Tests/CMakeLists.txt b/Tests/L1Tests/CMakeLists.txt new file mode 100644 index 0000000..004800d --- /dev/null +++ b/Tests/L1Tests/CMakeLists.txt @@ -0,0 +1,52 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2026 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. + +cmake_minimum_required(VERSION 3.8) + +set(PLUGIN_NAME L1TestsDS) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(${NAMESPACE}Plugins REQUIRED) + +set(TEST_SRC + tests/test_DeviceSettings.cpp +) + +set(TEST_LIB + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}DeviceSettingsImp +) + +add_library(${MODULE_NAME} SHARED ${TEST_SRC}) + +target_include_directories(${MODULE_NAME} + PRIVATE + ${CMAKE_SOURCE_DIR}/plugin + ${CMAKE_SOURCE_DIR}/../rdk-halif-device_settings/include + ${CMAKE_SOURCE_DIR}/../devicesettings/ds/include) + +target_link_libraries(${MODULE_NAME} PRIVATE ${TEST_LIB}) + +set_source_files_properties( + tests/test_DeviceSettings.cpp + PROPERTIES COMPILE_FLAGS "-fexceptions") + +install(TARGETS ${MODULE_NAME} DESTINATION lib) +write_config(${PLUGIN_NAME}) \ No newline at end of file diff --git a/Tests/L1Tests/tests/test_DeviceSettings.cpp b/Tests/L1Tests/tests/test_DeviceSettings.cpp new file mode 100644 index 0000000..2f708f7 --- /dev/null +++ b/Tests/L1Tests/tests/test_DeviceSettings.cpp @@ -0,0 +1,95 @@ +/* +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 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. +*/ + +#include + +#include "DeviceSettingsImplementation.h" + +using namespace WPEFramework; + +namespace { + +TEST(DeviceSettingsImpTest, ExposesMainInterface) +{ + Core::ProxyType implementation = Core::ProxyType::Create(); + + Exchange::IDeviceSettings* deviceSettings = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettings::ID)); + ASSERT_NE(nullptr, deviceSettings); + + deviceSettings->Release(); +} + +TEST(DeviceSettingsImpTest, ExposesComponentInterfaces) +{ + Core::ProxyType implementation = Core::ProxyType::Create(); + + Exchange::IDeviceSettingsFPD* fpd = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsFPD::ID)); + Exchange::IDeviceSettingsHDMIIn* hdmiIn = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsHDMIIn::ID)); + Exchange::IDeviceSettingsAudio* audio = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsAudio::ID)); + Exchange::IDeviceSettingsVideoPort* videoPort = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsVideoPort::ID)); + Exchange::IDeviceSettingsVideoDevice* videoDevice = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsVideoDevice::ID)); + Exchange::IDeviceSettingsHost* host = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsHost::ID)); + Exchange::IDeviceSettingsCompositeIn* compositeIn = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsCompositeIn::ID)); + Exchange::IDeviceSettingsDisplay* display = static_cast( + implementation->QueryInterface(Exchange::IDeviceSettingsDisplay::ID)); + + EXPECT_NE(nullptr, fpd); + EXPECT_NE(nullptr, hdmiIn); + EXPECT_NE(nullptr, audio); + EXPECT_NE(nullptr, videoPort); + EXPECT_NE(nullptr, videoDevice); + EXPECT_NE(nullptr, host); + EXPECT_NE(nullptr, compositeIn); + EXPECT_NE(nullptr, display); + + if (fpd != nullptr) { + fpd->Release(); + } + if (hdmiIn != nullptr) { + hdmiIn->Release(); + } + if (audio != nullptr) { + audio->Release(); + } + if (videoPort != nullptr) { + videoPort->Release(); + } + if (videoDevice != nullptr) { + videoDevice->Release(); + } + if (host != nullptr) { + host->Release(); + } + if (compositeIn != nullptr) { + compositeIn->Release(); + } + if (display != nullptr) { + display->Release(); + } +} + +} // namespace \ No newline at end of file diff --git a/Tests/L2Tests/CMakeLists.txt b/Tests/L2Tests/CMakeLists.txt new file mode 100644 index 0000000..a7cd6ea --- /dev/null +++ b/Tests/L2Tests/CMakeLists.txt @@ -0,0 +1,53 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2026 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. + +set(PLUGIN_NAME L2TestsDS) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) +set(THUNDER_PORT 9998) + +find_package(${NAMESPACE}Plugins REQUIRED) + +set(SRC_FILES + tests/DeviceSettings_L2Test.cpp +) + +add_library(${MODULE_NAME} SHARED ${SRC_FILES}) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED YES) + +target_compile_definitions(${MODULE_NAME} + PRIVATE + MODULE_NAME=Plugin_${PLUGIN_NAME} + THUNDER_PORT="${THUNDER_PORT}") + +target_compile_options(${MODULE_NAME} PRIVATE -Wno-error) +target_link_libraries(${MODULE_NAME} PRIVATE ${NAMESPACE}Plugins::${NAMESPACE}Plugins) + +target_include_directories( + ${MODULE_NAME} PRIVATE ./ + ../../plugin/DeviceSettings + ../../../entservices-testframework/Tests/mocks + ../../../entservices-testframework/Tests/mocks/thunder + ../../../entservices-testframework/Tests/mocks/devicesettings + ../../../entservices-testframework/Tests/mocks/MockPlugin + ../../../entservices-testframework/Tests/L2Tests/L2TestsPlugin + ${CMAKE_INSTALL_PREFIX}/include + ) + +install(TARGETS ${MODULE_NAME} DESTINATION lib) \ No newline at end of file diff --git a/Tests/L2Tests/tests/DeviceSettings_L2Test.cpp b/Tests/L2Tests/tests/DeviceSettings_L2Test.cpp new file mode 100644 index 0000000..1527e76 --- /dev/null +++ b/Tests/L2Tests/tests/DeviceSettings_L2Test.cpp @@ -0,0 +1,107 @@ +/* +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2026 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. +*/ + +#include +#include + +#include "L2Tests.h" +#include "L2TestsMock.h" +#include + +#include +#include +#include + +#define TEST_LOG(x, ...) \ + fprintf(stderr, "\033[1;32m[%s:%d](%s)" x "\n\033[0m", __FILE__, __LINE__, __FUNCTION__, getpid(), gettid(), ##__VA_ARGS__); \ + fflush(stderr); + +using ::testing::NiceMock; +using namespace WPEFramework; + +class DeviceSettings_L2Test : public L2TestMocks { +protected: + PluginHost::IShell* m_controller_DeviceSettings; + Exchange::IDeviceSettings* m_deviceSettingsPlugin; + +public: + DeviceSettings_L2Test(); + ~DeviceSettings_L2Test() override; + + uint32_t CreateDeviceSettingsInterfaceObject(); +}; + +DeviceSettings_L2Test::DeviceSettings_L2Test() + : L2TestMocks() + , m_controller_DeviceSettings(nullptr) + , m_deviceSettingsPlugin(nullptr) +{ + uint32_t status = Core::ERROR_GENERAL; + + status = ActivateService("org.rdk.DeviceSettings"); + EXPECT_EQ(Core::ERROR_NONE, status); +} + +DeviceSettings_L2Test::~DeviceSettings_L2Test() +{ + if (m_deviceSettingsPlugin != nullptr) { + m_deviceSettingsPlugin->Release(); + m_deviceSettingsPlugin = nullptr; + } + + if (m_controller_DeviceSettings != nullptr) { + m_controller_DeviceSettings->Release(); + m_controller_DeviceSettings = nullptr; + } + + uint32_t status = DeactivateService("org.rdk.DeviceSettings"); + EXPECT_EQ(Core::ERROR_NONE, status); +} + +uint32_t DeviceSettings_L2Test::CreateDeviceSettingsInterfaceObject() +{ + uint32_t return_value = Core::ERROR_GENERAL; + Core::ProxyType> DeviceSettings_Engine; + Core::ProxyType DeviceSettings_Client; + + TEST_LOG("Creating DeviceSettings_Engine"); + DeviceSettings_Engine = Core::ProxyType>::Create(); + DeviceSettings_Client = Core::ProxyType::Create(Core::NodeId("/tmp/communicator"), Core::ProxyType(DeviceSettings_Engine)); + + TEST_LOG("Creating DeviceSettings_Engine Announcements"); +#if ((THUNDER_VERSION == 2) || ((THUNDER_VERSION == 4) && (THUNDER_VERSION_MINOR == 2))) + DeviceSettings_Engine->Announcements(DeviceSettings_Client->Announcement()); +#endif + if (!DeviceSettings_Client.IsValid()) { + TEST_LOG("Invalid DeviceSettings_Client"); + } else { + m_controller_DeviceSettings = DeviceSettings_Client->Open(_T("org.rdk.DeviceSettings"), ~0, 3000); + if (m_controller_DeviceSettings) { + m_deviceSettingsPlugin = m_controller_DeviceSettings->QueryInterface(); + return_value = Core::ERROR_NONE; + } + } + return return_value; +} + +TEST_F(DeviceSettings_L2Test, DeviceSettings_L2_MethodTest) +{ + EXPECT_EQ(Core::ERROR_NONE, CreateDeviceSettingsInterfaceObject()); + ASSERT_NE(nullptr, m_deviceSettingsPlugin); +} \ No newline at end of file diff --git a/build_dependencies.sh b/build_dependencies.sh new file mode 100755 index 0000000..34b3da0 --- /dev/null +++ b/build_dependencies.sh @@ -0,0 +1,138 @@ +#!/bin/bash +set -x +set -e + +GITHUB_WORKSPACE="${PWD}" +ls -la "${GITHUB_WORKSPACE}" +cd "${GITHUB_WORKSPACE}" + +apt update +apt install -y libsqlite3-dev libcurl4-openssl-dev valgrind lcov clang libsystemd-dev libboost-all-dev libwebsocketpp-dev meson libcunit1 libcunit1-dev curl protobuf-compiler-grpc libgrpc-dev libgrpc++-dev libunwind-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libdrm-dev libglib2.0-dev pkg-config +pip install jsonref + +if [ ! -d "trower-base64" ]; then + git clone https://github.com/xmidt-org/trower-base64.git +fi +cd trower-base64 +meson setup --warnlevel 3 --werror build +ninja -C build +ninja -C build install +cd .. + +git clone --branch R4.4.3 https://github.com/rdkcentral/ThunderTools.git +git clone --branch R4.4.1 https://github.com/rdkcentral/Thunder.git +git clone --branch feature/RDKEMW-6078_DeviceSettingsInterface https://github.com/rdkcentral/entservices-apis.git +git clone --branch 1.0.14 https://github.com/rdkcentral/entservices-testframework.git +git clone --branch main https://github.com/rdkcentral/rdk-halif-device_settings.git +git clone --branch main https://github.com/rdkcentral/devicesettings.git +git clone --branch develop https://github.com/rdkcentral/iarmbus.git +git clone https://github.com/rdkcentral/iarmmgrs.git +git clone --branch DeviceSetting_Plugin https://github.com/rdkcentral/entservices-helpers.git + +# Keep backward-compatible parent path expected by some test CMake files. +if [ ! -e "$GITHUB_WORKSPACE/../entservices-helpers" ]; then + ln -s "$GITHUB_WORKSPACE/entservices-helpers" "$GITHUB_WORKSPACE/../entservices-helpers" +fi + +# Ensure mock iarmmgrs-hal headers exist in testframework for CI builds. +mkdir -p "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal" +touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal/sysMgr.h" +touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal/mfrMgr.h" + +# Generate minimal mock headers before building entservices-helpers. +mkdir -p "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus" +touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus/libIARM.h" +touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus/libIBus.h" +touch "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/iarm.h" + +echo "======================================================================================" +echo "building thunderTools" +cd ThunderTools +patch -p1 < "$GITHUB_WORKSPACE/entservices-testframework/patches/00010-R4.4-Add-support-for-project-dir.patch" +cd - + +cmake -G Ninja -S ThunderTools -B build/ThunderTools \ + -DEXCEPTIONS_ENABLE=ON \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + +cmake --build build/ThunderTools --target install + +echo "======================================================================================" +echo "building thunder" +cd Thunder +patch -p1 < "$GITHUB_WORKSPACE/entservices-testframework/patches/Use_Legact_Alt_Based_On_ThunderTools_R4.4.3.patch" +patch -p1 < "$GITHUB_WORKSPACE/entservices-testframework/patches/error_code_R4_4.patch" +patch -p1 < "$GITHUB_WORKSPACE/entservices-testframework/patches/1004-Add-support-for-project-dir.patch" +patch -p1 < "$GITHUB_WORKSPACE/entservices-testframework/patches/RDKEMW-733-Add-ENTOS-IDS.patch" +cd - + +cmake -G Ninja -S Thunder -B build/Thunder \ + -DMESSAGING=ON \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DGENERIC_CMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DBUILD_TYPE=Debug \ + -DBINDING=127.0.0.1 \ + -DPORT=55555 \ + -DEXCEPTIONS_ENABLE=ON + +cmake --build build/Thunder --target install + +echo "======================================================================================" +echo "building entservices-apis" +cd entservices-apis +rm -rf jsonrpc/DTV.json +cd .. + +cmake -G Ninja -S entservices-apis -B build/entservices-apis \ + -DEXCEPTIONS_ENABLE=ON \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" + +cmake --build build/entservices-apis --target install + +echo "======================================================================================" +echo "building entservices-helpers" + +cmake -G Ninja -S entservices-helpers -B build/entservices-helpers \ + -DEXCEPTIONS_ENABLE=ON \ + -DCOMCAST_CONFIG=OFF \ + -DPLUGIN_HELPERS=ON \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + "-DCMAKE_CXX_FLAGS=-I$GITHUB_WORKSPACE/entservices-testframework/Tests/mocks -I$GITHUB_WORKSPACE/entservices-testframework/Tests/headers -I$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmbus -include $GITHUB_WORKSPACE/entservices-testframework/Tests/mocks/Iarm.h" + +cmake --build build/entservices-helpers --target install + +mkdir -p "$GITHUB_WORKSPACE/install/usr/include/WPEFramework/interfaces" +find "$GITHUB_WORKSPACE/entservices-apis/apis/DeviceSettings" -name "IDeviceSettings*.h" -exec cp {} "$GITHUB_WORKSPACE/install/usr/include/WPEFramework/interfaces/" \; 2>/dev/null || true + +cp -r "$GITHUB_WORKSPACE/rdk-halif-device_settings/include/." "$GITHUB_WORKSPACE/install/usr/include/" +cp -r "$GITHUB_WORKSPACE/devicesettings/rpc/include/." "$GITHUB_WORKSPACE/install/usr/include/" +cp -r "$GITHUB_WORKSPACE/devicesettings/ds/include/." "$GITHUB_WORKSPACE/install/usr/include/" + +# Real IARM headers from iarmbus repo +cp -r "$GITHUB_WORKSPACE/iarmbus/core/include/." "$GITHUB_WORKSPACE/install/usr/include/" + +# Create stub headers for external dependencies with no public repos +touch "$GITHUB_WORKSPACE/install/usr/include/rfcapi.h" +touch "$GITHUB_WORKSPACE/install/usr/include/mfrMgr.h" +touch "$GITHUB_WORKSPACE/install/usr/include/secure_wrapper.h" + +# Copy real iarmmgrs public headers used by DeviceSettings. +cp "$GITHUB_WORKSPACE/iarmmgrs/sysmgr/include/sysMgr.h" "$GITHUB_WORKSPACE/install/usr/include/" +cp -r "$GITHUB_WORKSPACE/iarmmgrs/mfr/include/." "$GITHUB_WORKSPACE/install/usr/include/" + +# Copy external stubs from testframework (no public repos available) +find "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers" -maxdepth 1 -type f -name "*.h" -exec cp {} "$GITHUB_WORKSPACE/install/usr/include/" \; 2>/dev/null || true +find "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/ccec" -maxdepth 1 -type f -name "*.h" -exec cp {} "$GITHUB_WORKSPACE/install/usr/include/" \; 2>/dev/null || true +find "$GITHUB_WORKSPACE/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal" -maxdepth 1 -type f -name "*.h" -exec cp {} "$GITHUB_WORKSPACE/install/usr/include/" \; 2>/dev/null || true + +# Ensure real iarmmgrs headers take precedence after external stub copies. +cp "$GITHUB_WORKSPACE/iarmmgrs/sysmgr/include/sysMgr.h" "$GITHUB_WORKSPACE/install/usr/include/" +cp -r "$GITHUB_WORKSPACE/iarmmgrs/mfr/include/." "$GITHUB_WORKSPACE/install/usr/include/" + +echo "======================================================================================" +echo "device-settings repository dependencies are ready" \ No newline at end of file diff --git a/cmake/FindIARMBus.cmake b/cmake/FindIARMBus.cmake new file mode 100644 index 0000000..be3ea80 --- /dev/null +++ b/cmake/FindIARMBus.cmake @@ -0,0 +1,37 @@ +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2020 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. + +find_package(PkgConfig) + +find_library(IARMBUS_LIBRARIES NAMES IARMBus) +find_path(IARMBUS_INCLUDE_DIRS NAMES libIARM.h PATH_SUFFIXES rdk/iarmbus) +find_path(IARMRECEIVER_INCLUDE_DIRS NAMES receiverMgr.h PATH_SUFFIXES rdk/iarmmgrs/receiver) +find_path(IARMHAL_INCLUDE_DIRS NAMES sysMgr.h PATH_SUFFIXES rdk/iarmmgrs-hal) + +set(IARMBUS_LIBRARIES ${IARMBUS_LIBRARIES} CACHE PATH "Path to IARMBus library") +set(IARMBUS_INCLUDE_DIRS ${IARMBUS_INCLUDE_DIRS} ${IARMRECEIVER_INCLUDE_DIRS} ${IARMHAL_INCLUDE_DIRS}) +set(IARMBUS_INCLUDE_DIRS ${IARMBUS_INCLUDE_DIRS} ${IARMRECEIVER_INCLUDE_DIRS} ${IARMHAL_INCLUDE_DIRS} CACHE PATH "Path to IARMBus include") + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(IARMBUS DEFAULT_MSG IARMBUS_INCLUDE_DIRS IARMBUS_LIBRARIES) + +mark_as_advanced( + IARMBUS_FOUND + IARMBUS_INCLUDE_DIRS + IARMBUS_LIBRARIES + IARMBUS_LIBRARY_DIRS + IARMBUS_FLAGS) diff --git a/cmake/FindWPEFrameworkHelpers.cmake b/cmake/FindWPEFrameworkHelpers.cmake new file mode 100644 index 0000000..7e97221 --- /dev/null +++ b/cmake/FindWPEFrameworkHelpers.cmake @@ -0,0 +1,27 @@ +# - Try to find WPEFrameworkHelpers +# Once done this will define +# WPEFrameworkHelpers_FOUND - System has WPEFrameworkHelpers +# WPEFrameworkHelpers_INCLUDE_DIRS - The WPEFrameworkHelpers include directories +# +# Also creates an imported target: +# WPEFrameworkHelpers::WPEFrameworkHelpers + +find_path(WPEFrameworkHelpers_INCLUDE_DIRS + NAMES DeviceSettingsInterface.h UtilsLogging.h + PATH_SUFFIXES wpeframework/helpers wpeframework/helpers) + +set(WPEFrameworkHelpers_INCLUDE_DIRS ${WPEFrameworkHelpers_INCLUDE_DIRS} CACHE PATH "Path to WPEFrameworkHelpers includes") + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(WPEFrameworkHelpers DEFAULT_MSG + WPEFrameworkHelpers_INCLUDE_DIRS) + +if(WPEFrameworkHelpers_FOUND AND NOT TARGET WPEFrameworkHelpers::WPEFrameworkHelpers) + add_library(WPEFrameworkHelpers::WPEFrameworkHelpers INTERFACE IMPORTED) + set_target_properties(WPEFrameworkHelpers::WPEFrameworkHelpers PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${WPEFrameworkHelpers_INCLUDE_DIRS}") +endif() + +mark_as_advanced( + WPEFrameworkHelpers_FOUND + WPEFrameworkHelpers_INCLUDE_DIRS) \ No newline at end of file diff --git a/cov_build.sh b/cov_build.sh new file mode 100755 index 0000000..f92290d --- /dev/null +++ b/cov_build.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -x +set -e + +GITHUB_WORKSPACE="${PWD}" +ls -la "${GITHUB_WORKSPACE}" + +echo "building entservices-devicesettings" + +if ! pkg-config --exists glib-2.0; then + echo "glib-2.0 development files are missing; run build_dependencies.sh first" + exit 1 +fi + +if [ ! -d "$GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers" ]; then + echo "WPEFramework helpers headers are missing; run build_dependencies.sh first" + exit 1 +fi + +cd "${GITHUB_WORKSPACE}" +cmake -G Ninja -S "$GITHUB_WORKSPACE" -B build/entservices-devicesettings \ + -DUSE_THUNDER_R4=ON \ + -DCMAKE_INSTALL_PREFIX="$GITHUB_WORKSPACE/install/usr" \ + -DCMAKE_MODULE_PATH="$GITHUB_WORKSPACE/install/tools/cmake" \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_IARMBus=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_Udev=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_RFC=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_RBus=ON \ + -DCMAKE_DISABLE_FIND_PACKAGE_DS=ON \ + -DCOMCAST_CONFIG=OFF \ + -DRDK_SERVICES_COVERITY=ON \ + -DRDK_SERVICES_L1_TEST=ON \ + -DDS_FOUND=ON \ + -DHIDE_NON_EXTERNAL_SYMBOLS=OFF \ + -DWPEFrameworkHelpers_INCLUDE_DIRS="$GITHUB_WORKSPACE/install/usr/include/wpeframework/helpers" \ + -DPLUGIN_DEVICESETTINGS=ON \ + -DCMAKE_CXX_FLAGS="-DEXCEPTIONS_ENABLE=ON \ + -fprofile-arcs \ + -ftest-coverage \ + -I ${GITHUB_WORKSPACE}/install/usr/include \ + -I ${GITHUB_WORKSPACE}/install/usr/include/WPEFramework \ + -I ${GITHUB_WORKSPACE}/devicesettings/rpc/include \ + -I ${GITHUB_WORKSPACE}/devicesettings/ds/include \ + -I ${GITHUB_WORKSPACE}/rdk-halif-device_settings/include \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/audiocapturemgr \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/rdk/ds \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/rdk/iarmbus \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/rdk/iarmmgrs-hal \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/ccec/drivers \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests/headers/network \ + -I ${GITHUB_WORKSPACE}/entservices-testframework/Tests \ + -I ${GITHUB_WORKSPACE}/Thunder/Source \ + -I ${GITHUB_WORKSPACE}/Thunder/Source/core \ + -Wall -Wno-unused-result -Wno-deprecated-declarations -Wno-error=format \ + --coverage \ + -Wl,-wrap,system -Wl,-wrap,popen -Wl,-wrap,syslog -Wl,-wrap,v_secure_system -Wl,-wrap,v_secure_popen -Wl,-wrap,v_secure_pclose -Wl,-wrap,unlink \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/Rfc.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/RBus.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/Telemetry.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/Udev.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/maintenanceMGR.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/pkg.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/secure_wrappermock.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/wpa_ctrl_mock.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/gdialservice.h \ + -include ${GITHUB_WORKSPACE}/entservices-testframework/Tests/mocks/MotionDetection.h \ + -DENABLE_TELEMETRY_LOGGING \ + -DUSE_IARMBUS \ + -DENABLE_SYSTEM_GET_STORE_DEMO_LINK \ + -DENABLE_DEEP_SLEEP \ + -DENABLE_SET_WAKEUP_SRC_CONFIG \ + -DENABLE_THERMAL_PROTECTION \ + -DUSE_DRM_SCREENCAPTURE \ + -DHAS_API_SYSTEM \ + -DHAS_API_POWERSTATE \ + -DHAS_RBUS \ + -DCLOCK_BRIGHTNESS_ENABLED \ + -DUSE_DS \ + -DENABLE_DEVICE_MANUFACTURER_INFO \ + -DUSE_THUNDER_R4=ON -DTHUNDER_VERSION=4 -DTHUNDER_VERSION_MAJOR=4 -DTHUNDER_VERSION_MINOR=4" \ + +cmake --build build/entservices-devicesettings --target install +echo "======================================================================================" +exit 0 \ No newline at end of file diff --git a/plugin/Audio.cpp b/plugin/Audio.cpp new file mode 100644 index 0000000..78a1938 --- /dev/null +++ b/plugin/Audio.cpp @@ -0,0 +1,769 @@ +/* + * 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. + */ + +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "Audio.h" + +Audio::Audio(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("Audio Constructor"); + Platform_init(); +} + +void Audio::Platform_init() +{ + CallbackBundle bundle; + bundle.OnAudioOutHotPlug = [this](AudioPortType portType, uint32_t portNumber, bool isConnected) { + this->OnAudioOutHotPlug(portType, portNumber, isConnected); + }; + bundle.OnAudioFormatUpdate = [this](AudioFormat audioFormat) { + this->OnAudioFormatUpdate(audioFormat); + }; + bundle.OnDolbyAtmosCapabilitiesChanged = [this](DolbyAtmosCapability atmosCaps, bool status) { + this->OnDolbyAtmosCapabilitiesChanged(atmosCaps, status); + }; + bundle.OnAssociatedAudioMixingChanged = [this](bool mixing) { + this->OnAssociatedAudioMixingChanged(mixing); + }; + bundle.OnAudioFaderControlChanged = [this](int32_t mixerBalance) { + this->OnAudioFaderControlChanged(mixerBalance); + }; + bundle.OnAudioPrimaryLanguageChanged = [this](const std::string& primaryLanguage) { + this->OnAudioPrimaryLanguageChanged(primaryLanguage); + }; + bundle.OnAudioSecondaryLanguageChanged = [this](const std::string& secondaryLanguage) { + this->OnAudioSecondaryLanguageChanged(secondaryLanguage); + }; + bundle.OnAudioPortStateChanged = [this](AudioPortState audioPortState) { + this->OnAudioPortStateChanged(audioPortState); + }; + bundle.OnAudioLevelChanged = [this](float audioLevel) { + this->OnAudioLevelChanged(audioLevel); + }; + bundle.OnAudioModeChanged = [this](AudioPortType portType, AudioStereoMode mode) { + this->OnAudioModeChanged(portType, mode); + }; + if (_platform) { + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } +} + +void Audio::OnAudioOutHotPlug(AudioPortType portType, uint32_t portNumber, bool isConnected) +{ + LOGINFO("OnAudioOutHotPlug: portType=%d, portNumber=%u, connected=%s", static_cast(portType), portNumber, isConnected ? "true" : "false"); + // Trigger notification to parent for callback dispatch + _parent.OnAudioOutHotPlug(portType, portNumber, isConnected); +} + +void Audio::OnAudioFormatUpdate(AudioFormat audioFormat) +{ + LOGINFO("OnAudioFormatUpdate: format=%d", static_cast(audioFormat)); + // Trigger notification to parent for callback dispatch + _parent.OnAudioFormatUpdate(audioFormat); +} + +void Audio::OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCaps, bool status) +{ + LOGINFO("OnDolbyAtmosCapabilitiesChanged: caps=%d, status=%s", static_cast(atmosCaps), status ? "true" : "false"); + // Trigger notification to parent for callback dispatch + _parent.OnDolbyAtmosCapabilitiesChanged(atmosCaps, status); +} + +void Audio::OnAudioModeChanged(AudioPortType portType, AudioStereoMode mode) +{ + LOGINFO("OnAudioModeChanged: portType=%d, mode=%d", static_cast(portType), static_cast(mode)); + // Trigger notification to parent for callback dispatch + _parent.OnAudioModeEvent(portType, mode); +} + +// Event handler methods for audio state changes +void Audio::OnAssociatedAudioMixingChanged(bool mixing) +{ + LOGINFO("OnAssociatedAudioMixingChanged: mixing=%s", mixing ? "enabled" : "disabled"); + // Trigger notification to parent for callback dispatch + _parent.OnAssociatedAudioMixingChanged(mixing); +} + +void Audio::OnAudioFaderControlChanged(int32_t mixerBalance) +{ + LOGINFO("OnAudioFaderControlChanged: mixerBalance=%d", mixerBalance); + // Trigger notification to parent for callback dispatch + _parent.OnAudioFaderControlChanged(mixerBalance); +} + +void Audio::OnAudioPrimaryLanguageChanged(const std::string& primaryLanguage) +{ + LOGINFO("OnAudioPrimaryLanguageChanged: primaryLanguage=%s", primaryLanguage.c_str()); + // Trigger notification to parent for callback dispatch + _parent.OnAudioPrimaryLanguageChanged(primaryLanguage); +} + +void Audio::OnAudioSecondaryLanguageChanged(const std::string& secondaryLanguage) +{ + LOGINFO("OnAudioSecondaryLanguageChanged: secondaryLanguage=%s", secondaryLanguage.c_str()); + // Trigger notification to parent for callback dispatch + _parent.OnAudioSecondaryLanguageChanged(secondaryLanguage); +} + +void Audio::OnAudioPortStateChanged(AudioPortState audioPortState) +{ + LOGINFO("OnAudioPortStateChanged: audioPortState=%d", static_cast(audioPortState)); + // Trigger notification to parent for callback dispatch + _parent.OnAudioPortStateChanged(audioPortState); +} + +void Audio::OnAudioLevelChanged(float audioLevel) +{ + LOGINFO("OnAudioLevelChanged: audioLevel=%.2f", audioLevel); + // Trigger notification to parent for callback dispatch + _parent.OnAudioLevelChanged(static_cast(audioLevel)); +} + +uint32_t Audio::GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) { + LOGINFO("GetAudioPort: type=%d, index=%d", type, index); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioPort(type, index, handle); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioPort: SUCCESS - type=%d, index=%d, handle=%d", type, index, handle); + } else { + LOGERR("GetAudioPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioCapabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetAudioCapabilities: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioCapabilities(handle, capabilities); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioCapabilities: SUCCESS - handle=%d, capabilities=%d", handle, capabilities); + } else { + LOGERR("GetAudioCapabilities: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetAudioMS12Capabilities: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioMS12Capabilities(handle, capabilities); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioMS12Capabilities: SUCCESS - handle=%d, capabilities=%d", handle, capabilities); + } else { + LOGERR("GetAudioMS12Capabilities: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) { + LOGINFO("GetAudioFormat: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioFormat(handle, audioFormat); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioFormat: SUCCESS - handle=%d, audioFormat=%d", handle, audioFormat); + } else { + LOGERR("GetAudioFormat: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) { + LOGINFO("GetAudioEncoding: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioEncoding(handle, encoding); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioEncoding: SUCCESS - handle=%d, encoding=%d", handle, encoding); + } else { + LOGERR("GetAudioEncoding: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioLevel(const int32_t handle, const float audioLevel) { + LOGINFO("SetAudioLevel: handle=%d, audioLevel=%.2f", handle, audioLevel); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioLevel(handle, audioLevel); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioLevel: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioLevel: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioLevel(const int32_t handle, float &audioLevel) { + LOGINFO("GetAudioLevel: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioLevel(handle, audioLevel); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioLevel: SUCCESS - handle=%d, audioLevel=%.2f", handle, audioLevel); + } else { + LOGERR("GetAudioLevel: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioGain(const int32_t handle, const float gainLevel) { + LOGINFO("SetAudioGain: handle=%d, gainLevel=%.2f", handle, gainLevel); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioGain(handle, gainLevel); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioGain: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioGain: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioGain(const int32_t handle, float &gainLevel) { + LOGINFO("GetAudioGain: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioGain(handle, gainLevel); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioGain: SUCCESS - handle=%d, gainLevel=%.2f", handle, gainLevel); + } else { + LOGERR("GetAudioGain: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioMute(const int32_t handle, const bool mute) { + LOGINFO("SetAudioMute: handle=%d, mute=%s", handle, mute ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioMute(handle, mute); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioMute: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioMute: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::IsAudioMuted(const int32_t handle, bool &muted) { + LOGINFO("IsAudioMuted: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsAudioMuted(handle, muted); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsAudioMuted: SUCCESS - handle=%d, muted=%s", handle, muted ? "true" : "false"); + } else { + LOGERR("IsAudioMuted: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) { + LOGINFO("SetAudioDucking: handle=%d, duckingType=%d, duckingAction=%d, level=%d", handle, duckingType, duckingAction, level); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioDucking(handle, duckingType, duckingAction, level); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioDucking: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioDucking: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetStereoMode(const int32_t handle, AudioStereoMode &mode) { + LOGINFO("GetStereoMode: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetStereoMode(handle, mode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetStereoMode: SUCCESS - handle=%d, mode=%d", handle, mode); + } else { + LOGERR("GetStereoMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) { + LOGINFO("SetStereoMode: handle=%d, mode=%d, persist=%s", handle, mode, persist ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetStereoMode(handle, mode, persist); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetStereoMode: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetStereoMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAssociatedAudioMixing(const int32_t handle, const bool mixing) { + LOGINFO("SetAssociatedAudioMixing: handle=%d, mixing=%s", handle, mixing ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAssociatedAudioMixing(handle, mixing); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAssociatedAudioMixing: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAssociatedAudioMixing: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAssociatedAudioMixing(const int32_t handle, bool &mixing) { + LOGINFO("GetAssociatedAudioMixing: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAssociatedAudioMixing(handle, mixing); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAssociatedAudioMixing: SUCCESS - handle=%d, mixing=%s", handle, mixing ? "true" : "false"); + } else { + LOGERR("GetAssociatedAudioMixing: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance) { + LOGINFO("SetAudioFaderControl: handle=%d, mixerBalance=%d", handle, mixerBalance); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioFaderControl(handle, mixerBalance); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioFaderControl: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioFaderControl: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance) { + LOGINFO("GetAudioFaderControl: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioFaderControl(handle, mixerBalance); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioFaderControl: SUCCESS - handle=%d, mixerBalance=%d", handle, mixerBalance); + } else { + LOGERR("GetAudioFaderControl: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage) { + LOGINFO("SetAudioPrimaryLanguage: handle=%d, primaryAudioLanguage=%s", handle, primaryAudioLanguage.c_str()); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioPrimaryLanguage(handle, primaryAudioLanguage); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioPrimaryLanguage: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioPrimaryLanguage: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage) { + LOGINFO("GetAudioPrimaryLanguage: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioPrimaryLanguage(handle, primaryAudioLanguage); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioPrimaryLanguage: SUCCESS - handle=%d, primaryAudioLanguage=%s", handle, primaryAudioLanguage.c_str()); + } else { + LOGERR("GetAudioPrimaryLanguage: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage) { + LOGINFO("SetAudioSecondaryLanguage: handle=%d, secondaryAudioLanguage=%s", handle, secondaryAudioLanguage.c_str()); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioSecondaryLanguage(handle, secondaryAudioLanguage); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioSecondaryLanguage: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioSecondaryLanguage: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage) { + LOGINFO("GetAudioSecondaryLanguage: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioSecondaryLanguage(handle, secondaryAudioLanguage); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioSecondaryLanguage: SUCCESS - handle=%d, secondaryAudioLanguage=%s", handle, secondaryAudioLanguage.c_str()); + } else { + LOGERR("GetAudioSecondaryLanguage: FAILED - result=%u", result); + } + return result; +} + +// Additional key methods - implementing the most commonly used ones +uint32_t Audio::IsAudioOutputConnected(const int32_t handle, bool &isConnected) { + LOGINFO("IsAudioOutputConnected: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsAudioOutputConnected(handle, isConnected); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsAudioOutputConnected: SUCCESS - handle=%d, isConnected=%s", handle, isConnected ? "true" : "false"); + } else { + LOGERR("IsAudioOutputConnected: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability) { + LOGINFO("GetAudioSinkDeviceAtmosCapability: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetAudioSinkDeviceAtmosCapability(handle, atmosCapability); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetAudioSinkDeviceAtmosCapability: SUCCESS - handle=%d, atmosCapability=%d", handle, atmosCapability); + } else { + LOGERR("GetAudioSinkDeviceAtmosCapability: FAILED - result=%u", result); + } + return result; +} + +uint32_t Audio::SetAudioAtmosOutputMode(const int32_t handle, const bool enable) { + LOGINFO("SetAudioAtmosOutputMode: handle=%d, enable=%s", handle, enable ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetAudioAtmosOutputMode(handle, enable); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAudioAtmosOutputMode: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetAudioAtmosOutputMode: FAILED - result=%u", result); + } + return result; +} + + +// NOTE: The remaining methods (like SetAudioDelay, GetAudioDelay, etc.) would follow +// the same pattern. For brevity, I'm implementing the key ones that are commonly used +// and that correspond to the notification handlers we saw in DeviceSettingsManager.h + +// Missing Audio interface methods implementation + +uint32_t Audio::GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) { + uint32_t result = (_platform != nullptr) ? _platform->GetSupportedCompressions(handle, compressions) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioCompression(const int32_t handle, AudioCompression &compression) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioCompression(handle, compression) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioCompression(const int32_t handle, const AudioCompression compression) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioCompression(handle, compression) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +// Missing Audio interface methods implementation + +uint32_t Audio::IsAudioPortEnabled(const int32_t handle, bool &enabled) { + uint32_t result = (_platform != nullptr) ? _platform->IsAudioPortEnabled(handle, enabled) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::EnableAudioPort(const int32_t handle, const bool enable) { + uint32_t result = (_platform != nullptr) ? _platform->EnableAudioPort(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetSupportedARCTypes(const int32_t handle, int32_t &types) { + uint32_t result = (_platform != nullptr) ? _platform->GetSupportedARCTypes(handle, types) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) { + uint32_t result = (_platform != nullptr) ? _platform->SetSAD(handle, sadList, count) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::EnableARC(const int32_t handle, const AudioARCStatus arcStatus) { + uint32_t result = (_platform != nullptr) ? _platform->EnableARC(handle, arcStatus) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetStereoAuto(const int32_t handle, int32_t &mode) { + uint32_t result = (_platform != nullptr) ? _platform->GetStereoAuto(handle, mode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist) { + uint32_t result = (_platform != nullptr) ? _platform->SetStereoAuto(handle, mode, persist) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioEnablePersist(const int32_t handle, bool &enabled, string &portName) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioEnablePersist(handle, enabled, portName) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioEnablePersist(const int32_t handle, const bool enable, const string portName) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioEnablePersist(handle, enable, portName) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) { + uint32_t result = (_platform != nullptr) ? _platform->IsAudioMSDecoded(handle, hasms11Decode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) { + uint32_t result = (_platform != nullptr) ? _platform->IsAudioMS12Decoded(handle, hasms12Decode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioLEConfig(const int32_t handle, bool &enabled) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioLEConfig(handle, enabled) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::EnableAudioLEConfig(const int32_t handle, const bool enable) { + uint32_t result = (_platform != nullptr) ? _platform->EnableAudioLEConfig(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioDelay(const int32_t handle, const uint32_t audioDelay) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioDelay(handle, audioDelay) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioDelay(const int32_t handle, uint32_t &audioDelay) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioDelay(handle, audioDelay) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioDelayOffset(handle, delayOffset) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioDelayOffset(handle, delayOffset) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioCompression(const int32_t handle, const int32_t compressionLevel) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioCompression(handle, compressionLevel) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioCompression(const int32_t handle, int32_t &compressionLevel) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioCompression(handle, compressionLevel) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioDialogEnhancement(const int32_t handle, const int32_t level) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioDialogEnhancement(handle, level) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioDialogEnhancement(const int32_t handle, int32_t &level) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioDialogEnhancement(handle, level) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioDolbyVolumeMode(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioDolbyVolumeMode(handle, enabled) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioIntelligentEqualizerMode(handle, mode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioIntelligentEqualizerMode(handle, mode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioVolumeLeveller(handle, volumeLeveller) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioVolumeLeveller(handle, volumeLeveller) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioBassEnhancer(const int32_t handle, const int32_t boost) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioBassEnhancer(handle, boost) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioBassEnhancer(const int32_t handle, int32_t &boost) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioBassEnhancer(handle, boost) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::EnableAudioSurroundDecoder(const int32_t handle, const bool enable) { + uint32_t result = (_platform != nullptr) ? _platform->EnableAudioSurroudDecoder(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled) { + uint32_t result = (_platform != nullptr) ? _platform->IsAudioSurroudDecoderEnabled(handle, enabled) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioDRCMode(const int32_t handle, const int32_t drcMode) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioDRCMode(handle, drcMode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioDRCMode(const int32_t handle, int32_t &drcMode) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioDRCMode(handle, drcMode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioSurroudVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioSurroudVirtualizer(handle, surroundVirtualizer) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioSurroudVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioSurroudVirtualizer(handle, surroundVirtualizer) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioMISteering(const int32_t handle, const bool enable) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioMISteering(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioMISteering(const int32_t handle, bool &enable) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioMISteering(handle, enable) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioGraphicEqualizerMode(handle, mode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioGraphicEqualizerMode(handle, mode) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioMS12ProfileList(handle, ms12ProfileList) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioMS12Profile(const int32_t handle, string &profile) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioMS12Profile(handle, profile) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioMS12Profile(const int32_t handle, const string& profile) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioMS12Profile(handle, profile) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioMixerLevels(handle, audioInput, volume) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::SetAudioMS12SettingsOverride(const int32_t handle, const string& profileName, const string& profileSettingsName, const string& profileSettingValue, const string profileState) { + uint32_t result = (_platform != nullptr) ? _platform->SetAudioMS12SettingsOverride(handle, profileName, profileSettingsName, profileSettingValue, profileState) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::ResetAudioDialogEnhancement(const int32_t handle) { + uint32_t result = (_platform != nullptr) ? _platform->ResetAudioDialogEnhancement(handle) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::ResetAudioBassEnhancer(const int32_t handle) { + uint32_t result = (_platform != nullptr) ? _platform->ResetAudioBassEnhancer(handle) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::ResetAudioSurroundVirtualizer(const int32_t handle) { + uint32_t result = (_platform != nullptr) ? _platform->ResetAudioSurroundVirtualizer(handle) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::ResetAudioVolumeLeveller(const int32_t handle) { + uint32_t result = (_platform != nullptr) ? _platform->ResetAudioVolumeLeveller(handle) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +uint32_t Audio::GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) { + uint32_t result = (_platform != nullptr) ? _platform->GetAudioHDMIARCPortId(handle, portId) : WPEFramework::Core::ERROR_UNAVAILABLE; + return result; +} + +// ... Additional stub implementations would continue here following the same pattern +// For full implementation, each method would need proper platform delegation \ No newline at end of file diff --git a/plugin/Audio.h b/plugin/Audio.h new file mode 100644 index 0000000..b07bed8 --- /dev/null +++ b/plugin/Audio.h @@ -0,0 +1,248 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsAudio.h" + +#include "hal/dAudio.h" +#include "hal/dAudioImpl.h" +#include "DeviceSettingsTypes.h" + +using namespace WPEFramework::Exchange; + +class Audio { +public: + class INotification { + + public: + virtual ~INotification() = default; + virtual void OnAssociatedAudioMixingChanged(bool mixing) = 0; + virtual void OnAudioFaderControlChanged(int32_t mixerBalance) = 0; + virtual void OnAudioPrimaryLanguageChanged(const std::string& primaryLanguage) = 0; + virtual void OnAudioSecondaryLanguageChanged(const std::string& secondaryLanguage) = 0; + virtual void OnAudioOutHotPlug(AudioPortType portType, uint32_t uiPortNumber, bool isPortConnected) = 0; + virtual void OnAudioFormatUpdate(AudioFormat audioFormat) = 0; + virtual void OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCapability, bool status) = 0; + virtual void OnAudioPortStateChanged(AudioPortState audioPortState) = 0; + virtual void OnAudioLevelChanged(int32_t audioLevel) = 0; + virtual void OnAudioModeEvent(AudioPortType audioPortType, AudioStereoMode audioMode) = 0; + }; + +private: + using IPlatform = hal::dAudio::IPlatform; + using DefaultImpl = dAudioImpl; + + std::shared_ptr _platform; + INotification& _parent; + +public: + + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + // Audio Port Management + uint32_t GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle); + uint32_t GetMS12Capabilities(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + uint32_t GetAudioCapabilities(const int32_t handle, int32_t &capabilities); + uint32_t GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities); + + // Audio Format & Encoding + uint32_t GetAudioFormat(const int32_t handle, AudioFormat &audioFormat); + uint32_t GetAudioEncoding(const int32_t handle, AudioEncoding &encoding); + uint32_t GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + uint32_t GetAudioCompression(const int32_t handle, AudioCompression &compression); + uint32_t SetAudioCompression(const int32_t handle, const AudioCompression compression); + + // Audio Level & Volume Control + uint32_t SetAudioLevel(const int32_t handle, const float audioLevel); + uint32_t GetAudioLevel(const int32_t handle, float &audioLevel); + uint32_t SetAudioGain(const int32_t handle, const float gainLevel); + uint32_t GetAudioGain(const int32_t handle, float &gainLevel); + uint32_t SetAudioMute(const int32_t handle, const bool mute); + uint32_t IsAudioMuted(const int32_t handle, bool &muted); + + // Audio Ducking + uint32_t SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level); + + // Stereo Mode + uint32_t GetStereoMode(const int32_t handle, AudioStereoMode &mode); + uint32_t SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist); + uint32_t GetStereoAuto(const int32_t handle, int32_t &mode); + uint32_t SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist); + + // Associated Audio Mixing + uint32_t SetAssociatedAudioMixing(const int32_t handle, const bool mixing); + uint32_t GetAssociatedAudioMixing(const int32_t handle, bool &mixing); + + // Audio Fader Control + uint32_t SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance); + uint32_t GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance); + + // Audio Language Settings + uint32_t SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage); + uint32_t GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage); + uint32_t SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage); + uint32_t GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage); + + // Output Connection Status + uint32_t IsAudioOutputConnected(const int32_t handle, bool &isConnected); + + // Dolby Atmos + uint32_t GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability); + uint32_t SetAudioAtmosOutputMode(const int32_t handle, const bool enable); + + // Additional Audio Port Methods + uint32_t IsAudioPortEnabled(const int32_t handle, bool &enabled); + uint32_t EnableAudioPort(const int32_t handle, const bool enable); + uint32_t GetSupportedARCTypes(const int32_t handle, int32_t &types); + uint32_t SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count); + uint32_t EnableARC(const int32_t handle, const AudioARCStatus arcStatus); + + // Audio Persistence Configuration + uint32_t GetAudioEnablePersist(const int32_t handle, bool &enabled, std::string &portName); + uint32_t SetAudioEnablePersist(const int32_t handle, const bool enable, const std::string portName); + + // Audio Decoder Status + uint32_t IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode); + uint32_t IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode); + + // Loudness Equivalence Configuration + uint32_t GetAudioLEConfig(const int32_t handle, bool &enabled); + uint32_t EnableAudioLEConfig(const int32_t handle, const bool enable); + + // Audio Delay Controls + uint32_t SetAudioDelay(const int32_t handle, const uint32_t audioDelay); + uint32_t GetAudioDelay(const int32_t handle, uint32_t &audioDelay); + uint32_t SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset); + uint32_t GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset); + + // Audio Dynamic Range Control + uint32_t SetAudioCompression(const int32_t handle, const int32_t compressionLevel); + uint32_t GetAudioCompression(const int32_t handle, int32_t &compressionLevel); + + // Dialog Enhancement + uint32_t SetAudioDialogEnhancement(const int32_t handle, const int32_t level); + uint32_t GetAudioDialogEnhancement(const int32_t handle, int32_t &level); + + // Dolby Volume Mode + uint32_t SetAudioDolbyVolumeMode(const int32_t handle, const bool enable); + uint32_t GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled); + + // Intelligent Equalizer + uint32_t SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode); + uint32_t GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode); + + // Volume Leveller + uint32_t SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller); + uint32_t GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller); + + // Bass Enhancer + uint32_t SetAudioBassEnhancer(const int32_t handle, const int32_t boost); + uint32_t GetAudioBassEnhancer(const int32_t handle, int32_t &boost); + + // Surround Decoder + uint32_t EnableAudioSurroundDecoder(const int32_t handle, const bool enable); + uint32_t IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled); + + // DRC Mode + uint32_t SetAudioDRCMode(const int32_t handle, const int32_t drcMode); + uint32_t GetAudioDRCMode(const int32_t handle, int32_t &drcMode); + + // Surround Virtualizer + uint32_t SetAudioSurroudVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer); + uint32_t GetAudioSurroudVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer); + + // MI Steering + uint32_t SetAudioMISteering(const int32_t handle, const bool enable); + uint32_t GetAudioMISteering(const int32_t handle, bool &enable); + + // Graphic Equalizer + uint32_t SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode); + uint32_t GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode); + + // MS12 Profile Management + uint32_t GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const; + uint32_t GetAudioMS12Profile(const int32_t handle, std::string &profile); + uint32_t SetAudioMS12Profile(const int32_t handle, const std::string& profile); + + // Audio Mixer Levels + uint32_t SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume); + + // MS12 Settings Override + uint32_t SetAudioMS12SettingsOverride(const int32_t handle, const std::string& profileName, const std::string& profileSettingsName, const std::string& profileSettingValue, const std::string profileState); + + // Reset Functions + uint32_t ResetAudioDialogEnhancement(const int32_t handle); + uint32_t ResetAudioBassEnhancer(const int32_t handle); + uint32_t ResetAudioSurroundVirtualizer(const int32_t handle); + uint32_t ResetAudioVolumeLeveller(const int32_t handle); + + // HDMI ARC + uint32_t GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId); + + // Event handler methods for audio state changes + void OnAssociatedAudioMixingChanged(bool mixing); + void OnAudioFaderControlChanged(int32_t mixerBalance); + void OnAudioPrimaryLanguageChanged(const std::string& primaryLanguage); + void OnAudioSecondaryLanguageChanged(const std::string& secondaryLanguage); + void OnAudioPortStateChanged(AudioPortState audioPortState); + void OnAudioLevelChanged(float audioLevel); + void OnAudioModeChanged(AudioPortType portType, AudioStereoMode mode); + void OnAudioFormatUpdate(AudioFormat audioFormat); + void OnAudioOutHotPlug(AudioPortType portType, uint32_t portNumber, bool isPortConnected); + void OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCapability, bool status); + + template + static Audio Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dAudio::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return Audio(parent, std::move(impl)); + } + + private: + Audio(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } +}; \ No newline at end of file diff --git a/plugin/CHANGELOG.md b/plugin/CHANGELOG.md new file mode 100644 index 0000000..9566ca0 --- /dev/null +++ b/plugin/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog + +All notable changes to this RDK Service will be documented in this file. + +* Each RDK Service has a CHANGELOG file that contains all changes done so far. When version is updated, add a entry in the CHANGELOG.md at the top with user friendly information on what was changed with the new version. Please don't mention JIRA tickets in CHANGELOG. + +* Please Add entry in the CHANGELOG for each version change and indicate the type of change with these labels: + * **Added** for new features. + * **Changed** for changes in existing functionality. + * **Deprecated** for soon-to-be removed features. + * **Removed** for now removed features. + * **Fixed** for any bug fixes. + * **Security** in case of vulnerabilities. + +* Changes in CHANGELOG should be updated when commits are added to the main or release branches. There should be one CHANGELOG entry per JIRA Ticket. This is not enforced on sprint branches since there could be multiple changes for the same JIRA ticket during development. + diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt new file mode 100644 index 0000000..bb288d2 --- /dev/null +++ b/plugin/CMakeLists.txt @@ -0,0 +1,147 @@ +# If not stated otherwise in this file or this component's LICENSE file the +# following copyright and licenses apply: +# +# Copyright 2024 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. + +set(PLUGIN_NAME DeviceSettings) +set(MODULE_NAME ${NAMESPACE}${PLUGIN_NAME}) +set(PLUGIN_IMPLEMENTATION ${MODULE_NAME}Imp) + +set(PLUGIN_DEVICESETTINGS_AUTOSTART "true" CACHE STRING "Automatically start DeviceSettings plugin") +set(PLUGIN_DEVICESETTINGS_STARTUPORDER "15" CACHE STRING "To configure startup order of DeviceSettings plugin") +set(PLUGIN_DEVICESETTINGS_MODE "Local" CACHE STRING "Controls if the plugin should run in its own process, in process or remote") + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +find_package(${NAMESPACE}Plugins REQUIRED) +find_package(${NAMESPACE}Definitions REQUIRED) +find_package(CompileSettingsDebug CONFIG REQUIRED) +find_package(WPEFrameworkHelpers REQUIRED) +find_package(PkgConfig REQUIRED) +pkg_check_modules(GLIB2 REQUIRED glib-2.0) +find_library(PROCPS_LIBRARIES NAMES procps) +find_library(OEMHAL_LIBRARIES NAMES ds-hal) + +add_library(${MODULE_NAME} SHARED + Module.cpp + DeviceSettings.cpp) + +set_target_properties(${MODULE_NAME} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES) +target_link_libraries(${MODULE_NAME} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + ${NAMESPACE}Definitions::${NAMESPACE}Definitions) + +install(TARGETS ${MODULE_NAME} + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) + +add_library(${PLUGIN_IMPLEMENTATION} SHARED + Module.cpp + DeviceSettingsImplementation.cpp + DeviceSettingsHALConfig.cpp + DeviceSettingsFPDImplementation.cpp + DeviceSettingsVideoPortImplementation.cpp + DeviceSettingsVideoDeviceImplementation.cpp + DeviceSettingsHdmiInImplementation.cpp + DeviceSettingsAudioImplementation.cpp + DeviceSettingsHostImplementation.cpp + DeviceSettingsDisplayImplementation.cpp + DeviceSettingsCompositeInImplementation.cpp + fpd.cpp + VideoPort.cpp + VideoDevice.cpp + HdmiIn.cpp + Audio.cpp + Host.cpp + Display.cpp + CompositeIn.cpp + DSController.cpp + DSPwrEventListener.cpp + DSProductTraitsHandler.cpp + ) + +include_directories( + ${CMAKE_CURRENT_LIST_DIR} +) + +# DS HAL headers (dsUtl.h, dsError.h, dsTypes.h etc.) — previously pulled in by +# find_package(DS) via DS_INCLUDE_DIRS. Now resolved directly without the full DS package. +find_path(DSHAL_INCLUDE_DIRS NAMES dsTypes.h PATH_SUFFIXES rdk/halif/ds-hal) +if(NOT DSHAL_INCLUDE_DIRS) + message(FATAL_ERROR "DS HAL headers not found (dsTypes.h). Check sysroot.") +endif() + +# Add current directory to target include directories for proper header resolution +target_include_directories(${PLUGIN_IMPLEMENTATION} PRIVATE + ${CMAKE_CURRENT_LIST_DIR} + ${DSHAL_INCLUDE_DIRS} + ${CMAKE_SOURCE_DIR}/../rdk-halif-device_settings/include + ${CMAKE_SOURCE_DIR}/../devicesettings/ds/include + ${GLIB2_INCLUDE_DIRS} +) + +target_compile_definitions(${PLUGIN_IMPLEMENTATION} PRIVATE GLIB_AVAILABLE DS_AUDIO_SETTINGS_PERSISTENCE) + +set_target_properties(${PLUGIN_IMPLEMENTATION} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES) + +#if(RDK_SERVICES_L1_TEST OR RDK_SERVICE_L2_TEST) +# +# target_compile_definitions(${PLUGIN_IMPLEMENTATION} +# PUBLIC +# PLATCO_BOOTTO_STANDBY +# ENABLE_THERMAL_PROTECTION +# OFFLINE_MAINT_REBOOT) +# +# find_library(TESTMOCKLIB_LIBRARIES NAMES TestMocklib) +# if (TESTMOCKLIB_LIBRARIES) +# message ("linking mock libraries ${TESTMOCKLIB_LIBRARIES} library") +# target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${TESTMOCKLIB_LIBRARIES}) +# else (TESTMOCKLIB_LIBRARIES) +# message ("Require ${TESTMOCKLIB_LIBRARIES} library") +# endif () +#endif () + +if(PROCPS_LIBRARIES) + target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${PROCPS_LIBRARIES}) +endif() + +if (MFR_FOUND) + target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${MFR_LIBRARIES}) + target_include_directories(${PLUGIN_IMPLEMENTATION} PRIVATE ${MFR_INCLUDE_DIRS}) +endif() + +find_package(IARMBus REQUIRED) +target_include_directories(${PLUGIN_IMPLEMENTATION} PRIVATE ${IARMBUS_INCLUDE_DIRS}) +target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${IARMBUS_LIBRARIES}) + +target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${DSHALSRV_LIBRARIES}) +target_link_libraries(${PLUGIN_IMPLEMENTATION} PRIVATE ${OEMHAL_LIBRARIES}) + +target_link_libraries(${PLUGIN_IMPLEMENTATION} + PRIVATE + CompileSettingsDebug::CompileSettingsDebug + ${NAMESPACE}Plugins::${NAMESPACE}Plugins + WPEFrameworkHelpers::WPEFrameworkHelpers + ${GLIB2_LIBRARIES}) + +install(TARGETS ${PLUGIN_IMPLEMENTATION} + DESTINATION ${CMAKE_INSTALL_PREFIX}/lib/${STORAGE_DIRECTORY}/plugins) + +write_config(${PLUGIN_NAME}) diff --git a/plugin/CompositeIn.cpp b/plugin/CompositeIn.cpp new file mode 100644 index 0000000..d62f67d --- /dev/null +++ b/plugin/CompositeIn.cpp @@ -0,0 +1,155 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "CompositeIn.h" +#include "hal/dCompositeInImpl.h" + +CompositeIn::CompositeIn(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("CompositeIn Constructor"); + Platform_init(); +} + +void CompositeIn::Platform_init() +{ + LOGINFO("CompositeIn Init - Setting up event callbacks"); + + // Set up callback bundle for CompositeIn events - using global CallbackBundle pattern + CallbackBundle bundle; + + bundle.OnCompositeInHotPlug = [this](const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) { + this->OnCompositeInHotPlug(port, isConnected); // Call public method (matches other components) + }; + bundle.OnCompositeInSignalStatus = [this](const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) { + this->OnCompositeInSignalStatus(port, signalStatus); // Call public method (matches other components) + }; + bundle.OnCompositeInStatus = [this](const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) { + this->OnCompositeInStatus(activePort, isPresented); // Call public method (matches other components) + }; + bundle.OnCompositeInVideoModeUpdate = [this](const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution) { + this->OnCompositeInVideoModeUpdate(activePort, videoResolution); // Call public method (matches other components) + }; + + if (_platform) { + // Use interface method directly - no casting needed + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } + +} + +// CompositeIn interface methods - delegate to platform HAL implementation +uint32_t CompositeIn::GetNrOfCompositeInputs(int32_t &nrCompositeInputs) +{ + LOGINFO("GetNrOfCompositeInputs"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetNrOfCompositeInputs(nrCompositeInputs); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetNrOfCompositeInputs: SUCCESS - platform call completed successfully, nrCompositeInputs=%d", nrCompositeInputs); + } else { + LOGERR("GetNrOfCompositeInputs: FAILED - result=%u", result); + } + return result; +} + +uint32_t CompositeIn::GetCompositeInStatus(CompositeInStatus &status) +{ + LOGINFO("GetCompositeInStatus"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetCompositeInStatus(status); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetCompositeInStatus: SUCCESS - activePort=%d, isPresented=%s", + static_cast(status.activePort), status.isPresented ? "true" : "false"); + } else { + LOGERR("GetCompositeInStatus: FAILED - result=%u", result); + } + return result; +} + +uint32_t CompositeIn::SelectCompositeInPort(const CompositeInPort port) +{ + LOGINFO("SelectCompositeInPort: port=%d", static_cast(port)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SelectCompositeInPort(port); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SelectCompositeInPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SelectCompositeInPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t CompositeIn::ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect) +{ + LOGINFO("ScaleCompositeInVideo: x=%d, y=%d, width=%d, height=%d", + videoRect.x, videoRect.y, videoRect.width, videoRect.height); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().ScaleCompositeInVideo(videoRect); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("ScaleCompositeInVideo: SUCCESS - platform call completed successfully"); + } else { + LOGERR("ScaleCompositeInVideo: FAILED - result=%u", result); + } + return result; +} + +// Public event methods - Called by HAL callbacks to forward to INotification parent (matches other components) +void CompositeIn::OnCompositeInHotPlug(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) +{ + LOGINFO("CompositeIn OnCompositeInHotPlug event: port=%d, isConnected=%s", static_cast(port), isConnected ? "true" : "false"); + _parent.OnCompositeInHotPlug(port, isConnected); +} + +void CompositeIn::OnCompositeInSignalStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) +{ + LOGINFO("CompositeIn OnCompositeInSignalStatus event: port=%d, signalStatus=%d", static_cast(port), static_cast(signalStatus)); + _parent.OnCompositeInSignalStatus(port, signalStatus); +} + +void CompositeIn::OnCompositeInStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) +{ + LOGINFO("CompositeIn OnCompositeInStatus event: activePort=%d, isPresented=%s", static_cast(activePort), isPresented ? "true" : "false"); + _parent.OnCompositeInStatus(activePort, isPresented); +} + +void CompositeIn::OnCompositeInVideoModeUpdate(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution) +{ + LOGINFO("CompositeIn OnCompositeInVideoModeUpdate event: activePort=%d", static_cast(activePort)); + _parent.OnCompositeInVideoModeUpdate(activePort, videoResolution); +} + diff --git a/plugin/CompositeIn.h b/plugin/CompositeIn.h new file mode 100644 index 0000000..3963397 --- /dev/null +++ b/plugin/CompositeIn.h @@ -0,0 +1,102 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsCompositeIn.h" + +#include "hal/dCompositeIn.h" +#include "hal/dCompositeInImpl.h" +#include "DeviceSettingsTypes.h" + +class CompositeIn { + using IPlatform = hal::dCompositeIn::IPlatform; + using DefaultImpl = dCompositeInImpl; + + std::shared_ptr _platform; + +public: + class INotification { + public: + virtual ~INotification() = default; + virtual void OnCompositeInHotPlug(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) = 0; + virtual void OnCompositeInSignalStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) = 0; + virtual void OnCompositeInStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) = 0; + virtual void OnCompositeInVideoModeUpdate(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution) = 0; + }; + +public: + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + // CompositeIn HAL interface methods + uint32_t GetNrOfCompositeInputs(int32_t &nrCompositeInputs); + uint32_t GetCompositeInStatus(CompositeInStatus &status); + uint32_t SelectCompositeInPort(const CompositeInPort port); + uint32_t ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect); + +private: + CompositeIn(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + INotification& _parent; + +public: + template + static CompositeIn Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dCompositeIn::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return CompositeIn(parent, std::move(impl)); + } + + // Public event methods - called by HAL callbacks (matches other component pattern) + void OnCompositeInHotPlug(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected); + void OnCompositeInSignalStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus); + void OnCompositeInStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented); + void OnCompositeInVideoModeUpdate(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution); + ~CompositeIn() {}; + +}; \ No newline at end of file diff --git a/plugin/DSController.cpp b/plugin/DSController.cpp new file mode 100644 index 0000000..b9768cf --- /dev/null +++ b/plugin/DSController.cpp @@ -0,0 +1,1159 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DSController.h" +#include "DSPwrEventListener.h" + +#include +#include +#include +#include + +// C headers with built-in C++ protection +extern "C" { +#include "libIARM.h" +#include "libIBusDaemon.h" +#include "libIBus.h" +#include "iarmUtil.h" +#include "sysMgr.h" +#include "dsUtl.h" +#include "dsError.h" +#include "dsTypes.h" +#include "dsVideoPort.h" +#include "dsDisplay.h" +#include "dsAudio.h" +#include "rfcapi.h" +} + +#include +// For glib APIs - conditional include +#ifdef GLIB_AVAILABLE +#include +#else +// Provide minimal glib-like definitions when glib is not available +typedef void* gpointer; +typedef int gboolean; +typedef unsigned int guint; +typedef struct _GMainLoop GMainLoop; + +static inline GMainLoop* g_main_loop_new(void* context, gboolean is_running) { return nullptr; } +static inline void g_main_loop_run(GMainLoop* loop) {} +static inline void g_main_loop_quit(GMainLoop* loop) {} +static inline void g_main_loop_unref(GMainLoop* loop) {} +static inline gboolean g_main_loop_is_running(GMainLoop* loop) { return FALSE; } +static inline guint g_timeout_add_seconds(guint interval, gboolean (*function)(gpointer), gpointer data) { return 0; } +static inline gboolean g_source_remove(guint tag) { return FALSE; } +#endif + +// DS HAL function declarations +/*extern "C" { + bool dsGetHDMIDDCLineStatus(void); +}*/ + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DSController* DSController::_instance = nullptr; + + bool DSController::IsEUPlatform = false; + char DSController::fallBackResolutionList[6][64]; + + pthread_t DSController::_resolutionThreadID = 0; + pthread_mutex_t DSController::_mutexLock; + pthread_cond_t DSController::_mutexCond; + guint DSController::_hotplugEventSrc = 0; + volatile bool DSController::_dsMgr_thread_exit_flag = false; + int DSController::_tuneReady = 0; + int DSController::_initResolutionFlag = 0; + int DSController::_resolutionRetryCount = 5; + bool DSController::_hdcpAuthenticated = false; + bool DSController::_ignoreEdid = false; + dsDisplayEvent_t DSController::_displayEventStatus = dsDISPLAY_EVENT_MAX; + + // Platform configuration constants + #define RES_MAX_LEN 64 + #define RES_MAX_COUNT 6 + #define DEFAULT_PROGRESSIVE_FPS "60" + #define RESOLUTION_BASE_UHD "2160p" + #define RESOLUTION_BASE_FHD "1080p" + #define RESOLUTION_BASE_FHD_INT "1080i" + #define RESOLUTION_BASE_HD "720p" + #define RESOLUTION_BASE_PAL "576p" + #define RESOLUTION_BASE_NTSC "480p" + #define EU_PROGRESSIVE_FPS "50" + #define EU_INTERLACED_FPS "25" + + // Static Create function implementation + DSController* DSController::Create(DeviceSettingsImp* deviceSettingsInstance) { + return new DSController(deviceSettingsInstance); + } + + DSController::DSController(DeviceSettingsImp* deviceSettingsInstance) + : _deviceSettingsInstance(deviceSettingsInstance) + , _deviceSettings(nullptr) + , _pwrEventListener(nullptr) + , _mainLoop(nullptr) + , _easMode(0) + , m_refCount(1) // Initialize reference count + { + DSController::_instance = this; + + pthread_mutex_init(&_mutexLock, NULL); + pthread_cond_init(&_mutexCond, NULL); + } + + DSController::~DSController() { + LOGINFO("DSController Destructor - Instance Address: %p", this); + + _dsMgr_thread_exit_flag = true; + + if (_mainLoop && g_main_loop_is_running(_mainLoop)) { + g_main_loop_quit(_mainLoop); + } + + pthread_mutex_lock(&_mutexLock); + pthread_cond_signal(&_mutexCond); + pthread_mutex_unlock(&_mutexLock); + + if (_resolutionThreadID != 0) { + pthread_join(_resolutionThreadID, nullptr); + } + + DeinitializeDeviceSettingsComponents(); + DeinitializePowerEventListener(); + + pthread_mutex_destroy(&_mutexLock); + pthread_cond_destroy(&_mutexCond); + + if (_mainLoop) { + g_main_loop_unref(_mainLoop); + _mainLoop = nullptr; + } + + } + + DSController* DSController::instance(DSController* controller) + { + if (controller != nullptr) { + _instance = controller; + } + return _instance; + } + + // Migrated from DSMgr_Start + uint32_t DSController::Start() + { + setupPlatformConfig(); + InitializeDeviceSettingsComponents(); + + setvbuf(stdout, NULL, _IOLBF, 0); + + IARM_Bus_Init(IARM_BUS_DSMGR_NAME); + IARM_Bus_Connect(); + IARM_Bus_RegisterEvent(IARM_BUS_DSMGR_EVENT_MAX); + + Init(); + + _initResolutionFlag = 1; + + dsEdidIgnoreParam_t ignoreEdidParam; + memset(&ignoreEdidParam, 0, sizeof(ignoreEdidParam)); + ignoreEdidParam.handle = dsVIDEOPORT_TYPE_HDMI; + _ignoreEdid = ignoreEdidParam.ignoreEDID; + LOGINFO("ResOverride DSController::Start _ignoreEdid: %d", _ignoreEdid); + + IARM_Bus_RegisterEventHandler(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE, _EventHandler); + IARM_Bus_RegisterCall(IARM_BUS_COMMON_API_SysModeChange, _SysModeChange); + + // Initialize power event listener (migrated from dsMGR) + // Note: service parameter will be passed separately via InitializePowerEventListener() + _pwrEventListener = new DSPwrEventListener(); + LOGINFO("DSPwrEventListener created: %p", _pwrEventListener); + + InitializeResolutionThread(); + + _mainLoop = g_main_loop_new(NULL, FALSE); + if(_mainLoop != NULL){ + g_timeout_add_seconds(300, HeartbeatMsg, _mainLoop); + } else { + LOGERR("Fails to Create a main Loop for DS Manager"); + } + + FILE* fDSCtrptr = fopen("/opt/ddcDelay", "r"); + if (NULL != fDSCtrptr) { + if (0 > fscanf(fDSCtrptr, "%d", &_resolutionRetryCount)) { + LOGERR("Error: fscanf on ddcDelay failed"); + } + fclose(fDSCtrptr); + } + + /* Check TuneReady state and signal the resolution thread if already set. + * Do NOT call SetVideoPortResolution() here — it involves a synchronous + * GetDisplayEdid() (HDMI DDC read, 2-3s) that would block the WPEFramework + * plugin activation thread, preventing dependent plugins from getting a + * PluginInitializerService slot. + * + * The resolution thread (ResolutionThreadFunc) is already running and will + * call SetVideoPortResolution() when it is woken by: + * - TuneReady IARM event (IARM_BUS_SYSMGR_SYSSTATE_TUNEREADY) + * - HDMI hotplug (OnDisplayHDMIHotPlug / EventHandler) + * + * If TuneReady is already set before we start, signal the resolution thread + * now so it picks it up immediately without waiting for an event. */ + IARM_Bus_SYSMgr_GetSystemStates_Param_t tuneReadyParam; + memset(&tuneReadyParam, 0, sizeof(tuneReadyParam)); + IARM_Bus_Call(IARM_BUS_SYSMGR_NAME, IARM_BUS_SYSMGR_API_GetSystemStates, + &tuneReadyParam, sizeof(tuneReadyParam)); + + if (1 == tuneReadyParam.TuneReadyStatus.state) { + LOGINFO("DSController::Start - TuneReady already set, signalling resolution thread"); + _tuneReady = 1; + pthread_mutex_lock(&_mutexLock); + _displayEventStatus = dsDISPLAY_EVENT_CONNECTED; + pthread_cond_signal(&_mutexCond); + pthread_mutex_unlock(&_mutexLock); + } + + return Core::ERROR_NONE; + } + + uint32_t DSController::Stop() + { + _dsMgr_thread_exit_flag = true; + + if(_mainLoop) + { + g_main_loop_quit(_mainLoop); + } + + // TODO + /*dsMgrDeinitPwrControllerEvt(); + PowerController_Term();*/ + + Deinit(); + + IARM_Bus_Disconnect(); + IARM_Bus_Term(); + + return Core::ERROR_NONE; + } + + void DSController::Loop() + { + if(_mainLoop) + { + g_main_loop_run(_mainLoop); + } + } + + void DSController::InitializeResolutionThread() + { + pthread_mutex_init(&_mutexLock, NULL); + if (pthread_cond_init(&_mutexCond, NULL) != 0) { + LOGERR("Failed to create pthread_cond_init _mutexCond"); + return; + } + + if (pthread_create(&_resolutionThreadID, NULL, ResolutionThreadFunc, NULL) != 0) { + LOGERR("Failed pthread_create ResolutionThreadFunc"); + return; + } + } + + void DSController::InitializeDeviceSettingsComponents() + { + try { + // Use the injected instance instead of singleton + _deviceSettings = _deviceSettingsInstance; + if (_deviceSettings) { + _deviceSettings->Register(static_cast(this)); + } else { + LOGERR("Failed to get DeviceSettings implementation instance"); + } + } catch (const std::exception& e) { + LOGERR("Exception during DeviceSettings component initialization: %s", e.what()); + } + } + + void DSController::DeinitializeDeviceSettingsComponents() + { + if (_deviceSettings) { + _deviceSettings->Unregister(static_cast(this)); + } + + _deviceSettings = nullptr; + } + + void DSController::InitializePowerEventListener(PluginHost::IShell* service) + { + LOGINFO("InitializePowerEventListener called with service: %p", service); + + if (_pwrEventListener && service) { + LOGINFO("Initializing DSPwrEventListener with service"); + _pwrEventListener->Init(service); + } else { + LOGERR("Cannot initialize DSPwrEventListener - missing listener or service"); + } + } + + void DSController::DeinitializePowerEventListener() + { + LOGINFO("DeinitializePowerEventListener called"); + + if (_pwrEventListener) { + LOGINFO("Deinitializing and deleting DSPwrEventListener"); + _pwrEventListener->Deinit(); + delete _pwrEventListener; + _pwrEventListener = nullptr; + } + } + + void DSController::Init() + { + LOGINFO("DSController::Init - Initializing Device Settings subsystems"); + } + + void DSController::Deinit() + { + LOGINFO("DSController::Deinit - Terminating Device Settings subsystems"); + } + +// Helper methods using DeviceSettings components + int32_t DSController::GetVideoPortHandle(dsVideoPortType_t port) + { + int32_t handle = 0; + + if (_deviceSettings) { + VideoPortType vpType = static_cast(port); + uint32_t result = _deviceSettings->GetVideoPort(vpType, 0, handle); + if (result != Core::ERROR_NONE) { + // INVALID_PARAM for unconfigured ports (e.g. COMPONENT on TV) is expected + LOGWARN("GetVideoPortHandle: port type %d not available (result=%u)", port, result); + handle = 0; + } + } else { + LOGERR("GetVideoPortHandle: DeviceSettings not initialized"); + } + + return handle; + } + + bool DSController::IsHDMIConnected() + { + bool connected = false; + + if (_deviceSettings) { + int32_t handle = GetVideoPortHandle(dsVIDEOPORT_TYPE_HDMI); + if (handle != 0) { + uint32_t result = _deviceSettings->IsVideoPortDisplayConnected(handle, connected); + if (result != Core::ERROR_NONE) { + LOGERR("IsHDMIConnected: Failed to check connection status"); + connected = false; + } + } + } else { + LOGERR("IsHDMIConnected: DeviceSettings not initialized"); + } + + return connected; + } + + void* DSController::ResolutionThreadFunc(void *arg) + { + dsDisplayEvent_t edisplayEventStatusLocal = dsDISPLAY_EVENT_MAX; + + while (!_dsMgr_thread_exit_flag) { + LOGINFO("_DSMgrResnThreadFunc... wait for for HDMI or Tune Ready Events"); + + pthread_mutex_lock(&_mutexLock); + while (!_dsMgr_thread_exit_flag && _displayEventStatus == dsDISPLAY_EVENT_MAX) { + pthread_cond_wait(&_mutexCond, &_mutexLock); + } + edisplayEventStatusLocal = _displayEventStatus; + pthread_mutex_unlock(&_mutexLock); + + LOGINFO("Setting Resolution On:: HDMI %s Event with TuneReady status = %d", + (edisplayEventStatusLocal == dsDISPLAY_EVENT_CONNECTED ? "Connect" : "Disconnect"), + _tuneReady); + + if (_hotplugEventSrc) { + g_source_remove(_hotplugEventSrc); + LOGINFO("Cleared Hot Plug Event Time source %d", _hotplugEventSrc); + _hotplugEventSrc = 0; + } + + if ((1 == _tuneReady) && (dsDISPLAY_EVENT_CONNECTED == edisplayEventStatusLocal)) { + if (_hdcpAuthenticated) { + if (_instance) { + _instance->SetVideoPortResolution(); + } + } + if (_instance) { + _instance->SetAudioMode(); + } + } + else if ((1 == _tuneReady) && (dsDISPLAY_EVENT_DISCONNECTED == edisplayEventStatusLocal)) { + _hdcpAuthenticated = false; + if (_instance && _instance->isComponentPortPresent()) + { + _hotplugEventSrc = g_timeout_add_seconds((guint)5, SetResolutionHandler, _instance->_mainLoop); + LOGINFO("Schedule a handler to set the resolution after 5 sec for %d time src..", _hotplugEventSrc); + } + } + + pthread_mutex_lock(&_mutexLock); + _displayEventStatus = dsDISPLAY_EVENT_MAX; + pthread_mutex_unlock(&_mutexLock); + } + + return nullptr; + } + + void DSController::SetVideoPortResolution() + { + LOGINFO("SetVideoPortResolution - Enter"); + + int32_t hdmiHandle = 0; + int32_t compHandle = 0; + bool connected = false; + + hdmiHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_HDMI); + if (hdmiHandle != 0) { + usleep(100 * 1000); + + connected = IsHDMIConnected(); + if (_initResolutionFlag && connected) { + #ifdef _INIT_RESN_SETTINGS + int iCount = 0; + while (iCount < _resolutionRetryCount) { + sleep(1); + if (dsGetHDMIDDCLineStatus()) { + break; + } + LOGINFO("Waiting for HDMI DDC Line to be ready for resolution Change..."); + iCount++; + } + #endif + } + + if (connected) { + LOGINFO("Setting HDMI resolution.........."); + SetResolution(hdmiHandle, dsVIDEOPORT_TYPE_HDMI); + } else { + compHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_COMPONENT); + + if (0 != compHandle) { + LOGINFO("Setting Component/Composite Resolution.........."); + SetResolution(compHandle, dsVIDEOPORT_TYPE_COMPONENT); + } else { + LOGINFO("DSController: NULL Handle for component"); + int32_t compositeHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_BB); + if (0 != compositeHandle) { + LOGINFO("Setting BB Composite Resolution.........."); + SetResolution(compositeHandle, dsVIDEOPORT_TYPE_BB); + } else { + LOGINFO("DSController: NULL Handle for Composite"); + int32_t rfHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_RF); + if (0 != rfHandle) { + LOGINFO("Setting RF Resolution.........."); + SetResolution(rfHandle, dsVIDEOPORT_TYPE_RF); + } else { + LOGINFO("DSController: NULL Handle for RF"); + } + } + } + } + } + + LOGINFO("SetVideoPortResolution - Exit"); + } + + void DSController::SetResolution(int32_t handle, dsVideoPortType_t portType) + { + + int32_t displayHandle = 0; + int numResolutions = 0; + bool isValidResolution = false; + + // Return if Handle is NULL + if (handle == 0) { + LOGERR("SetResolution - Got NULL Handle"); + return; + } + + // Get the User Persisted Resolution Based on Handle + VideoPortResolution presolution; + if (_deviceSettings) { + uint32_t result = _deviceSettings->GetVideoPortResolution(handle, presolution); + if (result != Core::ERROR_NONE) { + LOGERR("SetResolution: Failed to get persisted resolution"); + return; + } + } + + LOGINFO("Got User Persisted Resolution - %s", presolution.name.c_str()); + + if (portType == dsVIDEOPORT_TYPE_HDMI) { + // Get The Display Handle + if (_deviceSettings) { + uint32_t result = _deviceSettings->GetDisplay(static_cast(dsVIDEOPORT_TYPE_HDMI), 0, displayHandle); + if (result == Core::ERROR_NONE && displayHandle != 0) { + // Get the EDID Display Handle + DisplayEDID edidData; + IDSVideoPortResolutionIterator* supportedResolutionList = nullptr; + + result = _deviceSettings->GetDisplayEdid(displayHandle, edidData, supportedResolutionList); + if (result == Core::ERROR_NONE) { + DumpHdmiEdidInfo(edidData); + numResolutions = edidData.numOfSupportedResolution; + LOGINFO("numResolutions is %d", numResolutions); + + // If HDMI is connected and Low power Mode, TV might not transmit EDID information + // Change the Resolution in Next Hot plug. Do not set if TV is in DVI mode + if ((0 == numResolutions) || (!edidData.hdmiDeviceType)) { + LOGERR("Do not Set Resolution..The HDMI is not Ready !!"); + LOGERR("numResolutions = %d edidData.hdmiDeviceType = %d !!", numResolutions, edidData.hdmiDeviceType); + return; + } + + std::set edidSupportedNames; + if (supportedResolutionList != nullptr) { + DisplayVideoPortResolution res; + while (supportedResolutionList->Next(res)) { + if (!res.name.empty()) { + edidSupportedNames.insert(res.name); + } + } + supportedResolutionList->Release(); + supportedResolutionList = nullptr; + } + LOGINFO("SetResolution: EDID supported resolution count from iterator: %zu", edidSupportedNames.size()); + + auto isResInEdid = [&](const char* name) -> bool { + if (!name || name[0] == '\0') return false; + bool found = edidSupportedNames.count(std::string(name)) > 0; + if (found) LOGINFO("Resolution supported in EDID: %s", name); + return found; + }; + + // First check if persisted resolution is directly supported + if (isResInEdid(presolution.name.c_str())) { + isValidResolution = true; + LOGINFO("Persisted resolution %s is directly supported", presolution.name.c_str()); + } + + // If resolution with 50Hz not supported, check for same resolution with 60Hz (EU fallback) + if (!isValidResolution && IsEUPlatform) { + char secResn[RES_MAX_LEN]; + // Get secondary resolution based on presolution + if (getSecondaryResolution(const_cast(presolution.name.c_str()), secResn)) { + if (isResInEdid(secResn)) { + LOGINFO("Got Secondary Resolution - %s", secResn); + isValidResolution = true; + // Update presolution to use the secondary resolution + presolution.name = std::string(secResn); + } + } + } + + // Fallback to next best resolution + if (!isValidResolution) { + int index = 0; + char baseResn[RES_MAX_LEN], fbResn[RES_MAX_LEN]; + parseResolution(presolution.name.c_str(), baseResn); + int fNumResolutions = sizeof(fallBackResolutionList) / sizeof(fallBackResolutionList[0]); + + // Find index of base resolution in fallback list + for (int i = 0; i < fNumResolutions; i++) { + if (strcmp(fallBackResolutionList[i], baseResn) == 0) { + index = i; + break; + } + } + + // Try each fallback resolution in order + for (int i = index + 1; i < fNumResolutions; i++) { + if (IsEUPlatform) { + getFallBackResolution(fallBackResolutionList[i], fbResn, 1); // EU fps + LOGINFO("Check next resolution: %s", fbResn); + if (isResInEdid(fbResn)) { + isValidResolution = true; + } + } + if (!isValidResolution) { + getFallBackResolution(fallBackResolutionList[i], fbResn, 0); // default fps + LOGINFO("Check next resolution: %s", fbResn); + if (isResInEdid(fbResn)) { + isValidResolution = true; + } + } + if (isValidResolution) { + LOGINFO("Got Next Best Resolution - %s", fbResn); + // Update presolution to use the fallback resolution + presolution.name = std::string(fbResn); + break; + } + } + } + } + } + } + } else if (portType == dsVIDEOPORT_TYPE_COMPONENT || portType == dsVIDEOPORT_TYPE_BB || portType == dsVIDEOPORT_TYPE_RF) { + // Set the Component / Composite Resolution + LOGINFO("Setting resolution for non-HDMI port type: %d", portType); + isValidResolution = true; // Assume valid for component/composite + } + + // Set The Video Port Resolution if valid + if (isValidResolution && _deviceSettings) { + uint32_t result = _deviceSettings->SetVideoPortResolution(handle, presolution, false, false); + if (result != Core::ERROR_NONE) { + LOGERR("SetResolution: Failed to set resolution"); + } else { + LOGINFO("Setting resolution to: %s", presolution.name.c_str()); + } + } else { + LOGERR("Failed to find any valid resolution!"); + } + + } + + void DSController::SetAudioMode() + { + + if (_easMode == 1) { // IARM_BUS_SYS_MODE_EAS + LOGINFO("EAS In progress..Do not Modify Audio"); + return; + } + + if (!_deviceSettings) { + LOGERR("SetAudioMode: DeviceSettings not initialized"); + return; + } + + // Get supported audio port types - for now use common types + AudioPortType supportedPortTypes[] = {AudioPortType::AUDIO_PORT_TYPE_SPDIF, AudioPortType::AUDIO_PORT_TYPE_HDMI, AudioPortType::AUDIO_PORT_TYPE_SPEAKER}; + int numPorts = sizeof(supportedPortTypes) / sizeof(supportedPortTypes[0]); + + for (int i = 0; i < numPorts; i++) { + int32_t handle = 0; + uint32_t result = _deviceSettings->GetAudioPort(supportedPortTypes[i], 0, handle); + if (result != Core::ERROR_NONE || handle == 0) { + continue; + } + + AudioStereoMode currentMode; + result = _deviceSettings->GetStereoMode(handle, currentMode); + if (result != Core::ERROR_NONE) { + continue; + } + + if (supportedPortTypes[i] == AudioPortType::AUDIO_PORT_TYPE_HDMI) { + // Check if HDMI is connected + int32_t vHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_HDMI); + bool connected = false; + bool isSurround = false; + + if (vHandle != 0 && _deviceSettings) { + _deviceSettings->IsVideoPortDisplayConnected(vHandle, connected); + } + + if (!connected) { + LOGINFO("HDMI Not Connected ..Do not Set Audio on HDMI !!!"); + continue; + } + + int32_t autoMode = 0; + result = _deviceSettings->GetStereoAuto(handle, autoMode); + if (result == Core::ERROR_NONE && autoMode) { + // If auto, then force surround + currentMode = AudioStereoMode::AUDIO_STEREO_SURROUND; + } + + // Assume surround is supported + isSurround = true; + + if (!isSurround) { + // If Surround not supported, then force Stereo + currentMode = AudioStereoMode::AUDIO_STEREO_STEREO; + LOGINFO("Surround mode not Supported on HDMI ..Set Stereo"); + } + } + + LOGINFO("Audio mode for audio port %d is : %d", static_cast(supportedPortTypes[i]), static_cast(currentMode)); + _deviceSettings->SetStereoMode(handle, currentMode, false); + } + + } + + void DSController::SetEASAudioMode() + { + + if (_easMode != 1) { // IARM_BUS_SYS_MODE_EAS + LOGINFO("EAS Not In progress..Do not Modify Audio"); + return; + } + + if (!_deviceSettings) { + LOGERR("SetEASAudioMode: DeviceSettings not initialized"); + return; + } + + // Get supported audio port types - for now use common types + AudioPortType supportedPortTypes[] = {AudioPortType::AUDIO_PORT_TYPE_SPDIF, AudioPortType::AUDIO_PORT_TYPE_HDMI, AudioPortType::AUDIO_PORT_TYPE_SPEAKER}; + int numPorts = sizeof(supportedPortTypes) / sizeof(supportedPortTypes[0]); + + for (int i = 0; i < numPorts; i++) { + int32_t handle = 0; + uint32_t result = _deviceSettings->GetAudioPort(supportedPortTypes[i], 0, handle); + if (result != Core::ERROR_NONE || handle == 0) { + continue; + } + + AudioStereoMode currentMode; + result = _deviceSettings->GetStereoMode(handle, currentMode); + if (result != Core::ERROR_NONE) { + continue; + } + + if (currentMode == AudioStereoMode::AUDIO_STEREO_PASSTHROUGH) { + // In EAS, fallback to Stereo + currentMode = AudioStereoMode::AUDIO_STEREO_STEREO; + } + + LOGINFO("EAS Audio mode for audio port %d is : %d", static_cast(supportedPortTypes[i]), static_cast(currentMode)); + _deviceSettings->SetStereoMode(handle, currentMode, false); + } + + } + + void DSController::SetBackgroundColor(dsVideoBackgroundColor_t color) + { + + // Get the HDMI Video Port Handle + int32_t hdmiHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_HDMI); + + if (hdmiHandle != 0 && _deviceSettings) { + VideoBackgroundColor bgColor = static_cast(color); + uint32_t result = _deviceSettings->SetBackgroundColor(hdmiHandle, bgColor); + if (result != Core::ERROR_NONE) { + LOGERR("SetBackgroundColor: Failed to set background color"); + } + } + + } + + void DSController::DumpHdmiEdidInfo(const DisplayEDID& edidData) + { + LOGINFO("Connected HDMI Display Device Info"); + + if (!edidData.monitorName.empty()) + LOGINFO("HDMI Monitor Name is %s", edidData.monitorName.c_str()); + LOGINFO("HDMI Manufacturing ID is %d", edidData.serialNumber); + LOGINFO("HDMI Product Code is %d", edidData.productCode); + LOGINFO("HDMI Device Type is %s", edidData.hdmiDeviceType ? "HDMI" : "DVI"); + LOGINFO("HDMI Sink Device %s a Repeater", edidData.isRepeater ? "is" : "is not"); + LOGINFO("HDMI Physical Address is %d:%d:%d:%d", + edidData.physicalAddressA, edidData.physicalAddressB, + edidData.physicalAddressC, edidData.physicalAddressD); + + } + + void DSController::ScheduleEdidDump() + { + // Schedule EDID dump after 1 second using GLib + g_timeout_add_seconds((guint)1, DumpEdidOnChecksumDiff, NULL); + } + + bool DSController::isEUPlatform() + { + char line[256]; + bool isEUflag = false; + const char* devPropPath = "/etc/device.properties"; + char deviceProp[15] = "FRIENDLY_ID"; + const char* USRegion = " US"; + + FILE *file = fopen(devPropPath, "r"); + if (file == NULL) { + LOGERR("Unable to open file %s", devPropPath); + return false; + } + + while (fgets(line, sizeof(line), file)) { + if (strstr(line, deviceProp) != NULL) { + if (strstr(line, USRegion) != NULL) { + LOGINFO("Detected US region: %s, isEUflag:%d", line, isEUflag); + } else { // EU - UK/IT/DE + isEUflag = true; + LOGINFO("Detected EU region: %s, isEUflag:%d", line, isEUflag); + } + break; + } + } + fclose(file); + return isEUflag; + } + + void DSController::setupPlatformConfig() + { + const char* resList[] = {"2160p","1080p","1080i","720p","576p","480p"}; + int count = 0, n = sizeof(resList) / sizeof(resList[0]); + + IsEUPlatform = isEUPlatform(); + + for (int i = 0; i < n; i++) { + // Include 576p for EU only + if ((strstr(resList[i], "576p") != NULL) && !IsEUPlatform) { + continue; + } + if (count < RES_MAX_COUNT) { + snprintf(fallBackResolutionList[count], RES_MAX_LEN, "%s", resList[i]); + LOGINFO("Fallback resolution[%d]: %s", count, fallBackResolutionList[count]); + count++; + } else { + break; + } + } + } + + bool DSController::getSecondaryResolution(char* res, char *secRes) + { + bool ret = true; + + if (strstr(res, RESOLUTION_BASE_HD) != NULL) { + snprintf(secRes, RES_MAX_LEN, "%s", RESOLUTION_BASE_HD); // 720p + } else if (strstr(res, RESOLUTION_BASE_FHD) != NULL) { + snprintf(secRes, RES_MAX_LEN, "%s%s", RESOLUTION_BASE_FHD, DEFAULT_PROGRESSIVE_FPS); // 1080p60 + } else if (strstr(res, RESOLUTION_BASE_FHD_INT) != NULL) { + snprintf(secRes, RES_MAX_LEN, "%s", RESOLUTION_BASE_FHD_INT); // 1080i + } else if (strstr(res, RESOLUTION_BASE_UHD) != NULL) { + snprintf(secRes, RES_MAX_LEN, "%s%s", RESOLUTION_BASE_UHD, DEFAULT_PROGRESSIVE_FPS); // 2160p60 + } else { + ret = false; // For other resolutions 480p 576p + } + + LOGINFO("Secondary resolution for %s: %s (ret=%d)", res, secRes, ret); + return ret; + } + + void DSController::parseResolution(const char* pResn, char* bResn) + { + char tmpResn[RES_MAX_LEN]; + int len = 0; + + snprintf(tmpResn, sizeof(tmpResn), "%s", pResn); + char *token = strtok(tmpResn, "ip"); + strncpy(bResn, token, RES_MAX_LEN); + len = strlen(bResn); + + if (strchr(pResn, 'i') != NULL) { + snprintf(bResn + len, RES_MAX_LEN - len, "%s", "i"); // Append 'i' + } else if (strchr(pResn, 'p') != NULL) { + snprintf(bResn + len, RES_MAX_LEN - len, "%s", "p"); // Append 'p' + } + + LOGINFO("Parsed resolution from %s to %s", pResn, bResn); + } + + void DSController::getFallBackResolution(char* Resn, char *fbResn, int flag) + { + char tmpResn[RES_MAX_LEN]; + snprintf(tmpResn, RES_MAX_LEN, "%s", Resn); + int len = strlen(tmpResn); + + if (flag) { // EU + if ((strcmp(Resn, RESOLUTION_BASE_UHD) == 0) || + (strcmp(Resn, RESOLUTION_BASE_FHD) == 0) || + (strcmp(Resn, RESOLUTION_BASE_HD) == 0)) { + snprintf(tmpResn + len, sizeof(tmpResn) - len, "%s", EU_PROGRESSIVE_FPS); // 2160p50, 1080p50, 720p50 + } else if (strcmp(Resn, RESOLUTION_BASE_FHD_INT) == 0) { + snprintf(tmpResn + len, sizeof(tmpResn) - len, "%s", EU_INTERLACED_FPS); // 1080i25 + } else { + // do nothing for 576p, 480p + } + } else { // US + if ((strcmp(Resn, RESOLUTION_BASE_UHD) == 0) || + (strcmp(Resn, RESOLUTION_BASE_FHD) == 0)) { + snprintf(tmpResn + len, sizeof(tmpResn) - len, "%s", DEFAULT_PROGRESSIVE_FPS); // 2160p60, 1080p60 + } + } + + snprintf(fbResn, RES_MAX_LEN, "%s", tmpResn); + LOGINFO("Fallback resolution for %s (EU=%d): %s", Resn, flag, fbResn); + } + + bool DSController::isResolutionSupported(dsDisplayEDID_t *edidData, int numResolutions, + int pNumResolutions, char *Resn, int* index) + { + bool supported = false; + dsVideoPortResolution_t *setResn = NULL; + + for (int i = numResolutions - 1; i >= 0; i--) { + setResn = &(edidData->suppResolutionList[i]); + if (strcmp(setResn->name, Resn) == 0) { + // Check if platform supports this resolution + // Note: kResolutions would need to be defined or passed as parameter + // For now, we'll mark as supported if found in EDID + LOGINFO("Resolution supported in EDID: %s", Resn); + supported = true; + *index = i; + break; + } + } + + return supported; + } + + // Static callback functions + gboolean DSController::HeartbeatMsg(gpointer data) + { + LOGINFO("I-ARM BUS DS Mgr: HeartBeat ping."); + return TRUE; + } + + gboolean DSController::SetResolutionHandler(gpointer data) + { + LOGINFO("Set Video Resolution after delayed time .."); + if (_instance) { + _instance->SetVideoPortResolution(); + _instance->_hotplugEventSrc = 0; + } + return FALSE; + } + + gboolean DSController::DumpEdidOnChecksumDiff(gpointer data) + { + LOGINFO("dumpEdidOnChecksumDiff HDMI-EDID Dump>>>>>>>>>>>>>>"); + + if (_instance && _instance->_deviceSettings) { + int32_t displayHandle = 0; + uint32_t result = _instance->_deviceSettings->GetDisplay(static_cast(dsVIDEOPORT_TYPE_HDMI), 0, displayHandle); + + if (result == Core::ERROR_NONE && displayHandle != 0) { + static int cached_EDID_checksum = 0; + int current_EDID_checksum = 0; + + uint8_t edidBytes[512]; + uint16_t length = sizeof(edidBytes); + + result = _instance->_deviceSettings->GetDisplayEdidBytes(displayHandle, edidBytes, length); + if (result == Core::ERROR_NONE && length > 0 && length <= 512) { + for (int i = 0; i < (length / 128); i++) + current_EDID_checksum += edidBytes[(i+1)*128 - 1]; + + if ((cached_EDID_checksum == 0) || (current_EDID_checksum != cached_EDID_checksum)) { + cached_EDID_checksum = current_EDID_checksum; + LOGINFO("HDMI-EDID Dump detected changes"); + } + } + } + } + + return false; + } + + void DSController::EventHandler(const char *owner, int eventId, void *data, size_t len) + { + + // Allows dsmgr to set initial resolution irrespective of ignore edid only during boot + static bool bootup_flag_enabled = true; + + // Handle only Sys Manager Events + if (strcmp(owner, IARM_BUS_SYSMGR_NAME) == 0) { + // Only handle state events + if (eventId != IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE) return; + + IARM_Bus_SYSMgr_EventData_t* sysEventData = (IARM_Bus_SYSMgr_EventData_t*)data; + IARM_Bus_SYSMgr_SystemState_t stateId = sysEventData->data.systemStates.stateId; + int state = sysEventData->data.systemStates.state; + LOGINFO("EventHandler invoked for stateid %d of state %d", stateId, state); + + switch (stateId) { + case IARM_BUS_SYSMGR_SYSSTATE_TUNEREADY: + LOGINFO("Tune Ready Events in DS Manager"); + + if (0 == _tuneReady) { + _tuneReady = 1; + + // Set audio mode from persistent + SetAudioMode(); + + // Un-block the Resolution Settings Thread + pthread_mutex_lock(&_mutexLock); + pthread_cond_signal(&_mutexCond); + pthread_mutex_unlock(&_mutexLock); + } + break; + default: + break; + } + } else if (strcmp(owner, IARM_BUS_DSMGR_NAME) == 0) { + switch (eventId) { + case IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG: + { + IARM_Bus_DSMgr_EventData_t* eventData = (IARM_Bus_DSMgr_EventData_t*)data; + + LOGINFO("Got HDMI %s Event", + (eventData->data.hdmi_hpd.event == dsDISPLAY_EVENT_CONNECTED ? "Connect" : "Disconnect")); + + SetBackgroundColor(dsVIDEO_BGCOLOR_NONE); + + // Un-Block the Resolution Settings Thread + pthread_mutex_lock(&_mutexLock); + _displayEventStatus = ((eventData->data.hdmi_hpd.event == dsDISPLAY_EVENT_CONNECTED) ? + dsDISPLAY_EVENT_CONNECTED : dsDISPLAY_EVENT_DISCONNECTED); + pthread_cond_signal(&_mutexCond); + pthread_mutex_unlock(&_mutexLock); + } + break; + + case IARM_BUS_DSMGR_EVENT_HDCP_STATUS: + { + IARM_Bus_DSMgr_EventData_t* eventData = (IARM_Bus_DSMgr_EventData_t*)data; + IARM_Bus_SYSMgr_EventData_t HDCPeventData; + int status = eventData->data.hdmi_hdcp.hdcpStatus; + + // HDCP is enabled + HDCPeventData.data.systemStates.stateId = IARM_BUS_SYSMGR_SYSSTATE_HDCP_ENABLED; + HDCPeventData.data.systemStates.state = 1; + + if (status == dsHDCP_STATUS_AUTHENTICATED) { + LOGINFO("Changed status to HDCP Authentication Pass !!!!!!!!"); + HDCPeventData.data.systemStates.state = 1; + _hdcpAuthenticated = true; + LOGINFO("HDCP success - Cleared hotplug_event_src Time source %d and set resolution immediately", _hotplugEventSrc); + + if (_hotplugEventSrc) { + _hotplugEventSrc = 0; + } + + SetBackgroundColor(dsVIDEO_BGCOLOR_NONE); + if ((!_ignoreEdid) || bootup_flag_enabled) { + SetVideoPortResolution(); + if (bootup_flag_enabled) + bootup_flag_enabled = false; + } + ScheduleEdidDump(); + } else if (status == dsHDCP_STATUS_AUTHENTICATIONFAILURE) { + LOGERR("Changed status to HDCP Authentication Fail !!!!!!!!"); + HDCPeventData.data.systemStates.state = 0; + SetBackgroundColor(dsVIDEO_BGCOLOR_BLUE); + _hdcpAuthenticated = false; + if (!_ignoreEdid) { + SetVideoPortResolution(); + } + ScheduleEdidDump(); + } + + IARM_Bus_BroadcastEvent(IARM_BUS_SYSMGR_NAME, (IARM_EventId_t)IARM_BUS_SYSMGR_EVENT_SYSTEMSTATE, + (void*)&HDCPeventData, sizeof(HDCPeventData)); + } + break; + + default: + break; + } + } + + } + + void DSController::SysModeChange(void *arg) + { + + IARM_Bus_CommonAPI_SysModeChange_Param_t* param = (IARM_Bus_CommonAPI_SysModeChange_Param_t*)arg; + int isNextEAS = 0; // IARM_BUS_SYS_MODE_NORMAL + + LOGINFO("Recvd Sysmode Change::New mode --> %d, Old mode --> %d", param->newMode, param->oldMode); + + if ((param->newMode == IARM_BUS_SYS_MODE_EAS) || + (param->newMode == IARM_BUS_SYS_MODE_NORMAL)) { + isNextEAS = param->newMode; + } else { + // Do not process any other mode change as of now for DS Manager + return; + } + + if ((_easMode == IARM_BUS_SYS_MODE_EAS) && (isNextEAS == IARM_BUS_SYS_MODE_NORMAL)) { + _easMode = IARM_BUS_SYS_MODE_NORMAL; + SetAudioMode(); + } else if ((_easMode == IARM_BUS_SYS_MODE_NORMAL) && (isNextEAS == IARM_BUS_SYS_MODE_EAS)) { + // Change the Audio Mode to Stereo if Current Audio Setting is Passthrough + _easMode = IARM_BUS_SYS_MODE_EAS; + SetEASAudioMode(); + } else { + // no op for no mode change + } + + } + + // Static IARM event handlers + void DSController::_EventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len) + { + if (_instance) { + _instance->EventHandler(owner, eventId, data, len); + } + } + + IARM_Result_t DSController::_SysModeChange(void *arg) + { + if (_instance) { + _instance->SysModeChange(arg); + } + return IARM_RESULT_SUCCESS; + } + + // Display::INotification implementation - Only for HDMI hotplug events + void DSController::OnDisplayRxSense(const DisplayEvent displayEvent) { + LOGINFO("OnDisplayRxSense: displayEvent = %d", static_cast(displayEvent)); + } + + void DSController::OnDisplayHDCPStatus() { + LOGINFO("OnDisplayHDCPStatus: HDCP status event"); + } + + void DSController::OnDisplayHDMIHotPlug(const DisplayEvent displayEvent) { + LOGINFO("OnDisplayHDMIHotPlug: displayEvent = %d - Converting to IARM event", static_cast(displayEvent)); + + IARM_Bus_DSMgr_EventData_t eventData; + eventData.data.hdmi_hpd.event = (displayEvent == DisplayEvent::DS_DISPLAY_EVENT_CONNECTED) ? + dsDISPLAY_EVENT_CONNECTED : dsDISPLAY_EVENT_DISCONNECTED; + + EventHandler(IARM_BUS_DSMGR_NAME, IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, &eventData, sizeof(eventData)); + } + + // Helper methods implementation + bool DSController::isComponentPortPresent() + { + int32_t compHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_COMPONENT); + bool present = (compHandle != 0); + + if (!present) { + // Also check for BB composite as fallback + int32_t compositeHandle = GetVideoPortHandle(dsVIDEOPORT_TYPE_BB); + present = (compositeHandle != 0); + } + + LOGINFO("isComponentPortPresent: %s", present ? "true" : "false"); + return present; + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DSController.h b/plugin/DSController.h new file mode 100644 index 0000000..08e147e --- /dev/null +++ b/plugin/DSController.h @@ -0,0 +1,222 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// IARM includes for event handling +#include "iarmUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fpd.h" +#include "HdmiIn.h" + +#include "DeviceSettingsTypes.h" + +#include "DeviceSettingsImplementation.h" +#include "DSPwrEventListener.h" +#include +#include +#include + +#include "dsTypes.h" +#include "dsVideoPort.h" +#include "dsDisplay.h" +#include "dsAudio.h" + +#include "DeviceSettingsTypes.h" + +typedef struct _GMainLoop GMainLoop; +typedef int gboolean; +typedef void* gpointer; +typedef unsigned int guint; + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsImp; + + class DSController : public Exchange::IDeviceSettingsDisplay::IDisplayHDMIHotPlugNotification { + public: + DSController(DeviceSettingsImp* deviceSettingsInstance); + ~DSController(); + + static DSController* Create(DeviceSettingsImp* deviceSettingsInstance); + static DSController* instance(DSController* DSController = nullptr); + + DSController(const DSController&) = delete; + DSController& operator=(const DSController&) = delete; + + // Build QueryInterface implementation for Core::IUnknown + BEGIN_INTERFACE_MAP(DSController) + INTERFACE_ENTRY(Exchange::IDeviceSettingsDisplay::IDisplayHDMIHotPlugNotification) + END_INTERFACE_MAP + + // Implement Core::IUnknown methods. Some branches expose AddRef as void, + // others as uint32_t, so deduce from Core::IUnknown to stay ABI-compatible. + using AddRefReturnType = decltype(std::declval().AddRef()); + using ReleaseReturnType = decltype(std::declval().Release()); + + AddRefReturnType AddRef() const override { + return AddRefImpl(std::is_void{}); + } + + ReleaseReturnType Release() const override { + return ReleaseImpl(std::is_void{}); + } + + public: + void InitializeIARM(); + uint32_t Start(); + uint32_t Stop(); + void Loop(); + + void Init(); + void Deinit(); + + void InitializeDeviceSettingsComponents(); + void DeinitializeDeviceSettingsComponents(); + void InitializePowerEventListener(PluginHost::IShell* service); + void DeinitializePowerEventListener(); + + int getEASMode() const { return _easMode; } + + void OnDisplayRxSense(const DisplayEvent displayEvent); + void OnDisplayHDCPStatus(); + void OnDisplayHDMIHotPlug(const DisplayEvent displayEvent); + + private: + uint32_t AddRefImpl(std::false_type) const { + return Core::InterlockedIncrement(m_refCount); + } + + void AddRefImpl(std::true_type) const { + Core::InterlockedIncrement(m_refCount); + } + + uint32_t ReleaseImpl(std::false_type) const { + const uint32_t l_Ref = Core::InterlockedDecrement(m_refCount); + if (l_Ref == 0) { + delete this; + } + return l_Ref; + } + + void ReleaseImpl(std::true_type) const { + const uint32_t l_Ref = Core::InterlockedDecrement(m_refCount); + if (l_Ref == 0) { + delete this; + } + } + + void InitializeResolutionThread(); + void SetVideoPortResolution(); + void SetResolution(int32_t handle, dsVideoPortType_t portType); + void SetAudioMode(); + void SetEASAudioMode(); + void SetBackgroundColor(dsVideoBackgroundColor_t color); + void DumpHdmiEdidInfo(const DisplayEDID& edidData); + void ScheduleEdidDump(); + + void EventHandler(const char *owner, int eventId, void *data, size_t len); + void SysModeChange(void *arg); + + int32_t GetVideoPortHandle(dsVideoPortType_t port); + bool IsHDMIConnected(); + bool isComponentPortPresent(); + bool dsGetHDMIDDCLineStatus(); + + static void setupPlatformConfig(); + static bool isEUPlatform(); + static bool getSecondaryResolution(char* res, char *secRes); + static void parseResolution(const char* pResn, char* bResn); + static void getFallBackResolution(char* Resn, char *fbResn, int flag); + static bool isResolutionSupported(dsDisplayEDID_t *edidData, int numResolutions, + int pNumResolutions, char *Resn, int* index); + + static void* ResolutionThreadFunc(void *arg); + + static gboolean HeartbeatMsg(gpointer data); + static gboolean SetResolutionHandler(gpointer data); + static gboolean DumpEdidOnChecksumDiff(gpointer data); + + static void _EventHandler(const char *owner, IARM_EventId_t eventId, void *data, size_t len); + static IARM_Result_t _SysModeChange(void *arg); + + private: + static DSController* _instance; + + // Injected DeviceSettings instance for dependency injection + DeviceSettingsImp* _deviceSettingsInstance; + + DeviceSettingsImp* _deviceSettings; + DSPwrEventListener* _pwrEventListener; + + static pthread_t _resolutionThreadID; + static pthread_mutex_t _mutexLock; + static pthread_cond_t _mutexCond; + + GMainLoop* _mainLoop; + static guint _hotplugEventSrc; + + static volatile bool _dsMgr_thread_exit_flag; + + static int _tuneReady; + static int _initResolutionFlag; + static int _resolutionRetryCount; + static bool _hdcpAuthenticated; + static bool _ignoreEdid; + static dsDisplayEvent_t _displayEventStatus; + + int _easMode; + + static bool IsEUPlatform; + static char fallBackResolutionList[6][64]; + + private: + mutable Core::CriticalSection _apiLock; + mutable Core::CriticalSection _callbackLock; + + // Reference counting for Core::IUnknown + mutable uint32_t m_refCount; + }; +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DSProductTraitsHandler.cpp b/plugin/DSProductTraitsHandler.cpp new file mode 100644 index 0000000..b779efc --- /dev/null +++ b/plugin/DSProductTraitsHandler.cpp @@ -0,0 +1,599 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DSProductTraitsHandler.h" +#include "DeviceSettingsTypes.h" +#include "DeviceSettingsImplementation.h" + +#include +#include +#include +#include +#include + +namespace WPEFramework { +namespace Plugin { +namespace DSProductTraits { + +// LambdaJob helper for WPEFramework timer callbacks +class LambdaJob : public Core::IDispatch { +public: + LambdaJob(std::function job) : _job(job) {} + void Dispatch() override { _job(); } +private: + std::function _job; +}; + +const unsigned int REBOOT_REASON_RETRY_INTERVAL_SECONDS = 2; +UXController* UXController::_singleton = nullptr; + +static reboot_type_t GetRebootType() +{ + const char* file_updated_flag = "/tmp/Update_rebootInfo_invoked"; + const char* reboot_info_file_name = "/opt/secure/reboot/previousreboot.info"; + const char* hard_reboot_match_string = R"("reason":"POWER_ON_RESET")"; + reboot_type_t ret = reboot_type_t::SOFT; + + if (0 != access(file_updated_flag, F_OK)) { + LOGINFO("Error! Reboot info file isn't updated yet"); + ret = reboot_type_t::UNAVAILABLE; + } else { + std::ifstream reboot_info_file(reboot_info_file_name); + std::string line; + if (true == reboot_info_file.is_open()) { + while (std::getline(reboot_info_file, line)) { + if (std::string::npos != line.find(hard_reboot_match_string)) { + LOGINFO("Detected hard reboot"); + ret = reboot_type_t::HARD; + break; + } + } + } else { + LOGINFO("Failed to open reboot info file"); + } + } + return ret; +} + +static void ScheduleRebootReasonCheck(UXController* controller, unsigned int retryCount = 0) +{ + constexpr unsigned int max_count = 120 / REBOOT_REASON_RETRY_INTERVAL_SECONDS; + + reboot_type_t reboot_type = GetRebootType(); + if (reboot_type_t::UNAVAILABLE == reboot_type) { + if (retryCount < max_count) { + // Schedule next retry using a separate thread (mimics g_timeout_add_seconds) + std::thread retryThread([controller, retryCount]() { + std::this_thread::sleep_for(std::chrono::seconds(REBOOT_REASON_RETRY_INTERVAL_SECONDS)); + ScheduleRebootReasonCheck(controller, retryCount + 1); + }); + retryThread.detach(); + } else { + LOGINFO("Exceeded retry limit"); + } + } else { + LOGINFO("Got reboot reason in async check. Applying display configuration"); + controller->SyncDisplayPortsWithRebootReason(reboot_type); + } +} + +static inline bool DoForceDisplayOnPostReboot() +{ + const char* flag_filename = "/opt/force_display_on_after_reboot"; + bool ret = false; + if (0 == access(flag_filename, F_OK)) { + ret = true; + } + LOGINFO("DoForceDisplayOnPostReboot: %s", (true == ret ? "true" : "false")); + return ret; +} + +/********************************* UXController Base Class ********************************/ + +UXController::UXController(unsigned int id, const std::string& name, deviceType_t deviceType) + : _id(id) + , _name(name) + , _deviceType(deviceType) + , _invalidateAsyncBootloaderPattern(false) + , _firstPowerTransitionComplete(false) + , _deviceSettings(nullptr) +{ + LOGINFO("UXController initializing for profile id %d, name %s", id, name.c_str()); + + // Get DeviceSettings implementation instance + _deviceSettings = DeviceSettingsImp::instance(); + if (!_deviceSettings) { + LOGERR("Failed to get DeviceSettings implementation instance"); + } + + InitializeSafeDefaults(); +} + +void UXController::InitializeSafeDefaults() +{ + _enableMultiColourLedSupport = false; + _enableSilentRebootSupport = true; + _preferedPowerModeOnReboot = POWER_MODE_LAST_KNOWN; + _invalidateAsyncBootloaderPattern = false; + _firstPowerTransitionComplete = false; + _ledColorInOnState = 0; + _ledColorInStandby = 0; + + if (DEVICE_TYPE_STB == _deviceType) { + _ledEnabledInStandby = false; + _ledEnabledInOnState = true; + } else { + _ledEnabledInStandby = true; + _ledEnabledInOnState = false; + } +} + +bool UXController::SetBootloaderPatternInternal(mfrBlPattern_t pattern) +{ + _mutex.lock(); + _invalidateAsyncBootloaderPattern = true; + _mutex.unlock(); + return SetBootloaderPattern(pattern); +} + +bool UXController::SetBootloaderPattern(mfrBlPattern_t pattern) const +{ + bool ret = true; + + if (false == _enableSilentRebootSupport) { + return true; + } + + IARM_Bus_MFRLib_SetBLPattern_Param_t mfrparam; + mfrparam.pattern = pattern; + if (IARM_RESULT_SUCCESS != IARM_Bus_Call(IARM_BUS_MFRLIB_NAME, IARM_BUS_MFRLIB_API_SetBootLoaderPattern, + (void*)&mfrparam, sizeof(mfrparam))) { + LOGINFO("Warning! Call to SetBootLoaderPattern failed"); + ret = false; + } else { + LOGINFO("Successfully set bootloader pattern %d", (int)pattern); + } + return ret; +} + +void UXController::SetBootloaderPatternAsync(mfrBlPattern_t pattern) const +{ + bool ret = true; + const unsigned int retry_interval_seconds = 5; + unsigned int remaining_retries = 12; + + LOGINFO("SetBootloaderPatternAsync start for pattern 0x%x", pattern); + do { + std::this_thread::sleep_for(std::chrono::seconds(retry_interval_seconds)); + std::unique_lock lock(_mutex); + if (false == _invalidateAsyncBootloaderPattern) { + ret = SetBootloaderPattern(pattern); + } else { + LOGINFO("Bootloader pattern invalidated. Aborting"); + break; + } + } while ((false == ret) && (0 < --remaining_retries)); + + LOGINFO("SetBootloaderPatternAsync returns"); +} + +bool UXController::SetBootloaderPatternFaultTolerant(mfrBlPattern_t pattern) +{ + bool ret = true; + ret = SetBootloaderPattern(pattern); + if (false == ret) { + _mutex.lock(); + _invalidateAsyncBootloaderPattern = false; + _mutex.unlock(); + std::thread retry_thread(&UXController::SetBootloaderPatternAsync, this, pattern); + retry_thread.detach(); + } + return ret; +} + +void UXController::SyncPowerLedWithPowerState(PowerState power_state) const +{ + if (true == _enableMultiColourLedSupport) { + LOGINFO("Warning! Device supports multi-colour LEDs but it isn't handled"); + } + + bool led_state; + if (WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == power_state) { + led_state = _ledEnabledInOnState; + } else { + led_state = _ledEnabledInStandby; + } + + try { + LOGINFO("Setting power LED State to %s", (led_state ? "ON" : "OFF")); + + if (_deviceSettings) { + FPDIndicator indicator = static_cast(dsFPD_INDICATOR_POWER); + FPDState fpdState = (led_state ? FPDState::DS_FPD_STATE_ON : FPDState::DS_FPD_STATE_OFF); + + uint32_t result = _deviceSettings->SetFPDState(indicator, fpdState); + if (result != WPEFramework::Core::ERROR_NONE) { + LOGERR("SetFPDState failed with error: %d", result); + } else { + LOGINFO("Successfully set FPD power state to %s", (led_state ? "ON" : "OFF")); + } + } else { + LOGERR("DeviceSettings implementation not available"); + } + } catch (...) { + LOGERR("Warning! exception caught when trying to change FP state"); + } +} + +void UXController::SyncDisplayPortsWithPowerState(PowerState power_state) const +{ + LOGINFO("SyncDisplayPortsWithPowerState: %d", static_cast(power_state)); + + if (_deviceSettings) { + try { + // Replicate _SetAVPortsPowerState functionality using DeviceSettings API + + // Set HDMI video port power state + int32_t hdmiHandle = 0; + VideoPortType vpType = VideoPortType::DS_VIDEO_PORT_TYPE_HDMI; + uint32_t result = _deviceSettings->GetVideoPort(vpType, 0, hdmiHandle); + + if (result == WPEFramework::Core::ERROR_NONE && hdmiHandle != 0) { + bool enable = (power_state == PowerState::POWER_STATE_ON); + result = _deviceSettings->EnableVideoPort(hdmiHandle, enable); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("Successfully set HDMI port power state to %s", enable ? "ON" : "OFF"); + } else { + LOGERR("EnableVideoPort failed with error: %d", result); + } + } else { + LOGINFO("HDMI video port not available, trying other ports"); + } + + // Set Component video port if available + int32_t componentHandle = 0; + vpType = VideoPortType::DS_VIDEO_PORT_TYPE_COMPONENT; + result = _deviceSettings->GetVideoPort(vpType, 0, componentHandle); + + if (result == WPEFramework::Core::ERROR_NONE && componentHandle != 0) { + bool enable = (power_state == PowerState::POWER_STATE_ON); + result = _deviceSettings->EnableVideoPort(componentHandle, enable); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("Successfully set Component port power state to %s", enable ? "ON" : "OFF"); + } + } + + // Set display power state + int32_t displayHandle = 0; + DisplayPortType displayType = DisplayPortType::DS_DISPLAY_PORT_TYPE_HDMI; + result = _deviceSettings->GetDisplay(displayType, 0, displayHandle); + if (result == WPEFramework::Core::ERROR_NONE && displayHandle != 0) { + bool enable = (power_state == PowerState::POWER_STATE_ON); + LOGINFO("Display HDMI state set to %s", enable ? "enabled" : "disabled"); + // Note: Additional display control can be added here if needed + } + + } catch (const std::exception& e) { + LOGERR("Exception in SyncDisplayPortsWithPowerState: %s", e.what()); + } + } else { + LOGERR("DeviceSettings implementation not available"); + } +} + +bool UXController::Initialize(unsigned int profile_id) +{ + bool ret = true; + + switch (profile_id) { + case DEFAULT_STB_PROFILE: + _singleton = new UXControllerStb(profile_id, "default-stb"); + break; + + case DEFAULT_TV_PROFILE: + _singleton = new UXControllerTv(profile_id, "default-tv"); + break; + + case DEFAULT_STB_PROFILE_EUROPE: + _singleton = new UXControllerStbEu(profile_id, "default-stb-eu"); + break; + + case DEFAULT_TV_PROFILE_EUROPE: + _singleton = new UXControllerTvEu(profile_id, "default-tv-eu"); + break; + + default: + LOGERR("Error! Unsupported product profile id %d", profile_id); + ret = false; + } + return ret; +} + +UXController* UXController::GetInstance() +{ + return _singleton; +} + +/********************************* UXControllerTvEu Class ********************************/ + +UXControllerTvEu::UXControllerTvEu(unsigned int id, const std::string& name) + : UXController(id, name, DEVICE_TYPE_TV) +{ + _preferedPowerModeOnReboot = POWER_MODE_LIGHT_SLEEP; +} + +bool UXControllerTvEu::ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) +{ + bool ret = true; + SyncDisplayPortsWithPowerState(newState); + ret = SetBootloaderPatternInternal((WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == newState ? mfrBL_PATTERN_NORMAL : mfrBL_PATTERN_SILENT_LED_ON)); + return ret; +} + +bool UXControllerTvEu::ApplyPreRebootConfig(PowerState currentState) const +{ + return true; +} + +bool UXControllerTvEu::ApplyPreMaintenanceRebootConfig(PowerState currentState) +{ + bool ret = true; + if (WPEFramework::Exchange::IPowerManager::POWER_STATE_ON != currentState) { + ret = SetBootloaderPatternInternal(mfrBL_PATTERN_SILENT); + } + return ret; +} + +bool UXControllerTvEu::ApplyPostRebootConfig(PowerState targetState, + PowerState lastKnownState) +{ + bool ret = true; + + if ((WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == lastKnownState) && (WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY == targetState)) { + SyncDisplayPortsWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); + } else { + SyncDisplayPortsWithPowerState(targetState); + } + + mfrBlPattern_t pattern = mfrBL_PATTERN_NORMAL; + switch (targetState) { + case WPEFramework::Exchange::IPowerManager::POWER_STATE_ON: + break; + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY: + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY_LIGHT_SLEEP: + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY_DEEP_SLEEP: + pattern = mfrBL_PATTERN_SILENT_LED_ON; + break; + default: + LOGINFO("Warning! Unhandled power transition. New state: %d", targetState); + break; + } + ret = SetBootloaderPatternFaultTolerant(pattern); + return ret; +} + +PowerState UXControllerTvEu::GetPreferredPostRebootPowerState( + PowerState prevState) const +{ + return WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY; +} + +/********************************* UXControllerStbEu Class ********************************/ + +UXControllerStbEu::UXControllerStbEu(unsigned int id, const std::string& name) + : UXController(id, name, DEVICE_TYPE_STB) +{ + _preferedPowerModeOnReboot = POWER_MODE_LIGHT_SLEEP; +} + +bool UXControllerStbEu::ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) +{ + SyncDisplayPortsWithPowerState(newState); + SyncPowerLedWithPowerState(newState); + return true; +} + +bool UXControllerStbEu::ApplyPreRebootConfig(PowerState currentState) const +{ + return true; +} + +bool UXControllerStbEu::ApplyPreMaintenanceRebootConfig(PowerState currentState) +{ + bool ret = true; + if (WPEFramework::Exchange::IPowerManager::POWER_STATE_ON != currentState) { + ret = SetBootloaderPatternInternal(mfrBL_PATTERN_SILENT); + } + return ret; +} + +bool UXControllerStbEu::ApplyPostRebootConfig(PowerState targetState, + PowerState lastKnownState) +{ + bool ret = true; + + if ((WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == lastKnownState) && (WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY == targetState)) { +/* Sync bootup LEDs is disabling the bootup LED pattern. +Now the LED pattern is set by IUI after bootup we modifying this behavior based on conditions */ +#ifdef ENABLE_LED_SYNC_IN_BOOTUP + SyncPowerLedWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); +#endif + SyncDisplayPortsWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); + } else { +#ifdef ENABLE_LED_SYNC_IN_BOOTUP + SyncPowerLedWithPowerState(targetState); +#endif + SyncDisplayPortsWithPowerState(targetState); + } + + ret = SetBootloaderPatternFaultTolerant(mfrBL_PATTERN_NORMAL); + return ret; +} + +PowerState UXControllerStbEu::GetPreferredPostRebootPowerState( + PowerState prevState) const +{ + return WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY; +} + +/********************************* UXControllerTv Class ********************************/ + +UXControllerTv::UXControllerTv(unsigned int id, const std::string& name) + : UXController(id, name, DEVICE_TYPE_TV) +{ + _preferedPowerModeOnReboot = POWER_MODE_LIGHT_SLEEP; +} + +bool UXControllerTv::ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) +{ + _mutex.lock(); + if (false == _firstPowerTransitionComplete) { + _firstPowerTransitionComplete = true; + } + _mutex.unlock(); + + SyncDisplayPortsWithPowerState(newState); + bool ret = SetBootloaderPatternInternal((WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == newState ? mfrBL_PATTERN_NORMAL : mfrBL_PATTERN_SILENT_LED_ON)); + return ret; +} + +bool UXControllerTv::ApplyPreRebootConfig(PowerState currentState) const +{ + return true; +} + +bool UXControllerTv::ApplyPreMaintenanceRebootConfig(PowerState currentState) +{ + bool ret = true; + if (WPEFramework::Exchange::IPowerManager::POWER_STATE_ON != currentState) { + ret = SetBootloaderPatternInternal(mfrBL_PATTERN_SILENT_LED_ON); + } + return ret; +} + +bool UXControllerTv::ApplyPostRebootConfig(PowerState targetState, + PowerState lastKnownState) +{ + bool ret = true; +#ifdef ENABLE_LED_SYNC_IN_BOOTUP + SyncPowerLedWithPowerState(targetState); +#endif + if ((WPEFramework::Exchange::IPowerManager::POWER_STATE_ON == lastKnownState) && (WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY == targetState)) { + if (true == DoForceDisplayOnPostReboot()) { + SyncDisplayPortsWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); + } else { + reboot_type_t isHardReboot = GetRebootType(); + switch (isHardReboot) { + case reboot_type_t::HARD: + SyncDisplayPortsWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY); + break; + case reboot_type_t::SOFT: + SyncDisplayPortsWithPowerState(WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); + break; + default: + ScheduleRebootReasonCheck(this); + break; + } + } + } else { + SyncDisplayPortsWithPowerState(targetState); + } + + mfrBlPattern_t pattern = mfrBL_PATTERN_NORMAL; + switch (targetState) { + case WPEFramework::Exchange::IPowerManager::POWER_STATE_ON: + break; + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY: + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY_LIGHT_SLEEP: + case WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY_DEEP_SLEEP: + pattern = mfrBL_PATTERN_SILENT_LED_ON; + break; + default: + LOGINFO("Warning! Unhandled power transition. New state: %d", targetState); + break; + } + ret = SetBootloaderPatternFaultTolerant(pattern); + return ret; +} + +PowerState UXControllerTv::GetPreferredPostRebootPowerState( + PowerState prevState) const +{ + return WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY; +} + +void UXControllerTv::SyncDisplayPortsWithRebootReason(reboot_type_t reboot_type) +{ + _mutex.lock(); + if (false == _firstPowerTransitionComplete) { + _mutex.unlock(); + SyncDisplayPortsWithPowerState(reboot_type_t::HARD == reboot_type ? WPEFramework::Exchange::IPowerManager::POWER_STATE_STANDBY : WPEFramework::Exchange::IPowerManager::POWER_STATE_ON); + } else { + _mutex.unlock(); + } +} + +/********************************* UXControllerStb Class ********************************/ + +UXControllerStb::UXControllerStb(unsigned int id, const std::string& name) + : UXController(id, name, DEVICE_TYPE_STB) +{ + _preferedPowerModeOnReboot = POWER_MODE_LAST_KNOWN; + _enableSilentRebootSupport = false; +} + +bool UXControllerStb::ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) +{ + SyncDisplayPortsWithPowerState(newState); + SyncPowerLedWithPowerState(newState); + return true; +} + +bool UXControllerStb::ApplyPreRebootConfig(PowerState currentState) const +{ + return true; +} + +bool UXControllerStb::ApplyPreMaintenanceRebootConfig(PowerState currentState) +{ + return true; +} + +bool UXControllerStb::ApplyPostRebootConfig(PowerState targetState, + PowerState lastKnownState) +{ + bool ret = true; + SyncPowerLedWithPowerState(targetState); + SyncDisplayPortsWithPowerState(targetState); + return ret; +} + +PowerState UXControllerStb::GetPreferredPostRebootPowerState( + PowerState prevState) const +{ + return prevState; +} + +} // namespace DSProductTraits +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DSProductTraitsHandler.h b/plugin/DSProductTraitsHandler.h new file mode 100644 index 0000000..277a7da --- /dev/null +++ b/plugin/DSProductTraitsHandler.h @@ -0,0 +1,200 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include +#include +#include +#include "PowerManagerInterface.h" +#include "mfrMgr.h" +#include "DeviceSettingsImplementation.h" + +// C headers with built-in C++ protection +#include "mfrTypes.h" +#include "libIBus.h" +#include "libIBusDaemon.h" +//Need to remove this once fix the issue while add DevisettingsTypes.h file +#ifdef DEBUG_LOGGING +#define ENTRY_LOG do { LOGINFO("%d: Enter %s", __LINE__, __func__); } while(0); +#define EXIT_LOG do { LOGINFO("%d: Exit %s", __LINE__, __func__); } while(0); +#else +#define ENTRY_LOG do { } while(0) +#define EXIT_LOG do { } while(0) +#endif + + +using PowerState = WPEFramework::Exchange::IPowerManager::PowerState; + +namespace WPEFramework { +namespace Plugin { +namespace DSProductTraits { + +typedef enum { + DEFAULT_STB_PROFILE = 0, + DEFAULT_TV_PROFILE, + DEFAULT_STB_PROFILE_EUROPE, + DEFAULT_TV_PROFILE_EUROPE, + PROFILE_MAX +} productProfileId_t; + +typedef enum { + DEVICE_TYPE_STB = 0, + DEVICE_TYPE_TV, + DEVICE_TYPE_MAX +} deviceType_t; + +typedef enum { + POWER_MODE_ON = 0, + POWER_MODE_LIGHT_SLEEP, + POWER_MODE_LAST_KNOWN, + POWER_MODE_UNSPECIFIED, + POWER_MODE_MAX +} powerModeTrait_t; + +enum class reboot_type_t { HARD, SOFT, UNAVAILABLE }; + +/* + * UX Controller - User Experience Controller + * Maintains and applies user experience attributes owned by power manager + */ +class UXController { +protected: + unsigned int _id; + std::string _name; + deviceType_t _deviceType; + bool _invalidateAsyncBootloaderPattern; + bool _firstPowerTransitionComplete; + mutable std::mutex _mutex; + + // DeviceSettings implementation for component access + DeviceSettingsImp* _deviceSettings; + + bool _enableMultiColourLedSupport; + bool _ledEnabledInStandby; + int _ledColorInStandby; + bool _ledEnabledInOnState; + int _ledColorInOnState; + + powerModeTrait_t _preferedPowerModeOnReboot; + bool _enableSilentRebootSupport; + + static UXController* _singleton; + + void InitializeSafeDefaults(); + void SyncPowerLedWithPowerState(PowerState state) const; + void SyncDisplayPortsWithPowerState(PowerState state) const; + bool SetBootloaderPattern(mfrBlPattern_t pattern) const; + void SetBootloaderPatternAsync(mfrBlPattern_t pattern) const; + bool SetBootloaderPatternInternal(mfrBlPattern_t pattern); + bool SetBootloaderPatternFaultTolerant(mfrBlPattern_t pattern); + +public: + static bool Initialize(unsigned int profile_id); + static UXController* GetInstance(); + + UXController(unsigned int id, const std::string& name, deviceType_t deviceType); + virtual ~UXController() {} + + virtual bool ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) { + return false; + } + + virtual bool ApplyPreRebootConfig(PowerState currentState) const { + return false; + } + + virtual bool ApplyPreMaintenanceRebootConfig(PowerState currentState) { + return false; + } + + virtual bool ApplyPostRebootConfig(PowerState newState, + PowerState prevState) { + return false; + } + + virtual PowerState GetPreferredPostRebootPowerState( + PowerState prevState) const { + return prevState; + } + + virtual void SyncDisplayPortsWithRebootReason(reboot_type_t type) {} +}; + +// TV Europe Profile +class UXControllerTvEu : public UXController { +public: + UXControllerTvEu(unsigned int id, const std::string& name); + virtual bool ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) override; + virtual bool ApplyPreRebootConfig(PowerState currentState) const override; + virtual bool ApplyPreMaintenanceRebootConfig(PowerState currentState) override; + virtual bool ApplyPostRebootConfig(PowerState newState, + PowerState prevState) override; + virtual PowerState GetPreferredPostRebootPowerState( + PowerState prevState) const override; +}; + +// STB Europe Profile +class UXControllerStbEu : public UXController { +public: + UXControllerStbEu(unsigned int id, const std::string& name); + virtual bool ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) override; + virtual bool ApplyPreRebootConfig(PowerState currentState) const override; + virtual bool ApplyPreMaintenanceRebootConfig(PowerState currentState) override; + virtual bool ApplyPostRebootConfig(PowerState newState, + PowerState prevState) override; + virtual PowerState GetPreferredPostRebootPowerState( + PowerState prevState) const override; +}; + +// TV Profile +class UXControllerTv : public UXController { +public: + UXControllerTv(unsigned int id, const std::string& name); + virtual bool ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) override; + virtual bool ApplyPreRebootConfig(PowerState currentState) const override; + virtual bool ApplyPreMaintenanceRebootConfig(PowerState currentState) override; + virtual bool ApplyPostRebootConfig(PowerState newState, + PowerState prevState) override; + virtual PowerState GetPreferredPostRebootPowerState( + PowerState prevState) const override; + virtual void SyncDisplayPortsWithRebootReason(reboot_type_t type) override; +}; + +// STB Profile +class UXControllerStb : public UXController { +public: + UXControllerStb(unsigned int id, const std::string& name); + virtual bool ApplyPowerStateChangeConfig(PowerState newState, + PowerState prevState) override; + virtual bool ApplyPreRebootConfig(PowerState currentState) const override; + virtual bool ApplyPreMaintenanceRebootConfig(PowerState currentState) override; + virtual bool ApplyPostRebootConfig(PowerState newState, + PowerState prevState) override; + virtual PowerState GetPreferredPostRebootPowerState( + PowerState prevState) const override; +}; + +} // namespace DSProductTraits +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DSPwrEventListener.cpp b/plugin/DSPwrEventListener.cpp new file mode 100644 index 0000000..1707da2 --- /dev/null +++ b/plugin/DSPwrEventListener.cpp @@ -0,0 +1,964 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DSPwrEventListener.h" +#include "DSProductTraitsHandler.h" +#include "DSController.h" +#include "DeviceSettingsTypes.h" +#include "DeviceSettingsImplementation.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern "C" { + extern void _setEASAudioMode(); +} + +#define PWRMGR_REBOOT_REASON_MAINTENANCE "MAINTENANCE_REBOOT" + +// Static variable accessible to functions outside namespace +static WPEFramework::Plugin::DSProductTraits::UXController* ux = nullptr; + +namespace WPEFramework { +namespace Plugin { + +DSPwrEventListener* DSPwrEventListener::_instance = nullptr; + +DSPwrEventListener::DSPwrEventListener() + : _pwrEventHandlerThreadID(0) + , _stopThread(false) + , _registeredPowerEventHandler(false) + , _curState(PowerState::POWER_STATE_STANDBY) + , _pwrMgrNotification(*this) + , _service(nullptr) + , _deviceSettings(nullptr) +{ + LOGINFO("DSPwrEventListener Constructor"); + memset(_standbyVideoPortSetting, 0, sizeof(_standbyVideoPortSetting)); + DSPwrEventListener::_instance = this; +} + +bool DSPwrEventListener::IsDeviceSettingsReady(bool refreshCacheIfEmpty) +{ + if (_deviceSettings == nullptr) { + _deviceSettings = DeviceSettingsImp::instance(); + if (_deviceSettings == nullptr) { + LOGERR("DeviceSettings implementation not available yet"); + return false; + } + + LOGINFO("DeviceSettings implementation recovered"); + RefreshPortConfigurationCache(); + return true; + } + + if (refreshCacheIfEmpty && _videoPortEntries.empty() && _audioPortEntries.empty()) { + RefreshPortConfigurationCache(); + } + + return true; +} + +void DSPwrEventListener::RefreshPortConfigurationCache() +{ + // Use GetDeviceSettingConfigs() to load all configs in a single call and + // build VideoPortEntry / AudioPortEntry vectors directly from DeviceSettingsInterface.h types — + // no intermediate VideoPortConfigStore / AudioConfigStore mirroring needed. + Exchange::IDeviceSettings::DeviceSettingConfigs rawCfg; + const Core::hresult rc = _deviceSettings->GetDeviceSettingConfigs(rawCfg); + if (rc != Core::ERROR_NONE) { + LOGERR("RefreshPortConfigurationCache: GetDeviceSettingConfigs failed: %u", + static_cast(rc)); + return; + } + + // Build VideoPortEntry vector directly from raw config + std::vector newVpEntries; + for (const auto& pc : rawCfg.videoPorts) { + VideoPortEntry e; + e.type = static_cast(pc.videoPortType); + e.index = pc.videoPortIndex; + for (const auto& tc : rawCfg.videoPortTypes) { + if (tc.typeId == pc.videoPortType) { + e.typeName = tc.name; + break; + } + } + e.name = getVideoPortName(e.type, e.index); + newVpEntries.push_back(std::move(e)); + } + + // Build AudioPortEntry vector directly from raw config + std::vector newAudioEntries; + for (const auto& pc : rawCfg.audioPorts) { + AudioPortEntry e; + e.type = static_cast(pc.audioPortType); + e.index = pc.audioPortIndex; + e.name = getAudioPortName(e.type, e.index); + newAudioEntries.push_back(std::move(e)); + } + + _videoPortEntries = std::move(newVpEntries); + _audioPortEntries = std::move(newAudioEntries); + LOGINFO("RefreshPortConfigurationCache: loaded %zu videoPorts, %zu audioPorts", + _videoPortEntries.size(), _audioPortEntries.size()); +} + +bool DSPwrEventListener::BuildVideoPortEntries(std::vector& entries) +{ + if (IsDeviceSettingsReady(true) == false) { + return false; + } + entries = _videoPortEntries; + return !entries.empty(); +} + +bool DSPwrEventListener::BuildAudioPortEntries(std::vector& entries) +{ + if (IsDeviceSettingsReady(true) == false) { + return false; + } + entries = _audioPortEntries; + return !entries.empty(); +} + +bool DSPwrEventListener::ResolveVideoPortEntryByName(const std::string& requestedPort, DSPwrEventListener::VideoPortEntry& resolvedEntry) +{ + if (IsDeviceSettingsReady(true) == false) { + return false; + } + // Resolve directly from cached _videoPortEntries — no config store lookup needed. + for (const auto& e : _videoPortEntries) { + if (e.name == requestedPort) { + resolvedEntry = e; + return true; + } + } + return false; +} + +DSPwrEventListener::~DSPwrEventListener() +{ + LOGINFO("DSPwrEventListener Destructor"); + Deinit(); +} + +void DSPwrEventListener::Init(PluginHost::IShell* service) +{ + LOGINFO("DSPwrEventListener::Init - Entering"); + + _service = service; + _service->AddRef(); + + if (IsDeviceSettingsReady(true) == false) { + LOGERR("Init: DeviceSettings implementation not ready, will retry lazily"); + } + + // profileType is already initialized in DeviceSettingsImplementation.cpp constructor + // No need to call searchRdkProfile() again here + + if (profileType == TV) { // TV + if (WPEFramework::Plugin::DSProductTraits::UXController::Initialize(WPEFramework::Plugin::DSProductTraits::DEFAULT_TV_PROFILE)) { + ux = WPEFramework::Plugin::DSProductTraits::UXController::GetInstance(); + } + } else { // STB + if (WPEFramework::Plugin::DSProductTraits::UXController::Initialize(WPEFramework::Plugin::DSProductTraits::DEFAULT_STB_PROFILE_EUROPE)) { + ux = WPEFramework::Plugin::DSProductTraits::UXController::GetInstance(); + } + } + + if (nullptr == ux) { + LOGINFO("DSMgr product traits not supported"); + } + + // Note: device::Manager::load() is intentionally disabled to avoid linker dependency + // on DS library which may not be available in all configurations. + // The WPEFramework plugin architecture handles initialization independently. + // Original code kept commented for reference: + // try { + // device::Manager::load(); + // LOGINFO("device::Manager::load success"); + // } catch (...) { + // LOGERR("Exception Caught during device::Manager::load"); + // } + + // TODO: Re-enable these DSMGR IARM API registrations when a client starts consuming them. + // Currently no client calls these APIs, so registration is intentionally disabled. + // + // IARM_Result_t rc; + // rc = IARM_Bus_RegisterCall(IARM_BUS_DSMGR_API_SetStandbyVideoState, SetStandbyVideoState); + // if (IARM_RESULT_SUCCESS != rc) { + // LOGERR("IARM_Bus_RegisterCall Failed for SetStandbyVideoState, Error: %d", rc); + // } + // + // rc = IARM_Bus_RegisterCall(IARM_BUS_DSMGR_API_GetStandbyVideoState, GetStandbyVideoState); + // if (IARM_RESULT_SUCCESS != rc) { + // LOGERR("IARM_Bus_RegisterCall Failed for GetStandbyVideoState, Error: %d", rc); + // } + // + // rc = IARM_Bus_RegisterCall(IARM_BUS_DSMGR_API_SetAvPortState, SetAvPortState); + // if (IARM_RESULT_SUCCESS != rc) { + // LOGERR("IARM_Bus_RegisterCall Failed for SetAvPortState, Error: %d", rc); + // } + // + // rc = IARM_Bus_RegisterCall(IARM_BUS_DSMGR_API_SetLEDStatus, SetLEDState); + // if (IARM_RESULT_SUCCESS != rc) { + // LOGERR("IARM_Bus_RegisterCall Failed for SetLEDStatus, Error: %d", rc); + // } + // + // rc = IARM_Bus_RegisterCall(IARM_BUS_DSMGR_API_SetRebootConfig, SetRebootConfig); + // if (IARM_RESULT_SUCCESS != rc) { + // LOGERR("IARM_Bus_RegisterCall Failed for SetRebootConfig, Error: %d", rc); + // } + + // Initialize mutexes and condition variables + pthread_mutex_init(&_pwrEventQueueMutexLock, NULL); + pthread_mutex_init(&_pwrEventMutexLock, NULL); + pthread_cond_init(&_pwrEventMutexCond, NULL); + + _stopThread = false; + if (pthread_create(&_pwrEventHandlerThreadID, NULL, PwrEventHandlingThreadFunc, this) != 0) { + LOGERR("DSMgr PwrEventHandlingThread creation failed"); + } + + // Initialize PowerManager connection using retry pattern (like original dsMgr) + LOGINFO("DSMgr PowerManager Connect setup in a Thread"); + PwrCtrlEstablishConnection(); +} + +void DSPwrEventListener::Deinit() +{ + LOGINFO("DSPwrEventListener::Deinit - Entering"); + + if (_powerManagerPlugin) { + _powerManagerPlugin->Unregister(_pwrMgrNotification.baseInterface()); + _powerManagerPlugin.Reset(); + } + _registeredPowerEventHandler = false; + + pthread_mutex_lock(&_pwrEventMutexLock); + _stopThread = true; + pthread_cond_signal(&_pwrEventMutexCond); + pthread_mutex_unlock(&_pwrEventMutexLock); + + LOGINFO("Before joining thread"); + pthread_join(_pwrEventHandlerThreadID, NULL); + LOGINFO("Completed joining thread"); + + pthread_mutex_lock(&_pwrEventQueueMutexLock); + while (!_pwrEventQueue.empty()) { + _pwrEventQueue.pop(); + } + pthread_mutex_unlock(&_pwrEventQueueMutexLock); + + pthread_cond_destroy(&_pwrEventMutexCond); + pthread_mutex_destroy(&_pwrEventQueueMutexLock); + pthread_mutex_destroy(&_pwrEventMutexLock); + + if (_service) { + _service->Release(); + _service = nullptr; + } +} + +void DSPwrEventListener::InitializePowerManager() +{ + LOGINFO("InitializePowerManager - Connecting to PowerManager plugin"); + PowerState pwrStateCur = PowerState::POWER_STATE_UNKNOWN; + PowerState pwrStatePrev = PowerState::POWER_STATE_UNKNOWN; + Core::hresult retStatus = Core::ERROR_GENERAL; + + _powerManagerPlugin = PowerManagerInterfaceBuilder(_T("org.rdk.PowerManager")) + .withIShell(_service) + .withRetryIntervalMS(200) + .withRetryCount(25) + .createInterface(); + + registerPowerEventHandler(); + + if (_powerManagerPlugin) { + retStatus = _powerManagerPlugin->GetPowerState(pwrStateCur, pwrStatePrev); + } + + if (Core::ERROR_NONE == retStatus) { + _curState = pwrStateCur; + LOGINFO("InitializePowerManager - Current power state: %d", _curState); + } else { + LOGERR("InitializePowerManager - Failed to get power state"); + } +} + +void DSPwrEventListener::registerPowerEventHandler() +{ + if (!_registeredPowerEventHandler && _powerManagerPlugin) { + LOGINFO("Registering PowerManager event handler"); + _registeredPowerEventHandler = true; + _powerManagerPlugin->Register(_pwrMgrNotification.baseInterface()); + } else { + LOGINFO("PowerManager event handler already registered or plugin not available"); + } +} + +void PowerManagerNotification::OnPowerModeChanged(const PowerState currentState, const PowerState newState) +{ + _parent.onPowerModeChanged(currentState, newState); +} + +void DSPwrEventListener::onPowerModeChanged(const PowerState currentState, const PowerState newState) +{ + LOGINFO("DSPwrEventListener::onPowerModeChanged - currentState: %d, newState: %d", currentState, newState); + + // Queue the event for thread processing (same pattern as dsMgr original) + pthread_mutex_lock(&_pwrEventQueueMutexLock); + _pwrEventQueue.emplace(currentState, newState); + pthread_mutex_unlock(&_pwrEventQueueMutexLock); + + LOGINFO("Sending signal to thread for processing callback event"); + pthread_mutex_lock(&_pwrEventMutexLock); + pthread_cond_signal(&_pwrEventMutexCond); + pthread_mutex_unlock(&_pwrEventMutexLock); +} + +void DSPwrEventListener::PwrCtrlEstablishConnection() +{ + LOGINFO("DSPwrEventListener::PwrCtrlEstablishConnection - Entering"); + + // Start retry thread for PowerManager connection (like original dsMgr pattern) + pthread_t pwrConnectThreadID; + + if (pthread_create(&pwrConnectThreadID, NULL, PwrRetryEstablishConnThread, this) == 0) { + if (pthread_detach(pwrConnectThreadID) != 0) { + LOGERR("DSPwrEventListener PwrCtrlEstablishConnection Thread detach Failed"); + } + } else { + LOGERR("DSPwrEventListener PwrCtrlEstablishConnection Thread Creation Failed"); + } +} + +void DSPwrEventListener::PwrControllerFetchNinitStateValues() +{ + LOGINFO("DSPwrEventListener::PwrControllerFetchNinitStateValues"); + + PowerState powerStateBeforeReboot = PowerState::POWER_STATE_STANDBY; + if (_powerManagerPlugin) { + Core::hresult retStatus = _powerManagerPlugin->GetPowerStateBeforeReboot(powerStateBeforeReboot); + if (Core::ERROR_NONE != retStatus) { + LOGERR("GetPowerStateBeforeReboot failed, defaulting to STANDBY"); + } + } + + // Note: _curState is already set in InitializePowerManager from GetPowerState + LOGINFO("Current Power State: %d, Power State Before Reboot: %d", _curState, powerStateBeforeReboot); + + if (nullptr != ux) { + ux->ApplyPostRebootConfig(_curState, powerStateBeforeReboot); + } + + if (nullptr == ux) { +#ifdef ENABLE_LED_SYNC_IN_BOOTUP + SetLEDStatus(_curState); +#endif + SetAVPortsPowerState(_curState); + } +} + +void DSPwrEventListener::HandlePwrEventData(const PowerState currentState, + const PowerState newState) +{ + LOGINFO("HandlePwrEventData - currentState: %d, newState: %d", currentState, newState); + + if (nullptr != ux) { + ux->ApplyPowerStateChangeConfig(newState, currentState); + } else { +#ifdef ENABLE_LED_SYNC_IN_BOOTUP + SetLEDStatus(newState); +#endif + SetAVPortsPowerState(newState); + } +} + +int DSPwrEventListener::SetLEDStatus(PowerState powerState) +{ + LOGINFO("SetLEDStatus - powerState: %d", powerState); + + try { + if (IsDeviceSettingsReady(true) == false) { + LOGERR("SetLEDStatus: DeviceSettings implementation not available"); + return -1; + } + + if (_deviceSettings) { + FPDIndicator indicator = static_cast(dsFPD_INDICATOR_POWER); + FPDState fpdState; + + if (PowerState::POWER_STATE_ON != powerState) { + if (profileType == TV) { + fpdState = FPDState::DS_FPD_STATE_ON; + LOGINFO("Settings Power LED State to ON"); + } else { + fpdState = FPDState::DS_FPD_STATE_OFF; + LOGINFO("Settings Power LED State to OFF"); + } + } else { + fpdState = FPDState::DS_FPD_STATE_ON; + LOGINFO("Settings Power LED State to ON"); + } + + uint32_t result = _deviceSettings->SetFPDState(indicator, fpdState); + if (result != WPEFramework::Core::ERROR_NONE) { + LOGERR("SetFPDState failed with error: %d", result); + return -1; + } + } else { + LOGERR("DeviceSettings implementation not available"); + return -1; + } + } catch (...) { + LOGERR("Exception Caught during SetLEDStatus"); + return -1; + } + + return 0; +} + +int DSPwrEventListener::SetAVPortsPowerState(PowerState powerState) +{ + LOGINFO("SetAVPortsPowerState - powerState: %d", powerState); + + try { + if (PowerState::POWER_STATE_ON != powerState) { + // Non-ON power state (standby or off) - certain ports may stay on in standby modes + try { + std::vector videoPorts; + if (!BuildVideoPortEntries(videoPorts)) { + LOGERR("Failed to enumerate video ports for powerState %d", static_cast(powerState)); + } + + LOGINFO("Number of Video Ports: %zu", videoPorts.size()); + + for (size_t i = 0; i < videoPorts.size(); i++) { + try { + const VideoPortEntry& vPort = videoPorts.at(i); + bool doEnable = GetVideoPortStandbySetting(vPort.name.c_str()); + LOGINFO("Video port %s will be %s for PowerState %d", + vPort.name.c_str(), + (doEnable ? "enabled" : "disabled"), + static_cast(powerState)); + + if ((false == doEnable) || (PowerState::POWER_STATE_OFF == powerState)) { + uint32_t result = ConfigureVideoPort(vPort.name, + vPort.type, + vPort.index, + false); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("VideoPort %s disabled for powerState %d", + vPort.name.c_str(), static_cast(powerState)); + } + } else { + LOGINFO("VideoPort %s stays enabled for powerState %d", + vPort.name.c_str(), static_cast(powerState)); + } + } catch (...) { + LOGERR("Exception caught in video port processing for port %zu", i); + } + } + } catch (...) { + LOGERR("Exception caught during video port enumeration"); + } + + // Configure Audio Ports + try { + std::vector audioPorts; + if (!BuildAudioPortEntries(audioPorts)) { + LOGERR("Failed to enumerate audio ports for powerState %d", static_cast(powerState)); + } + LOGINFO("Number of Audio Ports: %zu", audioPorts.size()); + + for (size_t i = 0; i < audioPorts.size(); i++) { + try { + const AudioPortEntry& aPort = audioPorts.at(i); + bool isConfigSkipped = false; + + uint32_t result = ConfigureAudioPort(aPort.name, + aPort.type, + aPort.index, + false, + &isConfigSkipped); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("AudioPort %s disabled for powerState %d", + aPort.name.c_str(), static_cast(powerState)); + } + } catch (...) { + LOGERR("Exception caught in audio port processing for port %zu", i); + } + } + } catch (...) { + LOGERR("Exception caught during audio port enumeration"); + } + } else { + // POWER_STATE_ON - Enable all ports + try { + std::vector videoPorts; + if (!BuildVideoPortEntries(videoPorts)) { + LOGERR("Failed to enumerate video ports for POWER_STATE_ON"); + } + + for (size_t i = 0; i < videoPorts.size(); i++) { + try { + const VideoPortEntry& vPort = videoPorts.at(i); + + uint32_t result = ConfigureVideoPort(vPort.name, + vPort.type, + vPort.index, + true); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("VideoPort %s enabled for powerState %d", + vPort.name.c_str(), static_cast(powerState)); + } + } catch (...) { + LOGERR("Exception caught in video port processing for port %zu", i); + } + } + + std::vector audioPorts; + if (!BuildAudioPortEntries(audioPorts)) { + LOGERR("Failed to enumerate audio ports for POWER_STATE_ON"); + } + for (size_t i = 0; i < audioPorts.size(); i++) { + try { + const AudioPortEntry& aPort = audioPorts.at(i); + bool isConfigSkipped = false; + + uint32_t result = ConfigureAudioPort(aPort.name, + aPort.type, + aPort.index, + true, + &isConfigSkipped); + if (result == WPEFramework::Core::ERROR_NONE && !isConfigSkipped) { + LOGINFO("AudioPort %s enabled for powerState %d", + aPort.name.c_str(), static_cast(powerState)); + } + } catch (...) { + LOGERR("Exception caught in audio port processing for port %zu", i); + } + } + + // Special EAS mode handling + if (DSController::instance()->getEASMode() == IARM_BUS_SYS_MODE_EAS) { + LOGINFO("Force Stereo in EAS mode"); + // Set EAS audio mode using original dsMgr function + _setEASAudioMode(); + } + + } catch (...) { + LOGERR("Exception caught during video port enumeration"); + } + } + } catch (...) { + LOGERR("Exception Caught during SetAVPortsPowerState"); + return -1; + } + + LOGINFO("Exiting SetAVPortsPowerState"); + return 0; +} + +bool DSPwrEventListener::GetVideoPortStandbySetting(const char* port) +{ + if (NULL == port) { + LOGERR("Port name is NULL"); + return false; + } + + for (int i = 0; i < MAX_NUM_VIDEO_PORTS; i++) { + if (0 == strncasecmp(port, _standbyVideoPortSetting[i].port, DSMGR_MAX_VIDEO_PORT_NAME_LENGTH)) { + return _standbyVideoPortSetting[i].isEnabled; + } + } + return false; // Default: video port is disabled in standby mode +} + + + +PowerState DSPwrEventListener::PwrMgrToPowerControllerPowerState(int pwrMgrState) +{ + PowerState powerState = PowerState::POWER_STATE_UNKNOWN; + + switch (pwrMgrState) { + case 0: // PWRMGR_POWERSTATE_OFF + powerState = PowerState::POWER_STATE_OFF; + break; + case 1: // PWRMGR_POWERSTATE_STANDBY + powerState = PowerState::POWER_STATE_STANDBY; + break; + case 2: // PWRMGR_POWERSTATE_ON + powerState = PowerState::POWER_STATE_ON; + break; + case 3: // PWRMGR_POWERSTATE_STANDBY_LIGHT_SLEEP + powerState = PowerState::POWER_STATE_STANDBY_LIGHT_SLEEP; + break; + case 4: // PWRMGR_POWERSTATE_STANDBY_DEEP_SLEEP + powerState = PowerState::POWER_STATE_STANDBY_DEEP_SLEEP; + break; + default: + LOGERR("Invalid Power State: %d", pwrMgrState); + break; + } + + LOGINFO("pwrMgrState=%d converted to powerState=%d", pwrMgrState, static_cast(powerState)); + return powerState; +} + +void DSPwrEventListener::InitPwrControllerEvt() +{ + LOGINFO("DSPwrEventListener::InitPwrControllerEvt - Entering"); + + // Initialize mutexes and condition variables (already done in constructor) + // Thread is already created in Init() method + + // This method is kept for compatibility with original dsMgr pattern + // The actual mutex/thread initialization happens in Init() + LOGINFO("Power Controller Event handling initialized"); +} + +void DSPwrEventListener::DeinitPwrControllerEvt() +{ + LOGINFO("DSPwrEventListener::DeinitPwrControllerEvt - Entering"); + + // Stop thread and cleanup + pthread_mutex_lock(&_pwrEventMutexLock); + _stopThread = true; + pthread_cond_signal(&_pwrEventMutexCond); + pthread_mutex_unlock(&_pwrEventMutexLock); + + LOGINFO("Before joining thread"); + pthread_join(_pwrEventHandlerThreadID, NULL); + LOGINFO("Completed joining thread"); + + // Clean the queue with guarding mutex + pthread_mutex_lock(&_pwrEventQueueMutexLock); + while (!_pwrEventQueue.empty()) { + _pwrEventQueue.pop(); + } + pthread_mutex_unlock(&_pwrEventQueueMutexLock); + + // Destroy condition variable and mutexes (handled in destructor) + LOGINFO("Power Controller Event handling deinitialized"); +} + +} // namespace Plugin +} // namespace WPEFramework + +// Static member functions defined outside namespace with full qualification +IARM_Result_t WPEFramework::Plugin::DSPwrEventListener::SetStandbyVideoState(void* arg) +{ + if (NULL == arg) { + return IARM_RESULT_INVALID_PARAM; + } + + if (!_instance) { + return IARM_RESULT_INVALID_STATE; + } + + dsMgrStandbyVideoStateParam_t* param = (dsMgrStandbyVideoStateParam_t*)arg; + param->result = 0; + + int i = 0; + for (i = 0; i < MAX_NUM_VIDEO_PORTS; i++) { + if (0 == strncasecmp(param->port, _instance->_standbyVideoPortSetting[i].port, DSMGR_MAX_VIDEO_PORT_NAME_LENGTH)) { + _instance->_standbyVideoPortSetting[i].isEnabled = ((0 == param->isEnabled) ? false : true); + break; + } + } + + if (MAX_NUM_VIDEO_PORTS == i) { + for (i = 0; i < MAX_NUM_VIDEO_PORTS; i++) { + if ('\0' == _instance->_standbyVideoPortSetting[i].port[0]) { + strncpy(_instance->_standbyVideoPortSetting[i].port, param->port, (DSMGR_MAX_VIDEO_PORT_NAME_LENGTH - 1)); + _instance->_standbyVideoPortSetting[i].isEnabled = ((0 == param->isEnabled) ? false : true); + break; + } + } + } + + if (MAX_NUM_VIDEO_PORTS == i) { + LOGERR("Error! Out of room to write new video port setting for standby mode"); + } + + // Apply setting immediately if currently in standby state (like original dsMgr) + try { + if (PowerState::POWER_STATE_ON != _instance->_curState && PowerState::POWER_STATE_OFF != _instance->_curState) { + // We're currently in one of the standby states. Apply this new setting right away. + LOGINFO("Setting standby %s port status to %s immediately", + param->port, (param->isEnabled ? "enabled" : "disabled")); + + VideoPortEntry resolvedPort; + if (_instance->ResolveVideoPortEntryByName(param->port, resolvedPort)) { + const uint32_t result = _instance->ConfigureVideoPort(resolvedPort.name, + resolvedPort.type, + resolvedPort.index, + (1 == param->isEnabled)); + if (result != WPEFramework::Core::ERROR_NONE) { + LOGERR("Failed to update standby video port state for %s", param->port); + param->result = -1; + } + } else { + LOGERR("Failed to resolve standby video port %s", param->port); + param->result = -1; + } + } else { + LOGINFO("Video port %s will be %s when going into standby mode", + param->port, (param->isEnabled ? "enabled" : "disabled")); + } + } catch (...) { + LOGERR("Exception caught during immediate video port setting for %s. Possible bad video port", param->port); + param->result = -1; + } + + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t WPEFramework::Plugin::DSPwrEventListener::GetStandbyVideoState(void* arg) +{ + if (NULL == arg) { + return IARM_RESULT_INVALID_PARAM; + } + + if (!_instance) { + return IARM_RESULT_INVALID_STATE; + } + + dsMgrStandbyVideoStateParam_t* param = (dsMgrStandbyVideoStateParam_t*)arg; + param->isEnabled = (_instance->GetVideoPortStandbySetting(param->port) ? 1 : 0); + param->result = 0; + + return IARM_RESULT_SUCCESS; +} + +void* WPEFramework::Plugin::DSPwrEventListener::PwrRetryEstablishConnThread(void* arg) +{ + LOGINFO("PwrRetryEstablishConnThread: Entry"); + DSPwrEventListener* listener = static_cast(arg); + + while (true) { + // Check if PowerManager connection is successful + if (listener->_powerManagerPlugin && listener->_registeredPowerEventHandler) { + LOGINFO("PwrRetryEstablishConnThread PowerManager connection is success"); + listener->PwrControllerFetchNinitStateValues(); + break; + } else { + // Retry PowerManager initialization after delay + usleep(DSMGR_PWR_CNTRL_CONNECT_WAIT_TIME_MS); + listener->InitializePowerManager(); + } + } + LOGINFO("PwrRetryEstablishConnThread Completed Exit"); + return arg; +} + +void* WPEFramework::Plugin::DSPwrEventListener::PwrEventHandlingThreadFunc(void* arg) +{ + LOGINFO("PwrEventHandlingThreadFunc: Entry"); + DSPwrEventListener* listener = static_cast(arg); + + while (true) { + pthread_mutex_lock(&listener->_pwrEventMutexLock); + LOGINFO("PwrEventHandlingThreadFunc... Wait for Events"); + + pthread_mutex_lock(&listener->_pwrEventQueueMutexLock); + bool queueEmpty = listener->_pwrEventQueue.empty(); + pthread_mutex_unlock(&listener->_pwrEventQueueMutexLock); + + while (!listener->_stopThread && queueEmpty) { + pthread_cond_wait(&listener->_pwrEventMutexCond, &listener->_pwrEventMutexLock); + pthread_mutex_lock(&listener->_pwrEventQueueMutexLock); + queueEmpty = listener->_pwrEventQueue.empty(); + pthread_mutex_unlock(&listener->_pwrEventQueueMutexLock); + } + + if (listener->_stopThread) { + LOGINFO("PwrEventHandlingThreadFunc Exiting due to stop thread"); + pthread_mutex_unlock(&listener->_pwrEventMutexLock); + break; + } + pthread_mutex_unlock(&listener->_pwrEventMutexLock); + + pthread_mutex_lock(&listener->_pwrEventQueueMutexLock); + while (!listener->_pwrEventQueue.empty()) { + DSMgr_Power_Event_State_t pwrEvent = listener->_pwrEventQueue.front(); + listener->_pwrEventQueue.pop(); + pthread_mutex_unlock(&listener->_pwrEventQueueMutexLock); + + listener->HandlePwrEventData(pwrEvent.currentState, pwrEvent.newState); + + pthread_mutex_lock(&listener->_pwrEventQueueMutexLock); + } + pthread_mutex_unlock(&listener->_pwrEventQueueMutexLock); + } + return arg; +} + +IARM_Result_t WPEFramework::Plugin::DSPwrEventListener::SetAvPortState(void* arg) { + + if (nullptr == arg || nullptr == _instance) { + return IARM_RESULT_INVALID_PARAM; + } + + dsMgrAVPortStateParam_t* param = (dsMgrAVPortStateParam_t*)arg; + PowerState powerState = _instance->PwrMgrToPowerControllerPowerState(param->avPortPowerState); + + if (PowerState::POWER_STATE_UNKNOWN != powerState) { + _instance->SetAVPortsPowerState(powerState); + } + + param->result = 0; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t WPEFramework::Plugin::DSPwrEventListener::SetLEDState(void* arg) +{ + if (NULL == arg || !_instance) { + return IARM_RESULT_INVALID_PARAM; + } + + dsMgrLEDStatusParam_t* param = (dsMgrLEDStatusParam_t*)arg; + PowerState powerState = _instance->PwrMgrToPowerControllerPowerState(param->ledState); + + if (PowerState::POWER_STATE_UNKNOWN != powerState) { + _instance->SetLEDStatus(powerState); + } + + param->result = 0; + return IARM_RESULT_SUCCESS; +} + +IARM_Result_t WPEFramework::Plugin::DSPwrEventListener::SetRebootConfig(void* arg) +{ + if (NULL == arg) { + return IARM_RESULT_INVALID_PARAM; + } + + dsMgrRebootConfigParam_t* param = (dsMgrRebootConfigParam_t*)arg; + param->reboot_reason_custom[sizeof(param->reboot_reason_custom) - 1] = '\0'; + + if (nullptr != ux) { + PowerState powerState = _instance->PwrMgrToPowerControllerPowerState(param->powerState); + + if (PowerState::POWER_STATE_UNKNOWN != powerState) { + if (0 == strncmp(PWRMGR_REBOOT_REASON_MAINTENANCE, param->reboot_reason_custom, + sizeof(param->reboot_reason_custom))) { + ux->ApplyPreMaintenanceRebootConfig(powerState); + } else { + ux->ApplyPreRebootConfig(powerState); + } + } + } + + param->result = 0; + return IARM_RESULT_SUCCESS; +} + +// DeviceSettings component methods (replacing legacy RPC calls) +uint32_t WPEFramework::Plugin::DSPwrEventListener::ConfigureVideoPort(const std::string& portName, VideoPortType portType, int index, bool requestEnable) +{ + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + + if (IsDeviceSettingsReady(true) == false) { + LOGERR("DeviceSettings implementation not available"); + return result; + } + + try { + int32_t handle = 0; + result = _deviceSettings->GetVideoPort(portType, index, handle); + + if (result == WPEFramework::Core::ERROR_NONE && handle != 0) { + result = _deviceSettings->EnableVideoPort(handle, requestEnable); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("VideoPort %s successfully %s", portName.c_str(), (requestEnable ? "enabled" : "disabled")); + } else { + LOGERR("Failed to set video port %s state, Error: %d", portName.c_str(), result); + } + } else { + LOGERR("Failed to get video port %s handle, Error: %d", portName.c_str(), result); + } + } catch (...) { + LOGERR("Exception caught during ConfigureVideoPort for %s", portName.c_str()); + result = WPEFramework::Core::ERROR_GENERAL; + } + + return result; +} + +uint32_t WPEFramework::Plugin::DSPwrEventListener::ConfigureAudioPort(const std::string& portName, AudioPortType portType, int index, bool requestEnable, bool* isConfigurationSkippedPtr) +{ + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + + if (!isConfigurationSkippedPtr) { + return WPEFramework::Core::ERROR_BAD_REQUEST; + } + + *isConfigurationSkippedPtr = false; + + if (IsDeviceSettingsReady(true) == false) { + LOGERR("DeviceSettings implementation not available"); + return result; + } + + try { + int32_t handle = 0; + result = _deviceSettings->GetAudioPort(portType, index, handle); + + if (result == WPEFramework::Core::ERROR_NONE && handle != 0) { + if (requestEnable) { + // Check if port should be enabled based on persistent settings + bool persistEnabled = true; + result = _deviceSettings->IsAudioPortEnabled(handle, persistEnabled); + if (result == WPEFramework::Core::ERROR_NONE) { + if (!persistEnabled) { + *isConfigurationSkippedPtr = true; + LOGINFO("Enable AudioPort %s skipped - persistent state is disabled", portName.c_str()); + return WPEFramework::Core::ERROR_NONE; + } + } + } + + result = _deviceSettings->EnableAudioPort(handle, requestEnable); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("AudioPort %s successfully %s", portName.c_str(), (requestEnable ? "enabled" : "disabled")); + } else { + LOGERR("Failed to set audio port %s state, Error: %d", portName.c_str(), result); + } + } else { + LOGERR("Failed to get audio port %s handle, Error: %d", portName.c_str(), result); + } + } catch (...) { + LOGERR("Exception caught during ConfigureAudioPort for %s", portName.c_str()); + result = WPEFramework::Core::ERROR_GENERAL; + } + + return result; +} diff --git a/plugin/DSPwrEventListener.h b/plugin/DSPwrEventListener.h new file mode 100644 index 0000000..ec0f61a --- /dev/null +++ b/plugin/DSPwrEventListener.h @@ -0,0 +1,172 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include "Module.h" + +#include "DeviceSettingsImplementation.h" +#include "DeviceSettingsTypes.h" + +// C headers with built-in C++ protection +#include "libIARM.h" +#include "libIBusDaemon.h" +#include "sysMgr.h" +#include "libIBus.h" + +using PowerState = WPEFramework::Exchange::IPowerManager::PowerState; + +namespace WPEFramework { +namespace Plugin { + +/* Retry every 300 msec */ +#define DSMGR_PWR_CNTRL_CONNECT_WAIT_TIME_MS (300*1000) +#define MAX_NUM_VIDEO_PORTS 5 +// DSMGR_MAX_VIDEO_PORT_NAME_LENGTH already defined in dsRpc.h + +typedef struct{ + char port[DSMGR_MAX_VIDEO_PORT_NAME_LENGTH]; + bool isEnabled; +} DSMgr_Standby_Video_State_t; + +/* Power Controller State Data Structure to Pass to the Thread */ +struct DSMgr_Power_Event_State_t { + PowerState currentState; + PowerState newState; + DSMgr_Power_Event_State_t(PowerState currSt, PowerState newSt) + : currentState(currSt), newState(newSt) {} +}; + +class DSPwrEventListener; + +class PowerManagerNotification : public Exchange::IPowerManager::IModeChangedNotification { +private: + PowerManagerNotification(const PowerManagerNotification&) = delete; + PowerManagerNotification& operator=(const PowerManagerNotification&) = delete; + +public: + explicit PowerManagerNotification(DSPwrEventListener& parent) + : _parent(parent) + { + } + ~PowerManagerNotification() override = default; + +public: + void OnPowerModeChanged(const PowerState currentState, const PowerState newState) override; + + template + T* baseInterface() + { + static_assert(std::is_base_of(), "base type mismatch"); + return static_cast(this); + } + + BEGIN_INTERFACE_MAP(PowerManagerNotification) + INTERFACE_ENTRY(Exchange::IPowerManager::IModeChangedNotification) + END_INTERFACE_MAP + +private: + DSPwrEventListener& _parent; +}; + +class DSPwrEventListener { +public: + DSPwrEventListener(); + ~DSPwrEventListener(); + + void Init(PluginHost::IShell* service); + void Deinit(); + void InitPwrControllerEvt(); + void DeinitPwrControllerEvt(); + void onPowerModeChanged(const PowerState currentState, const PowerState newState); + void registerPowerEventHandler(); + +private: + using VideoPortEntry = WPEFramework::Plugin::VideoPortEntry; + using AudioPortEntry = WPEFramework::Plugin::AudioPortEntry; + + static void* PwrEventHandlingThreadFunc(void* arg); + static void* PwrRetryEstablishConnThread(void* arg); + + void PwrCtrlEstablishConnection(); + void InitializePowerManager(); + void PwrControllerFetchNinitStateValues(); + void HandlePwrEventData(const PowerState currentState, + const PowerState newState); + + bool IsDeviceSettingsReady(bool refreshCacheIfEmpty = true); + void RefreshPortConfigurationCache(); + bool BuildVideoPortEntries(std::vector& entries); + bool BuildAudioPortEntries(std::vector& entries); + bool ResolveVideoPortEntryByName(const std::string& requestedPort, VideoPortEntry& resolvedEntry); + + int SetLEDStatus(PowerState powerState); + int SetAVPortsPowerState(PowerState powerState); + + // DeviceSettings integration methods + uint32_t ConfigureVideoPort(const std::string& portName, VideoPortType portType, int index, bool enabled); + uint32_t ConfigureAudioPort(const std::string& portName, AudioPortType portType, int index, bool enabled, bool* isConfigurationSkippedPtr); + + bool GetVideoPortStandbySetting(const char* port); + + // IARM API handlers + static IARM_Result_t SetStandbyVideoState(void* arg); + static IARM_Result_t GetStandbyVideoState(void* arg); + static IARM_Result_t SetAvPortState(void* arg); + static IARM_Result_t SetLEDState(void* arg); + static IARM_Result_t SetRebootConfig(void* arg); + + PowerState PwrMgrToPowerControllerPowerState(int pwrMgrState); + + //static PowerState PwrMgrToPowerControllerPowerState(int pwrMgrState); + +private: + static DSPwrEventListener* _instance; + + std::queue _pwrEventQueue; + pthread_t _pwrEventHandlerThreadID; + pthread_mutex_t _pwrEventMutexLock; + pthread_cond_t _pwrEventMutexCond; + pthread_mutex_t _pwrEventQueueMutexLock; + std::atomic _stopThread; + bool _registeredPowerEventHandler; + + PowerState _curState; + DSMgr_Standby_Video_State_t _standbyVideoPortSetting[MAX_NUM_VIDEO_PORTS]; + + PowerManagerInterfaceRef _powerManagerPlugin; + Core::Sink _pwrMgrNotification; + PluginHost::IShell* _service; + DeviceSettingsImp* _deviceSettings; + /** Cached video port entries — populated by RefreshPortConfigurationCache() from GetDeviceSettingConfigs(). */ + std::vector _videoPortEntries; + /** Cached audio port entries — populated by RefreshPortConfigurationCache() from GetDeviceSettingConfigs(). */ + std::vector _audioPortEntries; +}; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettings.conf.in b/plugin/DeviceSettings.conf.in new file mode 100644 index 0000000..fefeb23 --- /dev/null +++ b/plugin/DeviceSettings.conf.in @@ -0,0 +1,12 @@ +autostart = "@PLUGIN_DEVICESETTINGS_AUTOSTART@" +precondition = ["Platform"] +callsign = "org.rdk.DeviceSettings" +startuporder = "@PLUGIN_DEVICESETTINGS_STARTUPORDER@" + +configuration = JSON() +rootobject = JSON() + +rootobject.add("mode", "@PLUGIN_DEVICESETTINGS_MODE@") +rootobject.add("locator", "lib@PLUGIN_IMPLEMENTATION@.so") +configuration.add("root", rootobject) + diff --git a/plugin/DeviceSettings.config b/plugin/DeviceSettings.config new file mode 100644 index 0000000..69f28df --- /dev/null +++ b/plugin/DeviceSettings.config @@ -0,0 +1,14 @@ +set(autostart ${PLUGIN_DEVICESETTINGS_AUTOSTART}) + +if(PLUGIN_DEVICESETTINGS_STARTUPORDER) +set (startuporder ${PLUGIN_DEVICESETTINGS_STARTUPORDER}) +endif() + +map() + key(root) + map() + kv(mode ${PLUGIN_DEVICESETTINGS_MODE}) + kv(locator lib${PLUGIN_IMPLEMENTATION}.so) + end() +end() +ans(configuration) diff --git a/plugin/DeviceSettings.cpp b/plugin/DeviceSettings.cpp new file mode 100755 index 0000000..d73a253 --- /dev/null +++ b/plugin/DeviceSettings.cpp @@ -0,0 +1,431 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 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. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DeviceSettings.h" +#include +#include + +namespace WPEFramework { + +namespace Plugin +{ + SERVICE_REGISTRATION(DeviceSettings, API_VERSION_MAJOR, API_VERSION_MINOR, API_VERSION_PATCH); + + namespace { + static Metadata metadata( + // Version + API_VERSION_MAJOR, API_VERSION_MINOR, API_VERSION_PATCH, + // Preconditions + {}, + // Terminations + {}, + // Controls + {} + ); + } + + DeviceSettings::DeviceSettings() + : mConnectionId(0) + , mService(nullptr) + , _mDeviceSettings(nullptr) + , _mDeviceSettingsCompositeIn(nullptr) + , _mDeviceSettingsAudio(nullptr) + , _mDeviceSettingsFPD(nullptr) + , _mDeviceSettingsDisplay(nullptr) + , _mDeviceSettingsHDMIIn(nullptr) + , _mDeviceSettingsHost(nullptr) + , _mDeviceSettingsVideoPort(nullptr) + , _mDeviceSettingsVideoDevice(nullptr) + , mNotificationSink(this) + + { + #if (defined(RDK_LOGGER_ENABLED) || defined(DSMGR_LOGGER_ENABLED)) + + const char* PdebugConfigFile = NULL; + const char* DSMGR_DEBUG_ACTUAL_PATH = "/etc/debug.ini"; + const char* DSMGR_DEBUG_OVERRIDE_PATH = "/opt/debug.ini"; + + /* Init the logger */ + if (access(DSMGR_DEBUG_OVERRIDE_PATH, F_OK) != -1 ) { + PdebugConfigFile = DSMGR_DEBUG_OVERRIDE_PATH; + } + else { + PdebugConfigFile = DSMGR_DEBUG_ACTUAL_PATH; + } + + if (rdk_logger_init(PdebugConfigFile) == 0) { + b_rdk_logger_enabled = 1; + } + +#endif + + } + + + DeviceSettings::~DeviceSettings() + { + } + const string DeviceSettings::Initialize(PluginHost::IShell * service) + { + string message = ""; + using Clock = std::chrono::steady_clock; + using Ms = std::chrono::milliseconds; + auto tInit = Clock::now(); + LOGINFO("[DS-INIT-TIMING] DeviceSettings::Initialize — begin"); + + ASSERT(service != nullptr); + ASSERT(mService == nullptr); + ASSERT(mConnectionId == 0); + ASSERT(_mDeviceSettings == nullptr); + ASSERT(_mDeviceSettingsFPD == nullptr); + ASSERT(_mDeviceSettingsHDMIIn == nullptr); + ASSERT(_mDeviceSettingsVideoPort == nullptr); + ASSERT(_mDeviceSettingsVideoDevice == nullptr); + ASSERT(_mDeviceSettingsHost == nullptr); + ASSERT(_mDeviceSettingsCompositeIn == nullptr); + mService = service; + mService->AddRef(); + + mService->Register(mNotificationSink.baseInterface()); + mService->Register(mNotificationSink.baseInterface()); + +#ifdef USE_LEGACY_INTERFACE + // Get IDeviceSettingsFPD interface. + // Get the unified interface that provides both FPD and HDMI functionality + { + auto t0 = Clock::now(); + _mDeviceSettings = service->Root(mConnectionId, RPC::CommunicationTimeOut, _T("DeviceSettingsImp")); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "service->Root (DeviceSettingsImp)", + (long long)std::chrono::duration_cast(Clock::now() - t0).count()); + } + + if (_mDeviceSettings == nullptr) { + LOGERR("DeviceSettings::Initialize: Failed to initialise DeviceSettings plugin"); + message = _T("DeviceSettings plugin could not be initialised"); + LOGERR("Failed to get IDeviceSettings interface"); + } else { + LOGINFO("DeviceSettingsImp initialized successfully"); + + // Call Configure method on DeviceSettingsImp with the service + Core::hresult result = _mDeviceSettings->Configure(service); + if (result != Core::ERROR_NONE) { + LOGERR("Failed to configure DeviceSettings: %d", result); + message = _T("DeviceSettings configuration failed"); + } else { + // Initialize individual interface pointers for external COM-RPC access + auto tQI = Clock::now(); + _mDeviceSettingsFPD = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsFPD == nullptr) { + LOGERR("Failed to get IDeviceSettingsFPD interface for external access"); + } + + _mDeviceSettingsHDMIIn = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsHDMIIn == nullptr) { + LOGERR("Failed to get IDeviceSettingsHDMIIn interface for external access"); + } + + _mDeviceSettingsAudio = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsAudio == nullptr) { + LOGERR("Failed to get IDeviceSettingsAudio interface for external access"); + } + + _mDeviceSettingsVideoPort = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsVideoDevice = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsHost = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsCompositeIn = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsDisplay = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsVideoPort == nullptr) { + LOGERR("Failed to get IDeviceSettingsVideoPort interface for external access"); + } + if (_mDeviceSettingsVideoDevice == nullptr) { + LOGERR("Failed to get IDeviceSettingsVideoDevice interface for external access"); + } + if (_mDeviceSettingsHost == nullptr) { + LOGERR("Failed to get IDeviceSettingsHost interface for external access"); + } + if (_mDeviceSettingsCompositeIn == nullptr) { + LOGERR("Failed to get IDeviceSettingsCompositeIn interface for external access"); + } + if (_mDeviceSettingsDisplay == nullptr) { + LOGERR("Failed to get IDeviceSettingsDisplay interface for external access"); + } + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "QueryInterface x8 [legacy]", + (long long)std::chrono::duration_cast(Clock::now() - tQI).count()); + + LOGINFO("Individual interfaces initialized for external access - FPD: %p, HDMIIn: %p, Audio: %p, VideoPort: %p, VideoDevice: %p, Host: %p, CompositeIn: %p, Display: %p", + _mDeviceSettingsFPD, _mDeviceSettingsHDMIIn, _mDeviceSettingsAudio, _mDeviceSettingsVideoPort, _mDeviceSettingsVideoDevice, _mDeviceSettingsHost, _mDeviceSettingsCompositeIn, _mDeviceSettingsDisplay); + + // Register for HDMIIn event notifications + if (_mDeviceSettingsHDMIIn != nullptr) { + _mDeviceSettingsHDMIIn->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for HDMIIn event notifications"); + } + + // Register for VideoPort event notifications + if (_mDeviceSettingsVideoPort != nullptr) { + _mDeviceSettingsVideoPort->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for VideoPort event notifications"); + } + + // Register for VideoDevice event notifications + if (_mDeviceSettingsVideoDevice != nullptr) { + _mDeviceSettingsVideoDevice->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for VideoDevice event notifications"); + } + + // Register for CompositeIn event notifications + if (_mDeviceSettingsCompositeIn != nullptr) { + _mDeviceSettingsCompositeIn->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for CompositeIn event notifications"); + } + + // Register for Display event notifications + if (_mDeviceSettingsDisplay != nullptr) { + _mDeviceSettingsDisplay->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for Display event notifications"); + } + } + } +#else + // Get the unified interface that provides both FPD and HDMI functionality + { + auto t0 = Clock::now(); + _mDeviceSettings = service->Root(mConnectionId, RPC::CommunicationTimeOut, _T("DeviceSettingsImp")); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "service->Root (DeviceSettingsImp)", + (long long)std::chrono::duration_cast(Clock::now() - t0).count()); + } + + if (_mDeviceSettings == nullptr) { + LOGERR("DeviceSettings::Initialize: Failed to initialise DeviceSettings plugin"); + message = _T("DeviceSettings plugin could not be initialised"); + LOGERR("Failed to get IDeviceSettings interface"); + } else { + LOGINFO("DeviceSettingsImp initialized successfully"); + + // Call Configure method on DeviceSettingsImp with the service + auto tCfg = Clock::now(); + Core::hresult result = _mDeviceSettings->Configure(service); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "Configure(service)", + (long long)std::chrono::duration_cast(Clock::now() - tCfg).count()); + if (result != Core::ERROR_NONE) { + LOGERR("Failed to configure DeviceSettings: %d", result); + message = _T("DeviceSettings configuration failed"); + } else { + // Initialize individual interface pointers for external COM-RPC access + auto tQI = Clock::now(); + _mDeviceSettingsFPD = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsFPD == nullptr) { + LOGERR("Failed to get IDeviceSettingsFPD interface for external access"); + } + + _mDeviceSettingsHDMIIn = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsHDMIIn == nullptr) { + LOGERR("Failed to get IDeviceSettingsHDMIIn interface for external access"); + } + + _mDeviceSettingsCompositeIn = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsAudio = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsVideoPort = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsVideoDevice = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsHost = _mDeviceSettings->QueryInterface(); + _mDeviceSettingsDisplay = _mDeviceSettings->QueryInterface(); + if (_mDeviceSettingsCompositeIn == nullptr) { + LOGERR("Failed to get IDeviceSettingsCompositeIn interface for external access"); + } + if (_mDeviceSettingsAudio == nullptr) { + LOGERR("Failed to get DeviceSettingsAudio interface for external access"); + } + if (_mDeviceSettingsVideoPort == nullptr) { + LOGERR("Failed to get IDeviceSettingsVideoPort interface for external access"); + } + if (_mDeviceSettingsVideoDevice == nullptr) { + LOGERR("Failed to get IDeviceSettingsVideoDevice interface for external access"); + } + if (_mDeviceSettingsHost == nullptr) { + LOGERR("Failed to get IDeviceSettingsHost interface for external access"); + } + if (_mDeviceSettingsDisplay == nullptr) { + LOGERR("Failed to get IDeviceSettingsDisplay interface for external access"); + } + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "QueryInterface x8", + (long long)std::chrono::duration_cast(Clock::now() - tQI).count()); + + LOGINFO("Individual interfaces initialized for external access - FPD: %p, HDMIIn: %p, CompositeIn: %p, Audio: %p, VideoPort: %p, VideoDevice: %p, Host: %p, Display: %p", + _mDeviceSettingsFPD, _mDeviceSettingsHDMIIn, _mDeviceSettingsCompositeIn, _mDeviceSettingsAudio, _mDeviceSettingsVideoPort, _mDeviceSettingsVideoDevice, _mDeviceSettingsHost, _mDeviceSettingsDisplay); + + // Register for HDMIIn event notifications + if (_mDeviceSettingsHDMIIn != nullptr) { + _mDeviceSettingsHDMIIn->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for HDMIIn event notifications"); + } + + // Register for VideoPort event notifications + if (_mDeviceSettingsVideoPort != nullptr) { + _mDeviceSettingsVideoPort->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for VideoPort event notifications"); + } + + // Register for VideoDevice event notifications + if (_mDeviceSettingsVideoDevice != nullptr) { + _mDeviceSettingsVideoDevice->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for VideoDevice event notifications"); + } + + // Register for CompositeIn event notifications + if (_mDeviceSettingsCompositeIn != nullptr) { + _mDeviceSettingsCompositeIn->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for CompositeIn event notifications"); + } + + // Register for Display event notifications + if (_mDeviceSettingsDisplay != nullptr) { + _mDeviceSettingsDisplay->Register(mNotificationSink.baseInterface()); + LOGINFO("Registered for Display event notifications"); + } + } + } +#endif + if (0 != message.length()) { + Deinitialize(service); + } + + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "DeviceSettings::Initialize TOTAL", + (long long)std::chrono::duration_cast(Clock::now() - tInit).count()); + // On success return empty, to indicate there is no error text. + return (message); + } + + void DeviceSettings::Deinitialize(PluginHost::IShell* service VARIABLE_IS_NOT_USED) + { + if (mService != nullptr) { + ASSERT(mService == service); + mService->Unregister(mNotificationSink.baseInterface()); + mService->Unregister(mNotificationSink.baseInterface()); + + // Unregister from event notifications before releasing interfaces + if (_mDeviceSettingsHDMIIn != nullptr) { + _mDeviceSettingsHDMIIn->Unregister(mNotificationSink.baseInterface()); + LOGINFO("Unregistered from HDMIIn event notifications"); + } + + if (_mDeviceSettingsVideoPort != nullptr) { + _mDeviceSettingsVideoPort->Unregister(mNotificationSink.baseInterface()); + LOGINFO("Unregistered from VideoPort event notifications"); + } + + if (_mDeviceSettingsVideoDevice != nullptr) { + _mDeviceSettingsVideoDevice->Unregister(mNotificationSink.baseInterface()); + LOGINFO("Unregistered from VideoDevice event notifications"); + } + + if (_mDeviceSettingsCompositeIn != nullptr) { + _mDeviceSettingsCompositeIn->Unregister(mNotificationSink.baseInterface()); + LOGINFO("Unregistered from CompositeIn event notifications"); + } + + if (_mDeviceSettingsDisplay != nullptr) { + _mDeviceSettingsDisplay->Unregister(mNotificationSink.baseInterface()); + LOGINFO("Unregistered from Display event notifications"); + } + + // Release individual interface pointers + if (_mDeviceSettingsFPD != nullptr) { + _mDeviceSettingsFPD->Release(); + _mDeviceSettingsFPD = nullptr; + } + + if (_mDeviceSettingsHDMIIn != nullptr) { + _mDeviceSettingsHDMIIn->Release(); + _mDeviceSettingsHDMIIn = nullptr; + } + + if (_mDeviceSettingsCompositeIn != nullptr) { + _mDeviceSettingsCompositeIn->Release(); + _mDeviceSettingsCompositeIn = nullptr; + } + + if (_mDeviceSettingsAudio != nullptr) { + _mDeviceSettingsAudio->Release(); + _mDeviceSettingsAudio = nullptr; + } + + if (_mDeviceSettingsVideoPort != nullptr) { + _mDeviceSettingsVideoPort->Release(); + _mDeviceSettingsVideoPort = nullptr; + } + if (_mDeviceSettingsVideoDevice != nullptr) { + _mDeviceSettingsVideoDevice->Release(); + _mDeviceSettingsVideoDevice = nullptr; + } + + if (_mDeviceSettingsHost != nullptr) { + _mDeviceSettingsHost->Release(); + _mDeviceSettingsHost = nullptr; + } + + if (_mDeviceSettingsDisplay != nullptr) { + _mDeviceSettingsDisplay->Release(); + _mDeviceSettingsDisplay = nullptr; + } + + // Release the main device settings interface + if (_mDeviceSettings != nullptr) { + _mDeviceSettings->Release(); + _mDeviceSettings = nullptr; + } + mService->Release(); + mService = nullptr; + mConnectionId = 0; + LOGINFO("DeviceSettings de-initialised"); + } + } + + string DeviceSettings::Information() const + { + // No additional info to report. + return (string()); + } + + void DeviceSettings::Deactivated(RPC::IRemoteConnection* connection) + { + // This can potentially be called on a socket thread, so the deactivation (which in turn kills this object) must be done + // on a separate thread. Also make sure this call-stack can be unwound before we are totally destructed. + if (mConnectionId == connection->Id()) { + ASSERT(mService != nullptr); + Core::IWorkerPool::Instance().Submit(PluginHost::IShell::Job::Create(mService, PluginHost::IShell::DEACTIVATED, PluginHost::IShell::FAILURE)); + } + } + + void DeviceSettings::CallbackRevoked(const Core::IUnknown* remote, const uint32_t interfaceId) + { + // Add your handling code here, or leave empty if not needed + LOGINFO("CallbackRevoked called for interfaceId %u", interfaceId); + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettings.h b/plugin/DeviceSettings.h new file mode 100644 index 0000000..b73c60f --- /dev/null +++ b/plugin/DeviceSettings.h @@ -0,0 +1,320 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 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. +**/ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DeviceSettingsTypes.h" + + +namespace WPEFramework { +namespace Plugin { + + class DeviceSettings : public PluginHost::IPlugin + { + private: + class NotificationHandler : public RPC::IRemoteConnection::INotification + , public PluginHost::IShell::ICOMLink::INotification + , public DeviceSettingsCompositeIn::INotification + , public DeviceSettingsAudio::INotification + , public DeviceSettingsFPD::INotification + , public DeviceSettingsDisplay::INotification + , public DeviceSettingsHDMIIn::INotification + , public DeviceSettingsVideoPort::INotification + , public DeviceSettingsVideoDevice::INotification + { + private: + NotificationHandler() = delete; + NotificationHandler(const NotificationHandler&) = delete; + NotificationHandler& operator=(const NotificationHandler&) = delete; + + public: + explicit NotificationHandler(DeviceSettings* parent) + : mParent(*parent) + { + ASSERT(parent != nullptr); + } + + virtual ~NotificationHandler() + { + } + + template + T* baseInterface() + { + static_assert(std::is_base_of(), "base type mismatch"); + return static_cast(this); + } + + BEGIN_INTERFACE_MAP(NotificationHandler) + INTERFACE_ENTRY(DeviceSettingsCompositeIn::INotification) + INTERFACE_ENTRY(DeviceSettingsAudio::INotification) + INTERFACE_ENTRY(DeviceSettingsFPD::INotification) + INTERFACE_ENTRY(DeviceSettingsDisplay::INotification) + INTERFACE_ENTRY(DeviceSettingsHDMIIn::INotification) + INTERFACE_ENTRY(DeviceSettingsVideoPort::INotification) + INTERFACE_ENTRY(DeviceSettingsVideoDevice::INotification) + INTERFACE_ENTRY(RPC::IRemoteConnection::INotification) + END_INTERFACE_MAP + + void Activated(RPC::IRemoteConnection*) override + { + } + + void Deactivated(RPC::IRemoteConnection* connection) override + { + mParent.Deactivated(connection); + } + + void Dangling(const Core::IUnknown* remote, const uint32_t interfaceId) override + { + ASSERT(remote != nullptr); + mParent.CallbackRevoked(remote, interfaceId); + } + + void Revoked(const Core::IUnknown* remote, const uint32_t interfaceId) override + { + ASSERT(remote != nullptr); + mParent.CallbackRevoked(remote, interfaceId); + } + + void OnFPDTimeFormatChanged(const FPDTimeFormat timeFormat) override + { + LOGINFO("OnFPDTimeFormatChanged: timeFormat %d", timeFormat); + } + + // Audio notification handlers + void OnAssociatedAudioMixingChanged(bool mixing) override + { + LOGINFO("OnAssociatedAudioMixingChanged: mixing %d", mixing); + } + + void OnAudioFaderControlChanged(int32_t mixerBalance) override + { + LOGINFO("OnAudioFaderControlChanged: mixerBalance %d", mixerBalance); + } + + void OnAudioPrimaryLanguageChanged(const string& primaryLanguage) override + { + LOGINFO("OnAudioPrimaryLanguageChanged: primaryLanguage %s", primaryLanguage.c_str()); + } + + void OnAudioSecondaryLanguageChanged(const string& secondaryLanguage) override + { + LOGINFO("OnAudioSecondaryLanguageChanged: secondaryLanguage %s", secondaryLanguage.c_str()); + } + + void OnAudioOutHotPlug(AudioPortType portType, uint32_t uiPortNumber, bool isPortConnected) override + { + LOGINFO("OnAudioOutHotPlug: portType %d, port %d, connected %d", portType, uiPortNumber, isPortConnected); + } + + void OnAudioFormatUpdate(AudioFormat audioFormat) override + { + LOGINFO("OnAudioFormatUpdate: audioFormat %d", audioFormat); + } + + void OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCapability, bool status) override + { + LOGINFO("OnDolbyAtmosCapabilitiesChanged: capability %d, status %d", atmosCapability, status); + } + + void OnAudioPortStateChanged(AudioPortState audioPortState) override + { + LOGINFO("OnAudioPortStateChanged: state %d", audioPortState); + } + + void OnAudioLevelChanged(int32_t audioLevel) override + { + LOGINFO("OnAudioLevelChanged: level %d", audioLevel); + } + + void OnAudioModeEvent(AudioPortType audioPortType, AudioStereoMode audioMode) override + { + LOGINFO("OnAudioModeEvent: portType %d, mode %d", audioPortType, audioMode); + } + + void OnHDMIInEventHotPlug(const HDMIInPort port, const bool isConnected) override + { + LOGINFO("OnHDMIInEventHotPlug:"); + } + + void OnHDMIInEventSignalStatus(const HDMIInPort port, const HDMIInSignalStatus signalStatus) override + { + LOGINFO("OnHDMIInEventSignalStatus"); + } + + void OnHDMIInEventStatus(const HDMIInPort activePort, const bool isPresented) override + { + LOGINFO("OnHDMIInEventStatus"); + } + + void OnHDMIInVideoModeUpdate(const HDMIInPort port, const HDMIVideoPortResolution& videoPortResolution) override + { + LOGINFO("OnHDMIInVideoModeUpdate"); + } + + void OnHDMIInAllmStatus(const HDMIInPort port, const bool allmStatus) override + { + LOGINFO("OnHDMIInAllmStatus"); + } + + void OnHDMIInAVIContentType(const HDMIInPort port, const HDMIInAviContentType aviContentType) override + { + LOGINFO("OnHDMIInAVIContentType"); + } + + void OnHDMIInAVLatency(const int32_t audioDelay, const int32_t videoDelay) override + { + LOGINFO("OnHDMIInAVLatency"); + } + + void OnHDMIInVRRStatus(const HDMIInPort port, const HDMIInVRRType vrrType) override + { + LOGINFO("OnHDMIInVRRStatus"); + } + + // VideoPort notification handlers matching WPE interface + void OnResolutionPostChange(const ResolutionChange& resolution) override + { + LOGINFO("OnResolutionPostChange"); + } + + void OnResolutionPreChange(const ResolutionChange& resolution) override + { + LOGINFO("OnResolutionPreChange"); + } + + void OnHDCPStatusChange(const Exchange::IDeviceSettingsVideoPort::HDCPStatus hdcpStatus) override + { + LOGINFO("OnHDCPStatusChange: status=%d", (int)hdcpStatus); + } + + void OnVideoFormatUpdate(const Exchange::IDeviceSettingsVideoPort::HDRStandard videoFormatHDR) override + { + LOGINFO("OnVideoFormatUpdate: hdrStandard=%d", (int)videoFormatHDR); + } + + // CompositeIn notification handlers matching WPE interface + void OnCompositeInHotPlug(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) override + { + LOGINFO("OnCompositeInHotPlug: port=%d, isConnected=%s", (int)port, isConnected ? "true" : "false"); + } + + void OnCompositeInSignalStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) override + { + LOGINFO("OnCompositeInSignalStatus: port=%d, signalStatus=%d", (int)port, (int)signalStatus); + } + + void OnCompositeInStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) override + { + LOGINFO("OnCompositeInStatus: activePort=%d, isPresented=%s", (int)activePort, isPresented ? "true" : "false"); + } + + void OnCompositeInVideoModeUpdate(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution& videoResolution) override + { + LOGINFO("OnCompositeInVideoModeUpdate: activePort=%d, resolution=%s", (int)activePort, videoResolution.name.c_str()); + } + + // VideoDevice event handlers (matching actual IDeviceSettingsVideoDevice::INotification interface) + void OnZoomSettingsChanged(const Exchange::IDeviceSettingsVideoDevice::VideoZoom zoomSetting) override + { + LOGINFO("OnZoomSettingsChanged: zoomSetting=%d", static_cast(zoomSetting)); + } + + void OnDisplayFrameratePreChange(const string& frameRate) override + { + LOGINFO("OnDisplayFrameratePreChange: frameRate=%s", frameRate.c_str()); + } + + void OnDisplayFrameratePostChange(const string& frameRate) override + { + LOGINFO("OnDisplayFrameratePostChange: frameRate=%s", frameRate.c_str()); + } + + private: + DeviceSettings& mParent; + }; + public: + DeviceSettings(const DeviceSettings&) = delete; + DeviceSettings(DeviceSettings&&) = delete; + DeviceSettings& operator=(const DeviceSettings&) = delete; + DeviceSettings& operator=(DeviceSettings&) = delete; + + DeviceSettings(); + virtual ~DeviceSettings(); + + // Build QueryInterface implementation, specifying all possible interfaces to be returned. + BEGIN_INTERFACE_MAP(DeviceSettings) + INTERFACE_ENTRY(PluginHost::IPlugin) + INTERFACE_AGGREGATE(Exchange::IDeviceSettings, _mDeviceSettings) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsCompositeIn, _mDeviceSettingsCompositeIn) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsAudio, _mDeviceSettingsAudio) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsFPD, _mDeviceSettingsFPD) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsDisplay, _mDeviceSettingsDisplay) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsHDMIIn, _mDeviceSettingsHDMIIn) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsHost, _mDeviceSettingsHost) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsVideoPort, _mDeviceSettingsVideoPort) + INTERFACE_AGGREGATE(Exchange::IDeviceSettingsVideoDevice, _mDeviceSettingsVideoDevice) + END_INTERFACE_MAP + + public: + + // IPlugin methods + // ------------------------------------------------------------------------------------------------------- + const string Initialize(PluginHost::IShell* service) override; + void Deinitialize(PluginHost::IShell* service) override; + string Information() const override; + + private: + void Deactivated(RPC::IRemoteConnection* connection); + void CallbackRevoked(const Core::IUnknown* remote, const uint32_t interfaceId); + + private: + uint32_t mConnectionId; + PluginHost::IShell* mService; + Exchange::IDeviceSettings* _mDeviceSettings; + Exchange::IDeviceSettingsCompositeIn* _mDeviceSettingsCompositeIn; + DeviceSettingsAudio* _mDeviceSettingsAudio; + Exchange::IDeviceSettingsFPD* _mDeviceSettingsFPD; + Exchange::IDeviceSettingsDisplay* _mDeviceSettingsDisplay; + Exchange::IDeviceSettingsHDMIIn* _mDeviceSettingsHDMIIn; + Exchange::IDeviceSettingsHost* _mDeviceSettingsHost; + Exchange::IDeviceSettingsVideoPort* _mDeviceSettingsVideoPort; + Exchange::IDeviceSettingsVideoDevice* _mDeviceSettingsVideoDevice; + Core::Sink mNotificationSink; + + }; + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsAudioImplementation.cpp b/plugin/DeviceSettingsAudioImplementation.cpp new file mode 100644 index 0000000..5a746e9 --- /dev/null +++ b/plugin/DeviceSettingsAudioImplementation.cpp @@ -0,0 +1,637 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 RDK Management + Core::hresult DeviceSettingsAudioImpl::EnableAudioSurroundDecoder(const int32_t handle, const bool enable) { + * 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. + */ + +#include "DeviceSettingsAudioImplementation.h" + +#include +#include + +using namespace std; + +#include "DeviceSettingsHALConfig.h" + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsAudioImpl::DeviceSettingsAudioImpl() + : _audio(Audio::Create(*this)) + , _configLock() + , _callbackLock() + { + LOGINFO("DeviceSettingsAudioImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsAudioImpl::~DeviceSettingsAudioImpl() { + LOGINFO("DeviceSettingsAudioImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsAudioImpl::dispatchAudioEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _AudioNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IAudio event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsAudioImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsAudioImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsAudioImpl::Register(DeviceSettingsAudio::INotification* notification) + { + Core::hresult errorCode = Register(_AudioNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IAudio %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IAudio %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsAudioImpl::Unregister(DeviceSettingsAudio::INotification* notification) + { + Core::hresult errorCode = Unregister(_AudioNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IAudio %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IAudio %p unregistered successfully", notification); + } + return errorCode; + } + + // Audio notification implementations - hardware callbacks + void DeviceSettingsAudioImpl::OnAssociatedAudioMixingChanged(bool mixing) + { + LOGINFO("OnAssociatedAudioMixingChanged event Received: mixing=%s", mixing ? "true" : "false"); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAssociatedAudioMixingChanged, mixing); + } + + void DeviceSettingsAudioImpl::OnAudioFaderControlChanged(int32_t mixerBalance) + { + LOGINFO("OnAudioFaderControlChanged event Received: mixerBalance=%d", mixerBalance); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioFaderControlChanged, mixerBalance); + } + + void DeviceSettingsAudioImpl::OnAudioPrimaryLanguageChanged(const std::string& primaryLanguage) + { + LOGINFO("OnAudioPrimaryLanguageChanged event Received: primaryLanguage=%s", primaryLanguage.c_str()); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioPrimaryLanguageChanged, primaryLanguage); + } + + void DeviceSettingsAudioImpl::OnAudioSecondaryLanguageChanged(const std::string& secondaryLanguage) + { + LOGINFO("OnAudioSecondaryLanguageChanged event Received: secondaryLanguage=%s", secondaryLanguage.c_str()); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioSecondaryLanguageChanged, secondaryLanguage); + } + + void DeviceSettingsAudioImpl::OnAudioOutHotPlug(AudioPortType portType, uint32_t uiPortNumber, bool isPortConnected) + { + LOGINFO("OnAudioOutHotPlug event Received: portType=%d, port=%u, connected=%s", portType, uiPortNumber, isPortConnected ? "true" : "false"); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioOutHotPlug, portType, uiPortNumber, isPortConnected); + } + + void DeviceSettingsAudioImpl::OnAudioFormatUpdate(AudioFormat audioFormat) + { + LOGINFO("OnAudioFormatUpdate event Received: audioFormat=%d", audioFormat); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioFormatUpdate, audioFormat); + } + + void DeviceSettingsAudioImpl::OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCapability, bool status) + { + LOGINFO("OnDolbyAtmosCapabilitiesChanged event Received: capability=%d, status=%s", atmosCapability, status ? "true" : "false"); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnDolbyAtmosCapabilitiesChanged, atmosCapability, status); + } + + void DeviceSettingsAudioImpl::OnAudioPortStateChanged(AudioPortState audioPortState) + { + LOGINFO("OnAudioPortStateChanged event Received: audioPortState=%d", audioPortState); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioPortStateChanged, audioPortState); + } + + void DeviceSettingsAudioImpl::OnAudioLevelChanged(int32_t audioLevel) + { + LOGINFO("OnAudioLevelChanged event Received: audioLevel=%d", audioLevel); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioLevelChanged, audioLevel); + } + + void DeviceSettingsAudioImpl::OnAudioModeEvent(AudioPortType audioPortType, AudioStereoMode audioMode) + { + LOGINFO("OnAudioModeEvent event Received: portType=%d, mode=%d", audioPortType, audioMode); + dispatchAudioEvent(&DeviceSettingsAudio::INotification::OnAudioModeEvent, audioPortType, audioMode); + } + + // Audio port management + Core::hresult DeviceSettingsAudioImpl::GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) { + LOGINFO("GetAudioPort: type=%d, index=%d", type, index); + uint32_t result = _audio.GetAudioPort(type, index, handle); + return result; + } + + // Audio capabilities + Core::hresult DeviceSettingsAudioImpl::GetAudioCapabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetAudioCapabilities: handle=%d", handle); + uint32_t result = _audio.GetAudioCapabilities(handle, capabilities); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetAudioMS12Capabilities: handle=%d", handle); + uint32_t result = _audio.GetAudioMS12Capabilities(handle, capabilities); + return result; + } + + // Audio format and encoding + Core::hresult DeviceSettingsAudioImpl::GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) { + LOGINFO("GetAudioFormat: handle=%d", handle); + uint32_t result = _audio.GetAudioFormat(handle, audioFormat); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) { + LOGINFO("GetAudioEncoding: handle=%d", handle); + uint32_t result = _audio.GetAudioEncoding(handle, encoding); + return result; + } + + // Audio level and volume control + Core::hresult DeviceSettingsAudioImpl::SetAudioLevel(const int32_t handle, const float audioLevel) { + LOGINFO("SetAudioLevel: handle=%d, audioLevel=%.2f", handle, audioLevel); + uint32_t result = _audio.SetAudioLevel(handle, audioLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioLevel(const int32_t handle, float &audioLevel) { + LOGINFO("GetAudioLevel: handle=%d", handle); + uint32_t result = _audio.GetAudioLevel(handle, audioLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioGain(const int32_t handle, const float gainLevel) { + LOGINFO("SetAudioGain: handle=%d, gainLevel=%.2f", handle, gainLevel); + uint32_t result = _audio.SetAudioGain(handle, gainLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioGain(const int32_t handle, float &gainLevel) { + LOGINFO("GetAudioGain: handle=%d", handle); + uint32_t result = _audio.GetAudioGain(handle, gainLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioMute(const int32_t handle, const bool mute) { + LOGINFO("SetAudioMute: handle=%d, mute=%s", handle, mute ? "true" : "false"); + uint32_t result = _audio.SetAudioMute(handle, mute); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::IsAudioMuted(const int32_t handle, bool &muted) { + LOGINFO("IsAudioMuted: handle=%d", handle); + uint32_t result = _audio.IsAudioMuted(handle, muted); + return result; + } + + // Audio ducking + Core::hresult DeviceSettingsAudioImpl::SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) { + LOGINFO("SetAudioDucking: handle=%d, duckingType=%d, duckingAction=%d, level=%d", handle, duckingType, duckingAction, level); + uint32_t result = _audio.SetAudioDucking(handle, duckingType, duckingAction, level); + return result; + } + + // Stereo mode + Core::hresult DeviceSettingsAudioImpl::GetStereoMode(const int32_t handle, AudioStereoMode &mode) { + LOGINFO("GetStereoMode: handle=%d", handle); + uint32_t result = _audio.GetStereoMode(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) { + LOGINFO("SetStereoMode: handle=%d, mode=%d, persist=%s", handle, mode, persist ? "true" : "false"); + uint32_t result = _audio.SetStereoMode(handle, mode, persist); + return result; + } + + // Associated audio mixing + Core::hresult DeviceSettingsAudioImpl::SetAssociatedAudioMixing(const int32_t handle, const bool mixing) { + LOGINFO("SetAssociatedAudioMixing: handle=%d, mixing=%s", handle, mixing ? "true" : "false"); + uint32_t result = _audio.SetAssociatedAudioMixing(handle, mixing); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAssociatedAudioMixing(const int32_t handle, bool &mixing) { + LOGINFO("GetAssociatedAudioMixing: handle=%d", handle); + uint32_t result = _audio.GetAssociatedAudioMixing(handle, mixing); + return result; + } + + // Audio fader control + Core::hresult DeviceSettingsAudioImpl::SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance) { + LOGINFO("SetAudioFaderControl: handle=%d, mixerBalance=%d", handle, mixerBalance); + uint32_t result = _audio.SetAudioFaderControl(handle, mixerBalance); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance) { + LOGINFO("GetAudioFaderControl: handle=%d", handle); + uint32_t result = _audio.GetAudioFaderControl(handle, mixerBalance); + return result; + } + + // Audio language settings + Core::hresult DeviceSettingsAudioImpl::SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage) { + LOGINFO("SetAudioPrimaryLanguage: handle=%d, primaryAudioLanguage=%s", handle, primaryAudioLanguage.c_str()); + uint32_t result = _audio.SetAudioPrimaryLanguage(handle, primaryAudioLanguage); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage) { + LOGINFO("GetAudioPrimaryLanguage: handle=%d", handle); + uint32_t result = _audio.GetAudioPrimaryLanguage(handle, primaryAudioLanguage); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage) { + LOGINFO("SetAudioSecondaryLanguage: handle=%d, secondaryAudioLanguage=%s", handle, secondaryAudioLanguage.c_str()); + uint32_t result = _audio.SetAudioSecondaryLanguage(handle, secondaryAudioLanguage); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage) { + LOGINFO("GetAudioSecondaryLanguage: handle=%d", handle); + uint32_t result = _audio.GetAudioSecondaryLanguage(handle, secondaryAudioLanguage); + return result; + } + + // Output connection status + Core::hresult DeviceSettingsAudioImpl::IsAudioOutputConnected(const int32_t handle, bool &isConnected) { + LOGINFO("IsAudioOutputConnected: handle=%d", handle); + uint32_t result = _audio.IsAudioOutputConnected(handle, isConnected); + return result; + } + + // Dolby Atmos + Core::hresult DeviceSettingsAudioImpl::GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability) { + LOGINFO("GetAudioSinkDeviceAtmosCapability: handle=%d", handle); + uint32_t result = _audio.GetAudioSinkDeviceAtmosCapability(handle, atmosCapability); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioAtmosOutputMode(const int32_t handle, const bool enable) { + LOGINFO("SetAudioAtmosOutputMode: handle=%d, enable=%s", handle, enable ? "true" : "false"); + uint32_t result = _audio.SetAudioAtmosOutputMode(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) { + LOGINFO("GetSupportedCompressions: handle=%d", handle); + uint32_t result = _audio.GetSupportedCompressions(handle, compressions); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioCompression(const int32_t handle, AudioCompression &compression) { + LOGINFO("GetAudioCompression: handle=%d", handle); + uint32_t result = _audio.GetAudioCompression(handle, compression); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioCompression(const int32_t handle, const AudioCompression compression) { + LOGINFO("SetAudioCompression: handle=%d, compression=%d", handle, compression); + uint32_t result = _audio.SetAudioCompression(handle, compression); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetMS12Capabilities(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) { + LOGINFO("GetMS12Capabilities: handle=%d", handle); + uint32_t result = _audio.GetMS12Capabilities(handle, compressions); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetStereoAuto(const int32_t handle, int32_t &mode) { + LOGINFO("GetStereoAuto: handle=%d", handle); + uint32_t result = _audio.GetStereoAuto(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist) { + LOGINFO("SetStereoAuto: handle=%d, mode=%d, persist=%s", handle, mode, persist ? "true" : "false"); + uint32_t result = _audio.SetStereoAuto(handle, mode, persist); + return result; + } + + // Missing Audio interface methods implementation + + Core::hresult DeviceSettingsAudioImpl::IsAudioPortEnabled(const int32_t handle, bool &enabled) { + uint32_t result = _audio.IsAudioPortEnabled(handle, enabled); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::EnableAudioPort(const int32_t handle, const bool enable) { + uint32_t result = _audio.EnableAudioPort(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetSupportedARCTypes(const int32_t handle, int32_t &types) { + uint32_t result = _audio.GetSupportedARCTypes(handle, types); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) { + uint32_t result = _audio.SetSAD(handle, sadList, count); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::EnableARC(const int32_t handle, const AudioARCStatus arcStatus) { + uint32_t result = _audio.EnableARC(handle, arcStatus); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioEnablePersist(const int32_t handle, bool &enabled, string &portName) { + uint32_t result = _audio.GetAudioEnablePersist(handle, enabled, portName); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioEnablePersist(const int32_t handle, const bool enable, const string& portName) { + uint32_t result = _audio.SetAudioEnablePersist(handle, enable, portName); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) { + uint32_t result = _audio.IsAudioMSDecoded(handle, hasms11Decode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) { + uint32_t result = _audio.IsAudioMS12Decoded(handle, hasms12Decode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioLEConfig(const int32_t handle, bool &enabled) { + uint32_t result = _audio.GetAudioLEConfig(handle, enabled); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::EnableAudioLEConfig(const int32_t handle, const bool enable) { + uint32_t result = _audio.EnableAudioLEConfig(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioDelay(const int32_t handle, const uint32_t audioDelay) { + uint32_t result = _audio.SetAudioDelay(handle, audioDelay); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioDelay(const int32_t handle, uint32_t &audioDelay) { + uint32_t result = _audio.GetAudioDelay(handle, audioDelay); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) { + uint32_t result = _audio.SetAudioDelayOffset(handle, delayOffset); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) { + uint32_t result = _audio.GetAudioDelayOffset(handle, delayOffset); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioCompression(const int32_t handle, const int32_t compressionLevel) { + uint32_t result = _audio.SetAudioCompression(handle, compressionLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioCompression(const int32_t handle, int32_t &compressionLevel) { + uint32_t result = _audio.GetAudioCompression(handle, compressionLevel); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioDialogEnhancement(const int32_t handle, const int32_t level) { + uint32_t result = _audio.SetAudioDialogEnhancement(handle, level); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioDialogEnhancement(const int32_t handle, int32_t &level) { + uint32_t result = _audio.GetAudioDialogEnhancement(handle, level); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) { + uint32_t result = _audio.SetAudioDolbyVolumeMode(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) { + uint32_t result = _audio.GetAudioDolbyVolumeMode(handle, enabled); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) { + uint32_t result = _audio.SetAudioIntelligentEqualizerMode(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) { + uint32_t result = _audio.GetAudioIntelligentEqualizerMode(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller) { + uint32_t result = _audio.SetAudioVolumeLeveller(handle, volumeLeveller); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller) { + uint32_t result = _audio.GetAudioVolumeLeveller(handle, volumeLeveller); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioBassEnhancer(const int32_t handle, const int32_t boost) { + uint32_t result = _audio.SetAudioBassEnhancer(handle, boost); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioBassEnhancer(const int32_t handle, int32_t &boost) { + uint32_t result = _audio.GetAudioBassEnhancer(handle, boost); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::EnableAudioSurroundDecoder(const int32_t handle, const bool enable) { + uint32_t result = _audio.EnableAudioSurroundDecoder(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled) { + uint32_t result = _audio.IsAudioSurroundDecoderEnabled(handle, enabled); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioDRCMode(const int32_t handle, const int32_t drcMode) { + uint32_t result = _audio.SetAudioDRCMode(handle, drcMode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioDRCMode(const int32_t handle, int32_t &drcMode) { + uint32_t result = _audio.GetAudioDRCMode(handle, drcMode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioSurroundVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer) { + uint32_t result = _audio.SetAudioSurroudVirtualizer(handle, surroundVirtualizer); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioSurroundVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer) { + uint32_t result = _audio.GetAudioSurroudVirtualizer(handle, surroundVirtualizer); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioMISteering(const int32_t handle, const bool enable) { + uint32_t result = _audio.SetAudioMISteering(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioMISteering(const int32_t handle, bool &enable) { + uint32_t result = _audio.GetAudioMISteering(handle, enable); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) { + uint32_t result = _audio.SetAudioGraphicEqualizerMode(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) { + uint32_t result = _audio.GetAudioGraphicEqualizerMode(handle, mode); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const { + uint32_t result = _audio.GetAudioMS12ProfileList(handle, ms12ProfileList); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioMS12Profile(const int32_t handle, string &profile) { + uint32_t result = _audio.GetAudioMS12Profile(handle, profile); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioMS12Profile(const int32_t handle, const string& profile) { + uint32_t result = _audio.SetAudioMS12Profile(handle, profile); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume) { + uint32_t result = _audio.SetAudioMixerLevels(handle, audioInput, volume); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::SetAudioMS12SettingsOverride(const int32_t handle, const string& profileName, const string& profileSettingsName, const string& profileSettingValue, const AudioMS12ProfileState profileState) { + /* Convert AudioMS12ProfileState enum to the string ("ADD"/"REMOVE") expected + * by the Audio layer and dAudioImpl.h platform layer. */ + string stateStr = (profileState == AudioMS12ProfileState::AUDIO_MS12_PROFILE_STATE_ADD) ? "ADD" : "REMOVE"; + uint32_t result = _audio.SetAudioMS12SettingsOverride(handle, profileName, profileSettingsName, profileSettingValue, stateStr); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::ResetAudioDialogEnhancement(const int32_t handle) { + uint32_t result = _audio.ResetAudioDialogEnhancement(handle); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::ResetAudioBassEnhancer(const int32_t handle) { + uint32_t result = _audio.ResetAudioBassEnhancer(handle); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::ResetAudioSurroundVirtualizer(const int32_t handle) { + uint32_t result = _audio.ResetAudioSurroundVirtualizer(handle); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::ResetAudioVolumeLeveller(const int32_t handle) { + uint32_t result = _audio.ResetAudioVolumeLeveller(handle); + return result; + } + + Core::hresult DeviceSettingsAudioImpl::GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) { + uint32_t result = _audio.GetAudioHDMIARCPortId(handle, portId); + return result; + } + + void DeviceSettingsAudioImpl::getCachedConfigs( + std::vector& audioTypes, + std::vector& audioPorts) const + { + _configLock.Lock(); + + // AudioTypeConfigInfo is identical in IDeviceSettings — direct assignment + audioTypes.assign(_cachedAudioTypeConfigs.begin(), _cachedAudioTypeConfigs.end()); + + // AudioPortConfigInfo still differs (AudioPortType enum → int32_t) — keep cast + audioPorts.reserve(_cachedAudioPortConfigs.size()); + for (const auto& src : _cachedAudioPortConfigs) { + audioPorts.push_back({static_cast(src.audioPortType), src.audioPortIndex, + src.connectedVideoPortType, src.connectedVideoPortIndex}); + } + + _configLock.Unlock(); + } + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsAudioImplementation.h b/plugin/DeviceSettingsAudioImplementation.h new file mode 100644 index 0000000..84a3dfe --- /dev/null +++ b/plugin/DeviceSettingsAudioImplementation.h @@ -0,0 +1,282 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "Audio.h" +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsAudioImpl : public Audio::INotification + { + public: + // Note: No need to inherit from Exchange::IDeviceSettingsAudio anymore + // DeviceSettingsImp handles the WPEFramework interface contract + // This class only needs Audio::INotification for hardware callbacks + + DeviceSettingsAudioImpl(); + ~DeviceSettingsAudioImpl() override; + + static DeviceSettingsAudioImpl* Create() + { + return new DeviceSettingsAudioImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsAudioImpl(const DeviceSettingsAudioImpl&) = delete; + DeviceSettingsAudioImpl& operator=(const DeviceSettingsAudioImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + class EXTERNAL LambdaJob : public Core::IDispatch { + protected: + LambdaJob(DeviceSettingsAudioImpl* impl, std::function lambda) + : _impl(impl) + , _lambda(std::move(lambda)) + { + } + + public: + LambdaJob() = delete; + LambdaJob(const LambdaJob&) = delete; + LambdaJob& operator=(const LambdaJob&) = delete; + ~LambdaJob() {} + + static Core::ProxyType Create(DeviceSettingsAudioImpl* impl, std::function lambda) + { + return (Core::ProxyType(Core::ProxyType::Create(impl, std::move(lambda)))); + } + + virtual void Dispatch() + { + _lambda(); + } + + private: + DeviceSettingsAudioImpl* _impl; + std::function _lambda; + }; + + public: + void InitializeIARM(); + + // Audio Port Management + Core::hresult GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle); + Core::hresult GetMS12Capabilities(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + Core::hresult GetAudioCapabilities(const int32_t handle, int32_t &capabilities); + Core::hresult GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities); + + // Audio Format & Encoding + Core::hresult GetAudioFormat(const int32_t handle, AudioFormat &audioFormat); + Core::hresult GetAudioEncoding(const int32_t handle, AudioEncoding &encoding); + Core::hresult GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + Core::hresult GetAudioCompression(const int32_t handle, AudioCompression &compression); + Core::hresult SetAudioCompression(const int32_t handle, const AudioCompression compression); + + // Audio Level & Volume Control + Core::hresult SetAudioLevel(const int32_t handle, const float audioLevel); + Core::hresult GetAudioLevel(const int32_t handle, float &audioLevel); + Core::hresult SetAudioGain(const int32_t handle, const float gainLevel); + Core::hresult GetAudioGain(const int32_t handle, float &gainLevel); + Core::hresult SetAudioMute(const int32_t handle, const bool mute); + Core::hresult IsAudioMuted(const int32_t handle, bool &muted); + + // Audio Ducking + Core::hresult SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level); + + // Stereo Mode + Core::hresult GetStereoMode(const int32_t handle, AudioStereoMode &mode); + Core::hresult SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist); + Core::hresult GetStereoAuto(const int32_t handle, int32_t &mode); + Core::hresult SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist); + + // Associated Audio Mixing + Core::hresult SetAssociatedAudioMixing(const int32_t handle, const bool mixing); + Core::hresult GetAssociatedAudioMixing(const int32_t handle, bool &mixing); + + // Audio Fader Control + Core::hresult SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance); + Core::hresult GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance); + + // Audio Language Settings + Core::hresult SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage); + Core::hresult GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage); + Core::hresult SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage); + Core::hresult GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage); + + // Output Connection Status + Core::hresult IsAudioOutputConnected(const int32_t handle, bool &isConnected); + + // Dolby Atmos + Core::hresult GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability); + Core::hresult SetAudioAtmosOutputMode(const int32_t handle, const bool enable); + + // Additional Audio Port Methods + Core::hresult IsAudioPortEnabled(const int32_t handle, bool &enabled); + Core::hresult EnableAudioPort(const int32_t handle, const bool enable); + Core::hresult GetSupportedARCTypes(const int32_t handle, int32_t &types); + Core::hresult SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count); + Core::hresult EnableARC(const int32_t handle, const AudioARCStatus arcStatus); + + // Audio Persistence Configuration + Core::hresult GetAudioEnablePersist(const int32_t handle, bool &enabled, std::string &portName); + Core::hresult SetAudioEnablePersist(const int32_t handle, const bool enable, const std::string& portName); + + // Audio Decoder Status + Core::hresult IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode); + Core::hresult IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode); + + // Loudness Equivalence Configuration + Core::hresult GetAudioLEConfig(const int32_t handle, bool &enabled); + Core::hresult EnableAudioLEConfig(const int32_t handle, const bool enable); + + // Audio Delay Controls + Core::hresult SetAudioDelay(const int32_t handle, const uint32_t audioDelay); + Core::hresult GetAudioDelay(const int32_t handle, uint32_t &audioDelay); + Core::hresult SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset); + Core::hresult GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset); + + // Audio Dynamic Range Control + Core::hresult SetAudioCompression(const int32_t handle, const int32_t compressionLevel); + Core::hresult GetAudioCompression(const int32_t handle, int32_t &compressionLevel); + + // Dialog Enhancement + Core::hresult SetAudioDialogEnhancement(const int32_t handle, const int32_t level); + Core::hresult GetAudioDialogEnhancement(const int32_t handle, int32_t &level); + + // Dolby Volume Mode + Core::hresult SetAudioDolbyVolumeMode(const int32_t handle, const bool enable); + Core::hresult GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled); + + // Intelligent Equalizer + Core::hresult SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode); + Core::hresult GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode); + + // Volume Leveller + Core::hresult SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller); + Core::hresult GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller); + + // Bass Enhancer + Core::hresult SetAudioBassEnhancer(const int32_t handle, const int32_t boost); + Core::hresult GetAudioBassEnhancer(const int32_t handle, int32_t &boost); + + // Surround Decoder + Core::hresult EnableAudioSurroundDecoder(const int32_t handle, const bool enable); + Core::hresult IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled); + + // DRC Mode + Core::hresult SetAudioDRCMode(const int32_t handle, const int32_t drcMode); + Core::hresult GetAudioDRCMode(const int32_t handle, int32_t &drcMode); + + // Surround Virtualizer + Core::hresult SetAudioSurroundVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer); + Core::hresult GetAudioSurroundVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer); + + // MI Steering + Core::hresult SetAudioMISteering(const int32_t handle, const bool enable); + Core::hresult GetAudioMISteering(const int32_t handle, bool &enable); + + // Graphic Equalizer + Core::hresult SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode); + Core::hresult GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode); + + // MS12 Profile Management + Core::hresult GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const; + Core::hresult GetAudioMS12Profile(const int32_t handle, std::string &profile); + Core::hresult SetAudioMS12Profile(const int32_t handle, const std::string& profile); + + // Audio Mixer Levels + Core::hresult SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume); + + // MS12 Settings Override + Core::hresult SetAudioMS12SettingsOverride(const int32_t handle, const std::string& profileName, const std::string& profileSettingsName, const std::string& profileSettingValue, const AudioMS12ProfileState profileState); + + // Reset Functions + Core::hresult ResetAudioDialogEnhancement(const int32_t handle); + Core::hresult ResetAudioBassEnhancer(const int32_t handle); + Core::hresult ResetAudioSurroundVirtualizer(const int32_t handle); + Core::hresult ResetAudioVolumeLeveller(const int32_t handle); + + // HDMI ARC + Core::hresult GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId); + + // Notification registration/unregistration + Core::hresult Register(DeviceSettingsAudio::INotification* notification); + Core::hresult Unregister(DeviceSettingsAudio::INotification* notification); + + // Audio::INotification interface implementation - hardware callbacks + void OnAssociatedAudioMixingChanged(bool mixing) override; + void OnAudioFaderControlChanged(int32_t mixerBalance) override; + void OnAudioPrimaryLanguageChanged(const std::string& primaryLanguage) override; + void OnAudioSecondaryLanguageChanged(const std::string& secondaryLanguage) override; + void OnAudioOutHotPlug(AudioPortType portType, uint32_t uiPortNumber, bool isPortConnected) override; + void OnAudioFormatUpdate(AudioFormat audioFormat) override; + void OnDolbyAtmosCapabilitiesChanged(DolbyAtmosCapability atmosCapability, bool status) override; + void OnAudioPortStateChanged(AudioPortState audioPortState) override; + void OnAudioLevelChanged(int32_t audioLevel) override; + void OnAudioModeEvent(AudioPortType audioPortType, AudioStereoMode audioMode) override; + + // Fills IDeviceSettings consolidated config vectors from cached data + void getCachedConfigs(std::vector& audioTypes, + std::vector& audioPorts) const; + + private: + template + void dispatchAudioEvent(Func notifyFunc, Args&&... args); + + template + Core::hresult Register(std::list& list, T* notification); + + template + Core::hresult Unregister(std::list& list, const T* notification); + + Audio _audio; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _audio.InitialiseHAL(); } + std::list _AudioNotifications; + mutable Core::CriticalSection _configLock; + mutable Core::CriticalSection _callbackLock; + std::vector _cachedAudioTypeConfigs; + std::vector _cachedAudioPortConfigs; + }; +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsCompositeInImplementation.cpp b/plugin/DeviceSettingsCompositeInImplementation.cpp new file mode 100644 index 0000000..68e0f83 --- /dev/null +++ b/plugin/DeviceSettingsCompositeInImplementation.cpp @@ -0,0 +1,200 @@ +/* + * 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. + */ + +#include "DeviceSettingsCompositeInImplementation.h" + +#include + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsCompositeInImpl::DeviceSettingsCompositeInImpl() : + _CompositeInNotifications(), + _apiLock(), + _callbackLock(), + _compositeIn(CompositeIn::Create(*this)) + { + LOGINFO("DeviceSettingsCompositeInImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsCompositeInImpl::~DeviceSettingsCompositeInImpl() { + LOGINFO("DeviceSettingsCompositeInImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsCompositeInImpl::dispatchCompositeInEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _CompositeInNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process ICompositeIn event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsCompositeInImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsCompositeInImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsCompositeInImpl::Register(Exchange::IDeviceSettingsCompositeIn::INotification* notification) + { + Core::hresult errorCode = Register(_CompositeInNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("ICompositeIn %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("ICompositeIn %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsCompositeInImpl::Unregister(Exchange::IDeviceSettingsCompositeIn::INotification* notification) + { + Core::hresult errorCode = Unregister(_CompositeInNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("ICompositeIn %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("ICompositeIn %p unregistered successfully", notification); + } + return errorCode; + } + + // CompositeIn::INotification interface implementations (called by DS HAL) + void DeviceSettingsCompositeInImpl::OnCompositeInHotPlug(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) + { + LOGINFO("DS HAL OnCompositeInHotPlug event: port=%d, isConnected=%s", static_cast(port), isConnected ? "true" : "false"); + + // Port already converted to WPE type at HAL layer - direct dispatch + dispatchCompositeInEvent(&Exchange::IDeviceSettingsCompositeIn::INotification::OnCompositeInHotPlug, port, isConnected); + } + + void DeviceSettingsCompositeInImpl::OnCompositeInSignalStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) + { + LOGINFO("DS HAL OnCompositeInSignalStatus event: port=%d, signalStatus=%d", static_cast(port), static_cast(signalStatus)); + + // Types already converted to WPE types at HAL layer - direct dispatch + dispatchCompositeInEvent(&Exchange::IDeviceSettingsCompositeIn::INotification::OnCompositeInSignalStatus, port, signalStatus); + } + + void DeviceSettingsCompositeInImpl::OnCompositeInStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) + { + LOGINFO("DS HAL OnCompositeInStatus event: activePort=%d, isPresented=%s", static_cast(activePort), isPresented ? "true" : "false"); + + // Port already converted to WPE type at HAL layer - direct dispatch + dispatchCompositeInEvent(&Exchange::IDeviceSettingsCompositeIn::INotification::OnCompositeInStatus, activePort, isPresented); + } + + void DeviceSettingsCompositeInImpl::OnCompositeInVideoModeUpdate(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution) + { + LOGINFO("DS HAL OnCompositeInVideoModeUpdate event: activePort=%d", static_cast(activePort)); + + // Types already converted to WPE types at HAL layer - direct dispatch + dispatchCompositeInEvent(&Exchange::IDeviceSettingsCompositeIn::INotification::OnCompositeInVideoModeUpdate, activePort, videoResolution); + } + + // CompositeIn interface method implementations called by DeviceSettingsImp (delegate to _compositeIn) + uint32_t DeviceSettingsCompositeInImpl::GetNrOfCompositeInputs(int32_t &nrCompositeInputs) + { + uint32_t result = _compositeIn.GetNrOfCompositeInputs(nrCompositeInputs); + if (result == Core::ERROR_NONE) { + LOGINFO("GetNrOfCompositeInputs succeeded: nrCompositeInputs=%d", nrCompositeInputs); + } else { + LOGERR("GetNrOfCompositeInputs failed: error=%u", result); + } + return result; + } + + uint32_t DeviceSettingsCompositeInImpl::GetCompositeInStatus(CompositeInStatus &status) + { + uint32_t result = _compositeIn.GetCompositeInStatus(status); + if (result == Core::ERROR_NONE) { + LOGINFO("GetCompositeInStatus succeeded: activePort=%d, isPresented=%s", + static_cast(status.activePort), status.isPresented ? "true" : "false"); + } else { + LOGERR("GetCompositeInStatus failed: error=%u", result); + } + return result; + } + + uint32_t DeviceSettingsCompositeInImpl::SelectCompositeInPort(const CompositeInPort port) + { + uint32_t result = _compositeIn.SelectCompositeInPort(port); + if (result == Core::ERROR_NONE) { + LOGINFO("SelectCompositeInPort succeeded: port=%d", static_cast(port)); + } else { + LOGERR("SelectCompositeInPort failed: port=%d, error=%u", static_cast(port), result); + } + return result; + } + + uint32_t DeviceSettingsCompositeInImpl::ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect) + { + uint32_t result = _compositeIn.ScaleCompositeInVideo(videoRect); + if (result == Core::ERROR_NONE) { + LOGINFO("ScaleCompositeInVideo succeeded: x=%d, y=%d, width=%d, height=%d", + videoRect.x, videoRect.y, videoRect.width, videoRect.height); + } else { + LOGERR("ScaleCompositeInVideo failed: error=%u", result); + } + return result; + } + + + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsCompositeInImplementation.h b/plugin/DeviceSettingsCompositeInImplementation.h new file mode 100644 index 0000000..bc168af --- /dev/null +++ b/plugin/DeviceSettingsCompositeInImplementation.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "CompositeIn.h" + +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsCompositeInImpl : public CompositeIn::INotification + { + public: + // Note: No need to inherit from Exchange::IDeviceSettingsCompositeIn anymore + // DeviceSettingsImp handles the WPEFramework interface contract + // This class only needs CompositeIn::INotification for hardware callbacks + + DeviceSettingsCompositeInImpl(); + ~DeviceSettingsCompositeInImpl() override; + + static DeviceSettingsCompositeInImpl* Create() + { + return new DeviceSettingsCompositeInImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsCompositeInImpl(const DeviceSettingsCompositeInImpl&) = delete; + DeviceSettingsCompositeInImpl& operator=(const DeviceSettingsCompositeInImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + + // Template method for dispatching CompositeIn Events + template + void dispatchCompositeInEvent(Func notifyFunc, Args&&... args); + + // Template methods for notification management + template + Core::hresult Register(std::list& list, T* notification); + + template + Core::hresult Unregister(std::list& list, const T* notification); + + // Public notification registration methods called by DeviceSettingsImp + Core::hresult Register(Exchange::IDeviceSettingsCompositeIn::INotification* notification); + Core::hresult Unregister(Exchange::IDeviceSettingsCompositeIn::INotification* notification); + + // Required CompositeIn::INotification interface implementations - receive WPE Framework types from HAL + void OnCompositeInHotPlug(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const bool isConnected) override; + void OnCompositeInSignalStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort port, const Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) override; + void OnCompositeInStatus(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const bool isPresented) override; + void OnCompositeInVideoModeUpdate(const Exchange::IDeviceSettingsCompositeIn::CompositeInPort activePort, const Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution videoResolution) override; + + // CompositeIn interface method implementations called by DeviceSettingsImp + uint32_t GetNrOfCompositeInputs(int32_t &nrCompositeInputs); + uint32_t GetCompositeInStatus(CompositeInStatus &status); + uint32_t SelectCompositeInPort(const CompositeInPort port); + uint32_t ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect); + + private: + std::list _CompositeInNotifications; + + // Thread-safety locks + mutable Core::CriticalSection _apiLock; + mutable Core::CriticalSection _callbackLock; + + CompositeIn _compositeIn; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _compositeIn.InitialiseHAL(); } + }; + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsDisplayImplementation.cpp b/plugin/DeviceSettingsDisplayImplementation.cpp new file mode 100644 index 0000000..4517365 --- /dev/null +++ b/plugin/DeviceSettingsDisplayImplementation.cpp @@ -0,0 +1,259 @@ +/* + * 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. + */ + +#include "DeviceSettingsDisplayImplementation.h" + +#include + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsDisplayImpl::DeviceSettingsDisplayImpl() : + _DisplayNotifications(), + _DisplayHDMIHotPlugNotifications(), + _apiLock(), + _callbackLock(), + _display(Display::Create(*this)) + { + LOGINFO("DeviceSettingsDisplayImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsDisplayImpl::~DeviceSettingsDisplayImpl() { + LOGINFO("DeviceSettingsDisplayImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsDisplayImpl::dispatchDisplayEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _DisplayNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IDisplay event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + void DeviceSettingsDisplayImpl::dispatchDisplayHDMIHotPlugEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _DisplayHDMIHotPlugNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IDisplayHDMIHotPlug event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsDisplayImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsDisplayImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsDisplayImpl::Register(IDisplayNotification* notification) + { + Core::hresult errorCode = Register(_DisplayNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IDisplay %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IDisplay %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsDisplayImpl::Unregister(IDisplayNotification* notification) + { + Core::hresult errorCode = Unregister(_DisplayNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IDisplay %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IDisplay %p unregistered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsDisplayImpl::Register(IDisplayHDMIHotPlugNotification* notification) + { + Core::hresult errorCode = Register(_DisplayHDMIHotPlugNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IDisplayHDMIHotPlug %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IDisplayHDMIHotPlug %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsDisplayImpl::Unregister(IDisplayHDMIHotPlugNotification* notification) + { + Core::hresult errorCode = Unregister(_DisplayHDMIHotPlugNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IDisplayHDMIHotPlug %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IDisplayHDMIHotPlug %p unregistered successfully", notification); + } + return errorCode; + } + + void DeviceSettingsDisplayImpl::OnDisplayRxSense(const DisplayEvent displayEvent) + { + LOGINFO("DS HAL OnDisplayRxSense event: displayEvent=%d", static_cast(displayEvent)); + dispatchDisplayEvent(&IDisplayNotification::OnDisplayRxSense, displayEvent); + } + + void DeviceSettingsDisplayImpl::OnDisplayHDCPStatus() + { + LOGINFO("DS HAL OnDisplayHDCPStatus event"); + dispatchDisplayEvent(&IDisplayNotification::OnDisplayHDCPStatus); + } + + void DeviceSettingsDisplayImpl::OnDisplayHDMIHotPlug(const DisplayEvent displayEvent) + { + LOGINFO("DS HAL OnDisplayHDMIHotPlug event: displayEvent=%d", static_cast(displayEvent)); + dispatchDisplayHDMIHotPlugEvent(&IDisplayHDMIHotPlugNotification::OnDisplayHDMIHotPlug, displayEvent); + } + + // Display interface method implementations called by DeviceSettingsImp + uint32_t DeviceSettingsDisplayImpl::GetDisplayEdid(const int32_t handle, DisplayEDID &edId, IDSVideoPortResolutionIterator*& supportedResolutionList) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.GetDisplayEdid(handle, edId, supportedResolutionList); + if (result == Core::ERROR_NONE) { + LOGINFO("GetDisplayEdid succeeded: handle=%d", handle); + } else { + LOGERR("GetDisplayEdid failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.GetDisplayEdidBytes(handle, edIdBytes, edidLength); + if (result == Core::ERROR_NONE) { + LOGINFO("GetDisplayEdidBytes succeeded: handle=%d, edidLength=%d", handle, edidLength); + } else { + LOGERR("GetDisplayEdidBytes failed: handle=%d, edidLength=%d, error=%u", handle, edidLength, result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::GetDisplay(const DisplayPortType portType, const int32_t index, int32_t &handle) + { + + uint32_t result = Core::ERROR_GENERAL; + result = _display.GetDisplay(portType, index, handle); + if (result == Core::ERROR_NONE) { + LOGINFO("GetDisplay succeeded: portType=%d, index=%d, handle=%d", static_cast(portType), index, handle); + } else { + LOGERR("GetDisplay failed: portType=%d, index=%d, error=%u", static_cast(portType), index, result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::GetDisplayAspectRatio(const int32_t handle, Exchange::IDeviceSettingsDisplay::DisplayVideoAspectRatio &aspectRatio) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.GetDisplayAspectRatio(handle, aspectRatio); + if (result == Core::ERROR_NONE) { + LOGINFO("GetDisplayAspectRatio succeeded: handle=%d, aspectRatio=%d", handle, static_cast(aspectRatio)); + } else { + LOGERR("GetDisplayAspectRatio failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::SetAllmEnabled(const int32_t handle, const bool enabled) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.SetAllmEnabled(handle, enabled); + if (result == Core::ERROR_NONE) { + LOGINFO("SetAllmEnabled succeeded: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + } else { + LOGERR("SetAllmEnabled failed: handle=%d, enabled=%s, error=%u", handle, enabled ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::SetAVIContentType(const int32_t handle, const DisplayAVIContentType contentType) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.SetAVIContentType(handle, contentType); + if (result == Core::ERROR_NONE) { + LOGINFO("SetAVIContentType succeeded: handle=%d, contentType=%d", handle, static_cast(contentType)); + } else { + LOGERR("SetAVIContentType failed: handle=%d, contentType=%d, error=%u", handle, static_cast(contentType), result); + } + return result; + } + + uint32_t DeviceSettingsDisplayImpl::SetAVIScanInformation(const int32_t handle, const DisplayAVIScanInformation scanInfo) + { + uint32_t result = Core::ERROR_GENERAL; + result = _display.SetAVIScanInformation(handle, scanInfo); + if (result == Core::ERROR_NONE) { + LOGINFO("SetAVIScanInformation succeeded: handle=%d, scanInfo=%d", handle, static_cast(scanInfo)); + } else { + LOGERR("SetAVIScanInformation failed: handle=%d, scanInfo=%d, error=%u", handle, static_cast(scanInfo), result); + } + return result; + } + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsDisplayImplementation.h b/plugin/DeviceSettingsDisplayImplementation.h new file mode 100644 index 0000000..df832b4 --- /dev/null +++ b/plugin/DeviceSettingsDisplayImplementation.h @@ -0,0 +1,114 @@ +/* + * 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "Display.h" + +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsDisplayImpl : public Display::INotification + { + public: + DeviceSettingsDisplayImpl(); + ~DeviceSettingsDisplayImpl() override; + + static DeviceSettingsDisplayImpl* Create() + { + return new DeviceSettingsDisplayImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsDisplayImpl(const DeviceSettingsDisplayImpl&) = delete; + DeviceSettingsDisplayImpl& operator=(const DeviceSettingsDisplayImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + + // Template method for dispatching Display Events + template + void dispatchDisplayEvent(Func notifyFunc, Args&&... args); + + // Template methods for notification management + template + Core::hresult Register(std::list& list, T* notification); + + template + Core::hresult Unregister(std::list& list, const T* notification); + + // Public notification registration methods called by DeviceSettingsImp + Core::hresult Register(IDisplayNotification* notification); + Core::hresult Unregister(IDisplayNotification* notification); + Core::hresult Register(IDisplayHDMIHotPlugNotification* notification); + Core::hresult Unregister(IDisplayHDMIHotPlugNotification* notification); + + // Required Display::INotification interface implementations + void OnDisplayRxSense(const DisplayEvent displayEvent) override; + void OnDisplayHDCPStatus() override; + void OnDisplayHDMIHotPlug(const DisplayEvent displayEvent) override; + + // Display interface method implementations called by DeviceSettingsImp + uint32_t GetDisplayEdid(const int32_t handle, DisplayEDID &edId, IDSVideoPortResolutionIterator*& supportedResolutionList); + uint32_t GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength); + + // New Display interface methods + uint32_t GetDisplay(const DisplayPortType portType, const int32_t index, int32_t &handle); + uint32_t GetDisplayAspectRatio(const int32_t handle, Exchange::IDeviceSettingsDisplay::DisplayVideoAspectRatio &aspectRatio); + uint32_t SetAllmEnabled(const int32_t handle, const bool enabled); + uint32_t SetAVIContentType(const int32_t handle, const DisplayAVIContentType contentType); + uint32_t SetAVIScanInformation(const int32_t handle, const DisplayAVIScanInformation scanInfo); + + // Template method for event dispatch + template + void dispatchDisplayHDMIHotPlugEvent(Func notifyFunc, Args&&... args); + + private: + std::list _DisplayNotifications; + std::list _DisplayHDMIHotPlugNotifications; + + // Thread-safety locks + mutable Core::CriticalSection _apiLock; + mutable Core::CriticalSection _callbackLock; + + Display _display; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _display.InitialiseHAL(); } + }; + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsFPDImplementation.cpp b/plugin/DeviceSettingsFPDImplementation.cpp new file mode 100644 index 0000000..c5fdcf0 --- /dev/null +++ b/plugin/DeviceSettingsFPDImplementation.cpp @@ -0,0 +1,393 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DeviceSettingsFPDImplementation.h" + +#include +#include + +#include "DeviceSettingsHALConfig.h" + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsFPDImpl::DeviceSettingsFPDImpl() + : _fpd(FPD::Create(*this)) + { + LOGINFO("DeviceSettingsFPDImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsFPDImpl::~DeviceSettingsFPDImpl() { + LOGINFO("DeviceSettingsFPDImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsFPDImpl::dispatchFPDEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _FPDNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IFPD event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsFPDImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsFPDImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsFPDImpl::Register(DeviceSettingsFPD::INotification* notification) + { + Core::hresult errorCode = Register(_FPDNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IFPD %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IFPD %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::Unregister(DeviceSettingsFPD::INotification* notification) + { + Core::hresult errorCode = Unregister(_FPDNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IFPD %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IFPD %p unregistered successfully", notification); + } + return errorCode; + } + + // FPD notification implementation + void DeviceSettingsFPDImpl::OnFPDTimeFormatChanged(const FPDTimeFormat timeFormat) + { + LOGINFO("OnFPDTimeFormatChanged event Received: timeFormat=%d", timeFormat); + dispatchFPDEvent(&DeviceSettingsFPD::INotification::OnFPDTimeFormatChanged, timeFormat); + } + + //Depricated + Core::hresult DeviceSettingsFPDImpl::SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) { + LOGINFO("SetFPDTime: timeFormat=%d, minutes=%u, seconds=%u", timeFormat, minutes, seconds); + LOGINFO("SetFPDTime: SUCCESS - stub implementation completed"); + return Core::ERROR_NONE; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) { + LOGINFO("SetFPDScroll: scrollHoldDuration=%u, horizontal=%u, vertical=%u", scrollHoldDuration, nHorizontalScrollIterations, nVerticalScrollIterations); + LOGINFO("SetFPDScroll: SUCCESS - stub implementation completed"); + return Core::ERROR_NONE; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) { + LOGINFO("SetFPDTextBrightness: textDisplay=%d, brightNess=%u", textDisplay, brightNess); + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.SetFPDTextBrightness(textDisplay, brightNess) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDTextBrightness: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDTextBrightness: SUCCESS - platform call completed"); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.GetFPDTextBrightness(textDisplay, brightNess) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetFPDTextBrightness: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetFPDTextBrightness: SUCCESS - textDisplay=%d, brightNess=%d", textDisplay, brightNess); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::EnableFPDClockDisplay(const bool enable) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.EnableFPDClockDisplay(enable) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("EnableFPDClockDisplay: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("EnableFPDClockDisplay: enable=%s", enable ? "true" : "false"); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.GetFPDTimeFormat(fpdTimeFormat) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetFPDTimeFormat: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetFPDTimeFormat: SUCCESS - fpdTimeFormat=%d", fpdTimeFormat); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.SetFPDTimeFormat(fpdTimeFormat) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDTimeFormat: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDTimeFormat: fpdTimeFormat=%d", fpdTimeFormat); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.SetFPDBlink(indicator, blinkDuration, blinkIterations) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDBlink: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDBlink: indicator=%d, blinkDuration=%u, blinkIterations=%u", indicator, blinkDuration, blinkIterations); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDMode(const FPDMode fpdMode) { + Core::hresult errorCode = Core::ERROR_GENERAL; + if (_fpd.SetFPDMode(fpdMode) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDMode: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDMode: fpdMode=%d", fpdMode); + return errorCode; + } + //Depricated + + Core::hresult DeviceSettingsFPDImpl::SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist) { + LOGINFO("SetFPDBrightness: indicator=%d, brightNess=%u, persist=%s", indicator, brightNess, persist ? "true" : "false"); + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.SetFPDBrightness(indicator, brightNess, persist) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDBrightness: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDBrightness: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) { + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.GetFPDBrightness(indicator, brightNess) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetFPDBrightness: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetFPDBrightness: SUCCESS - indicator=%d, brightNess=%d", indicator, brightNess); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDState(const FPDIndicator indicator, const FPDState state) { + LOGINFO("SetFPDState: indicator=%d, state=%d", indicator, state); + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.SetFPDState(indicator, state) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDState: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDState: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::GetFPDState(const FPDIndicator indicator, FPDState &state) { + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.GetFPDState(indicator, state) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetFPDState: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetFPDState: SUCCESS - indicator=%d, state=%d", indicator, state); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::GetFPDColor(const FPDIndicator indicator, uint32_t &color) { + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.GetFPDColor(indicator, color) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetFPDColor: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetFPDColor: SUCCESS - indicator=%d, color=0x%X", indicator, color); + return errorCode; + } + + Core::hresult DeviceSettingsFPDImpl::SetFPDColor(const FPDIndicator indicator, const uint32_t color) { + LOGINFO("SetFPDColor: indicator=%d, color=0x%X", indicator, color); + + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_fpd.SetFPDColor(indicator, color) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetFPDColor: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetFPDColor: SUCCESS - platform call completed"); + + return errorCode; + } + + void DeviceSettingsFPDImpl::getCachedConfigs( + std::vector& textDisplays, + std::vector& indicators, + std::vector& colors, + std::vector& colorBindings) const + { + // FPD types are identical in IDeviceSettings — direct assignment, no field-by-field copy + _apiLock.Lock(); + textDisplays.assign(_cachedTextDisplayConfigs.begin(), _cachedTextDisplayConfigs.end()); + indicators.assign(_cachedIndicatorConfigs.begin(), _cachedIndicatorConfigs.end()); + colors.assign(_cachedColorConfigs.begin(), _cachedColorConfigs.end()); + colorBindings.assign(_cachedColorBindingConfigs.begin(), _cachedColorBindingConfigs.end()); + _apiLock.Unlock(); + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsFPDImplementation.h b/plugin/DeviceSettingsFPDImplementation.h new file mode 100644 index 0000000..b5d366e --- /dev/null +++ b/plugin/DeviceSettingsFPDImplementation.h @@ -0,0 +1,152 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "fpd.h" +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsFPDImpl : public FPD::INotification + { + public: + + DeviceSettingsFPDImpl(); + ~DeviceSettingsFPDImpl() override; + + static DeviceSettingsFPDImpl* Create() + { + return new DeviceSettingsFPDImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsFPDImpl(const DeviceSettingsFPDImpl&) = delete; + DeviceSettingsFPDImpl& operator=(const DeviceSettingsFPDImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + class EXTERNAL LambdaJob : public Core::IDispatch { + protected: + LambdaJob(DeviceSettingsFPDImpl* impl, std::function lambda) + : _impl(impl) + , _lambda(std::move(lambda)) + { + } + + public: + LambdaJob() = delete; + LambdaJob(const LambdaJob&) = delete; + LambdaJob& operator=(const LambdaJob&) = delete; + ~LambdaJob() {} + + static Core::ProxyType Create(DeviceSettingsFPDImpl* impl, std::function lambda) + { + return (Core::ProxyType(Core::ProxyType::Create(impl, std::move(lambda)))); + } + + virtual void Dispatch() + { + _lambda(); + } + + private: + DeviceSettingsFPDImpl* _impl; + std::function _lambda; + }; + + public: + void InitializeIARM(); + + // FPD implementation methods - no longer interface methods, just implementation + // These are called by DeviceSettingsImp which implements the Exchange interface + Core::hresult Register(Exchange::IDeviceSettingsFPD::INotification* notification); + Core::hresult Unregister(Exchange::IDeviceSettingsFPD::INotification* notification); + Core::hresult SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds); + Core::hresult SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations); + Core::hresult SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations); + Core::hresult SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist); + Core::hresult GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess); + Core::hresult SetFPDState(const FPDIndicator indicator, const FPDState state); + Core::hresult GetFPDState(const FPDIndicator indicator, FPDState &state); + Core::hresult GetFPDColor(const FPDIndicator indicator, uint32_t &color); + Core::hresult SetFPDColor(const FPDIndicator indicator, const uint32_t color); + Core::hresult SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess); + Core::hresult GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess); + Core::hresult EnableFPDClockDisplay(const bool enable); + Core::hresult GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat); + Core::hresult SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat); + Core::hresult SetFPDMode(const FPDMode fpdMode); + + // Fills IDeviceSettings consolidated config vectors from cached data + void getCachedConfigs(std::vector& textDisplays, + std::vector& indicators, + std::vector& colors, + std::vector& colorBindings) const; + + std::list _FPDNotifications; + + // lock to guard all apis of DeviceSettings + mutable Core::CriticalSection _apiLock; + // lock to guard all notification from DeviceSettings to clients and also their callback register & unregister + mutable Core::CriticalSection _callbackLock; + + std::vector _cachedColorConfigs; + std::vector _cachedIndicatorConfigs; + std::vector _cachedTextDisplayConfigs; + std::vector _cachedColorBindingConfigs; + + template + Core::hresult Register(std::list& list, T* notification); + template + Core::hresult Unregister(std::list& list, const T* notification); + + template + void dispatchFPDEvent(Func notifyFunc, Args&&... args); + + // FPD notification method + virtual void OnFPDTimeFormatChanged(const FPDTimeFormat timeFormat) override; + + FPD _fpd; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _fpd.InitialiseHAL(); } + }; +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsHALConfig.cpp b/plugin/DeviceSettingsHALConfig.cpp new file mode 100644 index 0000000..9b95cea --- /dev/null +++ b/plugin/DeviceSettingsHALConfig.cpp @@ -0,0 +1,825 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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 DeviceSettingsHALConfig.cpp + * @brief Shared HAL configuration loading for FPD, Audio, and VideoPort. + * + * All three components use the identical dlopen → dlsym → deep-copy → dlclose + * pattern. This file consolidates that duplicated code so each component's + * implementation file only calls the public DeviceSettingsHAL:: functions. + */ + +#include "Module.h" +#include "DeviceSettingsHALConfig.h" + +/* Component headers — each pulls in the raw HAL type headers it needs: + * fpd.h → dsFPDTypes.h (dsFPDColorConfig_t etc. at global scope) + * Audio.h → dsAudio.h (dsAudioTypeConfig_t etc. at global scope) + * + "using namespace WPEFramework::Exchange;" + * VideoPort.h → dsVideoPort.h (dsVideoPortTypeConfig_t etc. at global scope) + */ +#include "fpd.h" +#include "Audio.h" +#include "VideoPort.h" +#include "VideoDevice.h" + +#include +#include +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// File-local helpers (anonymous namespace = not visible outside this TU) +// --------------------------------------------------------------------------- +namespace { + +// ── Shared DL symbol helper ──────────────────────────────────────────────── + +typedef struct _dlSymbolLookup { + const char* name; + void** dataptr; +} dlSymbolLookup; + +static bool LoadDLSymbols(void* pDLHandle, const dlSymbolLookup* symbols, const int numberOfSymbols) +{ + int currentSymbols = 0; + bool isAllSymbolsLoaded = false; + + if ((pDLHandle == NULL) || (symbols == NULL)) { + LOGERR("LoadDLSymbols: Invalid handle or symbols"); + return false; + } + + for (int i = 0; i < numberOfSymbols; i++) { + if ((symbols[i].dataptr == NULL) || (symbols[i].name == NULL)) { + LOGERR("LoadDLSymbols: Invalid symbol entry at index %d", i); + continue; + } + + *(symbols[i].dataptr) = dlsym(pDLHandle, symbols[i].name); + if (*(symbols[i].dataptr) == NULL) { + LOGWARN("LoadDLSymbols: [%s] not found", symbols[i].name); + } else { + currentSymbols++; + } + } + + isAllSymbolsLoaded = (numberOfSymbols > 0) ? (currentSymbols == numberOfSymbols) : false; + return isAllSymbolsLoaded; +} + +// ── FPD ─────────────────────────────────────────────────────────────────── + +static const char* kDefaultSupportedCharacters = "ABCEDFG"; + +typedef struct _fpdConfigs { + const dsFPDColorConfig_t* pKFPDIndicatorColors; + const dsFPDIndicatorConfig_t* pKIndicators; + const dsFPDTextDisplayConfig_t* pKTextDisplays; + int* pKFPDIndicatorColors_size; + int* pKIndicators_size; + int* pKTextDisplays_size; +} fpdConfigs_t; + +static bool LoadFrontPanelConfigFromHAL(fpdConfigs_t& config, void*& outHandle) +{ + void* pDLHandle = NULL; + bool isSymbolsLoaded = false; + + outHandle = NULL; + memset(&config, 0, sizeof(config)); + + dlerror(); + pDLHandle = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (pDLHandle == NULL) { + const char* dlErr = dlerror(); + LOGWARN("LoadFrontPanelConfigFromHAL: dlopen failed for %s: %s", + RDK_DSHAL_NAME, (dlErr ? dlErr : "unknown")); + return false; + } + + dlSymbolLookup fpdConfigSymbols[] = { + {"kFPDIndicatorColors", (void**)&config.pKFPDIndicatorColors}, + {"kFPDIndicatorColors_size", (void**)&config.pKFPDIndicatorColors_size}, + {"kIndicators", (void**)&config.pKIndicators}, + {"kIndicators_size", (void**)&config.pKIndicators_size}, + {"kFPDTextDisplays", (void**)&config.pKTextDisplays}, + {"kFPDTextDisplays_size", (void**)&config.pKTextDisplays_size} + }; + + isSymbolsLoaded = LoadDLSymbols(pDLHandle, fpdConfigSymbols, + sizeof(fpdConfigSymbols) / sizeof(dlSymbolLookup)); + + if (!isSymbolsLoaded) { + LOGWARN("LoadFrontPanelConfigFromHAL: Failed to load all front panel symbols from HAL"); + dlclose(pDLHandle); + return false; + } + + if ((config.pKFPDIndicatorColors == NULL) || (config.pKIndicators == NULL) || + (config.pKTextDisplays == NULL) || (config.pKFPDIndicatorColors_size == NULL) || + (config.pKIndicators_size == NULL) || (config.pKTextDisplays_size == NULL)) { + LOGWARN("LoadFrontPanelConfigFromHAL: HAL symbols loaded but one or more pointers are null"); + dlclose(pDLHandle); + return false; + } + + outHandle = pDLHandle; + return true; +} + +// ── Audio ───────────────────────────────────────────────────────────────── + +typedef struct _audioConfigs { + const dsAudioTypeConfig_t* pKConfigs; + const dsAudioPortConfig_t* pKPorts; + int* pKConfigSize; + int* pKPortSize; +} audioConfigs_t; + +template +static uint32_t ToEnumMask(const EnumType* values, const size_t count) +{ + static_assert(std::is_enum::value, "EnumType must be an enum"); + + uint32_t mask = 0; + for (size_t index = 0; index < count; ++index) { + const uint32_t bit = static_cast(values[index]); + if (bit < (sizeof(mask) * 8)) { + mask |= (1u << bit); + } + } + return mask; +} + +static bool LoadAudioConfigFromHAL(audioConfigs_t& config, void*& outHandle) +{ + void* pDLHandle = NULL; + bool isSymbolsLoaded = false; + + outHandle = NULL; + memset(&config, 0, sizeof(config)); + + dlerror(); + pDLHandle = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (pDLHandle == NULL) { + const char* dlErr = dlerror(); + LOGWARN("LoadAudioConfigFromHAL: dlopen failed for %s: %s", + RDK_DSHAL_NAME, (dlErr ? dlErr : "unknown")); + return false; + } + + dlSymbolLookup audioConfigSymbols[] = { + {"kAudioConfigs", (void**)&config.pKConfigs}, + {"kAudioPorts", (void**)&config.pKPorts}, + {"kAudioConfigs_size", (void**)&config.pKConfigSize}, + {"kAudioPorts_size", (void**)&config.pKPortSize} + }; + + isSymbolsLoaded = LoadDLSymbols(pDLHandle, audioConfigSymbols, + sizeof(audioConfigSymbols) / sizeof(dlSymbolLookup)); + + if (!isSymbolsLoaded) { + LOGWARN("LoadAudioConfigFromHAL: Failed to load all audio symbols from HAL"); + dlclose(pDLHandle); + return false; + } + + if ((config.pKConfigs == NULL) || (config.pKPorts == NULL) || + (config.pKConfigSize == NULL) || (config.pKPortSize == NULL)) { + LOGWARN("LoadAudioConfigFromHAL: HAL symbols loaded but one or more pointers are null"); + dlclose(pDLHandle); + return false; + } + + outHandle = pDLHandle; + return true; +} + +// ── VideoPort ───────────────────────────────────────────────────────────── + +typedef struct _videoPortConfigs { + const dsVideoPortTypeConfig_t* pKConfigs; + int* pKVideoPortConfigs_size; + const dsVideoPortPortConfig_t* pKPorts; + int* pKVideoPortPorts_size; + dsVideoPortResolution_t* pKResolutionsSettings; + int* pKResolutionsSettings_size; +} videoPortConfigs_t; + +static bool LoadVideoPortConfigFromHAL(videoPortConfigs_t& config, void*& outHandle) +{ + void* pDLHandle = NULL; + bool isSymbolsLoaded = false; + + outHandle = NULL; + memset(&config, 0, sizeof(config)); + + dlerror(); + pDLHandle = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (pDLHandle == NULL) { + const char* dlErr = dlerror(); + LOGWARN("LoadVideoPortConfigFromHAL: dlopen failed for %s: %s", + RDK_DSHAL_NAME, (dlErr ? dlErr : "unknown")); + return false; + } + + dlSymbolLookup videoPortConfigSymbols[] = { + {"kVideoPortConfigs", (void**)&config.pKConfigs}, + {"kVideoPortConfigs_size", (void**)&config.pKVideoPortConfigs_size}, + {"kVideoPortPorts", (void**)&config.pKPorts}, + {"kVideoPortPorts_size", (void**)&config.pKVideoPortPorts_size}, + {"kResolutionsSettings", (void**)&config.pKResolutionsSettings}, + {"kResolutionsSettings_size",(void**)&config.pKResolutionsSettings_size} + }; + + isSymbolsLoaded = LoadDLSymbols(pDLHandle, videoPortConfigSymbols, + sizeof(videoPortConfigSymbols) / sizeof(dlSymbolLookup)); + + if (!isSymbolsLoaded) { + LOGWARN("LoadVideoPortConfigFromHAL: Failed to load all video port symbols from HAL"); + dlclose(pDLHandle); + return false; + } + + if ((config.pKConfigs == NULL) || (config.pKPorts == NULL) || + (config.pKResolutionsSettings == NULL) || (config.pKVideoPortConfigs_size == NULL) || + (config.pKVideoPortPorts_size == NULL) || (config.pKResolutionsSettings_size == NULL)) { + LOGWARN("LoadVideoPortConfigFromHAL: HAL symbols loaded but one or more pointers are null"); + dlclose(pDLHandle); + return false; + } + + outHandle = pDLHandle; + return true; +} + +// ── VideoDevice ───────────────────────────────────────────────────────────── + +typedef struct _videoDeviceConfigs { + const dsVideoConfig_t* pKConfigs; + int* pKVideoDeviceConfigs_size; +} videoDeviceConfigs_t; + +static uint32_t ToVideoZoomMask(const dsVideoZoom_t* values, const size_t count) +{ + uint32_t mask = 0; + for (size_t index = 0; index < count; ++index) { + const int32_t bit = static_cast(values[index]); + if ((bit >= 0) && (bit < static_cast(sizeof(mask) * 8))) { + mask |= (1u << static_cast(bit)); + } + } + return mask; +} + +static bool LoadVideoDeviceConfigFromHAL(videoDeviceConfigs_t& config, void*& outHandle) +{ + void* pDLHandle = NULL; + bool isSymbolsLoaded = false; + + outHandle = NULL; + memset(&config, 0, sizeof(config)); + + dlerror(); + pDLHandle = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (pDLHandle == NULL) { + const char* dlErr = dlerror(); + LOGWARN("LoadVideoDeviceConfigFromHAL: dlopen failed for %s: %s", + RDK_DSHAL_NAME, (dlErr ? dlErr : "unknown")); + return false; + } + + dlSymbolLookup videoDeviceConfigSymbols[] = { + {"kVideoDeviceConfigs", (void**)&config.pKConfigs}, + {"kVideoDeviceConfigs_size", (void**)&config.pKVideoDeviceConfigs_size} + }; + + isSymbolsLoaded = LoadDLSymbols(pDLHandle, videoDeviceConfigSymbols, + sizeof(videoDeviceConfigSymbols) / sizeof(dlSymbolLookup)); + + if (!isSymbolsLoaded) { + LOGWARN("LoadVideoDeviceConfigFromHAL: Failed to load all video device symbols from HAL"); + dlclose(pDLHandle); + return false; + } + + if ((config.pKConfigs == NULL) || (config.pKVideoDeviceConfigs_size == NULL)) { + LOGWARN("LoadVideoDeviceConfigFromHAL: HAL symbols loaded but one or more pointers are null"); + dlclose(pDLHandle); + return false; + } + + outHandle = pDLHandle; + return true; +} + +} // anonymous namespace + +// --------------------------------------------------------------------------- +// Public DeviceSettingsHAL namespace +// --------------------------------------------------------------------------- +namespace DeviceSettingsHAL { + +// ── FPD ─────────────────────────────────────────────────────────────────── + +void PopulateFPDConfig( + std::vector& colors, + std::vector& indicators, + std::vector& textDisplays, + std::vector& colorBindings) +{ + fpdConfigs_t halConfig; + void* halHandle = NULL; + memset(&halConfig, 0, sizeof(halConfig)); + bool loadedFromHAL = LoadFrontPanelConfigFromHAL(halConfig, halHandle); + + colors.clear(); + indicators.clear(); + textDisplays.clear(); + colorBindings.clear(); + + if (loadedFromHAL) { + const int colorCount = *(halConfig.pKFPDIndicatorColors_size); + const int indicatorCount = *(halConfig.pKIndicators_size); + const int textDisplayCount = *(halConfig.pKTextDisplays_size); + + for (int i = 0; i < colorCount; i++) { + const dsFPDColorConfig_t& cfg = halConfig.pKFPDIndicatorColors[i]; + FPDColorConfig colorCfg; + colorCfg.id = cfg.id; + colorCfg.color = cfg.color; + colors.push_back(colorCfg); + } + + for (int i = 0; i < indicatorCount; i++) { + const dsFPDIndicatorConfig_t& cfg = halConfig.pKIndicators[i]; + FPDIndicatorConfig indicatorCfg; + indicatorCfg.id = cfg.id; + indicatorCfg.maxBrightness = cfg.maxBrightness; + indicatorCfg.maxCycleRate = cfg.maxCycleRate; + indicatorCfg.minBrightness = cfg.minBrightness; + indicatorCfg.levels = cfg.levels; + indicatorCfg.colorMode = cfg.colorMode; + indicators.push_back(indicatorCfg); + + if (cfg.supportedColors != nullptr) { + for (int colorIndex = 0; colorIndex < colorCount; ++colorIndex) { + const dsFPDColorConfig_t& colorCfg = halConfig.pKFPDIndicatorColors[colorIndex]; + FPDColorBinding mapEntry; + mapEntry.targetType = 0; // DS_FPD_COLOR_TARGET_INDICATOR + mapEntry.targetId = cfg.id; + mapEntry.colorId = colorCfg.id; + colorBindings.push_back(mapEntry); + } + } + } + + for (int i = 0; i < textDisplayCount; i++) { + const dsFPDTextDisplayConfig_t& cfg = halConfig.pKTextDisplays[i]; + FPDTextDisplayConfig textDisplayCfg; + textDisplayCfg.id = cfg.id; + textDisplayCfg.name = (cfg.name ? cfg.name : ""); + textDisplayCfg.maxBrightness = cfg.maxBrightness; + textDisplayCfg.maxCycleRate = cfg.maxCycleRate; + textDisplayCfg.supportedCharacters = (cfg.supportedCharacters + ? cfg.supportedCharacters + : kDefaultSupportedCharacters); + textDisplayCfg.columns = cfg.columns; + textDisplayCfg.rows = cfg.rows; + textDisplayCfg.maxHorizontalIterations = cfg.maxHorizontalIterations; + textDisplayCfg.maxVerticalIterations = cfg.maxVerticalIterations; + textDisplayCfg.levels = cfg.levels; + textDisplayCfg.colorMode = cfg.colorMode; + textDisplays.push_back(textDisplayCfg); + + if (cfg.supportedColors != nullptr) { + for (int colorIndex = 0; colorIndex < colorCount; ++colorIndex) { + const dsFPDColorConfig_t& colorCfg = halConfig.pKFPDIndicatorColors[colorIndex]; + FPDColorBinding mapEntry; + mapEntry.targetType = 1; // DS_FPD_COLOR_TARGET_TEXTDISPLAY + mapEntry.targetId = cfg.id; + mapEntry.colorId = colorCfg.id; + colorBindings.push_back(mapEntry); + } + } + } + + LOGINFO("PopulateFPDConfig: Loaded config from HAL (colors=%d indicators=%d textDisplays=%d)", + colorCount, indicatorCount, textDisplayCount); + dlclose(halHandle); + halHandle = NULL; + return; + } + + LOGWARN("PopulateFPDConfig: HAL config not available, returning empty config"); +} + +void DumpFPDConfig( + const std::vector& colors, + const std::vector& indicators, + const std::vector& textDisplays, + const std::vector& colorBindings) +{ + if (-1 == access("/opt/dsMgrDumpDeviceConfigs", F_OK)) { + LOGINFO("DumpFPDConfig: Dumping of Device configs is disabled"); + return; + } + + LOGINFO("\n=============== Dump DeviceSettings FPD Cached Config ==============="); + LOGINFO("Colors count=%zu", colors.size()); + for (size_t i = 0; i < colors.size(); ++i) { + LOGINFO("colors[%zu]: id=%d color=%d", i, colors[i].id, colors[i].color); + } + + LOGINFO("Indicators count=%zu", indicators.size()); + for (size_t i = 0; i < indicators.size(); ++i) { + const FPDIndicatorConfig& cfg = indicators[i]; + LOGINFO("indicators[%zu]: id=%d maxBrightness=%d maxCycleRate=%d minBrightness=%d levels=%d colorMode=%d", + i, cfg.id, cfg.maxBrightness, cfg.maxCycleRate, + cfg.minBrightness, cfg.levels, cfg.colorMode); + } + + LOGINFO("TextDisplays count=%zu", textDisplays.size()); + for (size_t i = 0; i < textDisplays.size(); ++i) { + const FPDTextDisplayConfig& cfg = textDisplays[i]; + LOGINFO("textDisplays[%zu]: id=%d name=%s maxBrightness=%d maxCycleRate=%d columns=%d rows=%d maxHIter=%d maxVIter=%d levels=%d colorMode=%d supportedChars=%s", + i, cfg.id, cfg.name.c_str(), cfg.maxBrightness, cfg.maxCycleRate, + cfg.columns, cfg.rows, cfg.maxHorizontalIterations, + cfg.maxVerticalIterations, cfg.levels, cfg.colorMode, + cfg.supportedCharacters.c_str()); + } + + LOGINFO("ColorBindings count=%zu", colorBindings.size()); + for (size_t i = 0; i < colorBindings.size(); ++i) { + const FPDColorBinding& cfg = colorBindings[i]; + LOGINFO("colorBindings[%zu]: targetType=%d targetId=%d colorId=%d", + i, static_cast(cfg.targetType), cfg.targetId, cfg.colorId); + } + + LOGINFO("=============== Dump DeviceSettings FPD Cached Config done ===============\n"); +} + +// ── Audio ───────────────────────────────────────────────────────────────── + +void PopulateAudioConfig( + std::vector& audioTypes, + std::vector& audioPorts) +{ + audioConfigs_t halConfig; + void* halHandle = NULL; + memset(&halConfig, 0, sizeof(halConfig)); + const bool loadedFromHAL = LoadAudioConfigFromHAL(halConfig, halHandle); + + audioTypes.clear(); + audioPorts.clear(); + + if (!loadedFromHAL) { + LOGWARN("PopulateAudioConfig: HAL config not available, returning empty config"); + return; + } + + const int typeCount = *(halConfig.pKConfigSize); + const int portCount = *(halConfig.pKPortSize); + + for (int i = 0; i < typeCount; i++) { + const dsAudioTypeConfig_t& cfg = halConfig.pKConfigs[i]; + + AudioTypeConfigInfo typeCfg; + typeCfg.typeId = cfg.typeId; + typeCfg.name = (cfg.name ? cfg.name : ""); + typeCfg.supportedCompressionMask = (cfg.compressions != NULL) + ? ToEnumMask(cfg.compressions, cfg.numSupportedCompressions) + : 0; + typeCfg.supportedEncodingMask = (cfg.encodings != NULL) + ? ToEnumMask(cfg.encodings, cfg.numSupportedEncodings) + : 0; + typeCfg.supportedStereoModeMask = (cfg.stereoModes != NULL) + ? ToEnumMask(cfg.stereoModes, cfg.numSupportedStereoModes) + : 0; + audioTypes.push_back(typeCfg); + } + + for (int i = 0; i < portCount; i++) { + const dsAudioPortConfig_t& cfg = halConfig.pKPorts[i]; + + AudioPortConfigInfo portCfg; + portCfg.audioPortType = static_cast(cfg.id.type); + portCfg.audioPortIndex = cfg.id.index; + if (cfg.connectedVOPs != NULL) { + portCfg.connectedVideoPortType = static_cast(cfg.connectedVOPs->type); + portCfg.connectedVideoPortIndex = cfg.connectedVOPs->index; + } else { + portCfg.connectedVideoPortType = -1; + portCfg.connectedVideoPortIndex = -1; + } + audioPorts.push_back(portCfg); + } + + LOGINFO("PopulateAudioConfig: Loaded config from HAL (audioTypes=%zu audioPorts=%zu)", + audioTypes.size(), audioPorts.size()); + dlclose(halHandle); + halHandle = NULL; +} + +void DumpAudioConfig( + const std::vector& audioTypes, + const std::vector& audioPorts) +{ + if (-1 == access("/opt/dsMgrDumpDeviceConfigs", F_OK)) { + LOGINFO("DumpAudioConfig: Dumping of Device configs is disabled"); + return; + } + + LOGINFO("\n=============== Dump DeviceSettings Audio Cached Config ==============="); + LOGINFO("AudioTypes count=%zu", audioTypes.size()); + for (size_t i = 0; i < audioTypes.size(); ++i) { + const AudioTypeConfigInfo& cfg = audioTypes[i]; + LOGINFO("audioTypes[%zu]: typeId=%d name=%s compressionMask=0x%x encodingMask=0x%x stereoModeMask=0x%x", + i, + static_cast(cfg.typeId), + cfg.name.c_str(), + static_cast(cfg.supportedCompressionMask), + static_cast(cfg.supportedEncodingMask), + static_cast(cfg.supportedStereoModeMask)); + } + + LOGINFO("AudioPorts count=%zu", audioPorts.size()); + for (size_t i = 0; i < audioPorts.size(); ++i) { + const AudioPortConfigInfo& cfg = audioPorts[i]; + LOGINFO("audioPorts[%zu]: portType=%d portIndex=%d connectedVideoPortType=%d connectedVideoPortIndex=%d", + i, + static_cast(cfg.audioPortType), + cfg.audioPortIndex, + cfg.connectedVideoPortType, + cfg.connectedVideoPortIndex); + } + + LOGINFO("=============== Dump DeviceSettings Audio Cached Config done ===============\n"); +} + +// ── VideoPort ───────────────────────────────────────────────────────────── + +void PopulateVideoPortConfig( + std::vector& videoPortTypes, + std::vector& videoPorts) +{ + videoPortConfigs_t halConfig; + void* halHandle = NULL; + memset(&halConfig, 0, sizeof(halConfig)); + const bool loadedFromHAL = LoadVideoPortConfigFromHAL(halConfig, halHandle); + + videoPortTypes.clear(); + videoPorts.clear(); + + if (!loadedFromHAL) { + LOGWARN("PopulateVideoPortConfig: HAL config not available, returning empty config"); + return; + } + + const int configCount = *(halConfig.pKVideoPortConfigs_size); + const int portCount = *(halConfig.pKVideoPortPorts_size); + + for (int i = 0; i < configCount; i++) { + const dsVideoPortTypeConfig_t& cfg = halConfig.pKConfigs[i]; + + VideoPortTypeConfig typeCfg; + typeCfg.typeId = static_cast(cfg.typeId); + typeCfg.name = (cfg.name ? cfg.name : ""); + typeCfg.dtcpSupported = cfg.dtcpSupported; + typeCfg.hdcpSupported = cfg.hdcpSupported; + typeCfg.restrictedResolution = cfg.restrictedResollution; + if ((cfg.supportedResolutions != NULL) && (cfg.numSupportedResolutions > 0)) { + std::ostringstream supportedResolutions; + for (size_t j = 0; j < cfg.numSupportedResolutions; ++j) { + if (j != 0) { + supportedResolutions << ','; + } + supportedResolutions << cfg.supportedResolutions[j].name; + } + typeCfg.supportedResolutionNames = supportedResolutions.str(); + } else { + typeCfg.supportedResolutionNames.clear(); + } + videoPortTypes.push_back(typeCfg); + } + + for (int i = 0; i < portCount; i++) { + const dsVideoPortPortConfig_t& cfg = halConfig.pKPorts[i]; + + VideoPortPortConfig portCfg; + portCfg.videoPortType = static_cast(cfg.id.type); + portCfg.videoPortIndex = cfg.id.index; + portCfg.connectedAudioPortType = static_cast(cfg.connectedAOP.type); + portCfg.connectedAudioPortIndex = cfg.connectedAOP.index; + portCfg.defaultResolution = (cfg.defaultResolution ? cfg.defaultResolution : ""); + videoPorts.push_back(portCfg); + } + + LOGINFO("PopulateVideoPortConfig: Loaded config from HAL (videoPortTypes=%zu videoPorts=%zu)", + videoPortTypes.size(), videoPorts.size()); + dlclose(halHandle); + halHandle = NULL; +} + +void PopulateVideoPortResolutionConfig( + const VideoPortType videoPortType, + std::vector& resolutions) +{ + videoPortConfigs_t halConfig; + void* halHandle = NULL; + memset(&halConfig, 0, sizeof(halConfig)); + const bool loadedFromHAL = LoadVideoPortConfigFromHAL(halConfig, halHandle); + + resolutions.clear(); + + if (!loadedFromHAL) { + LOGWARN("PopulateVideoPortResolutionConfig: HAL config not available, returning empty config"); + return; + } + + const int configCount = *(halConfig.pKVideoPortConfigs_size); + const int resolutionCount = *(halConfig.pKResolutionsSettings_size); + std::set supportedResolutionNames; + bool typeFound = false; + + for (int i = 0; i < configCount; ++i) { + const dsVideoPortTypeConfig_t& cfg = halConfig.pKConfigs[i]; + if (static_cast(cfg.typeId) != videoPortType) { + continue; + } + + typeFound = true; + if ((cfg.supportedResolutions != NULL) && (cfg.numSupportedResolutions > 0)) { + for (size_t j = 0; j < cfg.numSupportedResolutions; ++j) { + const char* resolutionName = cfg.supportedResolutions[j].name; + if (resolutionName != NULL) { + supportedResolutionNames.insert(resolutionName); + } + } + } + break; + } + + if (!typeFound) { + LOGWARN("PopulateVideoPortResolutionConfig: videoPortType=%d not found in HAL type config", static_cast(videoPortType)); + dlclose(halHandle); + halHandle = NULL; + return; + } + + for (int i = 0; i < resolutionCount; ++i) { + const dsVideoPortResolution_t& cfg = halConfig.pKResolutionsSettings[i]; + if (cfg.name == NULL) { + continue; + } + + if (supportedResolutionNames.find(cfg.name) == supportedResolutionNames.end()) { + continue; + } + + VideoPortResolution resCfg; + resCfg.name = cfg.name; + resCfg.pixelResolution = static_cast(cfg.pixelResolution); + resCfg.aspectRatio = static_cast(cfg.aspectRatio); + resCfg.stereoScopicMode = static_cast(cfg.stereoScopicMode); + resCfg.frameRate = static_cast(cfg.frameRate); + resCfg.interlaced = cfg.interlaced; + resolutions.push_back(resCfg); + } + + LOGINFO("PopulateVideoPortResolutionConfig: Loaded resolution config from HAL (videoPortType=%d resolutions=%zu)", + static_cast(videoPortType), resolutions.size()); + dlclose(halHandle); + halHandle = NULL; +} + +void DumpVideoPortConfig( + const std::vector& videoPortTypes, + const std::vector& videoPorts, + const std::vector& resolutions) +{ + if (-1 == access("/opt/dsMgrDumpDeviceConfigs", F_OK)) { + LOGINFO("DumpVideoPortConfig: Dumping of Device configs is disabled"); + return; + } + + LOGINFO("\n=============== Dump DeviceSettings VideoPort Cached Config ==============="); + LOGINFO("VideoPortTypes count=%zu", videoPortTypes.size()); + for (size_t i = 0; i < videoPortTypes.size(); ++i) { + const VideoPortTypeConfig& cfg = videoPortTypes[i]; + LOGINFO("videoPortTypes[%zu]: typeId=%d name=%s dtcp=%s hdcp=%s restrictedRes=%d supportedResolutions=%s", + i, + static_cast(cfg.typeId), + cfg.name.c_str(), + cfg.dtcpSupported ? "true" : "false", + cfg.hdcpSupported ? "true" : "false", + cfg.restrictedResolution, + cfg.supportedResolutionNames.c_str()); + } + + LOGINFO("VideoPorts count=%zu", videoPorts.size()); + for (size_t i = 0; i < videoPorts.size(); ++i) { + const VideoPortPortConfig& cfg = videoPorts[i]; + LOGINFO("videoPorts[%zu]: videoPortType=%d videoPortIndex=%d connectedAudioPortType=%d connectedAudioPortIndex=%d defaultResolution=%s", + i, + static_cast(cfg.videoPortType), + cfg.videoPortIndex, + cfg.connectedAudioPortType, + cfg.connectedAudioPortIndex, + cfg.defaultResolution.c_str()); + } + + LOGINFO("Resolutions count=%zu", resolutions.size()); + for (size_t i = 0; i < resolutions.size(); ++i) { + const VideoPortResolution& cfg = resolutions[i]; + LOGINFO("resolutions[%zu]: name=%s pixelResolution=%d aspectRatio=%d stereoScopicMode=%d frameRate=%d interlaced=%s", + i, + cfg.name.c_str(), + static_cast(cfg.pixelResolution), + static_cast(cfg.aspectRatio), + static_cast(cfg.stereoScopicMode), + static_cast(cfg.frameRate), + cfg.interlaced ? "true" : "false"); + } + + LOGINFO("=============== Dump DeviceSettings VideoPort Cached Config done ===============\n"); +} + +// ── VideoDevice ─────────────────────────────────────────────────────────── + +void PopulateVideoDeviceConfig( + std::vector& videoDeviceConfigs) +{ + videoDeviceConfigs_t halConfig; + void* halHandle = NULL; + memset(&halConfig, 0, sizeof(halConfig)); + const bool loadedFromHAL = LoadVideoDeviceConfigFromHAL(halConfig, halHandle); + + videoDeviceConfigs.clear(); + + if (!loadedFromHAL) { + LOGWARN("PopulateVideoDeviceConfig: HAL config not available, returning empty config"); + return; + } + + const int configCount = *(halConfig.pKVideoDeviceConfigs_size); + for (int i = 0; i < configCount; i++) { + const dsVideoConfig_t& cfg = halConfig.pKConfigs[i]; + + VideoDeviceConfigInfo videoCfg; + videoCfg.numSupportedDFCs = static_cast(cfg.numSupportedDFCs); + videoCfg.supportedDFCsMask = (cfg.supportedDFCs != NULL) + ? ToVideoZoomMask(cfg.supportedDFCs, cfg.numSupportedDFCs) + : 0; + videoCfg.defaultDFC = static_cast(cfg.defaultDFC); + videoDeviceConfigs.push_back(videoCfg); + } + + LOGINFO("PopulateVideoDeviceConfig: Loaded config from HAL (videoDeviceConfigs=%zu)", + videoDeviceConfigs.size()); + dlclose(halHandle); + halHandle = NULL; +} + +void DumpVideoDeviceConfig( + const std::vector& videoDeviceConfigs) +{ + if (-1 == access("/opt/dsMgrDumpDeviceConfigs", F_OK)) { + LOGINFO("DumpVideoDeviceConfig: Dumping of Device configs is disabled"); + return; + } + + LOGINFO("\n=============== Dump DeviceSettings VideoDevice Cached Config ==============="); + LOGINFO("VideoDeviceConfigs count=%zu", videoDeviceConfigs.size()); + for (size_t i = 0; i < videoDeviceConfigs.size(); ++i) { + const VideoDeviceConfigInfo& cfg = videoDeviceConfigs[i]; + LOGINFO("videoDeviceConfigs[%zu]: numSupportedDFCs=%u supportedDFCsMask=0x%x defaultDFC=%d", + i, + static_cast(cfg.numSupportedDFCs), + static_cast(cfg.supportedDFCsMask), + static_cast(cfg.defaultDFC)); + } + LOGINFO("=============== Dump DeviceSettings VideoDevice Cached Config done ===============\n"); +} + +} // namespace DeviceSettingsHAL diff --git a/plugin/DeviceSettingsHALConfig.h b/plugin/DeviceSettingsHALConfig.h new file mode 100644 index 0000000..05c4545 --- /dev/null +++ b/plugin/DeviceSettingsHALConfig.h @@ -0,0 +1,96 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +/** + * @file DeviceSettingsHALConfig.h + * @brief Shared HAL configuration loading for DeviceSettings components. + * + * FPD, Audio, and VideoPort all use the same dlopen/dlsym/dlclose pattern to + * read static configuration tables from the HAL shared library at startup. + * This header centralises the public interface for those loaders so the three + * implementation files do not each carry a private copy of the same code. + * + * Include this header AFTER Module.h (or after any header that includes + * Module.h) in a DeviceSettings plugin compilation unit. + */ + +#include "DeviceSettingsTypes.h" +#include + +namespace DeviceSettingsHAL { + + // ─── Front Panel Display ─────────────────────────────────────────────────── + + /** + * Load FPD configuration from the HAL shared library and populate the + * supplied vectors with deep-copied, heap-owned data. The HAL library + * handle is closed internally once the copy is complete. + */ + void PopulateFPDConfig( + std::vector& colors, + std::vector& indicators, + std::vector& textDisplays, + std::vector& colorBindings); + + /** + * Log a human-readable dump of the FPD config vectors. + * Gated by /opt/dsMgrDumpDeviceConfigs — no-op when that file is absent. + */ + void DumpFPDConfig( + const std::vector& colors, + const std::vector& indicators, + const std::vector& textDisplays, + const std::vector& colorBindings); + + // ─── Audio ───────────────────────────────────────────────────────────────── + + void PopulateAudioConfig( + std::vector& audioTypes, + std::vector& audioPorts); + + void DumpAudioConfig( + const std::vector& audioTypes, + const std::vector& audioPorts); + + // ─── Video Port ──────────────────────────────────────────────────────────── + + void PopulateVideoPortConfig( + std::vector& videoPortTypes, + std::vector& videoPorts); + + void PopulateVideoPortResolutionConfig( + const VideoPortType videoPortType, + std::vector& resolutions); + + void DumpVideoPortConfig( + const std::vector& videoPortTypes, + const std::vector& videoPorts, + const std::vector& resolutions); + + // ─── Video Device ────────────────────────────────────────────────────────── + + void PopulateVideoDeviceConfig( + std::vector& videoDeviceConfigs); + + void DumpVideoDeviceConfig( + const std::vector& videoDeviceConfigs); + +} // namespace DeviceSettingsHAL diff --git a/plugin/DeviceSettingsHdmiInImplementation.cpp b/plugin/DeviceSettingsHdmiInImplementation.cpp new file mode 100644 index 0000000..034838c --- /dev/null +++ b/plugin/DeviceSettingsHdmiInImplementation.cpp @@ -0,0 +1,595 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DeviceSettingsHdmiInImplementation.h" + +#include + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsHdmiInImp::DeviceSettingsHdmiInImp() + : _hdmiIn(HdmiIn::Create(*this)) + { + LOGINFO("DeviceSettingsHdmiInImp Constructor - Instance Address: %p", this); + } + + DeviceSettingsHdmiInImp::~DeviceSettingsHdmiInImp() { + LOGINFO("DeviceSettingsHdmiInImp Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsHdmiInImp::dispatchHDMIInEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _HDMIInNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IHDMIIn event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsHdmiInImp::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsHdmiInImp::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + + Core::hresult DeviceSettingsHdmiInImp::Register(DeviceSettingsHDMIIn::INotification* notification) + { + Core::hresult errorCode = Register(_HDMIInNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IHDMIIn %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IHDMIIn %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::Unregister(DeviceSettingsHDMIIn::INotification* notification) + { + Core::hresult errorCode = Unregister(_HDMIInNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IHDMIIn %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IHDMIIn %p unregistered successfully", notification); + } + return errorCode; + } + + void DeviceSettingsHdmiInImp::OnHDMIInEventHotPlugNotification(const HDMIInPort port, const bool isConnected) + { + LOGINFO("OnHDMIInEventHotPlug event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInEventHotPlug, port, isConnected); + } + + void DeviceSettingsHdmiInImp::OnHDMIInEventSignalStatusNotification(const HDMIInPort port, const HDMIInSignalStatus signalStatus) + { + LOGINFO("OnHDMIInEventSignalStatus event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInEventSignalStatus, port, signalStatus); + } + + void DeviceSettingsHdmiInImp::OnHDMIInAVLatencyNotification(const int32_t audioDelay, const int32_t videoDelay) + { + LOGINFO("OnHDMIInAVLatency event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInAVLatency, audioDelay, videoDelay); + } + + void DeviceSettingsHdmiInImp::OnHDMIInEventStatusNotification(const HDMIInPort activePort, const bool isPresented) + { + LOGINFO("OnHDMIInEventStatus event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInEventStatus, activePort, isPresented); + } + + void DeviceSettingsHdmiInImp::OnHDMIInVideoModeUpdateNotification(const HDMIInPort port, const HDMIVideoPortResolution videoPortResolution) + { + LOGINFO("OnHDMIInVideoModeUpdate event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInVideoModeUpdate, port, videoPortResolution); + } + + void DeviceSettingsHdmiInImp::OnHDMIInAllmStatusNotification(const HDMIInPort port, const bool allmStatus) + { + LOGINFO("OnHDMIInAllmStatus event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInAllmStatus, port, allmStatus); + } + + void DeviceSettingsHdmiInImp::OnHDMIInAVIContentTypeNotification(const HDMIInPort port, const HDMIInAviContentType aviContentType) + { + LOGINFO("OnHDMIInAVIContentType event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInAVIContentType, port, aviContentType); + } + + void DeviceSettingsHdmiInImp::OnHDMIInVRRStatusNotification(const HDMIInPort port, const HDMIInVRRType vrrType) + { + LOGINFO("OnHDMIInVRRStatus event Received"); + dispatchHDMIInEvent(&DeviceSettingsHDMIIn::INotification::OnHDMIInVRRStatus, port, vrrType); + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIInNumberOfInputs(int32_t &count) { + + LOGINFO("GetHDMIInNumberOfInputs"); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIInNumberOfInputs(count) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIInNumberOfInputs: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIInNumberOfInputs: SUCCESS - count=%d", count); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) { + + LOGINFO("GetHDMIInStatus"); + Core::hresult errorCode = Core::ERROR_GENERAL; + + _apiLock.Lock(); + if (_hdmiIn.GetHDMIInStatus(hdmiStatus, portConnectionStatus) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIInStatus: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIInStatus: SUCCESS - platform call completed"); + LOGINFO("GetHDMIInStatus: activePort=%d, isPresented=%s", hdmiStatus.activePort, hdmiStatus.isPresented ? "true" : "false"); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) { + + LOGINFO("SelectHDMIInPort: port=%d, requestAudioMix=%s, topMostPlane=%s, videoPlaneType=%d", + port, requestAudioMix ? "true" : "false", topMostPlane ? "true" : "false", videoPlaneType); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.SelectHDMIInPort(port, requestAudioMix, topMostPlane, videoPlaneType) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SelectHDMIInPort: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SelectHDMIInPort: SUCCESS - platform call completed"); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) { + + LOGINFO("ScaleHDMIInVideo: x=%d, y=%d, w=%d, h=%d", videoPosition.x, videoPosition.y, videoPosition.width, videoPosition.height); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.ScaleHDMIInVideo(videoPosition) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("ScaleHDMIInVideo: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("ScaleHDMIInVideo: SUCCESS - platform call completed"); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) { + + LOGINFO("SelectHDMIZoomMode: zoomMode=%d", zoomMode); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.SelectHDMIZoomMode(zoomMode) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SelectHDMIZoomMode: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SelectHDMIZoomMode: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) { + + LOGINFO("GetSupportedGameFeaturesList"); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetSupportedGameFeaturesList(gameFeatureList) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetSupportedGameFeaturesList: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetSupportedGameFeaturesList: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) { + + LOGINFO("GetHDMIInAVLatency"); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIInAVLatency(videoLatency, audioLatency) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIInAVLatency: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIInAVLatency: SUCCESS - videoLatency=%u, audioLatency=%u", videoLatency, audioLatency); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) { + + LOGINFO("GetHDMIInAllmStatus: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIInAllmStatus(port, allmStatus) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIInAllmStatus: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIInAllmStatus: SUCCESS - port=%d, allmStatus=%s", port, allmStatus ? "true" : "false"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) { + + LOGINFO("GetHDMIInEdid2AllmSupport: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIInEdid2AllmSupport(port, allmSupport) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIInEdid2AllmSupport: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIInEdid2AllmSupport: SUCCESS - port=%d, allmSupport=%s", port, allmSupport ? "true" : "false"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) { + + LOGINFO("SetHDMIInEdid2AllmSupport: port=%d, allmSupport=%s", port, allmSupport ? "true" : "false"); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.SetHDMIInEdid2AllmSupport(port, allmSupport) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetHDMIInEdid2AllmSupport: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetHDMIInEdid2AllmSupport: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) { + + LOGINFO("GetEdidBytes: port=%d, edidBytesLength=%u", port, edidBytesLength); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetEdidBytes(port, edidBytesLength, edidBytes) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetEdidBytes: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetEdidBytes: SUCCESS - port=%d, edidBytes[0]=0x%X", port, edidBytes[0]); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) { + + LOGINFO("GetHDMISPDInformation: port=%d, spdBytesLength=%u", port, spdBytesLength); + Core::hresult errorCode = Core::ERROR_GENERAL; + if (spdBytes && spdBytesLength > 0) { + spdBytes[0] = 0x00; // Example value + } + _apiLock.Lock(); + if (_hdmiIn.GetHDMISPDInformation(port, spdBytesLength, spdBytes) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMISPDInformation: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMISPDInformation: SUCCESS - platform call completed"); + LOGINFO("GetHDMISPDInformation: port=%d, spdBytes[0]=0x%X", port, spdBytes[0]); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) { + + LOGINFO("GetHDMIEdidVersion: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIEdidVersion(port, edidVersion) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIEdidVersion: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIEdidVersion: SUCCESS - port=%d, edidVersion=%d", port, edidVersion); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) { + + LOGINFO("SetHDMIEdidVersion: port=%d, edidVersion=%d", port, edidVersion); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.SetHDMIEdidVersion(port, edidVersion) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetHDMIEdidVersion: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetHDMIEdidVersion: SUCCESS - platform call completed"); + + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) { + + LOGINFO("GetHDMIVideoMode"); + Core::hresult errorCode = Core::ERROR_GENERAL; + + _apiLock.Lock(); + if (_hdmiIn.GetHDMIVideoMode(videoPortResolution) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIVideoMode: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIVideoMode: SUCCESS - resolution=%s", videoPortResolution.name.c_str()); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) { + + LOGINFO("GetHDMIVersion: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetHDMIVersion(port, capabilityVersion) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetHDMIVersion: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetHDMIVersion: SUCCESS - port=%d, capabilityVersion=%d", port, capabilityVersion); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetVRRSupport(const HDMIInPort port, bool &vrrSupport) { + + LOGINFO("GetVRRSupport: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetVRRSupport(port, vrrSupport) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetVRRSupport: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetVRRSupport: SUCCESS - port=%d, vrrSupport=%s", port, vrrSupport ? "true" : "false"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::SetVRRSupport(const HDMIInPort port, const bool vrrSupport) { + + LOGINFO("SetVRRSupport: port=%d, vrrSupport=%s", port, vrrSupport ? "true" : "false"); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.SetVRRSupport(port, vrrSupport) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("SetVRRSupport: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("SetVRRSupport: SUCCESS - platform call completed"); + + return errorCode; + } + + Core::hresult DeviceSettingsHdmiInImp::GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) { + + LOGINFO("GetVRRStatus: port=%d", port); + Core::hresult errorCode = Core::ERROR_GENERAL; + _apiLock.Lock(); + if (_hdmiIn.GetVRRStatus(port, vrrStatus) == dsERR_NONE) { + errorCode = Core::ERROR_NONE; + } else { + errorCode = Core::ERROR_GENERAL; + } + _apiLock.Unlock(); + + if (errorCode != Core::ERROR_NONE) { + LOGWARN("GetVRRStatus: failed with errorCode=%u", errorCode); + return errorCode; + } + + LOGINFO("GetVRRStatus: SUCCESS - port=%d, vrrStatus.vrrType=%d", port, vrrStatus.vrrType); + + return errorCode; + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsHdmiInImplementation.h b/plugin/DeviceSettingsHdmiInImplementation.h new file mode 100644 index 0000000..a2603e4 --- /dev/null +++ b/plugin/DeviceSettingsHdmiInImplementation.h @@ -0,0 +1,150 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "fpd.h" +#include "HdmiIn.h" + +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsHdmiInImp : public HdmiIn::INotification + { + public: + + DeviceSettingsHdmiInImp(); + ~DeviceSettingsHdmiInImp() override; + + static DeviceSettingsHdmiInImp* Create() + { + return new DeviceSettingsHdmiInImp(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsHdmiInImp(const DeviceSettingsHdmiInImp&) = delete; + DeviceSettingsHdmiInImp& operator=(const DeviceSettingsHdmiInImp&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + class EXTERNAL LambdaJob : public Core::IDispatch { + protected: + LambdaJob(DeviceSettingsHdmiInImp* impl, std::function lambda) + : _impl(impl) + , _lambda(std::move(lambda)) + { + } + + public: + LambdaJob() = delete; + LambdaJob(const LambdaJob&) = delete; + LambdaJob& operator=(const LambdaJob&) = delete; + ~LambdaJob() {} + + static Core::ProxyType Create(DeviceSettingsHdmiInImp* impl, std::function lambda) + { + return (Core::ProxyType(Core::ProxyType::Create(impl, std::move(lambda)))); + } + + virtual void Dispatch() + { + _lambda(); + } + + private: + DeviceSettingsHdmiInImp* _impl; + std::function _lambda; + }; + + public: + void InitializeIARM(); + + // HDMIIn implementation methods - no longer interface methods, just implementation + // These are called by DeviceSettingsImp which implements the Exchange interface + Core::hresult Register(Exchange::IDeviceSettingsHDMIIn::INotification* notification); + Core::hresult Unregister(Exchange::IDeviceSettingsHDMIIn::INotification* notification); + Core::hresult GetHDMIInNumberOfInputs(int32_t &count); + Core::hresult GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus); + Core::hresult SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType); + Core::hresult ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition); + Core::hresult SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode); + Core::hresult GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList); + Core::hresult GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency); + Core::hresult GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus); + Core::hresult GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport); + Core::hresult SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport); + Core::hresult GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]); + Core::hresult GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]); + Core::hresult GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion); + Core::hresult SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion); + Core::hresult GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution); + Core::hresult GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion); + Core::hresult SetVRRSupport(const HDMIInPort port, const bool vrrSupport); + Core::hresult GetVRRSupport(const HDMIInPort port, bool &vrrSupport); + Core::hresult GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus); + + private: + std::list _HDMIInNotifications; + + // lock to guard all apis of DeviceSettings + mutable Core::CriticalSection _apiLock; + // lock to guard all notification from DeviceSettings to clients and also their callback register & unregister + mutable Core::CriticalSection _callbackLock; + + template + Core::hresult Register(std::list& list, T* notification); + template + Core::hresult Unregister(std::list& list, const T* notification); + + template + void dispatchHDMIInEvent(Func notifyFunc, Args&&... args); + + virtual void OnHDMIInEventHotPlugNotification(const HDMIInPort port, const bool isConnected) override; + virtual void OnHDMIInEventSignalStatusNotification(const HDMIInPort port, const HDMIInSignalStatus signalStatus) override; + virtual void OnHDMIInEventStatusNotification(const HDMIInPort activePort, const bool isPresented) override; + virtual void OnHDMIInVideoModeUpdateNotification(const HDMIInPort port, const HDMIVideoPortResolution videoPortResolution) override; + virtual void OnHDMIInAllmStatusNotification(const HDMIInPort port, const bool allmStatus) override; + virtual void OnHDMIInAVIContentTypeNotification(const HDMIInPort port, const HDMIInAviContentType aviContentType) override; + virtual void OnHDMIInAVLatencyNotification(const int32_t audioDelay, const int32_t videoDelay) override; + virtual void OnHDMIInVRRStatusNotification(const HDMIInPort port, const HDMIInVRRType vrrType) override; + + HdmiIn _hdmiIn; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _hdmiIn.InitialiseHAL(); } + }; +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsHostImplementation.cpp b/plugin/DeviceSettingsHostImplementation.cpp new file mode 100644 index 0000000..39a95ef --- /dev/null +++ b/plugin/DeviceSettingsHostImplementation.cpp @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#include "DeviceSettingsHostImplementation.h" + +#include +#include + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsHostImpl::DeviceSettingsHostImpl() : + _apiLock(), + _host(Host::Create()) + { + LOGINFO("DeviceSettingsHostImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsHostImpl::~DeviceSettingsHostImpl() { + LOGINFO("DeviceSettingsHostImpl Destructor - Instance Address: %p", this); + } + + Core::hresult DeviceSettingsHostImpl::GetEDID(uint8_t edId[], const uint16_t edIdLength) + { + uint32_t result = Core::ERROR_GENERAL; + result = _host.GetEDID(edId, edIdLength); + if (result == Core::ERROR_NONE) { + LOGINFO("GetEDID succeeded: edIdLength=%u", edIdLength); + } else { + LOGERR("GetEDID failed: edIdLength=%u, error=%u", edIdLength, result); + } + return result; + } + + Core::hresult DeviceSettingsHostImpl::GetMS12ConfigType(string &ms12Config) + { + uint32_t result = Core::ERROR_GENERAL; + result = _host.GetMS12ConfigType(ms12Config); + if (result == Core::ERROR_NONE) { + LOGINFO("GetMS12ConfigType succeeded: ms12Config='%s'", ms12Config.c_str()); + } else { + LOGERR("GetMS12ConfigType failed: error=%u", result); + } + return result; + } + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsHostImplementation.h b/plugin/DeviceSettingsHostImplementation.h new file mode 100644 index 0000000..99955ee --- /dev/null +++ b/plugin/DeviceSettingsHostImplementation.h @@ -0,0 +1,69 @@ +/* + * 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include "Host.h" + +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + + class DeviceSettingsHostImpl { + + private: + DeviceSettingsHostImpl(const DeviceSettingsHostImpl&) = delete; + DeviceSettingsHostImpl& operator=(const DeviceSettingsHostImpl&) = delete; + + public: + DeviceSettingsHostImpl(); + virtual ~DeviceSettingsHostImpl(); + + static DeviceSettingsHostImpl* Create() { + return new DeviceSettingsHostImpl(); + } + + public: + Core::hresult GetEDID(uint8_t edId[], const uint16_t edIdLength); + Core::hresult GetMS12ConfigType(string &ms12Config); + + private: + mutable Core::CriticalSection _apiLock; + + Host _host; + + public: + void InitialiseHAL() { _host.InitialiseHAL(); } + }; + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsImplementation.cpp b/plugin/DeviceSettingsImplementation.cpp new file mode 100644 index 0000000..3f83204 --- /dev/null +++ b/plugin/DeviceSettingsImplementation.cpp @@ -0,0 +1,1258 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#include "DeviceSettingsImplementation.h" +#include "DSController.h" +#include "DeviceSettingsFPDImplementation.h" +#include "DeviceSettingsHdmiInImplementation.h" +#include "DeviceSettingsAudioImplementation.h" +#include "DeviceSettingsHostImplementation.h" +#include "DeviceSettingsHALConfig.h" + +#include +#include +#include + +// Definition of the shared global declared in DeviceSettingsTypes.h +profile_t profileType = NOT_FOUND; +#include + +using namespace std; + +#define DELEGATE_TO_COMPONENT(component, method, ...) \ + ENTRY_LOG; \ + Core::hresult result = (component != nullptr) ? component->method(__VA_ARGS__) : Core::ERROR_UNAVAILABLE; \ + EXIT_LOG; \ + return result; + +// Macro for methods that don't take parameters +#define DELEGATE_TO_COMPONENT_NO_PARAMS(component, method) \ + ENTRY_LOG; \ + Core::hresult result = (component != nullptr) ? component->method() : Core::ERROR_UNAVAILABLE; \ + EXIT_LOG; \ + return result; + +namespace WPEFramework { +namespace Plugin { + + namespace DeviceSettingsHALLoader { + void* gLibraryHandle = nullptr; + std::mutex gLibraryLock; + + void* ResolveSymbol(const std::string& libName, const std::string& symbolName) + { + std::lock_guard guard(gLibraryLock); + + if (gLibraryHandle == nullptr) { + gLibraryHandle = dlopen(libName.c_str(), RTLD_LAZY); + if (gLibraryHandle == nullptr) { + LOGERR("dlopen failed for %s: %s", libName.c_str(), dlerror()); + return nullptr; + } + } + + void* symbol = dlsym(gLibraryHandle, symbolName.c_str()); + if (symbol == nullptr) { + LOGERR("dlsym failed for %s: %s", symbolName.c_str(), dlerror()); + } + + return symbol; + } + + void ReleaseAllLibraries() + { + std::lock_guard guard(gLibraryLock); + + if (gLibraryHandle != nullptr) { + dlclose(gLibraryHandle); + gLibraryHandle = nullptr; + } + } + } + + SERVICE_REGISTRATION(DeviceSettingsImp, 1, 0); + + DeviceSettingsImp* DeviceSettingsImp::_instance = nullptr; + + DeviceSettingsImp::DeviceSettingsImp() + : _dsController(nullptr) + , _fpdSettings(nullptr) + , _hdmiInSettings(nullptr) + , _audioSettings(nullptr) + , _videoPortSettings(nullptr) + , _videoDeviceSettings(nullptr) + , _hostSettings(nullptr) + , _displaySettings(nullptr) + , _compositeInSettings(nullptr) + , mConnectionId(0) + { + // Set the static instance for backward compatibility (if still needed) + DeviceSettingsImp::_instance = this; + + // Initialize profile type only — Start() is deferred to Configure() + // to avoid blocking the WPEFramework plugin activation thread. + profileType = searchRdkProfile(); + LOGINFO("Initialized profileType: %d (0=STB, 1=TV)", profileType); + + // ── Per-component creation timing ───────────────────────────────────── + using Clock = std::chrono::steady_clock; + using Ms = std::chrono::milliseconds; + auto tTotal = Clock::now(); + auto t0 = tTotal; + +#define DS_TIME_COMPONENT(label, expr) \ + t0 = Clock::now(); \ + expr; \ + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", label, \ + (long long)std::chrono::duration_cast(Clock::now() - t0).count()) + + // DSController must be created first — it provides system infrastructure. + DS_TIME_COMPONENT("DSController::Create", _dsController = DSController::Create(this)); + +#undef DS_TIME_COMPONENT + + // ── Two-stage parallel component creation ───────────────────────────── + // HAL is now initialised inside each HAL impl constructor (e.g. dVideoPortImpl). + // We must preserve the VO-wrapper dependency: VideoDevice/Display/Host must + // complete their HAL init (dsVideoDeviceInit / dsDisplayInit / dsHostInit) + // BEFORE VideoPort is created, because dsVideoPortInit shares the same + // underlying VO wrapper and will fail if run concurrently. + // + // Stage 1 (parallel): VideoDevice + Display + Host + // Stage 2 (parallel): VideoPort + Audio + FPD + HdmiIn + CompositeIn + + // Stage 1 + { + auto tS1 = Clock::now(); + LOGINFO("[DS-INIT-TIMING] Stage1 Create (VDev+Display+Host) — begin"); + std::thread tVDev ([this]{ _videoDeviceSettings = DeviceSettingsVideoDeviceImpl::Create(); }); + std::thread tDisplay([this]{ _displaySettings = DeviceSettingsDisplayImpl::Create(); }); + std::thread tHost ([this]{ _hostSettings = DeviceSettingsHostImpl::Create(); }); + tVDev.join(); tDisplay.join(); tHost.join(); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "Stage1 Create (VDev+Display+Host)", + (long long)std::chrono::duration_cast(Clock::now() - tS1).count()); + } + + // Stage 2 + { + auto tS2 = Clock::now(); + LOGINFO("[DS-INIT-TIMING] Stage2 Create (VPort+Audio+FPD+HdmiIn+Comp) — begin"); + std::thread tVPort ([this]{ _videoPortSettings = DeviceSettingsVideoPortImpl::Create(); }); + std::thread tAudio ([this]{ _audioSettings = DeviceSettingsAudioImpl::Create(); }); + std::thread tFPD ([this]{ _fpdSettings = DeviceSettingsFPDImpl::Create(); }); + std::thread tHdmi ([this]{ _hdmiInSettings = DeviceSettingsHdmiInImp::Create(); }); + std::thread tComp ([this]{ _compositeInSettings = DeviceSettingsCompositeInImpl::Create(); }); + tVPort.join(); tAudio.join(); tFPD.join(); tHdmi.join(); tComp.join(); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "Stage2 Create (VPort+others)", + (long long)std::chrono::duration_cast(Clock::now() - tS2).count()); + } + + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", + "DeviceSettingsImp ctor TOTAL", + (long long)std::chrono::duration_cast(Clock::now() - tTotal).count()); + } + + DeviceSettingsImp::~DeviceSettingsImp() { + LOGINFO("DeviceSettingsImp Destructor - Instance Address: %p", this); + + // Clean up created implementation instances + if (_fpdSettings != nullptr) { + delete _fpdSettings; + _fpdSettings = nullptr; + } + + if (_hdmiInSettings != nullptr) { + delete _hdmiInSettings; + _hdmiInSettings = nullptr; + } + + if (_audioSettings != nullptr) { + delete _audioSettings; + _audioSettings = nullptr; + } + + if (_videoPortSettings != nullptr) { + delete _videoPortSettings; + _videoPortSettings = nullptr; + } + if (_videoDeviceSettings != nullptr) { + delete _videoDeviceSettings; + _videoDeviceSettings = nullptr; + } + if (_hostSettings != nullptr) { + delete _hostSettings; + _hostSettings = nullptr; + } + if (_displaySettings != nullptr) { + delete _displaySettings; + _displaySettings = nullptr; + } + if (_compositeInSettings != nullptr) { + delete _compositeInSettings; + _compositeInSettings = nullptr; + } + + // Clean up DSController last as it provides system infrastructure + if (_dsController != nullptr) { + delete _dsController; + _dsController = nullptr; + } + + DeviceSettingsHALLoader::ReleaseAllLibraries(); + LOGINFO("DeviceSettingsImp Destructor - Released all HAL libraries"); + + } + + Core::hresult DeviceSettingsImp::Configure(PluginHost::IShell* service) + { + LOGINFO("DeviceSettingsImp Configure called with service: %p", service); + + using Clock = std::chrono::steady_clock; + using Ms = std::chrono::milliseconds; + auto tCfg = Clock::now(); + + if (service == nullptr) { + LOGERR("Service parameter is null"); + return Core::ERROR_BAD_REQUEST; + } + + if (_dsController != nullptr) { + LOGINFO("[DS-INIT-TIMING] DSController::Start — begin"); + auto t0 = Clock::now(); + _dsController->Start(); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "DSController::Start", + (long long)std::chrono::duration_cast(Clock::now() - t0).count()); + } else { + LOGERR("DSController is null - cannot start"); + return Core::ERROR_GENERAL; + } + + // Initialize DSController power event listener with the service + if (_dsController != nullptr) { + LOGINFO("[DS-INIT-TIMING] InitializePowerEventListener — begin"); + auto t0 = Clock::now(); + _dsController->InitializePowerEventListener(service); + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "InitializePowerEventListener", + (long long)std::chrono::duration_cast(Clock::now() - t0).count()); + } else { + LOGERR("DSController is null - cannot initialize power event listener"); + } + + // HAL initialisation is now done inside each HAL impl constructor as part of + // the two-stage parallel component Create() calls in DeviceSettingsImp(). + // No separate InitialiseHAL() pass is needed here. + + LOGINFO("[DS-INIT-TIMING] %-28s : %6lld ms", "Configure TOTAL", + (long long)std::chrono::duration_cast(Clock::now() - tCfg).count()); + LOGINFO("DeviceSettingsImp configured successfully"); + return Core::ERROR_NONE; + } + + // ============================================================================ + // IDeviceSettingsFPD interface implementation - delegate to _fpdSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsFPD::INotification* notification) { + Core::hresult result; + if (_fpdSettings != nullptr) { + result = _fpdSettings->Register(notification); + LOGINFO("FPD Register: SUCCESS - forwarded to implementation"); + } else { + LOGERR("FPD Register: FAILED - _fpdSettings is null"); + result = Core::ERROR_UNAVAILABLE; + } + return result; + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsFPD::INotification* notification) { + DELEGATE_TO_COMPONENT(_fpdSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDTime, timeFormat, minutes, seconds) + } + + Core::hresult DeviceSettingsImp::SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDScroll, scrollHoldDuration, nHorizontalScrollIterations, nVerticalScrollIterations) + } + + Core::hresult DeviceSettingsImp::SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDBlink, indicator, blinkDuration, blinkIterations) + } + + Core::hresult DeviceSettingsImp::SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDBrightness, indicator, brightNess, persist) + } + + Core::hresult DeviceSettingsImp::GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) { + DELEGATE_TO_COMPONENT(_fpdSettings, GetFPDBrightness, indicator, brightNess) + } + + Core::hresult DeviceSettingsImp::SetFPDState(const FPDIndicator indicator, const FPDState state) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDState, indicator, state) + } + + Core::hresult DeviceSettingsImp::GetFPDState(const FPDIndicator indicator, FPDState &state) { + DELEGATE_TO_COMPONENT(_fpdSettings, GetFPDState, indicator, state) + } + + Core::hresult DeviceSettingsImp::GetFPDColor(const FPDIndicator indicator, uint32_t &color) { + DELEGATE_TO_COMPONENT(_fpdSettings, GetFPDColor, indicator, color) + } + + Core::hresult DeviceSettingsImp::SetFPDColor(const FPDIndicator indicator, const uint32_t color) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDColor, indicator, color) + } + + Core::hresult DeviceSettingsImp::SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDTextBrightness, textDisplay, brightNess) + } + + Core::hresult DeviceSettingsImp::GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) { + DELEGATE_TO_COMPONENT(_fpdSettings, GetFPDTextBrightness, textDisplay, brightNess) + } + + Core::hresult DeviceSettingsImp::EnableFPDClockDisplay(const bool enable) { + DELEGATE_TO_COMPONENT(_fpdSettings, EnableFPDClockDisplay, enable) + } + + Core::hresult DeviceSettingsImp::GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) { + DELEGATE_TO_COMPONENT(_fpdSettings, GetFPDTimeFormat, fpdTimeFormat) + } + + Core::hresult DeviceSettingsImp::SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDTimeFormat, fpdTimeFormat) + } + + Core::hresult DeviceSettingsImp::SetFPDMode(const FPDMode fpdMode) { + DELEGATE_TO_COMPONENT(_fpdSettings, SetFPDMode, fpdMode) + } + + // ============================================================================ + // IDeviceSettingsHDMIIn interface implementation - delegate to _hdmiInSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsHDMIIn::INotification* notification) { + Core::hresult result; + if (_hdmiInSettings != nullptr) { + result = _hdmiInSettings->Register(notification); + LOGINFO("HDMIIn Register: SUCCESS - forwarded to implementation"); + } else { + LOGERR("HDMIIn Register: FAILED - _hdmiInSettings is null"); + result = Core::ERROR_UNAVAILABLE; + } + return result; + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsHDMIIn::INotification* notification) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetHDMIInNumberOfInputs(int32_t &count) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIInNumberOfInputs, count) + } + + Core::hresult DeviceSettingsImp::GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIInStatus, hdmiStatus, portConnectionStatus) + } + + Core::hresult DeviceSettingsImp::SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, SelectHDMIInPort, port, requestAudioMix, topMostPlane, videoPlaneType) + } + + Core::hresult DeviceSettingsImp::ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, ScaleHDMIInVideo, videoPosition) + } + + Core::hresult DeviceSettingsImp::SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, SelectHDMIZoomMode, zoomMode) + } + + Core::hresult DeviceSettingsImp::GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetSupportedGameFeaturesList, gameFeatureList) + } + + Core::hresult DeviceSettingsImp::GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIInAVLatency, videoLatency, audioLatency) + } + + Core::hresult DeviceSettingsImp::GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIInAllmStatus, port, allmStatus) + } + + Core::hresult DeviceSettingsImp::GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIInEdid2AllmSupport, port, allmSupport) + } + + Core::hresult DeviceSettingsImp::SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, SetHDMIInEdid2AllmSupport, port, allmSupport) + } + + Core::hresult DeviceSettingsImp::GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetEdidBytes, port, edidBytesLength, edidBytes) + } + + Core::hresult DeviceSettingsImp::GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMISPDInformation, port, spdBytesLength, spdBytes) + } + + Core::hresult DeviceSettingsImp::GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIEdidVersion, port, edidVersion) + } + + Core::hresult DeviceSettingsImp::SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, SetHDMIEdidVersion, port, edidVersion) + } + + Core::hresult DeviceSettingsImp::GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIVideoMode, videoPortResolution) + } + + Core::hresult DeviceSettingsImp::GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetHDMIVersion, port, capabilityVersion) + } + + Core::hresult DeviceSettingsImp::SetVRRSupport(const HDMIInPort port, const bool vrrSupport) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, SetVRRSupport, port, vrrSupport) + } + + Core::hresult DeviceSettingsImp::GetVRRSupport(const HDMIInPort port, bool &vrrSupport) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetVRRSupport, port, vrrSupport) + } + + Core::hresult DeviceSettingsImp::GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) { + DELEGATE_TO_COMPONENT(_hdmiInSettings, GetVRRStatus, port, vrrStatus) + } + + // ============================================================================ + // IDeviceSettingsAudio interface implementation - delegate to _audioSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsAudio::INotification* notification) { + DELEGATE_TO_COMPONENT(_audioSettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsAudio::INotification* notification) { + DELEGATE_TO_COMPONENT(_audioSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioPort, type, index, handle) + } + + Core::hresult DeviceSettingsImp::GetAudioCapabilities(const int32_t handle, int32_t &capabilities) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioCapabilities, handle, capabilities) + } + + Core::hresult DeviceSettingsImp::GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioMS12Capabilities, handle, capabilities) + } + + Core::hresult DeviceSettingsImp::GetMS12Capabilities(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) { + DELEGATE_TO_COMPONENT(_audioSettings, GetMS12Capabilities, handle, compressions) + } + + Core::hresult DeviceSettingsImp::GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioFormat, handle, audioFormat) + } + + Core::hresult DeviceSettingsImp::GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioEncoding, handle, encoding) + } + + Core::hresult DeviceSettingsImp::GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) { + DELEGATE_TO_COMPONENT(_audioSettings, GetSupportedCompressions, handle, compressions) + } + + Core::hresult DeviceSettingsImp::GetAudioCompression(const int32_t handle, AudioCompression &compression) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioCompression, handle, compression) + } + + Core::hresult DeviceSettingsImp::SetAudioCompression(const int32_t handle, const AudioCompression compression) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioCompression, handle, compression) + } + + Core::hresult DeviceSettingsImp::SetAudioLevel(const int32_t handle, const float audioLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioLevel, handle, audioLevel) + } + + Core::hresult DeviceSettingsImp::GetAudioLevel(const int32_t handle, float &audioLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioLevel, handle, audioLevel) + } + + Core::hresult DeviceSettingsImp::SetAudioGain(const int32_t handle, const float gainLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioGain, handle, gainLevel) + } + + Core::hresult DeviceSettingsImp::GetAudioGain(const int32_t handle, float &gainLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioGain, handle, gainLevel) + } + + Core::hresult DeviceSettingsImp::SetAudioMute(const int32_t handle, const bool mute) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioMute, handle, mute) + } + + Core::hresult DeviceSettingsImp::IsAudioMuted(const int32_t handle, bool &muted) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioMuted, handle, muted) + } + + Core::hresult DeviceSettingsImp::SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDucking, handle, duckingType, duckingAction, level) + } + + Core::hresult DeviceSettingsImp::GetStereoMode(const int32_t handle, AudioStereoMode &mode) { + DELEGATE_TO_COMPONENT(_audioSettings, GetStereoMode, handle, mode) + } + + Core::hresult DeviceSettingsImp::SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) { + DELEGATE_TO_COMPONENT(_audioSettings, SetStereoMode, handle, mode, persist) + } + + Core::hresult DeviceSettingsImp::SetAssociatedAudioMixing(const int32_t handle, const bool mixing) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAssociatedAudioMixing, handle, mixing) + } + + Core::hresult DeviceSettingsImp::GetAssociatedAudioMixing(const int32_t handle, bool &mixing) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAssociatedAudioMixing, handle, mixing) + } + + Core::hresult DeviceSettingsImp::SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioFaderControl, handle, mixerBalance) + } + + Core::hresult DeviceSettingsImp::GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioFaderControl, handle, mixerBalance) + } + + Core::hresult DeviceSettingsImp::SetAudioPrimaryLanguage(const int32_t handle, const string& primaryAudioLanguage) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioPrimaryLanguage, handle, primaryAudioLanguage) + } + + Core::hresult DeviceSettingsImp::GetAudioPrimaryLanguage(const int32_t handle, string &primaryAudioLanguage) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioPrimaryLanguage, handle, primaryAudioLanguage) + } + + Core::hresult DeviceSettingsImp::SetAudioSecondaryLanguage(const int32_t handle, const string& secondaryAudioLanguage) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioSecondaryLanguage, handle, secondaryAudioLanguage) + } + + Core::hresult DeviceSettingsImp::GetAudioSecondaryLanguage(const int32_t handle, string &secondaryAudioLanguage) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioSecondaryLanguage, handle, secondaryAudioLanguage) + } + + Core::hresult DeviceSettingsImp::IsAudioOutputConnected(const int32_t handle, bool &isConnected) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioOutputConnected, handle, isConnected) + } + + Core::hresult DeviceSettingsImp::GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioSinkDeviceAtmosCapability, handle, atmosCapability) + } + + Core::hresult DeviceSettingsImp::SetAudioAtmosOutputMode(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioAtmosOutputMode, handle, enable) + } + + // Missing Audio interface delegation methods + Core::hresult DeviceSettingsImp::IsAudioPortEnabled(const int32_t handle, bool &enabled) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioPortEnabled, handle, enabled) + } + + Core::hresult DeviceSettingsImp::EnableAudioPort(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, EnableAudioPort, handle, enable) + } + + Core::hresult DeviceSettingsImp::GetSupportedARCTypes(const int32_t handle, int32_t &types) { + DELEGATE_TO_COMPONENT(_audioSettings, GetSupportedARCTypes, handle, types) + } + + Core::hresult DeviceSettingsImp::SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) { + DELEGATE_TO_COMPONENT(_audioSettings, SetSAD, handle, sadList, count) + } + + Core::hresult DeviceSettingsImp::EnableARC(const int32_t handle, const AudioARCStatus arcStatus) { + DELEGATE_TO_COMPONENT(_audioSettings, EnableARC, handle, arcStatus) + } + + Core::hresult DeviceSettingsImp::GetStereoAuto(const int32_t handle, int32_t &mode) { + DELEGATE_TO_COMPONENT(_audioSettings, GetStereoAuto, handle, mode) + } + + Core::hresult DeviceSettingsImp::SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist) { + DELEGATE_TO_COMPONENT(_audioSettings, SetStereoAuto, handle, mode, persist) + } + + Core::hresult DeviceSettingsImp::GetAudioEnablePersist(const int32_t handle, bool &enabled, string &portName) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioEnablePersist, handle, enabled, portName) + } + + Core::hresult DeviceSettingsImp::SetAudioEnablePersist(const int32_t handle, const bool enable, const string& portName) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioEnablePersist, handle, enable, portName) + } + + Core::hresult DeviceSettingsImp::IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioMSDecoded, handle, hasms11Decode) + } + + Core::hresult DeviceSettingsImp::IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioMS12Decoded, handle, hasms12Decode) + } + + Core::hresult DeviceSettingsImp::GetAudioLEConfig(const int32_t handle, bool &enabled) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioLEConfig, handle, enabled) + } + + Core::hresult DeviceSettingsImp::EnableAudioLEConfig(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, EnableAudioLEConfig, handle, enable) + } + + Core::hresult DeviceSettingsImp::SetAudioDelay(const int32_t handle, const uint32_t audioDelay) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDelay, handle, audioDelay) + } + + Core::hresult DeviceSettingsImp::GetAudioDelay(const int32_t handle, uint32_t &audioDelay) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioDelay, handle, audioDelay) + } + + Core::hresult DeviceSettingsImp::SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDelayOffset, handle, delayOffset) + } + + Core::hresult DeviceSettingsImp::GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioDelayOffset, handle, delayOffset) + } + + Core::hresult DeviceSettingsImp::SetAudioCompression(const int32_t handle, const int32_t compressionLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioCompression, handle, compressionLevel) + } + + Core::hresult DeviceSettingsImp::GetAudioCompression(const int32_t handle, int32_t &compressionLevel) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioCompression, handle, compressionLevel) + } + + Core::hresult DeviceSettingsImp::SetAudioDialogEnhancement(const int32_t handle, const int32_t level) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDialogEnhancement, handle, level) + } + + Core::hresult DeviceSettingsImp::GetAudioDialogEnhancement(const int32_t handle, int32_t &level) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioDialogEnhancement, handle, level) + } + + Core::hresult DeviceSettingsImp::SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDolbyVolumeMode, handle, enable) + } + + Core::hresult DeviceSettingsImp::GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioDolbyVolumeMode, handle, enabled) + } + + Core::hresult DeviceSettingsImp::SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioIntelligentEqualizerMode, handle, mode) + } + + Core::hresult DeviceSettingsImp::GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioIntelligentEqualizerMode, handle, mode) + } + + Core::hresult DeviceSettingsImp::SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioVolumeLeveller, handle, volumeLeveller) + } + + Core::hresult DeviceSettingsImp::GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioVolumeLeveller, handle, volumeLeveller) + } + + Core::hresult DeviceSettingsImp::SetAudioBassEnhancer(const int32_t handle, const int32_t boost) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioBassEnhancer, handle, boost) + } + + Core::hresult DeviceSettingsImp::GetAudioBassEnhancer(const int32_t handle, int32_t &boost) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioBassEnhancer, handle, boost) + } + + Core::hresult DeviceSettingsImp::EnableAudioSurroundDecoder(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, EnableAudioSurroundDecoder, handle, enable) + } + + Core::hresult DeviceSettingsImp::IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled) { + DELEGATE_TO_COMPONENT(_audioSettings, IsAudioSurroundDecoderEnabled, handle, enabled) + } + + Core::hresult DeviceSettingsImp::SetAudioDRCMode(const int32_t handle, const int32_t drcMode) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioDRCMode, handle, drcMode) + } + + Core::hresult DeviceSettingsImp::GetAudioDRCMode(const int32_t handle, int32_t &drcMode) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioDRCMode, handle, drcMode) + } + + Core::hresult DeviceSettingsImp::SetAudioSurroundVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioSurroundVirtualizer, handle, surroundVirtualizer) + } + + Core::hresult DeviceSettingsImp::GetAudioSurroundVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioSurroundVirtualizer, handle, surroundVirtualizer) + } + + Core::hresult DeviceSettingsImp::SetAudioMISteering(const int32_t handle, const bool enable) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioMISteering, handle, enable) + } + + Core::hresult DeviceSettingsImp::GetAudioMISteering(const int32_t handle, bool &enable) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioMISteering, handle, enable) + } + + Core::hresult DeviceSettingsImp::SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioGraphicEqualizerMode, handle, mode) + } + + Core::hresult DeviceSettingsImp::GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioGraphicEqualizerMode, handle, mode) + } + + Core::hresult DeviceSettingsImp::GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioMS12ProfileList, handle, ms12ProfileList) + } + + Core::hresult DeviceSettingsImp::GetAudioMS12Profile(const int32_t handle, string &profile) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioMS12Profile, handle, profile) + } + + Core::hresult DeviceSettingsImp::SetAudioMS12Profile(const int32_t handle, const string& profile) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioMS12Profile, handle, profile) + } + + Core::hresult DeviceSettingsImp::SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume) { + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioMixerLevels, handle, audioInput, volume) + } + + Core::hresult DeviceSettingsImp::SetAudioMS12SettingsOverride(const int32_t handle, const string& profileName, const string& profileSettingsName, const string& profileSettingValue, const AudioMS12ProfileState profileState) { + // Pass the enum directly; DeviceSettingsAudioImpl converts it to "ADD"/"REMOVE" internally. + DELEGATE_TO_COMPONENT(_audioSettings, SetAudioMS12SettingsOverride, handle, profileName, profileSettingsName, profileSettingValue, profileState) + } + + Core::hresult DeviceSettingsImp::ResetAudioDialogEnhancement(const int32_t handle) { + DELEGATE_TO_COMPONENT(_audioSettings, ResetAudioDialogEnhancement, handle) + } + + Core::hresult DeviceSettingsImp::ResetAudioBassEnhancer(const int32_t handle) { + DELEGATE_TO_COMPONENT(_audioSettings, ResetAudioBassEnhancer, handle) + } + + Core::hresult DeviceSettingsImp::ResetAudioSurroundVirtualizer(const int32_t handle) { + DELEGATE_TO_COMPONENT(_audioSettings, ResetAudioSurroundVirtualizer, handle) + } + + Core::hresult DeviceSettingsImp::ResetAudioVolumeLeveller(const int32_t handle) { + DELEGATE_TO_COMPONENT(_audioSettings, ResetAudioVolumeLeveller, handle) + } + + Core::hresult DeviceSettingsImp::GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) { + DELEGATE_TO_COMPONENT(_audioSettings, GetAudioHDMIARCPortId, handle, portId) + } + + // ============================================================================ + // IDeviceSettingsVideoPort interface implementation - delegate to _videoPortSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsVideoPort::INotification* notification) { + DELEGATE_TO_COMPONENT(_videoPortSettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsVideoPort::INotification* notification) { + DELEGATE_TO_COMPONENT(_videoPortSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetVideoPort, videoPort, index, handle) + } + + Core::hresult DeviceSettingsImp::IsVideoPortEnabled(const int32_t handle, bool &enabled) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsVideoPortEnabled, handle, enabled) + } + + Core::hresult DeviceSettingsImp::GetVideoPortResolutionConfig(VideoPortType videoPortType, + IVideoPortResolutionIterator*& videoPortResolutions) const { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetVideoPortResolutionConfig, videoPortType, videoPortResolutions) + } + + Core::hresult DeviceSettingsImp::EnableVideoPort(const int32_t handle, const bool enabled) { + DELEGATE_TO_COMPONENT(_videoPortSettings, EnableVideoPort, handle, enabled) + } + + Core::hresult DeviceSettingsImp::IsVideoPortDisplayConnected(const int32_t handle, bool &connected) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsVideoPortDisplayConnected, handle, connected) + } + + Core::hresult DeviceSettingsImp::IsVideoPortActive(const int32_t handle, bool &active) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsVideoPortActive, handle, active) + } + + Core::hresult DeviceSettingsImp::GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetVideoPortResolution, handle, resolution) + } + + Core::hresult DeviceSettingsImp::GetColorDepth(const int32_t handle, uint32_t &colorDepth) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetColorDepth, handle, colorDepth) + } + + Core::hresult DeviceSettingsImp::GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace) { + VideoPortColorSpace internalColorSpace; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetColorSpace(handle, internalColorSpace) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + colorSpace = static_cast(internalColorSpace); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange) { + VideoPortQuantizationRange internalRange; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetQuantizationRange(handle, internalRange) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + quantizationRange = static_cast(internalRange); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetHDCPStatusOnVideoPort(const int32_t handle, Exchange::IDeviceSettingsVideoPort::HDCPStatus &hdcpStatus) { + VideoPortHdcpStatus internalStatus; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetVideoPortHDCPStatus(handle, internalStatus) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdcpStatus = static_cast(internalStatus); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + VideoPortHdcpProtocolVersion internalVersion; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetHDCPProtocolVersionOnVideoPort(handle, internalVersion) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdcpVersion = static_cast(internalVersion); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + VideoPortHdcpProtocolVersion internalVersion; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetHDCPReceiverProtocolVersionOnVideoPort(handle, internalVersion) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdcpVersion = static_cast(internalVersion); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + VideoPortHdcpProtocolVersion internalVersion; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetHDCPCurrentProtocolVersionOnVideoPort(handle, internalVersion) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdcpVersion = static_cast(internalVersion); + } + return result; + } + + Core::hresult DeviceSettingsImp::IsVideoPortDisplaySurround(const int32_t handle, bool &surround) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsVideoPortDisplaySurround, handle, surround) + } + + Core::hresult DeviceSettingsImp::GetVideoPortDisplaySurroundMode(const int32_t handle, Exchange::IDeviceSettingsVideoPort::VideoPortSurroundMode &surroundMode) { + VideoPortSurroundMode internalSurroundMode; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetVideoPortDisplaySurroundMode(handle, internalSurroundMode) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + surroundMode = static_cast(internalSurroundMode); + } + return result; + } + + Core::hresult DeviceSettingsImp::SetVideoPortResolution(const int32_t handle, const VideoPortResolution& videoPortResolution, const bool persist, const bool forceCompatibility) { + DELEGATE_TO_COMPONENT(_videoPortSettings, SetVideoPortResolution, handle, videoPortResolution, persist, forceCompatibility) + } + + Core::hresult DeviceSettingsImp::EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t hdcpKey[], const uint16_t hdcpKeySize) { + DELEGATE_TO_COMPONENT(_videoPortSettings, EnableHDCPOnVideoPort, handle, hdcpEnable, hdcpKey, hdcpKeySize) + } + + Core::hresult DeviceSettingsImp::IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsHDCPEnabledOnVideoPort, handle, hdcpEnabled) + } + + Core::hresult DeviceSettingsImp::GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetTVHDRCapabilities, handle, capabilities) + } + + Core::hresult DeviceSettingsImp::GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetTVSupportedResolutions, handle, resolutions) + } + + Core::hresult DeviceSettingsImp::SetForceDisable4K(const int32_t handle, const bool disable) { + DELEGATE_TO_COMPONENT(_videoPortSettings, SetForceDisable4K, handle, disable) + } + + Core::hresult DeviceSettingsImp::GetForceDisable4K(const int32_t handle, bool &disabled) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetForceDisable4K, handle, disabled) + } + + Core::hresult DeviceSettingsImp::IsVideoPortOutputHDR(const int32_t handle, bool &isHDR) { + DELEGATE_TO_COMPONENT(_videoPortSettings, IsVideoPortOutputHDR, handle, isHDR) + } + + Core::hresult DeviceSettingsImp::ResetVideoPortOutputToSDR() { + return _videoPortSettings ? _videoPortSettings->ResetVideoPortOutputToSDR() : Core::ERROR_GENERAL; + } + + Core::hresult DeviceSettingsImp::GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + VideoPortHdcpProtocolVersion internalVersion; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetHDMIPreference(handle, internalVersion) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdcpVersion = static_cast(internalVersion); + } + return result; + } + + Core::hresult DeviceSettingsImp::SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) { + return _videoPortSettings ? _videoPortSettings->SetHDMIPreference(handle, static_cast(hdcpVersion)) : Core::ERROR_GENERAL; + } + + Core::hresult DeviceSettingsImp::GetVideoEOTF(const int32_t handle, HDRStandard &hdrStandard) { + HDRStandard internalHdrStandard; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetVideoEOTF(handle, internalHdrStandard) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + hdrStandard = static_cast(internalHdrStandard); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetMatrixCoefficients(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DisplayMatrixCoefficients &matrixCoefficients) { + DisplayMatrixCoefficients internalMatrixCoefficients; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetMatrixCoefficients(handle, internalMatrixCoefficients) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + matrixCoefficients = static_cast(internalMatrixCoefficients); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetCurrentOutputSettings(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DSOutputSettings &outputSettings) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetCurrentOutputSettings, handle, outputSettings) + } + + Core::hresult DeviceSettingsImp::SetBackgroundColor(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::VideoBackgroundColor backgroundColor) { + return _videoPortSettings ? _videoPortSettings->SetBackgroundColor(handle, static_cast(backgroundColor)) : Core::ERROR_GENERAL; + } + + Core::hresult DeviceSettingsImp::SetForceHDRMode(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::HDRStandard hdrMode) { + return _videoPortSettings ? _videoPortSettings->SetForceHDRMode(handle, static_cast(hdrMode)) : Core::ERROR_GENERAL; + } + + Core::hresult DeviceSettingsImp::GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities) { + DELEGATE_TO_COMPONENT(_videoPortSettings, GetColorDepthCapabilities, handle, colorDepthCapabilities) + } + + Core::hresult DeviceSettingsImp::GetPreferredColorDepth(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DisplayColorDepth &colorDepth, const bool persist) { + DisplayColorDepth internalColorDepth; + Core::hresult result = _videoPortSettings ? _videoPortSettings->GetPreferredColorDepth(handle, internalColorDepth, persist) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + colorDepth = static_cast(internalColorDepth); + } + return result; + } + + Core::hresult DeviceSettingsImp::SetPreferredColorDepth(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::DisplayColorDepth colorDepth, const bool persist) { + return _videoPortSettings ? _videoPortSettings->SetPreferredColorDepth(handle, static_cast(colorDepth), persist) : Core::ERROR_GENERAL; + } + + // IDeviceSettingsVideoDevice interface implementation - delegate to _videoDeviceSettings interface + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsVideoDevice::INotification* notification) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsVideoDevice::INotification* notification) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetVideoDeviceHandle(const int32_t index, int32_t &handle) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetVideoDeviceHandle, index, handle) + } + + Core::hresult DeviceSettingsImp::SetVideoDeviceDFC(const int32_t handle, const Exchange::IDeviceSettingsVideoDevice::VideoZoom zoom) { + return _videoDeviceSettings ? _videoDeviceSettings->SetVideoDeviceDFC(handle, static_cast(zoom)) : Core::ERROR_GENERAL; + } + + Core::hresult DeviceSettingsImp::GetVideoDeviceDFC(const int32_t handle, Exchange::IDeviceSettingsVideoDevice::VideoZoom &zoom) { + VideoZoom internalZoom; + Core::hresult result = _videoDeviceSettings ? _videoDeviceSettings->GetVideoDeviceDFC(handle, internalZoom) : Core::ERROR_GENERAL; + if (result == Core::ERROR_NONE) { + zoom = static_cast(internalZoom); + } + return result; + } + + Core::hresult DeviceSettingsImp::GetHDRCapabilities(const int32_t handle, int32_t &capabilities) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetHDRCapabilities, handle, capabilities) + } + + Core::hresult DeviceSettingsImp::GetSupportedVideoCodingFormats(const int32_t handle, int32_t &supportedFormats) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetSupportedVideoCodingFormats, handle, supportedFormats) + } + + Core::hresult DeviceSettingsImp::SetDisplayFrameRate(const int32_t handle, const string& framerate) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, SetDisplayFrameRate, handle, framerate) + } + + Core::hresult DeviceSettingsImp::GetCodecInfo(const int32_t handle, const Exchange::IDeviceSettingsVideoDevice::VideoCodec videoCodec, Exchange::IDeviceSettingsVideoDevice::IDeviceSettingsVideoCodecProfileSupportIterator *&codecInfo) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetCodecInfo, handle, static_cast(videoCodec), codecInfo) + } + + Core::hresult DeviceSettingsImp::DisableHDR(const int32_t handle, const bool disable) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, DisableHDR, handle, disable) + } + + Core::hresult DeviceSettingsImp::SetFRFMode(const int32_t handle, const int32_t frfmode) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, SetFRFMode, handle, frfmode) + } + + Core::hresult DeviceSettingsImp::GetFRFMode(const int32_t handle, int32_t &frfmode) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetFRFMode, handle, frfmode) + } + + Core::hresult DeviceSettingsImp::GetCurrentDisplayFrameRate(const int32_t handle, string &framerate) { + DELEGATE_TO_COMPONENT(_videoDeviceSettings, GetCurrentDisplayFrameRate, handle, framerate) + } + + // ============================================================================ + // IDeviceSettingsHost interface implementation - delegate to _hostSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::GetEDID(uint8_t edId[], const uint16_t edIdLength) { + DELEGATE_TO_COMPONENT(_hostSettings, GetEDID, edId, edIdLength) + } + + Core::hresult DeviceSettingsImp::GetMS12ConfigType(string &ms12Config) { + DELEGATE_TO_COMPONENT(_hostSettings, GetMS12ConfigType, ms12Config) + } + + // ============================================================================ + // IDeviceSettingsDisplay interface implementation - delegate to _displaySettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(IDisplayNotification* notification) { + DELEGATE_TO_COMPONENT(_displaySettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(IDisplayNotification* notification) { + DELEGATE_TO_COMPONENT(_displaySettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetDisplayEdid(const int32_t handle, DisplayEDID &edId, IDSVideoPortResolutionIterator*& supportedResolutionList) { + DELEGATE_TO_COMPONENT(_displaySettings, GetDisplayEdid, handle, edId, supportedResolutionList) + } + + Core::hresult DeviceSettingsImp::GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength) { + DELEGATE_TO_COMPONENT(_displaySettings, GetDisplayEdidBytes, handle, edIdBytes, edidLength) + } + + Core::hresult DeviceSettingsImp::GetDisplay(const DisplayPortType portType, const int32_t index, int32_t &handle) { + DELEGATE_TO_COMPONENT(_displaySettings, GetDisplay, portType, index, handle) + } + + Core::hresult DeviceSettingsImp::Register(IDisplayHDMIHotPlugNotification* notification) { + DELEGATE_TO_COMPONENT(_displaySettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(IDisplayHDMIHotPlugNotification* notification) { + DELEGATE_TO_COMPONENT(_displaySettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetDisplayAspectRatio(const int32_t handle, Exchange::IDeviceSettingsDisplay::DisplayVideoAspectRatio &aspectRatio) { + DELEGATE_TO_COMPONENT(_displaySettings, GetDisplayAspectRatio, handle, aspectRatio) + } + + Core::hresult DeviceSettingsImp::SetAllmEnabled(const int32_t handle, const bool enabled) { + DELEGATE_TO_COMPONENT(_displaySettings, SetAllmEnabled, handle, enabled) + } + + Core::hresult DeviceSettingsImp::SetAVIContentType(const int32_t handle, const DisplayAVIContentType contentType) { + DELEGATE_TO_COMPONENT(_displaySettings, SetAVIContentType, handle, contentType) + } + + Core::hresult DeviceSettingsImp::SetAVIScanInformation(const int32_t handle, const DisplayAVIScanInformation scanInfo) { + DELEGATE_TO_COMPONENT(_displaySettings, SetAVIScanInformation, handle, scanInfo) + } + + // ============================================================================ + // IDeviceSettingsCompositeIn interface implementation - delegate to _compositeInSettings interface + // ============================================================================ + + Core::hresult DeviceSettingsImp::Register(Exchange::IDeviceSettingsCompositeIn::INotification* notification) { + DELEGATE_TO_COMPONENT(_compositeInSettings, Register, notification) + } + + Core::hresult DeviceSettingsImp::Unregister(Exchange::IDeviceSettingsCompositeIn::INotification* notification) { + DELEGATE_TO_COMPONENT(_compositeInSettings, Unregister, notification) + } + + Core::hresult DeviceSettingsImp::GetNrOfCompositeInputs(int32_t &nrCompositeInputs) { + DELEGATE_TO_COMPONENT(_compositeInSettings, GetNrOfCompositeInputs, nrCompositeInputs) + } + + Core::hresult DeviceSettingsImp::GetCompositeInStatus(CompositeInStatus &status) { + DELEGATE_TO_COMPONENT(_compositeInSettings, GetCompositeInStatus, status) + } + + Core::hresult DeviceSettingsImp::SelectCompositeInPort(const CompositeInPort port) { + DELEGATE_TO_COMPONENT(_compositeInSettings, SelectCompositeInPort, port) + } + + Core::hresult DeviceSettingsImp::ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect) { + DELEGATE_TO_COMPONENT(_compositeInSettings, ScaleCompositeInVideo, videoRect) + } + + // Static instance method implementation + DeviceSettingsImp* DeviceSettingsImp::instance(DeviceSettingsImp* DeviceSettingsImpl) + { + if (DeviceSettingsImpl != nullptr) { + _instance = DeviceSettingsImpl; + } + return _instance; + } + + // ============================================================================ + // IDeviceSettings::GetDeviceSettingConfigs — single consolidated config call + // ============================================================================ + + Core::hresult DeviceSettingsImp::GetDeviceSettingConfigs(Exchange::IDeviceSettings::DeviceSettingConfigs& configs) + { + // Serve from cache on all calls after the first. + if (_configLoaded.load(std::memory_order_acquire)) { + std::lock_guard lock(_configMutex); + configs = _cachedConfigs; + return Core::ERROR_NONE; + } + + // First call: load from HAL, cache result, then return. + // Config population is intentionally deferred here (not in constructors) + // so plugin activation is not delayed by HAL config loading. + + // ── FPD config — IDeviceSettings types identical, direct population ── + DeviceSettingsHAL::PopulateFPDConfig( + configs.colors, configs.indicators, configs.textDisplays, configs.colorBindings); + + // ── Audio config ───────────────────────────────────────────────────── + { + using AudioTypeCfg = Exchange::IDeviceSettings::AudioTypeConfigInfo; + using AudioPortCfg = Exchange::IDeviceSettingsAudio::AudioPortConfigInfo; + std::vector audioTypes; + std::vector audioPorts; + DeviceSettingsHAL::PopulateAudioConfig(audioTypes, audioPorts); + + // AudioTypeConfigInfo is identical in IDeviceSettings — direct copy + configs.audioTypes.assign(audioTypes.begin(), audioTypes.end()); + + // AudioPortConfigInfo still differs (AudioPortType enum → int32_t) + configs.audioPorts.reserve(audioPorts.size()); + for (const auto& src : audioPorts) { + configs.audioPorts.push_back({ + static_cast(src.audioPortType), + src.audioPortIndex, + src.connectedVideoPortType, + src.connectedVideoPortIndex}); + } + } + + // ── Video device config ─────────────────────────────────────────────── + { + using VDevCfg = Exchange::IDeviceSettingsVideoDevice::VideoDeviceConfigInfo; + std::vector videoDeviceConfigs; + DeviceSettingsHAL::PopulateVideoDeviceConfig(videoDeviceConfigs); + configs.videoConfigs.reserve(videoDeviceConfigs.size()); + for (const auto& src : videoDeviceConfigs) { + configs.videoConfigs.push_back({ + src.numSupportedDFCs, + src.supportedDFCsMask, + static_cast(src.defaultDFC)}); + } + } + + // ── Video port config ───────────────────────────────────────────────── + { + using VPortTypeCfg = Exchange::IDeviceSettingsVideoPort::VideoPortTypeConfig; + using VPortPortCfg = Exchange::IDeviceSettingsVideoPort::VideoPortPortConfig; + using VPortRes = Exchange::IDeviceSettingsVideoPort::VideoPortResolution; + std::vector videoPortTypes; + std::vector videoPorts; + DeviceSettingsHAL::PopulateVideoPortConfig(videoPortTypes, videoPorts); + + configs.videoPortTypes.reserve(videoPortTypes.size()); + for (const auto& src : videoPortTypes) { + configs.videoPortTypes.push_back({ + static_cast(src.typeId), + src.name, + src.dtcpSupported, + src.hdcpSupported, + src.restrictedResolution, + src.supportedResolutionNames}); + } + + configs.videoPorts.reserve(videoPorts.size()); + for (const auto& src : videoPorts) { + configs.videoPorts.push_back({ + static_cast(src.videoPortType), + src.videoPortIndex, + src.connectedAudioPortType, + src.connectedAudioPortIndex, + src.defaultResolution}); + } + + // Resolution config for the 0th video port type + if (!videoPortTypes.empty()) { + std::vector resolutions; + DeviceSettingsHAL::PopulateVideoPortResolutionConfig( + videoPortTypes[0].typeId, resolutions); + configs.videoPortResolutions.reserve(resolutions.size()); + for (const auto& src : resolutions) { + configs.videoPortResolutions.push_back({ + src.name, + static_cast(src.pixelResolution), + static_cast(src.aspectRatio), + static_cast(src.stereoScopicMode), + static_cast(src.frameRate), + src.interlaced}); + } + } + } + + LOGINFO("GetDeviceSettingConfigs: audioTypes=%zu audioPorts=%zu " + "textDisplays=%zu indicators=%zu colors=%zu colorBindings=%zu " + "videoConfigs=%zu videoPortTypes=%zu videoPorts=%zu videoPortResolutions=%zu", + configs.audioTypes.size(), configs.audioPorts.size(), + configs.textDisplays.size(), configs.indicators.size(), + configs.colors.size(), configs.colorBindings.size(), + configs.videoConfigs.size(), configs.videoPortTypes.size(), configs.videoPorts.size(), + configs.videoPortResolutions.size()); + + // Store in cache for subsequent calls + { + std::lock_guard lock(_configMutex); + _cachedConfigs = configs; + } + _configLoaded.store(true, std::memory_order_release); + + return Core::ERROR_NONE; + } + +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsImplementation.h b/plugin/DeviceSettingsImplementation.h new file mode 100644 index 0000000..99c86ff --- /dev/null +++ b/plugin/DeviceSettingsImplementation.h @@ -0,0 +1,386 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2024 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DeviceSettingsTypes.h" +#include "DeviceSettingsVideoPortImplementation.h" +#include "DeviceSettingsVideoDeviceImplementation.h" +#include "DeviceSettingsHostImplementation.h" +#include "DeviceSettingsDisplayImplementation.h" +#include "DeviceSettingsCompositeInImplementation.h" + +namespace WPEFramework { +namespace Plugin { + // Forward declare implementation classes + class DeviceSettingsFPDImpl; + class DeviceSettingsHdmiInImp; + class DeviceSettingsAudioImpl; + class DSController; + + class DeviceSettingsImp : public Exchange::IDeviceSettings + , public Exchange::IDeviceSettingsFPD + , public Exchange::IDeviceSettingsHDMIIn + , public Exchange::IDeviceSettingsAudio + , public Exchange::IDeviceSettingsVideoPort + , public Exchange::IDeviceSettingsVideoDevice + , public Exchange::IDeviceSettingsHost + , public Exchange::IDeviceSettingsCompositeIn // ✅ IMPLEMENTED + , public Exchange::IDeviceSettingsDisplay // ✅ IMPLEMENTED + { + public: + // We do not allow this plugin to be copied !! + DeviceSettingsImp(); + ~DeviceSettingsImp(); + + static DeviceSettingsImp* instance(DeviceSettingsImp* DeviceSettingsImpl = nullptr); + + // We do not allow this plugin to be copied !! + DeviceSettingsImp(const DeviceSettingsImp&) = delete; + DeviceSettingsImp& operator=(const DeviceSettingsImp&) = delete; + + // Build QueryInterface implementation, specifying all possible interfaces to be returned. + BEGIN_INTERFACE_MAP(DeviceSettingsImp) + INTERFACE_ENTRY(Exchange::IDeviceSettings) + INTERFACE_ENTRY(Exchange::IDeviceSettingsFPD) + INTERFACE_ENTRY(Exchange::IDeviceSettingsHDMIIn) + INTERFACE_ENTRY(Exchange::IDeviceSettingsAudio) + INTERFACE_ENTRY(Exchange::IDeviceSettingsVideoPort) + INTERFACE_ENTRY(Exchange::IDeviceSettingsVideoDevice) + INTERFACE_ENTRY(Exchange::IDeviceSettingsHost) + INTERFACE_ENTRY(Exchange::IDeviceSettingsCompositeIn) + INTERFACE_ENTRY(Exchange::IDeviceSettingsDisplay) + END_INTERFACE_MAP + + // IDeviceSettings interface implementation + Core::hresult Configure(PluginHost::IShell* service) override; + Core::hresult GetDeviceSettingConfigs(Exchange::IDeviceSettings::DeviceSettingConfigs& configs) override; + + // IDeviceSettingsFPD interface implementation - delegate to _fpdSettings interface + Core::hresult Register(Exchange::IDeviceSettingsFPD::INotification* notification) override; + Core::hresult Unregister(Exchange::IDeviceSettingsFPD::INotification* notification) override; + Core::hresult SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) override; + Core::hresult SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) override; + Core::hresult SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) override; + Core::hresult SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist) override; + Core::hresult GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) override; + Core::hresult SetFPDState(const FPDIndicator indicator, const FPDState state) override; + Core::hresult GetFPDState(const FPDIndicator indicator, FPDState &state) override; + Core::hresult GetFPDColor(const FPDIndicator indicator, uint32_t &color) override; + Core::hresult SetFPDColor(const FPDIndicator indicator, const uint32_t color) override; + Core::hresult SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) override; + Core::hresult GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) override; + Core::hresult EnableFPDClockDisplay(const bool enable) override; + Core::hresult GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) override; + Core::hresult SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) override; + Core::hresult SetFPDMode(const FPDMode fpdMode) override; + + // IDeviceSettingsHDMIIn interface implementation - delegate to _hdmiInSettings interface + Core::hresult Register(Exchange::IDeviceSettingsHDMIIn::INotification* notification) override; + Core::hresult Unregister(Exchange::IDeviceSettingsHDMIIn::INotification* notification) override; + Core::hresult GetHDMIInNumberOfInputs(int32_t &count) override; + Core::hresult GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) override; + Core::hresult SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) override; + Core::hresult ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) override; + Core::hresult SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) override; + Core::hresult GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) override; + Core::hresult GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) override; + Core::hresult GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) override; + Core::hresult GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) override; + Core::hresult SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) override; + Core::hresult GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) override; + Core::hresult GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) override; + Core::hresult GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) override; + Core::hresult SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) override; + Core::hresult GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) override; + Core::hresult GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) override; + Core::hresult SetVRRSupport(const HDMIInPort port, const bool vrrSupport) override; + Core::hresult GetVRRSupport(const HDMIInPort port, bool &vrrSupport) override; + Core::hresult GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) override; + + // IDeviceSettingsAudio interface implementation - delegate to _audioSettings interface + Core::hresult Register(Exchange::IDeviceSettingsAudio::INotification* notification) override; + Core::hresult Unregister(Exchange::IDeviceSettingsAudio::INotification* notification) override; + Core::hresult GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) override; + Core::hresult GetMS12Capabilities(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + Core::hresult GetAudioCapabilities(const int32_t handle, int32_t &capabilities); + Core::hresult GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities); + Core::hresult GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) override; + Core::hresult GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) override; + Core::hresult GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions); + Core::hresult GetAudioCompression(const int32_t handle, AudioCompression &compression); + Core::hresult SetAudioCompression(const int32_t handle, const AudioCompression compression); + Core::hresult SetAudioLevel(const int32_t handle, const float audioLevel) override; + Core::hresult GetAudioLevel(const int32_t handle, float &audioLevel) override; + Core::hresult SetAudioGain(const int32_t handle, const float gainLevel) override; + Core::hresult GetAudioGain(const int32_t handle, float &gainLevel) override; + Core::hresult SetAudioMute(const int32_t handle, const bool mute) override; + Core::hresult IsAudioMuted(const int32_t handle, bool &muted) override; + Core::hresult SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) override; + Core::hresult GetStereoMode(const int32_t handle, AudioStereoMode &mode) override; + Core::hresult SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) override; + Core::hresult GetStereoAuto(const int32_t handle, int32_t &mode) override; + Core::hresult SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist) override; + Core::hresult SetAssociatedAudioMixing(const int32_t handle, const bool mixing); + Core::hresult GetAssociatedAudioMixing(const int32_t handle, bool &mixing); + Core::hresult SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance); + Core::hresult GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance); + Core::hresult SetAudioPrimaryLanguage(const int32_t handle, const string& primaryAudioLanguage); + Core::hresult GetAudioPrimaryLanguage(const int32_t handle, string &primaryAudioLanguage); + Core::hresult SetAudioSecondaryLanguage(const int32_t handle, const string& secondaryAudioLanguage); + Core::hresult GetAudioSecondaryLanguage(const int32_t handle, string &secondaryAudioLanguage); + Core::hresult IsAudioOutputConnected(const int32_t handle, bool &isConnected); + Core::hresult GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability); + Core::hresult SetAudioAtmosOutputMode(const int32_t handle, const bool enable); + + // Additional Audio Port Methods + Core::hresult IsAudioPortEnabled(const int32_t handle, bool &enabled) override; + Core::hresult EnableAudioPort(const int32_t handle, const bool enable) override; + Core::hresult GetSupportedARCTypes(const int32_t handle, int32_t &types) override; + Core::hresult SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) override; + Core::hresult EnableARC(const int32_t handle, const AudioARCStatus arcStatus) override; + + // Audio Persistence Configuration + Core::hresult GetAudioEnablePersist(const int32_t handle, bool &enabled, string &portName) override; + Core::hresult SetAudioEnablePersist(const int32_t handle, const bool enable, const string& portName) override; + + // Audio Decoder Status + Core::hresult IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) override; + Core::hresult IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) override; + + // Loudness Equivalence Configuration + Core::hresult GetAudioLEConfig(const int32_t handle, bool &enabled) override; + Core::hresult EnableAudioLEConfig(const int32_t handle, const bool enable) override; + + // Audio Delay Controls + Core::hresult SetAudioDelay(const int32_t handle, const uint32_t audioDelay) override; + Core::hresult GetAudioDelay(const int32_t handle, uint32_t &audioDelay) override; + Core::hresult SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) override; + Core::hresult GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) override; + + // Audio Dynamic Range Control + Core::hresult SetAudioCompression(const int32_t handle, const int32_t compressionLevel) override; + Core::hresult GetAudioCompression(const int32_t handle, int32_t &compressionLevel) override; + + // Dialog Enhancement + Core::hresult SetAudioDialogEnhancement(const int32_t handle, const int32_t level) override; + Core::hresult GetAudioDialogEnhancement(const int32_t handle, int32_t &level) override; + + // Dolby Volume Mode + Core::hresult SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) override; + Core::hresult GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) override; + + // Intelligent Equalizer + Core::hresult SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) override; + Core::hresult GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) override; + + // Volume Leveller + Core::hresult SetAudioVolumeLeveller(const int32_t handle, const VolumeLeveller volumeLeveller) override; + Core::hresult GetAudioVolumeLeveller(const int32_t handle, VolumeLeveller &volumeLeveller) override; + + // Bass Enhancer + Core::hresult SetAudioBassEnhancer(const int32_t handle, const int32_t boost) override; + Core::hresult GetAudioBassEnhancer(const int32_t handle, int32_t &boost) override; + + // Surround Decoder + Core::hresult EnableAudioSurroundDecoder(const int32_t handle, const bool enable) override; + Core::hresult IsAudioSurroundDecoderEnabled(const int32_t handle, bool &enabled) override; + + // DRC Mode + Core::hresult SetAudioDRCMode(const int32_t handle, const int32_t drcMode) override; + Core::hresult GetAudioDRCMode(const int32_t handle, int32_t &drcMode) override; + + // Surround Virtualizer + Core::hresult SetAudioSurroundVirtualizer(const int32_t handle, const SurroundVirtualizer surroundVirtualizer) override; + Core::hresult GetAudioSurroundVirtualizer(const int32_t handle, SurroundVirtualizer &surroundVirtualizer) override; + + // MI Steering + Core::hresult SetAudioMISteering(const int32_t handle, const bool enable) override; + Core::hresult GetAudioMISteering(const int32_t handle, bool &enable) override; + + // Graphic Equalizer + Core::hresult SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) override; + Core::hresult GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) override; + + // MS12 Profile Management + Core::hresult GetAudioMS12ProfileList(const int32_t handle, IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const override; + Core::hresult GetAudioMS12Profile(const int32_t handle, string &profile) override; + Core::hresult SetAudioMS12Profile(const int32_t handle, const string& profile) override; + + // Audio Mixer Levels + Core::hresult SetAudioMixerLevels(const int32_t handle, const AudioInput audioInput, const int32_t volume) override; + + // MS12 Settings Override + Core::hresult SetAudioMS12SettingsOverride(const int32_t handle, const string& profileName, const string& profileSettingsName, const string& profileSettingValue, const AudioMS12ProfileState profileState) override; + + // Reset Functions + Core::hresult ResetAudioDialogEnhancement(const int32_t handle) override; + Core::hresult ResetAudioBassEnhancer(const int32_t handle) override; + Core::hresult ResetAudioSurroundVirtualizer(const int32_t handle) override; + Core::hresult ResetAudioVolumeLeveller(const int32_t handle) override; + + // HDMI ARC + Core::hresult GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) override; + + // IDeviceSettingsVideoPort interface implementation - delegate to _videoPortSettings interface + Core::hresult Register(Exchange::IDeviceSettingsVideoPort::INotification* notification) override; + Core::hresult Unregister(Exchange::IDeviceSettingsVideoPort::INotification* notification) override; + Core::hresult GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle) override; + Core::hresult IsVideoPortEnabled(const int32_t handle, bool &enabled) override; + + Core::hresult GetVideoPortResolutionConfig(VideoPortType videoPortType, + IVideoPortResolutionIterator*& videoPortResolutions) const override; + Core::hresult EnableVideoPort(const int32_t handle, const bool enabled) override; + Core::hresult IsVideoPortDisplayConnected(const int32_t handle, bool &connected) override; + Core::hresult IsVideoPortActive(const int32_t handle, bool &active) override; + Core::hresult GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution) override; + Core::hresult GetColorDepth(const int32_t handle, uint32_t &colorDepth) override; + Core::hresult GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace) override; + Core::hresult GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange) override; + Core::hresult GetHDCPStatusOnVideoPort(const int32_t handle, Exchange::IDeviceSettingsVideoPort::HDCPStatus &hdcpStatus) override; + Core::hresult GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) override; + Core::hresult GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) override; + Core::hresult GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) override; + + // Additional required VideoPort methods from WPE interface + Core::hresult IsVideoPortDisplaySurround(const int32_t handle, bool &surround) override; + Core::hresult GetVideoPortDisplaySurroundMode(const int32_t handle, Exchange::IDeviceSettingsVideoPort::VideoPortSurroundMode &surroundMode) override; + Core::hresult SetVideoPortResolution(const int32_t handle, const VideoPortResolution& videoPortResolution, const bool persist, const bool forceCompatibility) override; + Core::hresult EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t hdcpKey[], const uint16_t hdcpKeySize) override; + Core::hresult IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled) override; + Core::hresult GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities) override; + Core::hresult GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions) override; + Core::hresult SetForceDisable4K(const int32_t handle, const bool disable) override; + Core::hresult GetForceDisable4K(const int32_t handle, bool &disabled) override; + Core::hresult IsVideoPortOutputHDR(const int32_t handle, bool &isHDR) override; + Core::hresult ResetVideoPortOutputToSDR() override; + Core::hresult GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) override; + Core::hresult SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) override; + Core::hresult GetVideoEOTF(const int32_t handle, Exchange::IDeviceSettingsVideoPort::HDRStandard &hdrStandard) override; + Core::hresult GetMatrixCoefficients(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DisplayMatrixCoefficients &matrixCoefficients) override; + Core::hresult GetCurrentOutputSettings(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DSOutputSettings &outputSettings) override; + Core::hresult SetBackgroundColor(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::VideoBackgroundColor backgroundColor) override; + Core::hresult SetForceHDRMode(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::HDRStandard hdrMode) override; + Core::hresult GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities) override; + Core::hresult GetPreferredColorDepth(const int32_t handle, Exchange::IDeviceSettingsVideoPort::DisplayColorDepth &colorDepth, const bool persist) override; + Core::hresult SetPreferredColorDepth(const int32_t handle, const Exchange::IDeviceSettingsVideoPort::DisplayColorDepth colorDepth, const bool persist) override; + // Core::hresult IsContentProtected(const int32_t handle, bool &isContentProtected) override; // Method not in WPE interface + + //========================================================================= + // IDeviceSettingsVideoDevice interface methods + //========================================================================= + Core::hresult Register(Exchange::IDeviceSettingsVideoDevice::INotification* notification ) override; + Core::hresult Unregister(Exchange::IDeviceSettingsVideoDevice::INotification* notification ) override; + + Core::hresult GetVideoDeviceHandle(const int32_t index, int32_t &handle /* @out */) override; + Core::hresult SetVideoDeviceDFC(const int32_t handle , const Exchange::IDeviceSettingsVideoDevice::VideoZoom zoomSetting ) override; + Core::hresult GetVideoDeviceDFC(const int32_t handle , Exchange::IDeviceSettingsVideoDevice::VideoZoom &zoomSetting /* @out */) override; + Core::hresult GetHDRCapabilities(const int32_t handle , int32_t &capabilities /* @out */) override; + Core::hresult GetSupportedVideoCodingFormats(const int32_t handle , int32_t &supportedFormats /* @out */) override; + Core::hresult GetCodecInfo(const int32_t handle , const Exchange::IDeviceSettingsVideoDevice::VideoCodec videoCodec , Exchange::IDeviceSettingsVideoDevice::IDeviceSettingsVideoCodecProfileSupportIterator *&codecInfo /* @out */) override; + Core::hresult DisableHDR(const int32_t handle , const bool disable ) override; + Core::hresult SetFRFMode(const int32_t handle , const int32_t frfmode ) override; + Core::hresult GetFRFMode(const int32_t handle , int32_t &frfmode /* @out */) override; + Core::hresult GetCurrentDisplayFrameRate(const int32_t handle , string &framerate /* @out */) override; + Core::hresult SetDisplayFrameRate(const int32_t handle , const string& framerate ) override; + + //========================================================================= + // IDeviceSettingsHost interface methods + //========================================================================= + Core::hresult GetEDID(uint8_t edId[] /* @out @length:edIdLength @maxlength:edIdLength */, const uint16_t edIdLength ) override; + Core::hresult GetMS12ConfigType(string &ms12Config /* @out */) override; + + //========================================================================= + // IDeviceSettingsDisplay interface methods + //========================================================================= + Core::hresult Register(IDisplayNotification* notification ) override; + Core::hresult Unregister(IDisplayNotification* notification ) override; + Core::hresult Register(IDisplayHDMIHotPlugNotification* notification ) override; + Core::hresult Unregister(IDisplayHDMIHotPlugNotification* notification ) override; + + Core::hresult GetDisplayEdid(const int32_t handle, DisplayEDID &edId /* @out */, IDSVideoPortResolutionIterator*& supportedResolutionList /* @out */) override; + Core::hresult GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[] /* @out @length:edidLength @maxlength:edidLength */, const uint16_t edidLength) override; + Core::hresult GetDisplay(const DisplayPortType portType, const int32_t index, int32_t &handle /* @out */) override; + Core::hresult GetDisplayAspectRatio(const int32_t handle, Exchange::IDeviceSettingsDisplay::DisplayVideoAspectRatio &aspectRatio /* @out */) override; + Core::hresult SetAllmEnabled(const int32_t handle, const bool enabled) override; + Core::hresult SetAVIContentType(const int32_t handle, const DisplayAVIContentType contentType) override; + Core::hresult SetAVIScanInformation(const int32_t handle, const DisplayAVIScanInformation scanInfo) override; + + //========================================================================= + // IDeviceSettingsCompositeIn interface methods + //========================================================================= + Core::hresult Register(Exchange::IDeviceSettingsCompositeIn::INotification* notification ) override; + Core::hresult Unregister(Exchange::IDeviceSettingsCompositeIn::INotification* notification ) override; + + Core::hresult GetNrOfCompositeInputs(int32_t &nrCompositeInputs /* @out */) override; + Core::hresult GetCompositeInStatus(CompositeInStatus &status /* @out */) override; + Core::hresult SelectCompositeInPort(const CompositeInPort port ) override; + Core::hresult ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect ) override; + + private: + // DSController must be initialized first as it provides system infrastructure + DSController* _dsController; + + // Component implementation instances + DeviceSettingsFPDImpl* _fpdSettings; + DeviceSettingsHdmiInImp* _hdmiInSettings; + DeviceSettingsAudioImpl* _audioSettings; + DeviceSettingsVideoPortImpl* _videoPortSettings; + DeviceSettingsVideoDeviceImpl* _videoDeviceSettings; + DeviceSettingsHostImpl* _hostSettings; + DeviceSettingsDisplayImpl* _displaySettings; + DeviceSettingsCompositeInImpl* _compositeInSettings; + + // Interface pointers for future implementation (currently unused) + // Exchange::IDeviceSettingsCompositeIn* _compositeInSettings; + // Exchange::IDeviceSettingsDisplay* _displaySettings; + // Exchange::IDeviceSettingsHost* _hostSettings; + // Exchange::IDeviceSettingsVideoDevice* _videoDeviceSettings; + + uint32_t mConnectionId; + static DeviceSettingsImp* _instance; + + // Cached consolidated config — populated once on first GetDeviceSettingConfigs() call + Exchange::IDeviceSettings::DeviceSettingConfigs _cachedConfigs; + std::atomic _configLoaded{false}; + mutable std::mutex _configMutex; + }; +} // namespace Plugin +} // namespace WPEFramework diff --git a/plugin/DeviceSettingsTypes.h b/plugin/DeviceSettingsTypes.h new file mode 100644 index 0000000..c15a382 --- /dev/null +++ b/plugin/DeviceSettingsTypes.h @@ -0,0 +1,649 @@ +/** +* If not stated otherwise in this file or this component's LICENSE +* file the following copyright and licenses apply: +* +* Copyright 2024 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. +**/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// RDK profile search - inlined from UtilsSearchRDKProfile +#define RDK_PROFILE "RDK_PROFILE=" +#define PROFILE_TV "TV" +#define PROFILE_STB "STB" + +typedef enum profile { + NOT_FOUND = -1, + STB = 0, + TV, + MAX +} profile_t; + +extern profile_t profileType; + +inline profile_t searchRdkProfile(void) { + const char* devPropPath = "/etc/device.properties"; + char line[256], *rdkProfile = NULL; + profile_t ret = NOT_FOUND; + FILE* file; + + file = fopen(devPropPath, "r"); + if (file == NULL) { + printf("File not found issue \n"); + return NOT_FOUND; + } + + while (fgets(line, sizeof(line), file)) { + rdkProfile = strstr(line, RDK_PROFILE); + if (rdkProfile != NULL) { + rdkProfile += strlen(RDK_PROFILE); + printf("Found RDK_PROFILE: %s \n", rdkProfile); + break; + } + } + + if (rdkProfile != NULL) { + if (strncmp(rdkProfile, PROFILE_TV, strlen(PROFILE_TV)) == 0) { + ret = TV; + } else if (strncmp(rdkProfile, PROFILE_STB, strlen(PROFILE_STB)) == 0) { + ret = STB; + } + } else { + printf("Found RDK_PROFILE: NOT_FOUND \n"); + ret = NOT_FOUND; + } + fclose(file); + return ret; +} +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USE_LEGACY_INTERFACE + +#ifdef USE_LEGACY_INTERFACE +using DeviceSetting = WPEFramework::Exchange::IDeviceSettings; +using DeviceSettingsFPD = WPEFramework::Exchange::IDeviceSettingsFPD; +using DeviceSettingsHDMIIn = WPEFramework::Exchange::IDeviceSettingsHDMIIn; +using DeviceSettingsCompositeIn = WPEFramework::Exchange::IDeviceSettingsCompositeIn; +using DeviceSettingsAudio = WPEFramework::Exchange::IDeviceSettingsAudio; +using DeviceSettingsVideoDevice = WPEFramework::Exchange::IDeviceSettingsVideoDevice; +using DeviceSettingsDisplay = WPEFramework::Exchange::IDeviceSettingsDisplay; +using DeviceSettingsHost = WPEFramework::Exchange::IDeviceSettingsHost; +using DeviceSettingsVideoPort = WPEFramework::Exchange::IDeviceSettingsVideoPort; +#else +using DeviceSettingsManagerFPD = WPEFramework::Exchange::IDeviceSettingsManager::IFPD; +using DeviceSettingsManagerHDMIIn = WPEFramework::Exchange::IDeviceSettingsManager::IHDMIIn; +using DeviceSettingsManagerCompositeIn = WPEFramework::Exchange::IDeviceSettingsManager::ICompositeIn; +using DeviceSettingsManagerAudio = WPEFramework::Exchange::IDeviceSettingsManager::IAudio; +using DeviceSettingsManagerVideoDevice = WPEFramework::Exchange::IDeviceSettingsManager::IVideoDevice; +using DeviceSettingsManagerDisplay = WPEFramework::Exchange::IDeviceSettingsManager::IDisplay; +using DeviceSettingsManagerHost = WPEFramework::Exchange::IDeviceSettingsManager::IHost; +using DeviceSettingsManagerVideoPort = WPEFramework::Exchange::IDeviceSettingsManager::IVideoPort; +#endif + +// HDMI In type aliases for convenience +using HDMIInPort = DeviceSettingsHDMIIn::HDMIInPort; +using HDMIInSignalStatus = DeviceSettingsHDMIIn::HDMIInSignalStatus; +using HDMIVideoPortResolution = DeviceSettingsHDMIIn::HDMIVideoPortResolution; +using HDMIInAviContentType = DeviceSettingsHDMIIn::HDMIInAviContentType; +using HDMIInVRRType = DeviceSettingsHDMIIn::HDMIInVRRType; +using HDMIInStatus = DeviceSettingsHDMIIn::HDMIInStatus; +using HDMIVideoPlaneType = DeviceSettingsHDMIIn::HDMIVideoPlaneType; +using HDMIInVRRStatus = DeviceSettingsHDMIIn::HDMIInVRRStatus; +using HDMIInCapabilityVersion = DeviceSettingsHDMIIn::HDMIInCapabilityVersion; +using HDMIInEdidVersion = DeviceSettingsHDMIIn::HDMIInEdidVersion; +using HDMIInVideoZoom = DeviceSettingsHDMIIn::HDMIInVideoZoom; +using HDMIInVideoRectangle = DeviceSettingsHDMIIn::HDMIInVideoRectangle; +using HDMIVideoAspectRatio = DeviceSettingsHDMIIn::HDMIVideoAspectRatio; +using HDMIInVideoStereoScopicMode = DeviceSettingsHDMIIn::HDMIInVideoStereoScopicMode; +using HDMIInVideoFrameRate = DeviceSettingsHDMIIn::HDMIInVideoFrameRate; +using HDMIInVideoResolution = DeviceSettingsHDMIIn::HDMIInVideoResolution; +using HDMIInTVResolution = DeviceSettingsHDMIIn::HDMIInTVResolution; +using IHDMIInPortConnectionStatusIterator = DeviceSettingsHDMIIn::IHDMIInPortConnectionStatusIterator; +using IHDMIInGameFeatureListIterator = DeviceSettingsHDMIIn::IHDMIInGameFeatureListIterator; +//using GameFeatureListIteratorImpl = WPEFramework::Core::Service>; + +// FPD type aliases for convenience +using FPDTimeFormat = DeviceSettingsFPD::FPDTimeFormat; +using FPDIndicator = DeviceSettingsFPD::FPDIndicator; +using FPDState = DeviceSettingsFPD::FPDState; +using FPDTextDisplay = DeviceSettingsFPD::FPDTextDisplay; +using FPDMode = DeviceSettingsFPD::FPDMode; +using FPDLEDState = DeviceSettingsFPD::FPDLEDState; +using FPDColorConfig = DeviceSetting::FPDColorConfig; +using FPDIndicatorConfig = DeviceSetting::FPDIndicatorConfig; +using FPDColorBinding = DeviceSetting::FPDColorBinding; +using FPDTextDisplayConfig = DeviceSetting::FPDTextDisplayConfig; + +// Audio type aliases for convenience +using AudioPortType = DeviceSettingsAudio::AudioPortType; +using AudioPortState = DeviceSettingsAudio::AudioPortState; +using AudioFormat = DeviceSettingsAudio::AudioFormat; +using AudioEncoding = DeviceSettingsAudio::AudioEncoding; +using AudioStereoMode = DeviceSettingsAudio::StereoMode; +using AudioDuckingType = DeviceSettingsAudio::AudioDuckingType; +using AudioDuckingAction = DeviceSettingsAudio::AudioDuckingAction; +using DolbyAtmosCapability = DeviceSettingsAudio::DolbyAtmosCapability; +using AudioCompression = DeviceSettingsAudio::AudioCompression; +using AudioCapabilities = DeviceSettingsAudio::AudioCapabilities; +using AudioARCType = DeviceSettingsAudio::AudioARCType; +using AudioInput = DeviceSettingsAudio::AudioInput; +using MS12Capabilities = DeviceSettingsAudio::MS12Capabilities; +using MS12AudioProfile = DeviceSettingsAudio::MS12AudioProfile; +using VolumeLeveller = DeviceSettingsAudio::VolumeLeveller; +using SurroundVirtualizer = DeviceSettingsAudio::SurroundVirtualizer; +using SurroundMode = DeviceSettingsAudio::SurroundMode; +using MS12Feature = DeviceSettingsAudio::MS12Feature; +using AudioMS12ProfileState = DeviceSettingsAudio::MS12ProfileState; +using AudioARCStatus = DeviceSettingsAudio::AudioARCStatus; +using AudioTypeConfigInfo = DeviceSetting::AudioTypeConfigInfo; +using AudioPortConfigInfo = DeviceSettingsAudio::AudioPortConfigInfo; +using IDeviceSettingsAudioEncodingIterator = DeviceSettingsAudio::IDeviceSettingsAudioEncodingIterator; +using IDeviceSettingsAudioCompressionIterator = DeviceSettingsAudio::IDeviceSettingsAudioCompressionIterator; +using IDeviceSettingsStereoModeIterator = DeviceSettingsAudio::IDeviceSettingsStereoModeIterator; +using IDeviceSettingsAudioMS12AudioProfileIterator = DeviceSettingsAudio::IDeviceSettingsAudioMS12AudioProfileIterator; + +// VideoPort type aliases for convenience +using VideoPortType = DeviceSettingsVideoPort::VideoPort; +using VideoPortResolution = DeviceSettingsVideoPort::VideoPortResolution; +using VideoResolution = DeviceSettingsVideoPort::VideoResolution; +using VideoAspectRatio = DeviceSettingsVideoPort::VideoAspectRatio; +using VideoStereoScopicMode = DeviceSettingsVideoPort::VideoStereoScopicMode; +using VideoFrameRate = DeviceSettingsVideoPort::VideoFrameRate; +using VideoPortColorSpace = DeviceSettingsVideoPort::DisplayColorSpace; +using VideoPortQuantizationRange = DeviceSettingsVideoPort::DisplayQuantizationRange; +using VideoPortHdcpStatus = DeviceSettingsVideoPort::HDCPStatus; +using VideoPortHdcpProtocolVersion = DeviceSettingsVideoPort::HDCPProtocolVersion; +using HDRStandard = DeviceSettingsVideoPort::HDRStandard; +using ResolutionChange = DeviceSettingsVideoPort::ResolutionChange; +using DisplayMatrixCoefficients = DeviceSettingsVideoPort::DisplayMatrixCoefficients; +using DSOutputSettings = DeviceSettingsVideoPort::DSOutputSettings; +using VideoBackgroundColor = DeviceSettingsVideoPort::VideoBackgroundColor; +using DisplayColorDepth = DeviceSettingsVideoPort::DisplayColorDepth; +using TVResolution = DeviceSettingsVideoPort::TVResolution; +using VideoPortSurroundMode = DeviceSettingsVideoPort::VideoPortSurroundMode; +using VideoScanMode = DeviceSettingsVideoPort::VideoScanMode; +using VideoPortTypeConfig = DeviceSettingsVideoPort::VideoPortTypeConfig; +using VideoPortPortConfig = DeviceSettingsVideoPort::VideoPortPortConfig; +using IVideoPortResolutionIterator = DeviceSettingsVideoPort::IVideoPortResolutionIterator; + +// Display type aliases for convenience +using DisplayEvent = DeviceSettingsDisplay::DisplayEvent; +using DisplayTVResolution = DeviceSettingsDisplay::DisplayTVResolution; +using DisplayVideoAspectRatio = DeviceSettingsDisplay::DisplayVideoAspectRatio; +using DisplayInVideoStereoScopicMode = DeviceSettingsDisplay::DisplayInVideoStereoScopicMode; +using DisplayInVideoFrameRate = DeviceSettingsDisplay::DisplayInVideoFrameRate; +using DisplayPortType = DeviceSettingsDisplay::DisplayPortType; +using DisplayAVIContentType = DeviceSettingsDisplay::DisplayAVIContentType; +using DisplayAVIScanInformation = DeviceSettingsDisplay::DisplayAVIScanInformation; +using DisplayVideoPortResolution = DeviceSettingsDisplay::DisplayVideoPortResolution; +using DisplayEDID = DeviceSettingsDisplay::DisplayEDID; +using IDSVideoPortResolutionIterator = DeviceSettingsDisplay::IDSVideoPortResolutionIterator; +using IDisplayNotification = DeviceSettingsDisplay::INotification; +using IDisplayHDMIHotPlugNotification = DeviceSettingsDisplay::IDisplayHDMIHotPlugNotification; + +// CompositeIn type aliases for convenience +using CompositeInPort = DeviceSettingsCompositeIn::CompositeInPort; +using CompositeInSignalStatus = DeviceSettingsCompositeIn::CompositeInSignalStatus; +using CompositeInStatus = DeviceSettingsCompositeIn::CompositeInStatus; +using CompositeInVideoRectangle = DeviceSettingsCompositeIn::VideoRectangle; + +// VideoDevice type aliases for convenience +using VideoDeviceZoom = DeviceSettingsVideoDevice::VideoZoom; +using VideoDeviceCodec = DeviceSettingsVideoDevice::VideoCodec; +using VideoDeviceCodecHEVCProfile = DeviceSettingsVideoDevice::VideoCodecHEVCProfile; +using VideoDeviceCodecProfileSupport = DeviceSettingsVideoDevice::VideoCodecProfileSupport; +using VideoDeviceConfigInfo = DeviceSettingsVideoDevice::VideoDeviceConfigInfo; +using IDeviceSettingsVideoCodecProfileSupportIterator = DeviceSettingsVideoDevice::IDeviceSettingsVideoCodecProfileSupportIterator; + +// Legacy DSMGR/RPC compatibility definitions used by DSController and DSPwrEventListener. +#ifndef DSMGR_MAX_VIDEO_PORT_NAME_LENGTH +#define DSMGR_MAX_VIDEO_PORT_NAME_LENGTH 16 +#endif + +#ifndef PWRMGR_MAX_REBOOT_REASON_LENGTH +#define PWRMGR_MAX_REBOOT_REASON_LENGTH 100 +#endif + +#ifndef IARM_BUS_DSMGR_NAME +#define IARM_BUS_DSMGR_NAME "DSMgr_Plugin" +#endif + +typedef enum _DSMgr_EventId_t { + IARM_BUS_DSMGR_EVENT_RES_PRECHANGE = 0, + IARM_BUS_DSMGR_EVENT_RES_POSTCHANGE, + IARM_BUS_DSMGR_EVENT_ZOOM_SETTINGS, + IARM_BUS_DSMGR_EVENT_HDMI_HOTPLUG, + IARM_BUS_DSMGR_EVENT_AUDIO_MODE, + IARM_BUS_DSMGR_EVENT_HDCP_STATUS, + IARM_BUS_DSMGR_EVENT_RX_SENSE, + IARM_BUS_DSMGR_EVENT_HDMI_IN_HOTPLUG, + IARM_BUS_DSMGR_EVENT_HDMI_IN_SIGNAL_STATUS, + IARM_BUS_DSMGR_EVENT_HDMI_IN_STATUS, + IARM_BUS_DSMGR_EVENT_HDMI_IN_VIDEO_MODE_UPDATE, + IARM_BUS_DSMGR_EVENT_HDMI_IN_ALLM_STATUS, + IARM_BUS_DSMGR_EVENT_HDMI_IN_VRR_STATUS, + IARM_BUS_DSMGR_EVENT_COMPOSITE_IN_HOTPLUG, + IARM_BUS_DSMGR_EVENT_COMPOSITE_IN_SIGNAL_STATUS, + IARM_BUS_DSMGR_EVENT_COMPOSITE_IN_STATUS, + IARM_BUS_DSMGR_EVENT_COMPOSITE_IN_VIDEO_MODE_UPDATE, + IARM_BUS_DSMGR_EVENT_TIME_FORMAT_CHANGE, + IARM_BUS_DSMGR_EVENT_AUDIO_LEVEL_CHANGED, + IARM_BUS_DSMGR_EVENT_AUDIO_OUT_HOTPLUG, + IARM_BUS_DSMGR_EVENT_AUDIO_FORMAT_UPDATE, + IARM_BUS_DSMGR_EVENT_AUDIO_PRIMARY_LANGUAGE_CHANGED, + IARM_BUS_DSMGR_EVENT_AUDIO_SECONDARY_LANGUAGE_CHANGED, + IARM_BUS_DSMGR_EVENT_AUDIO_FADER_CONTROL_CHANGED, + IARM_BUS_DSMGR_EVENT_AUDIO_ASSOCIATED_AUDIO_MIXING_CHANGED, + IARM_BUS_DSMGR_EVENT_VIDEO_FORMAT_UPDATE, + IARM_BUS_DSMGR_EVENT_DISPLAY_FRAMRATE_PRECHANGE, + IARM_BUS_DSMGR_EVENT_DISPLAY_FRAMRATE_POSTCHANGE, + IARM_BUS_DSMGR_EVENT_AUDIO_PORT_STATE, + IARM_BUS_DSMGR_EVENT_SLEEP_MODE_CHANGED, + IARM_BUS_DSMGR_EVENT_HDMI_IN_AVI_CONTENT_TYPE, + IARM_BUS_DSMGR_EVENT_HDMI_IN_AV_LATENCY, + IARM_BUS_DSMGR_EVENT_ATMOS_CAPS_CHANGED, + IARM_BUS_DSMGR_EVENT_MAX, +} IARM_Bus_DSMgr_EventId_t; + +typedef struct _DSMgr_EventData_t { + union { + struct { + int event; + } hdmi_hpd; + struct { + int hdcpStatus; + } hdmi_hdcp; + } data; +} IARM_Bus_DSMgr_EventData_t; + +typedef struct _dsMgrStandbyVideoStateParam_t { + char port[DSMGR_MAX_VIDEO_PORT_NAME_LENGTH]; + int isEnabled; + int result; +} dsMgrStandbyVideoStateParam_t; + +typedef struct _dsMgrRebootConfigParam_t { + char reboot_reason_custom[PWRMGR_MAX_REBOOT_REASON_LENGTH]; + int powerState; + int result; +} dsMgrRebootConfigParam_t; + +typedef struct _dsMgrAVPortStateParam_t { + int avPortPowerState; + int result; +} dsMgrAVPortStateParam_t; + +typedef struct _dsMgrLEDStatusParam_t { + int ledState; + int result; +} dsMgrLEDStatusParam_t; + +typedef struct _dsEdidIgnoreParam_t { + intptr_t handle; + bool ignoreEDID; +} dsEdidIgnoreParam_t; + +// Plugin-wide exception logging helpers. +// Use these instead of catching device::Exception from lib32-devicesettings. +namespace WPEFramework { +namespace Plugin { +namespace DeviceSettingsExceptionHelper { + inline void LogException(const char* context, const std::exception& e) + { + LOGERR("%s: %s", context, e.what()); + } + + inline void LogUnknownException(const char* context) + { + LOGERR("%s: unknown exception", context); + } +} // namespace DeviceSettingsExceptionHelper +} // namespace Plugin +} // namespace WPEFramework + +// Common constants +#define API_VERSION_MAJOR 1 +#define API_VERSION_MINOR 0 +#define API_VERSION_PATCH 0 + +#define TVSETTINGS_DALS_RFC_PARAM "Device.DeviceInfo.X_RDKCENTRAL-COM_RFC.Feature.TvSettings.DynamicAutoLatency" +#define RDK_DSHAL_NAME "libds-hal.so" + +#ifdef DEBUG_LOGGING +#define ENTRY_LOG do { LOGINFO("%d: Enter %s", __LINE__, __func__); } while(0); +#define EXIT_LOG do { LOGINFO("%d: Exit %s", __LINE__, __func__); } while(0); +#else +#define ENTRY_LOG do { } while(0) +#define EXIT_LOG do { } while(0) +#endif + +#ifdef DEBUG_LOGGING +#define DEBUG_LOG(fmt, ...) LOGINFO(fmt, ##__VA_ARGS__) +#else +#define DEBUG_LOG(fmt, ...) do { } while(0) +#endif + +namespace WPEFramework { +namespace Plugin { + namespace DeviceSettingsHALLoader { + extern void* gLibraryHandle; + extern std::mutex gLibraryLock; + + void* ResolveSymbol(const std::string& libName, const std::string& symbolName); + void ReleaseAllLibraries(); + } +} +} + +// Exact replica of original HostPersistence implementation to avoid DS_LIBRARIES dependency +namespace device { + class HostPersistence { + private: + std::map _properties; + std::map _defaultProperties; + std::string filePath; + std::string defaultFilePath; + bool _isInitialized = false; + + void ensureInitialized() { + if (!_isInitialized) { + load(); + _isInitialized = true; + } + } + + void loadFromFile(const std::string &fileName, std::map &map) { + char keyValue[1024] = ""; + char key[1024] = ""; + FILE *filePtr = NULL; + + filePtr = fopen(fileName.c_str(), "r"); + if (filePtr != NULL) { + while (fscanf(filePtr, "%1023s\t%1023s", key, keyValue) == 2) { + map.insert({key, keyValue}); + } + fclose(filePtr); + } else { + // File doesn't exist - this is okay for initial startup + } + } + + void writeToFile(const std::string &fileName) { + unlink(fileName.c_str()); + + if (_properties.size() > 0) { + /* + * Replacing the ofstream to fwrite + * Because the ofstream.close or ofstream.flush or ofstream.rdbuf->sync + * does not sync the data onto disk. + * TBD - This need to be changed to C++ APIs in future. + */ + + FILE *file = fopen(fileName.c_str(), "w"); + if (file != NULL) { + for (auto it = _properties.begin(); it != _properties.end(); ++it) { + std::string dataToWrite = it->first + "\t" + it->second + "\n"; + unsigned int size = dataToWrite.length(); + size_t written = fwrite(dataToWrite.c_str(), 1, size, file); + if (written != size) { + LOGERR("HostPersistence write failed for key %s", it->first.c_str()); + break; + } + } + + fflush(file); // Flush buffers to FS + fsync(fileno(file)); // Flush file to HDD + fclose(file); + } + } + } + + public: + HostPersistence() { + /* + * TBD This need to be removed and + * Persistent path shall be set from startup script + * To do this Host Persistent need to be part of DS Manager + * TBD + */ + + #if defined(HAS_HDD_PERSISTENT) + /*Product having HDD Persistent*/ + filePath = "/tmp/mnt/diska3/persistent/ds/hostData"; + #elif defined(HAS_FLASH_PERSISTENT) + /*Product having Flash Persistent*/ + filePath = "/opt/persistent/ds/hostData"; + #else + /*Product having Flash Persistent*/ + filePath = "/opt/persistent/ds/hostData"; + #endif + defaultFilePath = "/etc/hostDataDefault"; + // _isInitialized remains false — load() will be called lazily on first access + } + + HostPersistence(const std::string &storeFileName) { + filePath = storeFileName; + defaultFilePath = "/etc/hostDataDefault"; + // _isInitialized remains false — load() will be called lazily on first access + } + + virtual ~HostPersistence() { + // Auto-generated destructor stub + } + + static HostPersistence& getInstance() { + static HostPersistence instance; + return instance; + } + + void load() { + LOGINFO("HostPersistence::load: loading user data from '%s'", filePath.c_str()); + LOGINFO("HostPersistence::load: loading default data from '%s'", defaultFilePath.c_str()); + try { + loadFromFile(filePath, _properties); + LOGINFO("HostPersistence::load: loaded %zu user properties from '%s'", _properties.size(), filePath.c_str()); + } catch (...) { + // Backup file is corrupt or not available + LOGWARN("HostPersistence::load: '%s' not available, trying backup '%stmpDB'", filePath.c_str(), filePath.c_str()); + try { + loadFromFile(filePath + "tmpDB", _properties); + LOGINFO("HostPersistence::load: loaded %zu user properties from backup '%stmpDB'", _properties.size(), filePath.c_str()); + } catch (...) { + LOGWARN("HostPersistence::load: backup also not available, starting with empty user properties"); + /* Remove all properties, and start with default values */ + } + } + + try { + loadFromFile(defaultFilePath, _defaultProperties); + LOGINFO("HostPersistence::load: loaded %zu default properties from '%s'", _defaultProperties.size(), defaultFilePath.c_str()); + } catch (...) { + LOGWARN("HostPersistence::load: '%s' not available, default properties will be empty", defaultFilePath.c_str()); + // System file is corrupt or not available + } + } + + std::string getProperty(const std::string &key) { + /* Ensure data is loaded before accessing properties */ + ensureInitialized(); + + /* Check the validness of the key */ + if (key.empty()) { + throw std::invalid_argument("The KEY is empty"); + } + + LOGINFO("HostPersistence::getProperty: key='%s' from '%s'", key.c_str(), filePath.c_str()); + std::map::const_iterator eFound = _properties.find(key); + if (eFound == _properties.end()) { + LOGWARN("HostPersistence::getProperty: key='%s' NOT FOUND in '%s'", key.c_str(), filePath.c_str()); + throw std::invalid_argument("The Item IS NOT FOUND"); + } else { + LOGINFO("HostPersistence::getProperty: key='%s' value='%s' (from '%s')", key.c_str(), eFound->second.c_str(), filePath.c_str()); + return eFound->second; + } + } + + std::string getProperty(const std::string &key, const std::string &defValue) { + /* Ensure data is loaded before accessing properties */ + ensureInitialized(); + + /* Check the validness of the key */ + if (key.empty()) { + throw std::invalid_argument("The KEY is empty"); + } + + LOGINFO("HostPersistence::getProperty(defVal): key='%s' from '%s'", key.c_str(), filePath.c_str()); + std::map::const_iterator eFound = _properties.find(key); + if (eFound == _properties.end()) { + LOGINFO("HostPersistence::getProperty(defVal): key='%s' NOT FOUND, returning default='%s'", key.c_str(), defValue.c_str()); + return defValue; + } else { + LOGINFO("HostPersistence::getProperty(defVal): key='%s' value='%s' (from '%s')", key.c_str(), eFound->second.c_str(), filePath.c_str()); + return eFound->second; + } + } + + std::string getDefaultProperty(const std::string &key) { + /* Ensure data is loaded before accessing properties */ + ensureInitialized(); + + /* Check the validness of the key */ + if (key.empty()) { + throw std::invalid_argument("The KEY is empty"); + } + + LOGINFO("HostPersistence::getDefaultProperty: key='%s' from '%s'", key.c_str(), defaultFilePath.c_str()); + std::map::const_iterator eFound = _defaultProperties.find(key); + if (eFound == _defaultProperties.end()) { + LOGWARN("HostPersistence::getDefaultProperty: key='%s' NOT FOUND in '%s'", key.c_str(), defaultFilePath.c_str()); + throw std::invalid_argument("The Item IS NOT FOUND"); + } else { + LOGINFO("HostPersistence::getDefaultProperty: key='%s' value='%s' (from '%s')", key.c_str(), eFound->second.c_str(), defaultFilePath.c_str()); + return eFound->second; + } + } + + void persistHostProperty(const std::string &key, const std::string &value) { + /* Ensure data is loaded before accessing properties */ + ensureInitialized(); + + if (key.empty() || value.empty()) { + throw std::invalid_argument("Given KEY or VALUE is empty"); + } + + LOGINFO("HostPersistence::persistHostProperty: key='%s' value='%s' to '%s'", key.c_str(), value.c_str(), filePath.c_str()); + + try { + std::string eRet = getProperty(key); + + if (eRet.compare(value) == 0) { + /* Same value. No need to do anything */ + LOGINFO("HostPersistence::persistHostProperty: key='%s' value unchanged, skip write", key.c_str()); + return; + } + + /* Save a current copy before modifying */ + writeToFile(filePath + "tmpDB"); + + /* First of all check whether the entry is already present in the hashtable */ + _properties.erase(key); + + } catch (const std::invalid_argument &e) { + // Entry Not found + } catch (...) { + // Other exceptions + } + + _properties.insert({key, value}); + writeToFile(filePath); + LOGINFO("HostPersistence::persistHostProperty: key='%s' value='%s' written to '%s'", key.c_str(), value.c_str(), filePath.c_str()); + } + }; +} + +struct CallbackBundle { + // HDMIIn callbacks + std::function OnHDMIInHotPlugEvent; + std::function OnHDMIInSignalStatusEvent; + std::function OnHDMIInStatusEvent; + std::function OnHDMIInVideoModeUpdateEvent; + std::function OnHDMIInAllmStatusEvent; + std::function OnHDMIInAVIContentTypeEvent; + std::function OnHDMIInAVLatencyEvent; + std::function OnHDMIInVRRStatusEvent; + + // VideoPort callbacks + std::function OnResolutionPreChange; + std::function OnResolutionPostChange; + std::function OnHDCPStatusChange; + std::function OnVideoFormatUpdate; + + // Display event callbacks (for HAL implementations) + std::function OnDisplayRxSense; + std::function OnDisplayHDCPStatus; + std::function OnDisplayHDMIHotPlug; + + // CompositeIn callbacks + std::function OnCompositeInHotPlug; + std::function OnCompositeInSignalStatus; + std::function OnCompositeInStatus; + std::function OnCompositeInVideoModeUpdate; + + // CompositeIn event callbacks (for HAL implementations) + std::function CompositeInHotPlugEventCallback; + std::function CompositeInSignalStatusEventCallback; + std::function CompositeInStatusEventCallback; + std::function CompositeInVideoModeUpdateEventCallback; + + // VideoDevice callbacks + std::function OnZoomSettingsChanged; + std::function OnDisplayFrameratePreChange; + std::function OnDisplayFrameratePostChange; + + // Audio callbacks + std::function OnAudioOutHotPlug; + std::function OnAudioFormatUpdate; + std::function OnDolbyAtmosCapabilitiesChanged; + std::function OnAssociatedAudioMixingChanged; + std::function OnAudioFaderControlChanged; + std::function OnAudioPrimaryLanguageChanged; + std::function OnAudioSecondaryLanguageChanged; + std::function OnAudioPortStateChanged; + std::function OnAudioLevelChanged; + std::function OnAudioModeChanged; + // Add other callbacks as needed +}; diff --git a/plugin/DeviceSettingsVideoDeviceImplementation.cpp b/plugin/DeviceSettingsVideoDeviceImplementation.cpp new file mode 100644 index 0000000..f9d3e91 --- /dev/null +++ b/plugin/DeviceSettingsVideoDeviceImplementation.cpp @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#include "DeviceSettingsVideoDeviceImplementation.h" + +#include +#include + +#include "DeviceSettingsHALConfig.h" + +using namespace std; + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsVideoDeviceImpl::DeviceSettingsVideoDeviceImpl() : + _VideoDeviceNotifications(), + _apiLock(), + _callbackLock(), + _videoDevice(VideoDevice::Create(*this)) + { + LOGINFO("DeviceSettingsVideoDeviceImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsVideoDeviceImpl::~DeviceSettingsVideoDeviceImpl() { + LOGINFO("DeviceSettingsVideoDeviceImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsVideoDeviceImpl::dispatchVideoDeviceEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _VideoDeviceNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IVideoDevice event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsVideoDeviceImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsVideoDeviceImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsVideoDeviceImpl::Register(Exchange::IDeviceSettingsVideoDevice::INotification* notification) + { + Core::hresult errorCode = Register(_VideoDeviceNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IVideoDevice %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IVideoDevice %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsVideoDeviceImpl::Unregister(Exchange::IDeviceSettingsVideoDevice::INotification* notification) + { + Core::hresult errorCode = Unregister(_VideoDeviceNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IVideoDevice %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IVideoDevice %p unregistered successfully", notification); + } + return errorCode; + } + + // VideoDevice::INotification interface implementations (called by DS HAL) + void DeviceSettingsVideoDeviceImpl::OnZoomSettingsChanged(const VideoDeviceZoom zoomSetting) + { + LOGINFO("DS HAL OnZoomSettingsChanged event: zoomSetting=%d", static_cast(zoomSetting)); + dispatchVideoDeviceEvent(&Exchange::IDeviceSettingsVideoDevice::INotification::OnZoomSettingsChanged, zoomSetting); + } + + void DeviceSettingsVideoDeviceImpl::OnDisplayFrameratePreChange(const string frameRate) + { + LOGINFO("DS HAL OnDisplayFrameratePreChange event: frameRate=%s", frameRate.c_str()); + dispatchVideoDeviceEvent(&Exchange::IDeviceSettingsVideoDevice::INotification::OnDisplayFrameratePreChange, frameRate); + } + + void DeviceSettingsVideoDeviceImpl::OnDisplayFrameratePostChange(const string frameRate) + { + LOGINFO("DS HAL OnDisplayFrameratePostChange event: frameRate=%s", frameRate.c_str()); + dispatchVideoDeviceEvent(&Exchange::IDeviceSettingsVideoDevice::INotification::OnDisplayFrameratePostChange, frameRate); + } + + // VideoDevice interface method implementations called by DeviceSettingsImp + uint32_t DeviceSettingsVideoDeviceImpl::GetVideoDeviceHandle(const int32_t index, int32_t &handle) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetVideoDeviceHandle(index, handle); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoDeviceHandle succeeded: index=%d, handle=%d", index, handle); + } else { + LOGERR("GetVideoDeviceHandle failed: index=%d, error=%u", index, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.SetVideoDeviceDFC(handle, zoomSetting); + if (result == Core::ERROR_NONE) { + LOGINFO("SetVideoDeviceDFC succeeded for handle: %d, zoomSetting: %d", handle, static_cast(zoomSetting)); + } else { + LOGERR("SetVideoDeviceDFC failed for handle: %d, zoomSetting: %d, error: %u", handle, static_cast(zoomSetting), result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom &zoomSetting) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetVideoDeviceDFC(handle, zoomSetting); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoDeviceDFC succeeded for handle: %d, zoomSetting: %d", handle, static_cast(zoomSetting)); + } else { + LOGERR("GetVideoDeviceDFC failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetHDRCapabilities(const int32_t handle, int32_t &capabilities) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetHDRCapabilities(handle, capabilities); + if (result == Core::ERROR_NONE) { + LOGINFO("GetHDRCapabilities succeeded for handle: %d, capabilities: 0x%x", handle, capabilities); + } else { + LOGERR("GetHDRCapabilities failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetSupportedVideoCodingFormats(const int32_t handle, int32_t &supportedFormats) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetSupportedVideoCodingFormats(handle, supportedFormats); + if (result == Core::ERROR_NONE) { + LOGINFO("GetSupportedVideoCodingFormats succeeded for handle: %d, supportedFormats: 0x%x", handle, supportedFormats); + } else { + LOGERR("GetSupportedVideoCodingFormats failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetCodecInfo(handle, videoCodec, codecInfo); + if (result == Core::ERROR_NONE) { + LOGINFO("GetCodecInfo succeeded for handle: %d, videoCodec: %d", handle, static_cast(videoCodec)); + } else { + LOGERR("GetCodecInfo failed for handle: %d, videoCodec: %d, error: %u", handle, static_cast(videoCodec), result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::DisableHDR(const int32_t handle, const bool disable) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.DisableHDR(handle, disable); + if (result == Core::ERROR_NONE) { + LOGINFO("DisableHDR succeeded for handle: %d, disable: %s", handle, disable ? "true" : "false"); + } else { + LOGERR("DisableHDR failed for handle: %d, disable: %s, error: %u", handle, disable ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::SetFRFMode(const int32_t handle, const int32_t frfmode) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.SetFRFMode(handle, frfmode); + if (result == Core::ERROR_NONE) { + LOGINFO("SetFRFMode succeeded for handle: %d, frfmode: %d", handle, frfmode); + } else { + LOGERR("SetFRFMode failed for handle: %d, frfmode: %d, error: %u", handle, frfmode, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetFRFMode(const int32_t handle, int32_t &frfmode) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetFRFMode(handle, frfmode); + if (result == Core::ERROR_NONE) { + LOGINFO("GetFRFMode succeeded for handle: %d, frfmode: %d", handle, frfmode); + } else { + LOGERR("GetFRFMode failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::GetCurrentDisplayFrameRate(const int32_t handle, string &framerate) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.GetCurrentDisplayFrameRate(handle, framerate); + if (result == Core::ERROR_NONE) { + LOGINFO("GetCurrentDisplayFrameRate succeeded for handle: %d, framerate: %s", handle, framerate.c_str()); + } else { + LOGERR("GetCurrentDisplayFrameRate failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoDeviceImpl::SetDisplayFrameRate(const int32_t handle, const string framerate) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoDevice.SetDisplayFrameRate(handle, framerate); + if (result == Core::ERROR_NONE) { + LOGINFO("SetDisplayFrameRate succeeded for handle: %d, framerate: %s", handle, framerate.c_str()); + } else { + LOGERR("SetDisplayFrameRate failed for handle: %d, framerate: %s, error: %u", handle, framerate.c_str(), result); + } + return result; + } + + void DeviceSettingsVideoDeviceImpl::getCachedConfigs( + std::vector& videoConfigs) const + { + _apiLock.Lock(); + + videoConfigs.reserve(_cachedVideoDeviceConfigs.size()); + for (const auto& src : _cachedVideoDeviceConfigs) { + videoConfigs.push_back({src.numSupportedDFCs, src.supportedDFCsMask, + static_cast(src.defaultDFC)}); + } + + _apiLock.Unlock(); + } + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsVideoDeviceImplementation.h b/plugin/DeviceSettingsVideoDeviceImplementation.h new file mode 100644 index 0000000..3c59c3d --- /dev/null +++ b/plugin/DeviceSettingsVideoDeviceImplementation.h @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "VideoDevice.h" +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsVideoDeviceImpl : public VideoDevice::INotification + { + public: + + DeviceSettingsVideoDeviceImpl(); + ~DeviceSettingsVideoDeviceImpl() override; + + static DeviceSettingsVideoDeviceImpl* Create() + { + return new DeviceSettingsVideoDeviceImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsVideoDeviceImpl(const DeviceSettingsVideoDeviceImpl&) = delete; + DeviceSettingsVideoDeviceImpl& operator=(const DeviceSettingsVideoDeviceImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + + // Template method for dispatching VideoDevice Events + template + void dispatchVideoDeviceEvent(Func notifyFunc, Args&&... args); + + // Template methods for notification management + template + Core::hresult Register(std::list& list, T* notification); + + template + Core::hresult Unregister(std::list& list, const T* notification); + + // Public notification registration methods called by DeviceSettingsImp + Core::hresult Register(Exchange::IDeviceSettingsVideoDevice::INotification* notification); + Core::hresult Unregister(Exchange::IDeviceSettingsVideoDevice::INotification* notification); + + // Required VideoDevice::INotification interface implementations + void OnZoomSettingsChanged(const VideoDeviceZoom zoomSetting) override; + void OnDisplayFrameratePreChange(const string frameRate) override; + void OnDisplayFrameratePostChange(const string frameRate) override; + + // VideoDevice interface method implementations called by DeviceSettingsImp + uint32_t GetVideoDeviceHandle(const int32_t index, int32_t &handle); + uint32_t SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting); + uint32_t GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom &zoomSetting); + uint32_t GetHDRCapabilities(const int32_t handle, int32_t &capabilities); + uint32_t GetSupportedVideoCodingFormats(const int32_t handle, int32_t &supportedFormats); + uint32_t GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo); + uint32_t DisableHDR(const int32_t handle, const bool disable); + uint32_t SetFRFMode(const int32_t handle, const int32_t frfmode); + uint32_t GetFRFMode(const int32_t handle, int32_t &frfmode); + uint32_t GetCurrentDisplayFrameRate(const int32_t handle, string &framerate); + uint32_t SetDisplayFrameRate(const int32_t handle, const string framerate); + + // Fills IDeviceSettings consolidated config vectors from cached data + void getCachedConfigs(std::vector& videoConfigs) const; + + private: + std::list _VideoDeviceNotifications; + + // Thread-safety locks + mutable Core::CriticalSection _apiLock; + mutable Core::CriticalSection _callbackLock; + + std::vector _cachedVideoDeviceConfigs; + + VideoDevice _videoDevice; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _videoDevice.InitialiseHAL(); } + }; + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsVideoPortImplementation.cpp b/plugin/DeviceSettingsVideoPortImplementation.cpp new file mode 100644 index 0000000..357d413 --- /dev/null +++ b/plugin/DeviceSettingsVideoPortImplementation.cpp @@ -0,0 +1,705 @@ +/* + * 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. + */ + +#include "DeviceSettingsVideoPortImplementation.h" + +#include +#include +#include + +using namespace std; + +#include "DeviceSettingsHALConfig.h" + +namespace WPEFramework { +namespace Plugin { + + DeviceSettingsVideoPortImpl::DeviceSettingsVideoPortImpl() : + _VideoPortNotifications(), + _apiLock(), + _callbackLock(), + _videoPort(VideoPort::Create(*this)) + { + LOGINFO("DeviceSettingsVideoPortImpl Constructor - Instance Address: %p", this); + } + + DeviceSettingsVideoPortImpl::~DeviceSettingsVideoPortImpl() { + LOGINFO("DeviceSettingsVideoPortImpl Destructor - Instance Address: %p", this); + } + + template + void DeviceSettingsVideoPortImpl::dispatchVideoPortEvent(Func notifyFunc, Args&&... args) { + LOGINFO(">>"); + _callbackLock.Lock(); + for (auto& notification : _VideoPortNotifications) { + auto start = std::chrono::steady_clock::now(); + (notification->*notifyFunc)(std::forward(args)...); + auto elapsed = std::chrono::steady_clock::now() - start; + LOGINFO("client %p took %" PRId64 "ms to process IVideoPort event", notification, std::chrono::duration_cast(elapsed).count()); + } + _callbackLock.Unlock(); + LOGINFO("<<"); + } + + template + Core::hresult DeviceSettingsVideoPortImpl::Register(std::list& list, T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + + _callbackLock.Lock(); + // Make sure we can't register the same notification callback multiple times + if (std::find(list.begin(), list.end(), notification) == list.end()) { + list.push_back(notification); + notification->AddRef(); + status = Core::ERROR_NONE; + } else { + LOGWARN("Notification %p already registered - skipping", notification); + } + _callbackLock.Unlock(); + + return status; + } + + template + Core::hresult DeviceSettingsVideoPortImpl::Unregister(std::list& list, const T* notification) + { + uint32_t status = Core::ERROR_GENERAL; + ASSERT(nullptr != notification); + _callbackLock.Lock(); + + // Make sure we can't unregister the same notification callback multiple times + auto itr = std::find(list.begin(), list.end(), notification); + if (itr != list.end()) { + (*itr)->Release(); + list.erase(itr); + status = Core::ERROR_NONE; + } + + _callbackLock.Unlock(); + return status; + } + + Core::hresult DeviceSettingsVideoPortImpl::Register(Exchange::IDeviceSettingsVideoPort::INotification* notification) + { + Core::hresult errorCode = Register(_VideoPortNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IVideoPort %p, errorCode: %u", notification, errorCode); + } else { + LOGINFO("IVideoPort %p registered successfully", notification); + } + return errorCode; + } + + Core::hresult DeviceSettingsVideoPortImpl::Unregister(Exchange::IDeviceSettingsVideoPort::INotification* notification) + { + Core::hresult errorCode = Unregister(_VideoPortNotifications, notification); + if (errorCode != Core::ERROR_NONE) { + LOGERR("IVideoPort %p, errorcode: %u", notification, errorCode); + } else { + LOGINFO("IVideoPort %p unregistered successfully", notification); + } + return errorCode; + } + + // Intermediate notification methods removed - DS HAL callbacks now directly call dispatchVideoPortEvent + + // VideoPort::INotification interface implementations (called by DS HAL) + void DeviceSettingsVideoPortImpl::OnResolutionPreChange(const ResolutionChange resolution) + { + LOGINFO("DS HAL OnResolutionPreChange event: width=%u, height=%u", resolution.width, resolution.height); + dispatchVideoPortEvent(&Exchange::IDeviceSettingsVideoPort::INotification::OnResolutionPreChange, resolution); + } + + void DeviceSettingsVideoPortImpl::OnResolutionPostChange(const ResolutionChange resolution) + { + LOGINFO("DS HAL OnResolutionPostChange event: width=%u, height=%u", resolution.width, resolution.height); + dispatchVideoPortEvent(&Exchange::IDeviceSettingsVideoPort::INotification::OnResolutionPostChange, resolution); + } + + void DeviceSettingsVideoPortImpl::OnHDCPStatusChange(const VideoPortHdcpStatus hdcpStatus) + { + LOGINFO("DS HAL OnHDCPStatusChange event: hdcpStatus=%d", static_cast(hdcpStatus)); + dispatchVideoPortEvent(&Exchange::IDeviceSettingsVideoPort::INotification::OnHDCPStatusChange, hdcpStatus); + } + + void DeviceSettingsVideoPortImpl::OnVideoFormatUpdate(const HDRStandard videoFormatHDR) + { + LOGINFO("DS HAL OnVideoFormatUpdate event: videoFormatHDR=0x%x", static_cast(videoFormatHDR)); + dispatchVideoPortEvent(&Exchange::IDeviceSettingsVideoPort::INotification::OnVideoFormatUpdate, videoFormatHDR); + } + + // VideoPort interface method implementations called by DeviceSettingsImp + uint32_t DeviceSettingsVideoPortImpl::GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetVideoPort(videoPort, index, handle); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoPort succeeded: videoPort=%d, index=%d, handle=%d", static_cast(videoPort), index, handle); + } else { + LOGERR("GetVideoPort failed: videoPort=%d, index=%d, error=%u", static_cast(videoPort), index, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsVideoPortEnabled(const int32_t handle, bool &enabled) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsVideoPortEnabled(handle, enabled); + if (result == Core::ERROR_NONE) { + LOGINFO("IsVideoPortEnabled succeeded for handle: %d, enabled: %s", handle, enabled ? "true" : "false"); + } else { + LOGERR("IsVideoPortEnabled failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoPortResolutionConfig(VideoPortType videoPortType, + IVideoPortResolutionIterator*& resolutions) const + { + std::vector resolutionConfigs; + + DeviceSettingsHAL::PopulateVideoPortResolutionConfig(videoPortType, resolutionConfigs); + + using ResolutionIterator = RPC::IteratorType; + resolutions = Core::Service::Create(resolutionConfigs); + + LOGINFO("GetVideoPortResolutionConfig: videoPortType=%d resolutions=%zu", + static_cast(videoPortType), resolutionConfigs.size()); + return Core::ERROR_NONE; + } + + uint32_t DeviceSettingsVideoPortImpl::EnableVideoPort(const int32_t handle, const bool enabled) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.EnableVideoPort(handle, enabled); + if (result == Core::ERROR_NONE) { + LOGINFO("EnableVideoPort succeeded for handle: %d, enabled: %s", handle, enabled ? "true" : "false"); + } else { + LOGERR("EnableVideoPort failed for handle: %d, enabled: %s, error: %u", handle, enabled ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsVideoPortDisplayConnected(const int32_t handle, bool &connected) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsVideoPortDisplayConnected(handle, connected); + if (result == Core::ERROR_NONE) { + LOGINFO("IsVideoPortDisplayConnected succeeded for handle: %d, connected: %s", handle, connected ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplayConnected failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsVideoPortActive(const int32_t handle, bool &active) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsVideoPortActive(handle, active); + if (result == Core::ERROR_NONE) { + LOGINFO("IsVideoPortActive succeeded for handle: %d, active: %s", handle, active ? "true" : "false"); + } else { + LOGERR("IsVideoPortActive failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetVideoPortResolution(handle, resolution); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoPortResolution succeeded for handle: %d", handle); + } else { + LOGERR("GetVideoPortResolution failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetVideoPortResolution(const int32_t handle, const VideoPortResolution resolution, const bool persist, const bool forceCompatibility) + { + uint32_t result = Core::ERROR_GENERAL; + + // Callers (e.g. DisplaySettings) may supply only the name with other fields uninitialised. + // Look up the full params from the HAL-populated cache before passing to the HAL layer. + VideoPortResolution resolvedResolution = resolution; + if (!resolution.name.empty()) { + _apiLock.Lock(); + // Lazy one-time population to avoid cost at plugin activation. + if (_cachedVideoPortResolutions.empty()) { + std::set seen; + for (int t = static_cast(VideoPortType::DS_VIDEO_PORT_TYPE_RF); + t < static_cast(VideoPortType::DS_VIDEO_PORT_TYPE_MAX); ++t) { + std::vector tmp; + DeviceSettingsHAL::PopulateVideoPortResolutionConfig( + static_cast(t), tmp); + for (const auto& r : tmp) { + if (seen.insert(r.name).second) + _cachedVideoPortResolutions.push_back(r); + } + } + LOGINFO("SetVideoPortResolution: lazily cached %zu resolutions from HAL", + _cachedVideoPortResolutions.size()); + } + for (const auto& cached : _cachedVideoPortResolutions) { + if (cached.name == resolution.name) { + resolvedResolution = cached; + break; + } + } + _apiLock.Unlock(); + } + + result = _videoPort.SetVideoPortResolution(handle, resolvedResolution, persist, forceCompatibility); + if (result == Core::ERROR_NONE) { + LOGINFO("SetVideoPortResolution succeeded for handle: %d, persist: %s, forceCompatibility: %s", handle, persist ? "true" : "false", forceCompatibility ? "true" : "false"); + } else { + LOGERR("SetVideoPortResolution failed for handle: %d, persist: %s, forceCompatibility: %s, error: %u", handle, persist ? "true" : "false", forceCompatibility ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetColorSpace(handle, colorSpace); + if (result == Core::ERROR_NONE) { + LOGINFO("GetColorSpace succeeded for handle: %d", handle); + } else { + LOGERR("GetColorSpace failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace, const bool persist) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetColorSpace(handle, colorSpace); + if (result == Core::ERROR_NONE) { + LOGINFO("SetColorSpace succeeded for handle: %d, persist: %s", handle, persist ? "true" : "false"); + } else { + LOGERR("SetColorSpace failed for handle: %d, persist: %s, error: %u", handle, persist ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetQuantizationRange(handle, quantizationRange); + if (result == Core::ERROR_NONE) { + LOGINFO("GetQuantizationRange succeeded for handle: %d", handle); + } else { + LOGERR("GetQuantizationRange failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange, const bool persist) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetVideoPortQuantizationRange(handle, quantizationRange); + if (result == Core::ERROR_NONE) { + LOGINFO("SetQuantizationRange succeeded for handle: %d, persist: %s", handle, persist ? "true" : "false"); + } else { + LOGERR("SetQuantizationRange failed for handle: %d, persist: %s, error: %u", handle, persist ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus &hdcpStatus) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetVideoPortHDCPStatus(handle, hdcpStatus); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoPortHDCPStatus succeeded for handle: %d", handle); + } else { + LOGERR("GetVideoPortHDCPStatus failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetHDCPProtocolVersionOnVideoPort(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("GetHDCPProtocolVersionOnVideoPort succeeded for handle: %d", handle); + } else { + LOGERR("GetHDCPProtocolVersionOnVideoPort failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoPortHDCPCurrentProtocol(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetHDCPCurrentProtocolVersionOnVideoPort(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoPortHDCPCurrentProtocol succeeded for handle: %d", handle); + } else { + LOGERR("GetVideoPortHDCPCurrentProtocol failed for handle: %d, error: %u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetVideoPortHDCPProfile(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion, const bool persist) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetHDMIPreference(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("SetVideoPortHDCPProfile succeeded for handle: %d, persist: %s", handle, persist ? "true" : "false"); + } else { + LOGERR("SetVideoPortHDCPProfile failed for handle: %d, persist: %s, error: %u", handle, persist ? "true" : "false", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients &matrixCoefficients) + { + uint32_t result = Core::ERROR_GENERAL; + DisplayMatrixCoefficients displayMatrixCoefficients; + result = _videoPort.GetMatrixCoefficients(handle, displayMatrixCoefficients); + if (result == Core::ERROR_NONE) { + matrixCoefficients = static_cast(displayMatrixCoefficients); + LOGINFO("GetMatrixCoefficients succeeded: handle=%d, matrixCoefficients=%d", handle, static_cast(matrixCoefficients)); + } else { + LOGERR("GetMatrixCoefficients failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetCurrentOutputSettings(const int32_t handle, DSOutputSettings &outputSettings) + { + uint32_t result = Core::ERROR_GENERAL; + DSOutputSettings dsOutputSettings; + result = _videoPort.GetCurrentOutputSettings(handle, dsOutputSettings); + if (result == Core::ERROR_NONE) { + // Convert DSOutputSettings to DSOutputSettings + outputSettings.videoEotf = static_cast(dsOutputSettings.videoEotf); + outputSettings.matrixCoefficients = static_cast(dsOutputSettings.matrixCoefficients); + outputSettings.colorDepth = dsOutputSettings.colorDepth; + outputSettings.colorSpace = static_cast(dsOutputSettings.colorSpace); + outputSettings.quantizationRange = static_cast(dsOutputSettings.quantizationRange); + LOGINFO("GetCurrentOutputSettings succeeded: handle=%d", handle); + } else { + LOGERR("GetCurrentOutputSettings failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetBackgroundColor(handle, backgroundColor); + if (result == Core::ERROR_NONE) { + LOGINFO("SetBackgroundColor succeeded: handle=%d, backgroundColor=%d", handle, static_cast(backgroundColor)); + } else { + LOGERR("SetBackgroundColor failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetForceHDRMode(handle, hdrMode); + if (result == Core::ERROR_NONE) { + LOGINFO("SetForceHDRMode succeeded: handle=%d, hdrMode=%d", handle, static_cast(hdrMode)); + } else { + LOGERR("SetForceHDRMode failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetColorDepthCapabilities(handle, colorDepthCapabilities); + if (result == Core::ERROR_NONE) { + LOGINFO("GetColorDepthCapabilities succeeded: handle=%d, colorDepthCapabilities=0x%x", handle, colorDepthCapabilities); + } else { + LOGERR("GetColorDepthCapabilities failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetPreferredColorDepth(const int32_t handle, DisplayColorDepth &colorDepth, const bool persist) + { + uint32_t result = Core::ERROR_GENERAL; + DisplayColorDepth displayColorDepth; + result = _videoPort.GetPreferredColorDepth(handle, displayColorDepth, persist); + if (result == Core::ERROR_NONE) { + colorDepth = static_cast(displayColorDepth); + LOGINFO("GetPreferredColorDepth succeeded: handle=%d, colorDepth=%d, persist=%s", handle, static_cast(colorDepth), persist ? "true" : "false"); + } else { + LOGERR("GetPreferredColorDepth failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetPreferredColorDepth(handle, static_cast(colorDepth), persist); + if (result == Core::ERROR_NONE) { + LOGINFO("SetPreferredColorDepth succeeded: handle=%d, colorDepth=%d, persist=%s", handle, static_cast(colorDepth), persist ? "true" : "false"); + } else { + LOGERR("SetPreferredColorDepth failed: handle=%d, error=%u", handle, result); + } + return result; + } + + // Additional methods required by DeviceSettingsImplementation.cpp and IDeviceSettingsVideoPort.h interface + + uint32_t DeviceSettingsVideoPortImpl::GetColorDepth(const int32_t handle, uint32_t &colorDepth) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetColorDepth(handle, colorDepth); + if (result == Core::ERROR_NONE) { + LOGINFO("GetColorDepth succeeded: handle=%d, colorDepth=%u", handle, colorDepth); + } else { + LOGERR("GetColorDepth failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t hdcpKey[], const uint16_t hdcpKeySize) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.EnableHDCPOnVideoPort(handle, hdcpEnable, hdcpKey, hdcpKeySize); + if (result == Core::ERROR_NONE) { + LOGINFO("EnableHDCPOnVideoPort succeeded: handle=%d, hdcpEnable=%s", handle, hdcpEnable ? "true" : "false"); + } else { + LOGERR("EnableHDCPOnVideoPort failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsHDCPEnabledOnVideoPort(handle, hdcpEnabled); + if (result == Core::ERROR_NONE) { + LOGINFO("IsHDCPEnabledOnVideoPort succeeded: handle=%d, hdcpEnabled=%s", handle, hdcpEnabled ? "true" : "false"); + } else { + LOGERR("IsHDCPEnabledOnVideoPort failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetTVHDRCapabilities(handle, capabilities); + if (result == Core::ERROR_NONE) { + LOGINFO("GetTVHDRCapabilities succeeded: handle=%d, capabilities=0x%x", handle, capabilities); + } else { + LOGERR("GetTVHDRCapabilities failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetTVSupportedResolutions(handle, resolutions); + if (result == Core::ERROR_NONE) { + LOGINFO("GetTVSupportedResolutions succeeded: handle=%d, resolutions=0x%x", handle, resolutions); + } else { + LOGERR("GetTVSupportedResolutions failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetForceDisable4K(const int32_t handle, const bool disable) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetForceDisable4K(handle, disable); + if (result == Core::ERROR_NONE) { + LOGINFO("SetForceDisable4K succeeded: handle=%d, disable=%s", handle, disable ? "true" : "false"); + } else { + LOGERR("SetForceDisable4K failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetForceDisable4K(const int32_t handle, bool &disabled) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetForceDisable4K(handle, disabled); + if (result == Core::ERROR_NONE) { + LOGINFO("GetForceDisable4K succeeded: handle=%d, disabled=%s", handle, disabled ? "true" : "false"); + } else { + LOGERR("GetForceDisable4K failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsVideoPortOutputHDR(const int32_t handle, bool &isHDR) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsVideoPortOutputHDR(handle, isHDR); + if (result == Core::ERROR_NONE) { + LOGINFO("IsVideoPortOutputHDR succeeded: handle=%d, isHDR=%s", handle, isHDR ? "true" : "false"); + } else { + LOGERR("IsVideoPortOutputHDR failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::ResetVideoPortOutputToSDR() + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.ResetVideoPortOutputToSDR(); + if (result == Core::ERROR_NONE) { + LOGINFO("ResetVideoPortOutputToSDR succeeded"); + } else { + LOGERR("ResetVideoPortOutputToSDR failed: error=%u", result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetHDMIPreference(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("GetHDMIPreference succeeded: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + } else { + LOGERR("GetHDMIPreference failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.SetHDMIPreference(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("SetHDMIPreference succeeded: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + } else { + LOGERR("SetHDMIPreference failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoEOTF(const int32_t handle, HDRStandard &hdrStandard) + { + uint32_t result = Core::ERROR_GENERAL; + HDRStandard interfaceHdrStandard; + result = _videoPort.GetVideoEOTF(handle, interfaceHdrStandard); + if (result == Core::ERROR_NONE) { + hdrStandard = static_cast(interfaceHdrStandard); + LOGINFO("GetVideoEOTF succeeded: handle=%d, hdrStandard=%d", handle, static_cast(hdrStandard)); + } else { + LOGERR("GetVideoEOTF failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::IsVideoPortDisplaySurround(const int32_t handle, bool &surround) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.IsVideoPortDisplaySurround(handle, surround); + if (result == Core::ERROR_NONE) { + LOGINFO("IsVideoPortDisplaySurround succeeded: handle=%d, surround=%s", handle, surround ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplaySurround failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode &surroundMode) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetVideoPortDisplaySurroundMode(handle, surroundMode); + if (result == Core::ERROR_NONE) { + LOGINFO("GetVideoPortDisplaySurroundMode succeeded: handle=%d, surroundMode=%d", handle, static_cast(surroundMode)); + } else { + LOGERR("GetVideoPortDisplaySurroundMode failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetHDCPReceiverProtocolVersionOnVideoPort(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("GetHDCPReceiverProtocolVersionOnVideoPort succeeded: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + } else { + LOGERR("GetHDCPReceiverProtocolVersionOnVideoPort failed: handle=%d, error=%u", handle, result); + } + return result; + } + + uint32_t DeviceSettingsVideoPortImpl::GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) + { + uint32_t result = Core::ERROR_GENERAL; + result = _videoPort.GetHDCPCurrentProtocolVersionOnVideoPort(handle, hdcpVersion); + if (result == Core::ERROR_NONE) { + LOGINFO("GetHDCPCurrentProtocolVersionOnVideoPort succeeded: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + } else { + LOGERR("GetHDCPCurrentProtocolVersionOnVideoPort failed: handle=%d, error=%u", handle, result); + } + return result; + } + + void DeviceSettingsVideoPortImpl::getCachedConfigs( + std::vector& videoPortTypes, + std::vector& videoPorts, + std::vector& videoPortResolutions) const + { + _apiLock.Lock(); + + videoPortTypes.reserve(_cachedVideoPortTypes.size()); + for (const auto& src : _cachedVideoPortTypes) { + videoPortTypes.push_back({static_cast(src.typeId), src.name, + src.dtcpSupported, src.hdcpSupported, + src.restrictedResolution, src.supportedResolutionNames}); + } + + videoPorts.reserve(_cachedVideoPorts.size()); + for (const auto& src : _cachedVideoPorts) { + videoPorts.push_back({static_cast(src.videoPortType), src.videoPortIndex, + src.connectedAudioPortType, src.connectedAudioPortIndex, src.defaultResolution}); + } + + // Resolution config is cached from the 0th video port type during init. + // Copy it whenever the cache is non-empty (i.e. at least one type exists). + if (!_cachedVideoPortResolutions.empty()) { + videoPortResolutions.reserve(_cachedVideoPortResolutions.size()); + for (const auto& src : _cachedVideoPortResolutions) { + videoPortResolutions.push_back({ + src.name, + static_cast(src.pixelResolution), + static_cast(src.aspectRatio), + static_cast(src.stereoScopicMode), + static_cast(src.frameRate), + src.interlaced}); + } + } + + _apiLock.Unlock(); + } + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/DeviceSettingsVideoPortImplementation.h b/plugin/DeviceSettingsVideoPortImplementation.h new file mode 100644 index 0000000..f73019d --- /dev/null +++ b/plugin/DeviceSettingsVideoPortImplementation.h @@ -0,0 +1,154 @@ +/* + * 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. + */ + +#pragma once + +#include "Module.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include "VideoPort.h" +#include "DeviceSettingsTypes.h" + +namespace WPEFramework { +namespace Plugin { + class DeviceSettingsVideoPortImpl : public VideoPort::INotification + { + public: + + DeviceSettingsVideoPortImpl(); + ~DeviceSettingsVideoPortImpl() override; + + static DeviceSettingsVideoPortImpl* Create() + { + return new DeviceSettingsVideoPortImpl(); + } + + // We do not allow this plugin to be copied !! + DeviceSettingsVideoPortImpl(const DeviceSettingsVideoPortImpl&) = delete; + DeviceSettingsVideoPortImpl& operator=(const DeviceSettingsVideoPortImpl&) = delete; + + // INTERFACE_MAP not needed - this is an implementation class aggregated by DeviceSettingsImp + // DeviceSettingsImp handles QueryInterface for all component interfaces + + public: + + // Template method for dispatching VideoPort Events + template + void dispatchVideoPortEvent(Func notifyFunc, Args&&... args); + + // Template methods for notification management + template + Core::hresult Register(std::list& list, T* notification); + + template + Core::hresult Unregister(std::list& list, const T* notification); + + // Public notification registration methods called by DeviceSettingsImp + Core::hresult Register(Exchange::IDeviceSettingsVideoPort::INotification* notification); + Core::hresult Unregister(Exchange::IDeviceSettingsVideoPort::INotification* notification); + + // Event notification methods removed - DS HAL callbacks now directly call dispatchVideoPortEvent + + // Required VideoPort::INotification interface implementations + void OnResolutionPreChange(const ResolutionChange resolution) override; + void OnResolutionPostChange(const ResolutionChange resolution) override; + void OnHDCPStatusChange(const VideoPortHdcpStatus hdcpStatus) override; + void OnVideoFormatUpdate(const HDRStandard videoFormatHDR) override; + + // VideoPort interface method implementations called by DeviceSettingsImp + uint32_t GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle); + uint32_t IsVideoPortEnabled(const int32_t handle, bool &enabled); + + uint32_t GetVideoPortResolutionConfig(VideoPortType videoPortType, + IVideoPortResolutionIterator*& resolutions) const; + uint32_t EnableVideoPort(const int32_t handle, const bool enabled); + uint32_t IsVideoPortDisplayConnected(const int32_t handle, bool &connected); + uint32_t IsVideoPortActive(const int32_t handle, bool &active); + uint32_t GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution); + uint32_t SetVideoPortResolution(const int32_t handle, const VideoPortResolution resolution, const bool persist, const bool forceCompatibility); + uint32_t GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace); + uint32_t SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace, const bool persist); + uint32_t GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange); + uint32_t SetQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange, const bool persist); + uint32_t GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus &hdcpStatus); + uint32_t GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t GetVideoPortHDCPCurrentProtocol(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t SetVideoPortHDCPProfile(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion, const bool persist); + uint32_t EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t* hdcpKey, const uint16_t hdcpKeySize); + uint32_t IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled); + uint32_t GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities); + uint32_t GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions); + uint32_t SetForceDisable4K(const int32_t handle, const bool disable); + uint32_t GetForceDisable4K(const int32_t handle, bool &disabled); + uint32_t IsVideoPortOutputHDR(const int32_t handle, bool &isHDR); + uint32_t ResetVideoPortOutputToSDR(); + uint32_t GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion); + uint32_t GetVideoEOTF(const int32_t handle, HDRStandard &hdrStandard); + uint32_t IsVideoPortDisplaySurround(const int32_t handle, bool &surround); + uint32_t GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode &surroundMode); + uint32_t GetColorDepth(const int32_t handle, uint32_t &colorDepth); + + // Additional VideoPort methods + uint32_t GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients &matrixCoefficients); + uint32_t GetCurrentOutputSettings(const int32_t handle, DSOutputSettings &outputSettings); + uint32_t SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor); + uint32_t SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode); + uint32_t GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities); + uint32_t GetPreferredColorDepth(const int32_t handle, DisplayColorDepth &colorDepth, const bool persist); + uint32_t SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist); + + // Fills IDeviceSettings consolidated config vectors from cached data + void getCachedConfigs(std::vector& videoPortTypes, + std::vector& videoPorts, + std::vector& videoPortResolutions) const; + + private: + std::list _VideoPortNotifications; + + // Thread-safety locks + mutable Core::CriticalSection _apiLock; + mutable Core::CriticalSection _callbackLock; + + std::vector _cachedVideoPortTypes; + std::vector _cachedVideoPorts; + std::vector _cachedVideoPortResolutions; + + VideoPort _videoPort; + + public: + /** Called from DeviceSettingsImp::Configure() to trigger deferred HAL init. */ + void InitialiseHAL() { _videoPort.InitialiseHAL(); } + }; + +} // namespace Plugin +} // namespace WPEFramework \ No newline at end of file diff --git a/plugin/Display.cpp b/plugin/Display.cpp new file mode 100644 index 0000000..f423785 --- /dev/null +++ b/plugin/Display.cpp @@ -0,0 +1,217 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "Display.h" + +Display::Display(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("Display Constructor"); + Platform_init(); +} + +void Display::Platform_init() +{ + LOGINFO("Display Init - Setting up event callbacks"); + + // Set up callback bundle for Display events - using global CallbackBundle pattern + CallbackBundle bundle; + + bundle.OnDisplayRxSense = [this](const uint8_t /*port*/, const bool rxSenseOn) { + this->OnDisplayRxSense(rxSenseOn ? DisplayEvent::DS_DISPLAY_RXSENSE_ON + : DisplayEvent::DS_DISPLAY_RXSENSE_OFF); + }; + bundle.OnDisplayHDCPStatus = [this](const uint8_t /*port*/, const bool /*authenticated*/) { + this->OnDisplayHDCPStatus(); + }; + bundle.OnDisplayHDMIHotPlug = [this](const uint8_t /*port*/, const bool connected) { + this->OnDisplayHDMIHotPlug(connected ? DisplayEvent::DS_DISPLAY_EVENT_CONNECTED + : DisplayEvent::DS_DISPLAY_EVENT_DISCONNECTED); + }; + + if (_platform) { + // Use interface method directly - no casting needed + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } + +} + +void Display::OnDisplayRxSense(const DisplayEvent displayEvent) +{ + LOGINFO("Display OnDisplayRxSense event: displayEvent=%d", static_cast(displayEvent)); + _parent.OnDisplayRxSense(displayEvent); +} + +void Display::OnDisplayHDCPStatus() +{ + LOGINFO("Display OnDisplayHDCPStatus event"); + _parent.OnDisplayHDCPStatus(); +} + +void Display::OnDisplayHDMIHotPlug(const DisplayEvent displayEvent) +{ + LOGINFO("Display OnDisplayHDMIHotPlug event: displayEvent=%d", static_cast(displayEvent)); + _parent.OnDisplayHDMIHotPlug(displayEvent); +} + +uint32_t Display::GetDisplayEdid(const int32_t handle, DisplayEDID &edId, IDSVideoPortResolutionIterator*& supportedResolutionList) +{ + uint32_t result = this->platform().GetDisplayEdid(handle, edId); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetDisplayEdid succeeded: handle=%d", handle); + } else { + LOGERR("GetDisplayEdid failed: handle=%d, error=%u", handle, result); + } + return result; +} + +uint32_t Display::GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength) +{ + uint32_t result = this->platform().GetDisplayEdidBytes(handle, edIdBytes, edidLength); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetDisplayEdidBytes succeeded: handle=%d, edidLength=%d", handle, edidLength); + } else { + LOGERR("GetDisplayEdidBytes failed: handle=%d, error=%u", handle, result); + } + return result; +} + +uint32_t Display::DisplayInit() +{ + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + // Initialize through platform interface - HAL is already initialized in constructor + result = WPEFramework::Core::ERROR_NONE; + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("DisplayInit succeeded"); + } else { + LOGERR("DisplayInit failed: error=%u", result); + } + return result; +} + +uint32_t Display::DisplayTerm() +{ + uint32_t result = WPEFramework::Core::ERROR_NONE; + // Termination handled by platform destructors + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("DisplayTerm succeeded"); + } else { + LOGERR("DisplayTerm failed: error=%u", result); + } + return result; +} + +uint32_t Display::GetDisplay(const int32_t type, const int32_t index, int32_t &handle) +{ + + uint32_t result = this->platform().GetDisplay(type, index, handle); + + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("Display::GetDisplay SUCCESS: type=%d, index=%d, handle=%d", type, index, handle); + } else { + LOGERR("Display::GetDisplay FAILED: type=%d, index=%d, error=%u", type, index, result); + } + return result; +} + +uint32_t Display::GetDisplayAspectRatio(const int32_t handle, DisplayVideoAspectRatio &aspectRatio) +{ + uint32_t result = this->platform().GetDisplayAspectRatio(handle, aspectRatio); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetDisplayAspectRatio succeeded: handle=%d, aspectRatio=%d", handle, static_cast(aspectRatio)); + } else { + LOGERR("GetDisplayAspectRatio failed: handle=%d, error=%u", handle, result); + } + return result; +} + +uint32_t Display::SetAllmEnabled(const int32_t handle, const bool enabled) +{ + uint32_t result = this->platform().SetAllmEnabled(handle, enabled); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAllmEnabled succeeded: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + } else { + LOGERR("SetAllmEnabled failed: handle=%d, error=%u", handle, result); + } + return result; +} + +uint32_t Display::SetAVIContentType(const int32_t handle, const int32_t contentType) +{ + uint32_t result = this->platform().SetAVIContentType(handle, contentType); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAVIContentType succeeded: handle=%d, contentType=%d", handle, contentType); + } else { + LOGERR("SetAVIContentType failed: handle=%d, error=%u", handle, result); + } + return result; +} + +uint32_t Display::SetAVIScanInformation(const int32_t handle, const int32_t scanInfo) +{ + uint32_t result = this->platform().SetAVIScanInformation(handle, scanInfo); + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetAVIScanInformation succeeded: handle=%d, scanInfo=%d", handle, scanInfo); + } else { + LOGERR("SetAVIScanInformation failed: handle=%d, error=%u", handle, result); + } + return result; +} + +void Display::RegisterDisplayEventCallback() +{ + // Event callbacks are registered through platform initialization + LOGINFO("RegisterDisplayEventCallback - handled by platform layer"); +} + +void Display::OnDisplayEvent(const int32_t handle, const DisplayEvent event, void *eventData) +{ + + switch(event) { + case DisplayEvent::DS_DISPLAY_RXSENSE_ON: + case DisplayEvent::DS_DISPLAY_RXSENSE_OFF: + OnDisplayRxSense(event); + break; + + case DisplayEvent::DS_DISPLAY_HDCPPROTOCOL_CHANGE: + OnDisplayHDCPStatus(); + break; + + case DisplayEvent::DS_DISPLAY_EVENT_CONNECTED: + case DisplayEvent::DS_DISPLAY_EVENT_DISCONNECTED: + OnDisplayHDMIHotPlug(event); + break; + + default: + LOGERR("Unknown display event: %d", static_cast(event)); + break; + } + +} \ No newline at end of file diff --git a/plugin/Display.h b/plugin/Display.h new file mode 100644 index 0000000..5eedb6a --- /dev/null +++ b/plugin/Display.h @@ -0,0 +1,110 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsDisplay.h" + +#include "hal/dDisplay.h" +#include "hal/dDisplayImpl.h" +#include "DeviceSettingsTypes.h" + +class Display { + using IPlatform = hal::dDisplay::IPlatform; + using DefaultImpl = dDisplayImpl; + + std::shared_ptr _platform; + +public: + class INotification { + + public: + virtual ~INotification() = default; + virtual void OnDisplayRxSense(const DisplayEvent displayEvent) = 0; + virtual void OnDisplayHDCPStatus() = 0; + virtual void OnDisplayHDMIHotPlug(const DisplayEvent displayEvent) = 0; + }; + +public: + + // Display interface methods - exactly replicating dsDisplay.c functionality + uint32_t GetDisplayEdid(const int32_t handle, DisplayEDID &edId, IDSVideoPortResolutionIterator*& supportedResolutionList); + uint32_t GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength); + + // General display initialization and management + uint32_t DisplayInit(); + uint32_t DisplayTerm(); + uint32_t GetDisplay(const int32_t type, const int32_t index, int32_t &handle); + uint32_t GetDisplayAspectRatio(const int32_t handle, DisplayVideoAspectRatio &aspectRatio); + uint32_t SetAllmEnabled(const int32_t handle, const bool enabled); + uint32_t SetAVIContentType(const int32_t handle, const int32_t contentType); + uint32_t SetAVIScanInformation(const int32_t handle, const int32_t scanInfo); + + // Display event handling methods - Called by DS HAL to forward events to parent + void OnDisplayRxSense(const DisplayEvent displayEvent); + void OnDisplayHDCPStatus(); + void OnDisplayHDMIHotPlug(const DisplayEvent displayEvent); + + template + static Display Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dDisplay::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return Display(parent, std::move(impl)); + } + + private: + Display(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + void Platform_init(); + +public: + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + void RegisterDisplayEventCallback(); + void OnDisplayEvent(const int32_t handle, const DisplayEvent event, void *eventData); + + INotification& _parent; +}; \ No newline at end of file diff --git a/plugin/HdmiIn.cpp b/plugin/HdmiIn.cpp new file mode 100755 index 0000000..ba8b022 --- /dev/null +++ b/plugin/HdmiIn.cpp @@ -0,0 +1,276 @@ +/* + * 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. + */ + +#include "HdmiIn.h" +#include "DeviceSettingsTypes.h" + +using IPlatform = hal::dHdmiIn::IPlatform; +using DefaultImpl = dHdmiInImpl; + +#include "hal/dHdmiIn.h" +namespace hal { +namespace dHdmiIn { + IPlatform::~IPlatform() {} +} +} + +HdmiIn::HdmiIn(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + Platform_init(); +} + +void HdmiIn::Platform_init() +{ + CallbackBundle bundle; + bundle.OnHDMIInHotPlugEvent = [this](HDMIInPort port, bool isConnected) { + this->OnHDMIInHotPlugEvent(port, isConnected); + }; + bundle.OnHDMIInSignalStatusEvent = [this](HDMIInPort port, HDMIInSignalStatus signalStatus) { + this->OnHDMIInSignalStatusEvent(port, signalStatus); + }; + bundle.OnHDMIInStatusEvent = [this](HDMIInPort port, bool isConnected) { + this->OnHDMIInStatusEvent(port, isConnected); + }; + bundle.OnHDMIInVideoModeUpdateEvent = [this](HDMIInPort port, HDMIVideoPortResolution videoPortResolution) { + this->OnHDMIInVideoModeUpdateEvent(port, videoPortResolution); + }; + bundle.OnHDMIInAllmStatusEvent = [this](HDMIInPort port, bool allmStatus) { + this->OnHDMIInAllmStatusEvent(port, allmStatus); + }; + bundle.OnHDMIInAVIContentTypeEvent = [this](HDMIInPort port, HDMIInAviContentType aviContentType) { + this->OnHDMIInAVIContentTypeEvent(port, aviContentType); + }; + bundle.OnHDMIInAVLatencyEvent = [this](int32_t audioDelay, int32_t videoDelay) { + this->OnHDMIInAVLatencyEvent(audioDelay, videoDelay); + }; + bundle.OnHDMIInVRRStatusEvent = [this](HDMIInPort port, HDMIInVRRType vrrType) { + this->OnHDMIInVRRStatusEvent(port, vrrType); + }; + if (_platform) { + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } +} + +void HdmiIn::OnHDMIInHotPlugEvent(const HDMIInPort port, const bool isConnected) +{ + _parent.OnHDMIInEventHotPlugNotification(port, isConnected); +} + +void HdmiIn::OnHDMIInSignalStatusEvent(const HDMIInPort port, const HDMIInSignalStatus signalStatus) +{ + _parent.OnHDMIInEventSignalStatusNotification(port, signalStatus); +} + +void HdmiIn::OnHDMIInStatusEvent(const HDMIInPort activePort, const bool isPresented) +{ + _parent.OnHDMIInEventStatusNotification(activePort, isPresented); +} + +void HdmiIn::OnHDMIInVideoModeUpdateEvent(const HDMIInPort port, const HDMIVideoPortResolution videoPortResolution) +{ + _parent.OnHDMIInVideoModeUpdateNotification(port, videoPortResolution); +} + +void HdmiIn::OnHDMIInAllmStatusEvent(const HDMIInPort port, const bool allmStatus) +{ + _parent.OnHDMIInAllmStatusNotification(port, allmStatus); +} + +void HdmiIn::OnHDMIInAVIContentTypeEvent(const HDMIInPort port, const HDMIInAviContentType aviContentType) +{ + _parent.OnHDMIInAVIContentTypeNotification(port, aviContentType); +} + +void HdmiIn::OnHDMIInAVLatencyEvent(const int32_t audioDelay, const int32_t videoDelay) +{ + _parent.OnHDMIInAVLatencyNotification(audioDelay, videoDelay); +} + +void HdmiIn::OnHDMIInVRRStatusEvent(const HDMIInPort port, const HDMIInVRRType vrrType) +{ + _parent.OnHDMIInVRRStatusNotification(port, vrrType); +} + +uint32_t HdmiIn::GetHDMIInNumberOfInputs(int32_t &count) { + + LOGINFO("GetHDMIInNumberOfInputs"); + this->platform().GetHDMIInNumberOfInputs(count); + LOGINFO("GetHDMIInNumberOfInputs: SUCCESS - count=%d", count); + + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) { + + LOGINFO("GetHDMIInStatus"); + this->platform().GetHDMIInStatus(hdmiStatus, portConnectionStatus); + LOGINFO("GetHDMIInStatus: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) { + + LOGINFO("SelectHDMIInPort: port=%d, requestAudioMix=%s, topMostPlane=%s, videoPlaneType=%d", + port, requestAudioMix ? "true" : "false", topMostPlane ? "true" : "false", videoPlaneType); + this->platform().SelectHDMIInPort(port, requestAudioMix, topMostPlane, videoPlaneType); + LOGINFO("SelectHDMIInPort: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) { + + LOGINFO("ScaleHDMIInVideo: x=%d, y=%d, w=%d, h=%d", videoPosition.x, videoPosition.y, videoPosition.width, videoPosition.height); + this->platform().ScaleHDMIInVideo(videoPosition); + LOGINFO("ScaleHDMIInVideo: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) { + + LOGINFO("SelectHDMIZoomMode: zoomMode=%d", zoomMode); + this->platform().SelectHDMIZoomMode(zoomMode); + LOGINFO("SelectHDMIZoomMode: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) { + + LOGINFO("GetSupportedGameFeaturesList"); + this->platform().GetSupportedGameFeaturesList(gameFeatureList); + LOGINFO("GetSupportedGameFeaturesList: SUCCESS - platform call completed"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) { + + LOGINFO("GetHDMIInAVLatency"); + this->platform().GetHDMIInAVLatency(videoLatency, audioLatency); + LOGINFO("GetHDMIInAVLatency: SUCCESS - videoLatency=%u, audioLatency=%u", videoLatency, audioLatency); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) { + + LOGINFO("GetHDMIInAllmStatus: port=%d", port); + this->platform().GetHDMIInAllmStatus(port, allmStatus); + LOGINFO("GetHDMIInAllmStatus: SUCCESS - port=%d, allmStatus=%s", port, allmStatus ? "true" : "false"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) { + + LOGINFO("GetHDMIInEdid2AllmSupport: port=%d", port); + this->platform().GetHDMIInEdid2AllmSupport(port, allmSupport); + LOGINFO("GetHDMIInEdid2AllmSupport: SUCCESS - port=%d, allmSupport=%s", port, allmSupport ? "true" : "false"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) { + + LOGINFO("SetHDMIInEdid2AllmSupport: port=%d, allmSupport=%s", port, allmSupport ? "true" : "false"); + this->platform().SetHDMIInEdid2AllmSupport(port, allmSupport); + LOGINFO("SetHDMIInEdid2AllmSupport: SUCCESS - platform call completed"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) { + + LOGINFO("GetEdidBytes: port=%d, edidBytesLength=%u", port, edidBytesLength); + this->platform().GetEdidBytes(port, edidBytesLength, edidBytes); + LOGINFO("GetEdidBytes: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) { + + LOGINFO("GetHDMISPDInformation: port=%d, spdBytesLength=%u", port, spdBytesLength); + this->platform().GetHDMISPDInformation(port, spdBytesLength, spdBytes); + LOGINFO("GetHDMISPDInformation: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) { + + LOGINFO("GetHDMIEdidVersion: port=%d", port); + this->platform().GetHDMIEdidVersion(port, edidVersion); + LOGINFO("GetHDMIEdidVersion: SUCCESS - port=%d, edidVersion=%d", port, edidVersion); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) { + + this->platform().SetHDMIEdidVersion(port, edidVersion); + LOGINFO("SetHDMIEdidVersion: SUCCESS - port=%d, edidVersion=%d", port, edidVersion); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) { + + LOGINFO("GetHDMIVideoMode"); + this->platform().GetHDMIVideoMode(videoPortResolution); + LOGINFO("GetHDMIVideoMode: SUCCESS - platform call completed"); + + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) { + + LOGINFO("GetHDMIVersion: port=%d", port); + this->platform().GetHDMIVersion(port, capabilityVersion); + LOGINFO("GetHDMIVersion: SUCCESS - port=%d, capabilityVersion=%d", port, capabilityVersion); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetVRRSupport(const HDMIInPort port, bool &vrrSupport) { + + LOGINFO("GetVRRSupport: port=%d", port); + this->platform().GetVRRSupport(port, vrrSupport); + LOGINFO("GetVRRSupport: SUCCESS - port=%d, vrrSupport=%s", port, vrrSupport ? "true" : "false"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::SetVRRSupport(const HDMIInPort port, const bool vrrSupport) { + + LOGINFO("SetVRRSupport: port=%d, vrrSupport=%s", port, vrrSupport ? "true" : "false"); + this->platform().SetVRRSupport(port, vrrSupport); + LOGINFO("SetVRRSupport: SUCCESS - platform call completed"); + return WPEFramework::Core::ERROR_NONE; +} + +uint32_t HdmiIn::GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) { + + LOGINFO("GetVRRStatus: port=%d", port); + memset(&vrrStatus, 0, sizeof(vrrStatus)); + this->platform().GetVRRStatus(port, vrrStatus); + LOGINFO("GetVRRStatus: SUCCESS - port=%d, vrrType=%d", port, vrrStatus.vrrType); + + return WPEFramework::Core::ERROR_NONE; +} \ No newline at end of file diff --git a/plugin/HdmiIn.h b/plugin/HdmiIn.h new file mode 100755 index 0000000..fcbfbc1 --- /dev/null +++ b/plugin/HdmiIn.h @@ -0,0 +1,113 @@ +/* + * 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. + */ +#pragma once + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include "DeviceSettingsTypes.h" +#include "hal/dHdmiInImpl.h" + +class HdmiIn { + using IPlatform = hal::dHdmiIn::IPlatform; + using DefaultImpl = dHdmiInImpl; + + std::shared_ptr _platform; +public: + class INotification { + + public: + virtual ~INotification() = default; + + virtual void OnHDMIInEventHotPlugNotification(const HDMIInPort port, const bool isConnected) = 0; + virtual void OnHDMIInEventSignalStatusNotification(const HDMIInPort port, const HDMIInSignalStatus signalStatus) = 0; + virtual void OnHDMIInEventStatusNotification(const HDMIInPort activePort, const bool isPresented) = 0; + virtual void OnHDMIInVideoModeUpdateNotification(const HDMIInPort port, const HDMIVideoPortResolution videoPortResolution) = 0; + virtual void OnHDMIInAllmStatusNotification(const HDMIInPort port, const bool allmStatus) = 0; + virtual void OnHDMIInAVIContentTypeNotification(const HDMIInPort port, const HDMIInAviContentType aviContentType) = 0; + virtual void OnHDMIInAVLatencyNotification(const int32_t audioDelay, const int32_t videoDelay) = 0; + virtual void OnHDMIInVRRStatusNotification(const HDMIInPort port, const HDMIInVRRType vrrType) = 0; + }; + + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + uint32_t GetHDMIInNumberOfInputs(int32_t &count); + uint32_t GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus); + uint32_t SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType); + uint32_t ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition); + uint32_t SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode); + uint32_t GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList); + uint32_t GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency); + uint32_t GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus); + uint32_t GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport); + uint32_t SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport); + uint32_t GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]); + uint32_t GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]); + uint32_t GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion); + uint32_t SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion); + uint32_t GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution); + uint32_t GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion); + uint32_t SetVRRSupport(const HDMIInPort port, const bool vrrSupport); + uint32_t GetVRRSupport(const HDMIInPort port, bool &vrrSupport); + uint32_t GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus); + +private: + HdmiIn (INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + INotification& _parent; + +public: + template + static HdmiIn Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dHdmiIn::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return HdmiIn(parent, std::move(impl)); + } + + void OnHDMIInHotPlugEvent(const HDMIInPort port, const bool isConnected); + void OnHDMIInSignalStatusEvent(const HDMIInPort port, const HDMIInSignalStatus signalStatus); + void OnHDMIInStatusEvent(const HDMIInPort activePort, const bool isPresented); + void OnHDMIInVideoModeUpdateEvent(const HDMIInPort port, const HDMIVideoPortResolution videoPortResolution); + void OnHDMIInAllmStatusEvent(const HDMIInPort port, const bool allmStatus); + void OnHDMIInAVIContentTypeEvent(const HDMIInPort port, const HDMIInAviContentType aviContentType); + void OnHDMIInAVLatencyEvent(const int32_t audioDelay, const int32_t videoDelay); + void OnHDMIInVRRStatusEvent(const HDMIInPort port, const HDMIInVRRType vrrType); + ~HdmiIn() {}; + +}; diff --git a/plugin/Host.cpp b/plugin/Host.cpp new file mode 100644 index 0000000..c251204 --- /dev/null +++ b/plugin/Host.cpp @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "Host.h" +#include "hal/dHostImpl.h" + +Host::Host(std::shared_ptr platform) + : _platform(std::move(platform)) +{ + LOGINFO("Host Constructor"); + Platform_init(); +} + +Host Host::Create() { + return Host(std::make_shared()); +} + +void Host::Platform_init() +{ + LOGINFO("Host Init - Setting up event callbacks"); + + CallbackBundle bundle; + if (_platform) { + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } +} + +uint32_t Host::GetEDID(uint8_t edId[], const uint16_t edIdLength) { + LOGINFO("GetEDID: edIdLength=%u", edIdLength); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetEDID(edId, edIdLength); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetEDID: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetEDID: FAILED - result=%u", result); + } + return result; +} + +uint32_t Host::GetMS12ConfigType(string &ms12Config) { + LOGINFO("GetMS12ConfigType"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetMS12ConfigType(ms12Config); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetMS12ConfigType: SUCCESS - ms12Config='%s'", ms12Config.c_str()); + } else { + LOGERR("GetMS12ConfigType: FAILED - result=%u", result); + } + return result; +} // namespace end \ No newline at end of file diff --git a/plugin/Host.h b/plugin/Host.h new file mode 100644 index 0000000..c5dab0b --- /dev/null +++ b/plugin/Host.h @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "hal/dHost.h" +#include "hal/dHostImpl.h" +#include "DeviceSettingsTypes.h" + +class Host { + using IPlatform = hal::dHost::IPlatform; + using DefaultImpl = dHostImpl; + + std::shared_ptr _platform; + +public: + Host(std::shared_ptr platform = nullptr); + + static Host Create(); + + Host(const Host&) = default; + Host& operator=(const Host&) = default; + Host(Host&&) = default; + Host& operator=(Host&&) = default; + + uint32_t GetEDID(uint8_t edId[], const uint16_t edIdLength); + uint32_t GetMS12ConfigType(string &ms12Config); + + IPlatform& platform() { return *_platform; } + +private: + void Platform_init(); + +public: + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } +}; \ No newline at end of file diff --git a/plugin/Module.cpp b/plugin/Module.cpp new file mode 100644 index 0000000..713d4b1 --- /dev/null +++ b/plugin/Module.cpp @@ -0,0 +1,22 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2024 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. +*/ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/plugin/Module.h b/plugin/Module.h new file mode 100644 index 0000000..12a791f --- /dev/null +++ b/plugin/Module.h @@ -0,0 +1,29 @@ +/* +* If not stated otherwise in this file or this component's LICENSE file the +* following copyright and licenses apply: +* +* Copyright 2024 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. +*/ + +#pragma once +#ifndef MODULE_NAME +#define MODULE_NAME Plugin_DeviceSettingsManager +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/plugin/VideoDevice.cpp b/plugin/VideoDevice.cpp new file mode 100644 index 0000000..5177b4a --- /dev/null +++ b/plugin/VideoDevice.cpp @@ -0,0 +1,233 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "VideoDevice.h" +#include "hal/dVideoDeviceImpl.h" + +VideoDevice::VideoDevice(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("VideoDevice Constructor"); + Platform_init(); +} + +void VideoDevice::Platform_init() +{ + LOGINFO("VideoDevice Init - Setting up event callbacks"); + + // Set up callback bundle for VideoDevice events - using global CallbackBundle pattern + CallbackBundle bundle; + + bundle.OnZoomSettingsChanged = [this](const VideoDeviceZoom zoomSetting) { + this->OnZoomSettingsChanged(zoomSetting); + }; + bundle.OnDisplayFrameratePreChange = [this](const string frameRate) { + this->OnDisplayFrameratePreChange(frameRate); + }; + bundle.OnDisplayFrameratePostChange = [this](const string frameRate) { + this->OnDisplayFrameratePostChange(frameRate); + }; + + if (_platform) { + // Use interface method directly - no casting needed + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } + +} + +uint32_t VideoDevice::GetVideoDeviceHandle(const int32_t index, int32_t &handle) { + LOGINFO("GetVideoDeviceHandle: index=%d", index); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoDeviceHandle(index, handle); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoDeviceHandle: SUCCESS - platform call completed successfully, handle=%d", handle); + } else { + LOGERR("GetVideoDeviceHandle: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting) { + LOGINFO("SetVideoDeviceDFC: handle=%d, zoomSetting=%d", handle, static_cast(zoomSetting)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetVideoDeviceDFC(handle, zoomSetting); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetVideoDeviceDFC: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetVideoDeviceDFC: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom &zoomSetting) { + LOGINFO("GetVideoDeviceDFC: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoDeviceDFC(handle, zoomSetting); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoDeviceDFC: SUCCESS - zoomSetting=%d", static_cast(zoomSetting)); + } else { + LOGERR("GetVideoDeviceDFC: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetHDRCapabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetHDRCapabilities: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetHDRCapabilities(handle, capabilities); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetHDRCapabilities: SUCCESS - capabilities=0x%x", capabilities); + } else { + LOGERR("GetHDRCapabilities: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetSupportedVideoCodingFormats(const int32_t handle, int32_t &supportedFormats) { + LOGINFO("GetSupportedVideoCodingFormats: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetSupportedVideoCodingFormats(handle, supportedFormats); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetSupportedVideoCodingFormats: SUCCESS - supportedFormats=0x%x", supportedFormats); + } else { + LOGERR("GetSupportedVideoCodingFormats: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo) { + LOGINFO("GetCodecInfo: handle=%d, videoCodec=%d", handle, static_cast(videoCodec)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetCodecInfo(handle, videoCodec, codecInfo); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetCodecInfo: SUCCESS - codecInfo returned"); + } else { + LOGERR("GetCodecInfo: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::DisableHDR(const int32_t handle, const bool disable) { + LOGINFO("DisableHDR: handle=%d, disable=%s", handle, disable ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().DisableHDR(handle, disable); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("DisableHDR: SUCCESS - platform call completed successfully"); + } else { + LOGERR("DisableHDR: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::SetFRFMode(const int32_t handle, const int32_t frfmode) { + LOGINFO("SetFRFMode: handle=%d, frfmode=%d", handle, frfmode); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFRFMode(handle, frfmode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFRFMode: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFRFMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetFRFMode(const int32_t handle, int32_t &frfmode) { + LOGINFO("GetFRFMode: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFRFMode(handle, frfmode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFRFMode: SUCCESS - frfmode=%d", frfmode); + } else { + LOGERR("GetFRFMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::GetCurrentDisplayFrameRate(const int32_t handle, string &framerate) { + LOGINFO("GetCurrentDisplayFrameRate: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetCurrentDisplayFrameRate(handle, framerate); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetCurrentDisplayFrameRate: SUCCESS - framerate=%s", framerate.c_str()); + } else { + LOGERR("GetCurrentDisplayFrameRate: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoDevice::SetDisplayFrameRate(const int32_t handle, const string framerate) { + LOGINFO("SetDisplayFrameRate: handle=%d, framerate=%s", handle, framerate.c_str()); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetDisplayFrameRate(handle, framerate); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetDisplayFrameRate: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetDisplayFrameRate: FAILED - result=%u", result); + } + return result; +} + +// VideoDevice event handlers - called by DS HAL to forward events to parent +void VideoDevice::OnZoomSettingsChanged(const VideoDeviceZoom zoomSetting) { + LOGINFO("DS HAL OnZoomSettingsChanged event: zoomSetting=%d", static_cast(zoomSetting)); + _parent.OnZoomSettingsChanged(zoomSetting); +} + +void VideoDevice::OnDisplayFrameratePreChange(const string frameRate) { + LOGINFO("DS HAL OnDisplayFrameratePreChange event: frameRate=%s", frameRate.c_str()); + _parent.OnDisplayFrameratePreChange(frameRate); +} + +void VideoDevice::OnDisplayFrameratePostChange(const string frameRate) { + LOGINFO("DS HAL OnDisplayFrameratePostChange event: frameRate=%s", frameRate.c_str()); + _parent.OnDisplayFrameratePostChange(frameRate); +} \ No newline at end of file diff --git a/plugin/VideoDevice.h b/plugin/VideoDevice.h new file mode 100644 index 0000000..c279b25 --- /dev/null +++ b/plugin/VideoDevice.h @@ -0,0 +1,104 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsVideoDevice.h" + +#include "hal/dVideoDevice.h" +#include "hal/dVideoDeviceImpl.h" +#include "DeviceSettingsTypes.h" + +class VideoDevice { + using IPlatform = hal::dVideoDevice::IPlatform; + using DefaultImpl = dVideoDeviceImpl; + + std::shared_ptr _platform; + +public: + class INotification { + + public: + virtual ~INotification() = default; + virtual void OnZoomSettingsChanged(const VideoDeviceZoom zoomSetting) = 0; + virtual void OnDisplayFrameratePreChange(const string frameRate) = 0; + virtual void OnDisplayFrameratePostChange(const string frameRate) = 0; + }; + +public: + + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + uint32_t GetVideoDeviceHandle(const int32_t index, int32_t &handle); + uint32_t SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting); + uint32_t GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom &zoomSetting); + uint32_t GetHDRCapabilities(const int32_t handle, int32_t &capabilities); + uint32_t GetSupportedVideoCodingFormats(const int32_t handle, int32_t &supportedFormats); + uint32_t GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo); + uint32_t DisableHDR(const int32_t handle, const bool disable); + uint32_t SetFRFMode(const int32_t handle, const int32_t frfmode); + uint32_t GetFRFMode(const int32_t handle, int32_t &frfmode); + uint32_t GetCurrentDisplayFrameRate(const int32_t handle, string &framerate); + uint32_t SetDisplayFrameRate(const int32_t handle, const string framerate); + + // VideoDevice event handling methods - Called by DS HAL to forward events to parent + void OnZoomSettingsChanged(const VideoDeviceZoom zoomSetting); + void OnDisplayFrameratePreChange(const string frameRate); + void OnDisplayFrameratePostChange(const string frameRate); + + template + static VideoDevice Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dVideoDevice::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return VideoDevice(parent, std::move(impl)); + } + + private: + VideoDevice(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + INotification& _parent; +}; \ No newline at end of file diff --git a/plugin/VideoPort.cpp b/plugin/VideoPort.cpp new file mode 100644 index 0000000..5ca8ac0 --- /dev/null +++ b/plugin/VideoPort.cpp @@ -0,0 +1,637 @@ +/* + * 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. + */ + +#include +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "VideoPort.h" +#include "hal/dVideoPortImpl.h" + +VideoPort::VideoPort(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("VideoPort Constructor"); + Platform_init(); +} + +void VideoPort::Platform_init() +{ + LOGINFO("VideoPort Init - Setting up event callbacks"); + + // Set up callback bundle for VideoPort events - using global CallbackBundle pattern + CallbackBundle bundle; + + bundle.OnResolutionPreChange = [this](const ResolutionChange resolution) { + this->OnResolutionPreChange(resolution); + }; + bundle.OnResolutionPostChange = [this](const ResolutionChange resolution) { + this->OnResolutionPostChange(resolution); + }; + bundle.OnHDCPStatusChange = [this](const VideoPortHdcpStatus hdcpStatus) { + this->OnHDCPStatusChange(hdcpStatus); + }; + bundle.OnVideoFormatUpdate = [this](const HDRStandard videoFormatHDR) { + this->OnVideoFormatUpdate(videoFormatHDR); + }; + + if (_platform) { + // Use interface method directly - no casting needed + this->platform().setAllCallbacks(bundle); + this->platform().getPersistenceValue(); + } + +} + +uint32_t VideoPort::GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle) { + LOGINFO("GetVideoPort: videoPort=%d, index=%d", static_cast(videoPort), index); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoPort(videoPort, index, handle); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoPort: SUCCESS - platform call completed successfully, handle=%d", handle); + } else { + LOGERR("GetVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsVideoPortEnabled(const int32_t handle, bool &enabled) { + LOGINFO("IsVideoPortEnabled: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsVideoPortEnabled(handle, enabled); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsVideoPortEnabled: SUCCESS - enabled=%s", enabled ? "true" : "false"); + } else { + LOGERR("IsVideoPortEnabled: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::EnableVideoPort(const int32_t handle, const bool enabled) { + LOGINFO("EnableVideoPort: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().EnableVideoPort(handle, enabled); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("EnableVideoPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("EnableVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsVideoPortDisplayConnected(const int32_t handle, bool &connected) { + LOGINFO("IsVideoPortDisplayConnected: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsVideoPortDisplayConnected(handle, connected); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsVideoPortDisplayConnected: SUCCESS - connected=%s", connected ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplayConnected: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsVideoPortActive(const int32_t handle, bool &active) { + LOGINFO("IsVideoPortActive: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsVideoPortActive(handle, active); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsVideoPortActive: SUCCESS - active=%s", active ? "true" : "false"); + } else { + LOGERR("IsVideoPortActive: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution) { + LOGINFO("GetVideoPortResolution: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoPortResolution(handle, resolution); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoPortResolution: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetVideoPortResolution: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetColorDepth(const int32_t handle, uint32_t &colorDepth) { + LOGINFO("GetColorDepth: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetColorDepth(handle, colorDepth); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetColorDepth: SUCCESS - colorDepth=%u", colorDepth); + } else { + LOGERR("GetColorDepth: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetVideoPortColorDepth(const int32_t handle, const uint32_t colorDepth) { + LOGINFO("SetVideoPortColorDepth: handle=%d, colorDepth=%u", handle, colorDepth); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetVideoPortColorDepth(handle, colorDepth); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetVideoPortColorDepth: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetVideoPortColorDepth: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange) { + LOGINFO("GetQuantizationRange: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetQuantizationRange(handle, quantizationRange); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetQuantizationRange: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetQuantizationRange: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetVideoPortQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange) { + LOGINFO("SetVideoPortQuantizationRange: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetVideoPortQuantizationRange(handle, quantizationRange); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetVideoPortQuantizationRange: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetVideoPortQuantizationRange: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace) { + LOGINFO("GetColorSpace: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetColorSpace(handle, colorSpace); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetColorSpace: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetColorSpace: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace) { + LOGINFO("SetColorSpace: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetColorSpace(handle, colorSpace); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetColorSpace: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetColorSpace: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetVideoPortFrameRate(const int32_t handle, uint32_t &frameRate) { + LOGINFO("GetVideoPortFrameRate: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoPortFrameRate(handle, frameRate); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoPortFrameRate: SUCCESS - frameRate=%u", frameRate); + } else { + LOGERR("GetVideoPortFrameRate: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetVideoPortFrameRate(const int32_t handle, const uint32_t frameRate) { + LOGINFO("SetVideoPortFrameRate: handle=%d, frameRate=%u", handle, frameRate); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetVideoPortFrameRate(handle, frameRate); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetVideoPortFrameRate: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetVideoPortFrameRate: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus &hdcpStatus) { + LOGINFO("GetVideoPortHDCPStatus: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoPortHDCPStatus(handle, hdcpStatus); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoPortHDCPStatus: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetVideoPortHDCPStatus: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + LOGINFO("GetHDCPProtocolVersionOnVideoPort: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetHDCPProtocolVersionOnVideoPort(handle, hdcpVersion); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetHDCPProtocolVersionOnVideoPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetHDCPProtocolVersionOnVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + LOGINFO("GetHDCPReceiverProtocolVersionOnVideoPort: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetHDCPReceiverProtocolVersionOnVideoPort(handle, hdcpVersion); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetHDCPReceiverProtocolVersionOnVideoPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetHDCPReceiverProtocolVersionOnVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + LOGINFO("GetHDCPCurrentProtocolVersionOnVideoPort: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetHDCPCurrentProtocolVersionOnVideoPort(handle, hdcpVersion); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetHDCPCurrentProtocolVersionOnVideoPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetHDCPCurrentProtocolVersionOnVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetVideoPortResolution(const int32_t handle, const VideoPortResolution& resolution, const bool persist, const bool forceCompatibility) { + LOGINFO("SetVideoPortResolution: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetVideoPortResolution(handle, resolution, persist, forceCompatibility); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetVideoPortResolution: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetVideoPortResolution: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t* hdcpKey, const uint16_t hdcpKeySize) { + LOGINFO("EnableHDCPOnVideoPort: handle=%d, hdcpEnable=%s", handle, hdcpEnable ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().EnableHDCPOnVideoPort(handle, hdcpEnable, hdcpKey, hdcpKeySize); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("EnableHDCPOnVideoPort: SUCCESS - platform call completed successfully"); + } else { + LOGERR("EnableHDCPOnVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled) { + LOGINFO("IsHDCPEnabledOnVideoPort: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsHDCPEnabledOnVideoPort(handle, hdcpEnabled); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsHDCPEnabledOnVideoPort: SUCCESS - hdcpEnabled=%s", hdcpEnabled ? "true" : "false"); + } else { + LOGERR("IsHDCPEnabledOnVideoPort: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities) { + LOGINFO("GetTVHDRCapabilities: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetTVHDRCapabilities(handle, capabilities); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetTVHDRCapabilities: SUCCESS - capabilities=0x%x", capabilities); + } else { + LOGERR("GetTVHDRCapabilities: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions) { + LOGINFO("GetTVSupportedResolutions: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetTVSupportedResolutions(handle, resolutions); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetTVSupportedResolutions: SUCCESS - resolutions=0x%x", resolutions); + } else { + LOGERR("GetTVSupportedResolutions: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetForceDisable4K(const int32_t handle, const bool disable) { + LOGINFO("SetForceDisable4K: handle=%d, disable=%s", handle, disable ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetForceDisable4K(handle, disable); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetForceDisable4K: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetForceDisable4K: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetForceDisable4K(const int32_t handle, bool &disabled) { + LOGINFO("GetForceDisable4K: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetForceDisable4K(handle, disabled); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetForceDisable4K: SUCCESS - disabled=%s", disabled ? "true" : "false"); + } else { + LOGERR("GetForceDisable4K: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsVideoPortOutputHDR(const int32_t handle, bool &isHDR) { + LOGINFO("IsVideoPortOutputHDR: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsVideoPortOutputHDR(handle, isHDR); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsVideoPortOutputHDR: SUCCESS - isHDR=%s", isHDR ? "true" : "false"); + } else { + LOGERR("IsVideoPortOutputHDR: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::ResetVideoPortOutputToSDR() { + LOGINFO("ResetVideoPortOutputToSDR"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().ResetVideoPortOutputToSDR(); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("ResetVideoPortOutputToSDR: SUCCESS - platform call completed successfully"); + } else { + LOGERR("ResetVideoPortOutputToSDR: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion) { + LOGINFO("GetHDMIPreference: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetHDMIPreference(handle, hdcpVersion); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetHDMIPreference: SUCCESS - hdcpVersion=%d", static_cast(hdcpVersion)); + } else { + LOGERR("GetHDMIPreference: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) { + LOGINFO("SetHDMIPreference: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetHDMIPreference(handle, hdcpVersion); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetHDMIPreference: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetHDMIPreference: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetVideoEOTF(const int32_t handle, HDRStandard &hdrStandard) { + LOGINFO("GetVideoEOTF: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoEOTF(handle, hdrStandard); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoEOTF: SUCCESS - hdrStandard=%d", static_cast(hdrStandard)); + } else { + LOGERR("GetVideoEOTF: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients &matrixCoefficients) { + LOGINFO("GetMatrixCoefficients: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetMatrixCoefficients(handle, matrixCoefficients); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetMatrixCoefficients: SUCCESS - matrixCoefficients=%d", static_cast(matrixCoefficients)); + } else { + LOGERR("GetMatrixCoefficients: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::IsVideoPortDisplaySurround(const int32_t handle, bool &surround) { + LOGINFO("IsVideoPortDisplaySurround: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().IsVideoPortDisplaySurround(handle, surround); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("IsVideoPortDisplaySurround: SUCCESS - surround=%s", surround ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplaySurround: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode &surroundMode) { + LOGINFO("GetVideoPortDisplaySurroundMode: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetVideoPortDisplaySurroundMode(handle, surroundMode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetVideoPortDisplaySurroundMode: SUCCESS - surroundMode=%d", static_cast(surroundMode)); + } else { + LOGERR("GetVideoPortDisplaySurroundMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetCurrentOutputSettings(const int32_t handle, DSOutputSettings &outputSettings) { + LOGINFO("GetCurrentOutputSettings: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetCurrentOutputSettings(handle, outputSettings); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetCurrentOutputSettings: SUCCESS - platform call completed successfully"); + } else { + LOGERR("GetCurrentOutputSettings: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor) { + LOGINFO("SetBackgroundColor: handle=%d, backgroundColor=%d", handle, static_cast(backgroundColor)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetBackgroundColor(handle, backgroundColor); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetBackgroundColor: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetBackgroundColor: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode) { + LOGINFO("SetForceHDRMode: handle=%d, hdrMode=%d", handle, static_cast(hdrMode)); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetForceHDRMode(handle, hdrMode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetForceHDRMode: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetForceHDRMode: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities) { + LOGINFO("GetColorDepthCapabilities: handle=%d", handle); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetColorDepthCapabilities(handle, colorDepthCapabilities); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetColorDepthCapabilities: SUCCESS - colorDepthCapabilities=0x%x", colorDepthCapabilities); + } else { + LOGERR("GetColorDepthCapabilities: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::GetPreferredColorDepth(const int32_t handle, DisplayColorDepth &colorDepth, const bool persist) { + LOGINFO("GetPreferredColorDepth: handle=%d, persist=%s", handle, persist ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetPreferredColorDepth(handle, colorDepth, persist); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetPreferredColorDepth: SUCCESS - colorDepth=%d", static_cast(colorDepth)); + } else { + LOGERR("GetPreferredColorDepth: FAILED - result=%u", result); + } + return result; +} + +uint32_t VideoPort::SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist) { + LOGINFO("SetPreferredColorDepth: handle=%d, colorDepth=%d, persist=%s", handle, static_cast(colorDepth), persist ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetPreferredColorDepth(handle, colorDepth, persist); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetPreferredColorDepth: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetPreferredColorDepth: FAILED - result=%u", result); + } + return result; +} + +// VideoPort event handling methods - Forward DS HAL events to parent notification system +void VideoPort::OnResolutionPreChange(const ResolutionChange resolution) +{ + LOGINFO("VideoPort::OnResolutionPreChange: forwarding to parent"); + _parent.OnResolutionPreChange(resolution); +} + +void VideoPort::OnResolutionPostChange(const ResolutionChange resolution) +{ + LOGINFO("VideoPort::OnResolutionPostChange: forwarding to parent"); + _parent.OnResolutionPostChange(resolution); +} + +void VideoPort::OnHDCPStatusChange(const VideoPortHdcpStatus hdcpStatus) +{ + LOGINFO("VideoPort::OnHDCPStatusChange: forwarding to parent"); + _parent.OnHDCPStatusChange(hdcpStatus); +} + +void VideoPort::OnVideoFormatUpdate(const HDRStandard videoFormatHDR) +{ + LOGINFO("VideoPort::OnVideoFormatUpdate: forwarding to parent"); + _parent.OnVideoFormatUpdate(videoFormatHDR); +} \ No newline at end of file diff --git a/plugin/VideoPort.h b/plugin/VideoPort.h new file mode 100644 index 0000000..9cc0362 --- /dev/null +++ b/plugin/VideoPort.h @@ -0,0 +1,135 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsDisplay.h" +#include "dsVideoPort.h" + +#include "hal/dVideoPort.h" +#include "hal/dVideoPortImpl.h" +#include "DeviceSettingsTypes.h" + +class VideoPort { + using IPlatform = hal::dVideoPort::IPlatform; + using DefaultImpl = dVideoPortImpl; + + std::shared_ptr _platform; + +public: + class INotification { + + public: + virtual ~INotification() = default; + virtual void OnResolutionPreChange(const ResolutionChange resolution) = 0; + virtual void OnResolutionPostChange(const ResolutionChange resolution) = 0; + virtual void OnHDCPStatusChange(const VideoPortHdcpStatus hdcpStatus) = 0; + virtual void OnVideoFormatUpdate(const HDRStandard videoFormatHDR) = 0; + }; + +public: + + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + uint32_t GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t &handle); + uint32_t IsVideoPortEnabled(const int32_t handle, bool &enabled); + uint32_t EnableVideoPort(const int32_t handle, const bool enabled); + uint32_t IsVideoPortDisplayConnected(const int32_t handle, bool &connected); + uint32_t IsVideoPortActive(const int32_t handle, bool &active); + uint32_t GetVideoPortResolution(const int32_t handle, VideoPortResolution &resolution); + uint32_t SetVideoPortResolution(const int32_t handle, const VideoPortResolution& resolution, const bool persist, const bool forceCompatibility); + uint32_t GetColorDepth(const int32_t handle, uint32_t &colorDepth); + uint32_t SetVideoPortColorDepth(const int32_t handle, const uint32_t colorDepth); + uint32_t GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange &quantizationRange); + uint32_t SetVideoPortQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange); + uint32_t GetColorSpace(const int32_t handle, VideoPortColorSpace &colorSpace); + uint32_t SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace); + uint32_t GetVideoPortFrameRate(const int32_t handle, uint32_t &frameRate); + uint32_t SetVideoPortFrameRate(const int32_t handle, const uint32_t frameRate); + uint32_t GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus &hdcpStatus); + uint32_t GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t* hdcpKey, const uint16_t hdcpKeySize); + uint32_t IsHDCPEnabledOnVideoPort(const int32_t handle, bool &hdcpEnabled); + uint32_t GetTVHDRCapabilities(const int32_t handle, int32_t &capabilities); + uint32_t GetTVSupportedResolutions(const int32_t handle, int32_t &resolutions); + uint32_t SetForceDisable4K(const int32_t handle, const bool disable); + uint32_t GetForceDisable4K(const int32_t handle, bool &disabled); + uint32_t IsVideoPortOutputHDR(const int32_t handle, bool &isHDR); + uint32_t ResetVideoPortOutputToSDR(); + uint32_t GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion &hdcpVersion); + uint32_t SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion); + uint32_t GetVideoEOTF(const int32_t handle, HDRStandard &hdrStandard); + uint32_t GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients &matrixCoefficients); + uint32_t IsVideoPortDisplaySurround(const int32_t handle, bool &surround); + uint32_t GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode &surroundMode); + uint32_t GetCurrentOutputSettings(const int32_t handle, DSOutputSettings &outputSettings); + uint32_t SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor); + uint32_t SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode); + uint32_t GetColorDepthCapabilities(const int32_t handle, uint32_t &colorDepthCapabilities); + uint32_t GetPreferredColorDepth(const int32_t handle, DisplayColorDepth &colorDepth, const bool persist); + uint32_t SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist); + + // VideoPort event handling methods - Called by DS HAL to forward events to parent + void OnResolutionPreChange(const ResolutionChange resolution); + void OnResolutionPostChange(const ResolutionChange resolution); + void OnHDCPStatusChange(const VideoPortHdcpStatus hdcpStatus); + void OnVideoFormatUpdate(const HDRStandard videoFormatHDR); + + template + static VideoPort Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dVideoPort::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return VideoPort(parent, std::move(impl)); + } + + private: + VideoPort(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + INotification& _parent; +}; \ No newline at end of file diff --git a/plugin/fpd.cpp b/plugin/fpd.cpp new file mode 100755 index 0000000..83fd74d --- /dev/null +++ b/plugin/fpd.cpp @@ -0,0 +1,267 @@ +/* + * 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. + */ + +#include +#include + +#include +#include +#include + +#include "secure_wrapper.h" +#include "fpd.h" + +FPD::FPD(INotification& parent, std::shared_ptr platform) + : _platform(std::move(platform)) + , _parent(parent) +{ + LOGINFO("FPD Constructor"); + Platform_init(); +} + +void FPD::Platform_init() +{ + // Initialize FPD platform + LOGINFO("FPD Init"); +} + +//Depricated +uint32_t FPD::SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) { + LOGINFO("SetFPDTime: timeFormat=%d, minutes=%u, seconds=%u", timeFormat, minutes, seconds); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDTime(timeFormat, minutes, seconds); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDTime: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDTime: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) { + LOGINFO("SetFPDScroll: scrollHoldDuration=%u, horizontal=%u, vertical=%u", scrollHoldDuration, nHorizontalScrollIterations, nVerticalScrollIterations); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDScroll(scrollHoldDuration, nHorizontalScrollIterations, nVerticalScrollIterations); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDScroll: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDScroll: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) { + LOGINFO("SetFPDTextBrightness: textDisplay=%d, brightNess=%u", textDisplay, brightNess); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDTextBrightness(textDisplay, brightNess); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDTextBrightness: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDTextBrightness: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) { + LOGINFO("GetFPDTextBrightness: textDisplay=%d", textDisplay); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFPDTextBrightness(textDisplay, brightNess); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFPDTextBrightness: SUCCESS - textDisplay=%d, brightNess=%u", textDisplay, brightNess); + } else { + LOGERR("GetFPDTextBrightness: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::EnableFPDClockDisplay(const bool enable) { + LOGINFO("EnableFPDClockDisplay: enable=%s", enable ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().EnableFPDClockDisplay(enable); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("EnableFPDClockDisplay: SUCCESS - platform call completed successfully"); + } else { + LOGERR("EnableFPDClockDisplay: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) { + LOGINFO("GetFPDTimeFormat"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFPDTimeFormat(fpdTimeFormat); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFPDTimeFormat: SUCCESS - fpdTimeFormat=%d", fpdTimeFormat); + } else { + LOGERR("GetFPDTimeFormat: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) { + LOGINFO("SetFPDTimeFormat: fpdTimeFormat=%d", fpdTimeFormat); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDTimeFormat(fpdTimeFormat); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDTimeFormat: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDTimeFormat: FAILED - result=%u", result); + } + return result; +} +//Depricated + +uint32_t FPD::SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) { + + LOGINFO("SetFPDBlink: indicator=%d, blinkDuration=%u, blinkIterations:%u", indicator, blinkDuration, blinkIterations); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDBlink(indicator, blinkDuration, blinkIterations); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDBlink: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDBlink: FAILED - result=%u", result); + } + + return result; +} + +uint32_t FPD::GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) { + + LOGINFO("GetFPDBrightness: indicator=%d", indicator); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFPDBrightness(indicator, brightNess); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFPDBrightness: SUCCESS - indicator=%d, brightNess=%d", indicator, brightNess); + } else { + LOGERR("GetFPDBrightness: FAILED - result=%u", result); + } + + return result; +} + +uint32_t FPD::SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist) { + + LOGINFO("SetFPDBrightness: indicator=%d, brightNess=%u, persist=%s", indicator, brightNess, persist ? "true" : "false"); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDBrightness(indicator, brightNess, persist); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDBrightness: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDBrightness: FAILED - result=%u", result); + } + + return result; +} + +uint32_t FPD::GetFPDState(const FPDIndicator indicator, FPDState &state) { + + LOGINFO("GetFPDState: indicator=%d", indicator); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFPDState(indicator, state); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFPDState: SUCCESS - indicator=%d, state=%d", indicator, state); + } else { + LOGERR("GetFPDState: FAILED - result=%u", result); + } + + return result; +} + +uint32_t FPD::SetFPDState(const FPDIndicator indicator, const FPDState state) { + + LOGINFO("SetFPDState: indicator=%d, state=%d", indicator, state); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDState(indicator, state); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDState: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDState: FAILED - result=%u", result); + } + + return result; +} + +uint32_t FPD::GetFPDColor(const FPDIndicator indicator, uint32_t &color) { + + LOGINFO("GetFPDColor: indicator=%d", indicator); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().GetFPDColor(indicator, color); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("GetFPDColor: SUCCESS - indicator=%d, colour=%d", indicator, color); + } else { + LOGERR("GetFPDColor: FAILED - result=%u", result); + } + return result; +} + +uint32_t FPD::SetFPDColor(const FPDIndicator indicator, const uint32_t color) { + + LOGINFO("SetFPDColor: indicator=%d, colour=%d", indicator, color); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDColor(indicator, color); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDColor: SUCCESS - indicator=%d, colour=%d", indicator, color); + } else { + LOGERR("SetFPDColor: FAILED - indicator=%d, colour=%d, result=%u", indicator, color, result); + } + + return result; +} + +uint32_t FPD::SetFPDMode(const FPDMode fpdMode) { + LOGINFO("SetFPDMode: fpdMode=%d", fpdMode); + uint32_t result = WPEFramework::Core::ERROR_GENERAL; + if (_platform) { + result = this->platform().SetFPDMode(fpdMode); + } + if (result == WPEFramework::Core::ERROR_NONE) { + LOGINFO("SetFPDMode: SUCCESS - platform call completed successfully"); + } else { + LOGERR("SetFPDMode: FAILED - result=%u", result); + } + return result; +} diff --git a/plugin/fpd.h b/plugin/fpd.h new file mode 100755 index 0000000..a0b1eab --- /dev/null +++ b/plugin/fpd.h @@ -0,0 +1,102 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include "dsUtl.h" +#include "dsError.h" +#include "dsDisplay.h" +#include "dsFPDTypes.h" + +#include "hal/dFPD.h" +#include "hal/dFPDImpl.h" +#include "DeviceSettingsTypes.h" + +class FPD { + using IPlatform = hal::dFPD::IPlatform; + using DefaultImpl = dFPDImpl; + + std::shared_ptr _platform; + +public: + class INotification { + + public: + virtual ~INotification() = default; + virtual void OnFPDTimeFormatChanged(const FPDTimeFormat timeFormat) = 0; + }; + +public: + + void Platform_init(); + /** Deferred HAL init — called from DeviceSettingsImp::Configure() */ + void InitialiseHAL() { std::static_pointer_cast(_platform)->InitialiseHAL(); } + + uint32_t SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds); + uint32_t SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations); + uint32_t SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations); + uint32_t SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist); + uint32_t GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess); + uint32_t SetFPDState(const FPDIndicator indicator, const FPDState state); + uint32_t GetFPDState(const FPDIndicator indicator, FPDState &state); + uint32_t GetFPDColor(const FPDIndicator indicator, uint32_t &color); + uint32_t SetFPDColor(const FPDIndicator indicator, const uint32_t color); + uint32_t SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess); + uint32_t GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess); + uint32_t EnableFPDClockDisplay(const bool enable); + uint32_t GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat); + uint32_t SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat); + uint32_t SetFPDMode(const FPDMode fpdMode); + + template + static FPD Create(INotification& parent, Args&&... args) + { + ENTRY_LOG; + static_assert(std::is_base_of::value, "Impl must derive from hal::dFPD::IPlatform"); + auto impl = std::shared_ptr(new IMPL(std::forward(args)...)); + ASSERT(impl != nullptr); + EXIT_LOG; + return FPD(parent, std::move(impl)); + } + + private: + FPD(INotification& parent, std::shared_ptr platform); + + inline IPlatform& platform() const + { + return *_platform; + } + + INotification& _parent; +}; diff --git a/plugin/hal/dAudio.h b/plugin/hal/dAudio.h new file mode 100644 index 0000000..97cc6a5 --- /dev/null +++ b/plugin/hal/dAudio.h @@ -0,0 +1,204 @@ +/* + * 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. + */ +#pragma once + +#include "dsAudio.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" +#include + +#include +#include +#include "Module.h" +#include "DeviceSettingsTypes.h" + +using namespace WPEFramework::Exchange; + +namespace hal { +namespace dAudio { + + class IPlatform { + + public: + virtual ~IPlatform() = default; + + // Callback management + virtual void setAllCallbacks(const CallbackBundle bundle) = 0; + virtual void getPersistenceValue() = 0; + + // Static callback functions for HAL events + static void audioOutPortConnectCallback(dsAudioPortType_t portType, unsigned int uiPortNo, bool isPortConnected); + static void audioFormatUpdateCallback(dsAudioFormat_t audioFormat); + static void audioAtmosCapsChangeCallback(dsATMOSCapability_t atmosCaps, bool status); + + // Event notification functions (static helpers) + static void notifyAssociatedAudioMixingChanged(bool mixing); + static void notifyAudioFaderControlChanged(int32_t mixerBalance); + static void notifyAudioPrimaryLanguageChanged(const std::string& primaryLanguage); + static void notifyAudioSecondaryLanguageChanged(const std::string& secondaryLanguage); + static void notifyAudioPortStateChanged(AudioPortType portType, bool enabled); + static void notifyAudioLevelChanged(int32_t audioLevel); + static void notifyAudioModeChanged(AudioPortType portType, AudioStereoMode mode); + + // Audio Platform interface methods - all pure virtual + virtual uint32_t GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) = 0; + // GetAudioPorts and GetSupportedAudioPorts methods removed - iterator type doesn't exist in interface + virtual uint32_t GetAudioCapabilities(const int32_t handle, int32_t &capabilities) = 0; + virtual uint32_t GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities) = 0; + + // Audio format and encoding + virtual uint32_t GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) = 0; + virtual uint32_t GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) = 0; + virtual uint32_t GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) = 0; + virtual uint32_t GetAudioCompression(const int32_t handle, AudioCompression &compression) = 0; + virtual uint32_t SetAudioCompression(const int32_t handle, const AudioCompression compression) = 0; + + // Audio level and volume control + virtual uint32_t SetAudioLevel(const int32_t handle, const float audioLevel) = 0; + virtual uint32_t GetAudioLevel(const int32_t handle, float &audioLevel) = 0; + virtual uint32_t SetAudioGain(const int32_t handle, const float gainLevel) = 0; + virtual uint32_t GetAudioGain(const int32_t handle, float &gainLevel) = 0; + virtual uint32_t SetAudioMute(const int32_t handle, const bool mute) = 0; + virtual uint32_t IsAudioMuted(const int32_t handle, bool &muted) = 0; + + // Audio ducking + virtual uint32_t SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) = 0; + + // Stereo mode (needs to use AudioStereoMode to avoid HAL conflict) + virtual uint32_t GetStereoMode(const int32_t handle, AudioStereoMode &mode) = 0; + virtual uint32_t SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) = 0; + + // Associated audio mixing + virtual uint32_t SetAssociatedAudioMixing(const int32_t handle, const bool mixing) = 0; + virtual uint32_t GetAssociatedAudioMixing(const int32_t handle, bool &mixing) = 0; + + // Audio fader control + virtual uint32_t SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance) = 0; + virtual uint32_t GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance) = 0; + + // Audio language settings + virtual uint32_t SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage) = 0; + virtual uint32_t GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage) = 0; + virtual uint32_t SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage) = 0; + virtual uint32_t GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage) = 0; + + // Output connection status + virtual uint32_t IsAudioOutputConnected(const int32_t handle, bool &isConnected) = 0; + + // Dolby Atmos + virtual uint32_t GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability) = 0; + virtual uint32_t SetAudioAtmosOutputMode(const int32_t handle, const bool enable) = 0; + + // Audio port control + virtual uint32_t IsAudioPortEnabled(const int32_t handle, bool &enabled) = 0; + virtual uint32_t EnableAudioPort(const int32_t handle, const bool enable) = 0; + virtual uint32_t GetSupportedARCTypes(const int32_t handle, int32_t &types) = 0; + virtual uint32_t SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) = 0; + virtual uint32_t EnableARC(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::AudioARCStatus arcStatus) = 0; + + // Persistence + virtual uint32_t GetAudioEnablePersist(const int32_t handle, bool &enabled, std::string &portName) = 0; + virtual uint32_t SetAudioEnablePersist(const int32_t handle, const bool enable, const std::string portName) = 0; + + // MS decode status + virtual uint32_t IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) = 0; + virtual uint32_t IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) = 0; + + // LE config + virtual uint32_t GetAudioLEConfig(const int32_t handle, bool &enabled) = 0; + virtual uint32_t EnableAudioLEConfig(const int32_t handle, const bool enable) = 0; + + // Audio delay + virtual uint32_t SetAudioDelay(const int32_t handle, const uint32_t audioDelay) = 0; + virtual uint32_t GetAudioDelay(const int32_t handle, uint32_t &audioDelay) = 0; + virtual uint32_t SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) = 0; + virtual uint32_t GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) = 0; + + // Audio compression + virtual uint32_t SetAudioCompression(const int32_t handle, const int32_t compressionLevel) = 0; + virtual uint32_t GetAudioCompression(const int32_t handle, int32_t &compressionLevel) = 0; + + // Dialog enhancement + virtual uint32_t SetAudioDialogEnhancement(const int32_t handle, const int32_t level) = 0; + virtual uint32_t GetAudioDialogEnhancement(const int32_t handle, int32_t &level) = 0; + + // Dolby volume mode + virtual uint32_t SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) = 0; + virtual uint32_t GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) = 0; + + // Intelligent equalizer + virtual uint32_t SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) = 0; + virtual uint32_t GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) = 0; + + // Volume leveller + virtual uint32_t SetAudioVolumeLeveller(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::VolumeLeveller volumeLeveller) = 0; + virtual uint32_t GetAudioVolumeLeveller(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::VolumeLeveller &volumeLeveller) = 0; + + // Bass enhancer + virtual uint32_t SetAudioBassEnhancer(const int32_t handle, const int32_t boost) = 0; + virtual uint32_t GetAudioBassEnhancer(const int32_t handle, int32_t &boost) = 0; + + // Surround decoder + virtual uint32_t EnableAudioSurroudDecoder(const int32_t handle, const bool enable) = 0; + virtual uint32_t IsAudioSurroudDecoderEnabled(const int32_t handle, bool &enabled) = 0; + + // DRC mode + virtual uint32_t SetAudioDRCMode(const int32_t handle, const int32_t drcMode) = 0; + virtual uint32_t GetAudioDRCMode(const int32_t handle, int32_t &drcMode) = 0; + + // Surround virtualizer + virtual uint32_t SetAudioSurroudVirtualizer(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::SurroundVirtualizer surroundVirtualizer) = 0; + virtual uint32_t GetAudioSurroudVirtualizer(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::SurroundVirtualizer &surroundVirtualizer) = 0; + + // MI Steering + virtual uint32_t SetAudioMISteering(const int32_t handle, const bool enable) = 0; + virtual uint32_t GetAudioMISteering(const int32_t handle, bool &enable) = 0; + + // Graphic equalizer + virtual uint32_t SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) = 0; + virtual uint32_t GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) = 0; + + // MS12 profile + virtual uint32_t GetAudioMS12ProfileList(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const = 0; + virtual uint32_t GetAudioMS12Profile(const int32_t handle, std::string &profile) = 0; + virtual uint32_t SetAudioMS12Profile(const int32_t handle, const std::string& profile) = 0; + + // Mixer levels + virtual uint32_t SetAudioMixerLevels(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::AudioInput audioInput, const int32_t volume) = 0; + + // MS12 settings override + virtual uint32_t SetAudioMS12SettingsOverride(const int32_t handle, const std::string profileName, const std::string profileSettingsName, const std::string profileSettingValue, const std::string profileState) = 0; + + // Reset methods + virtual uint32_t ResetAudioDialogEnhancement(const int32_t handle) = 0; + virtual uint32_t ResetAudioBassEnhancer(const int32_t handle) = 0; + virtual uint32_t ResetAudioSurroundVirtualizer(const int32_t handle) = 0; + virtual uint32_t ResetAudioVolumeLeveller(const int32_t handle) = 0; + + // HDMI ARC Port ID + virtual uint32_t GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) = 0; + + // Stereo auto mode + virtual uint32_t GetStereoAuto(const int32_t handle, int32_t &mode) = 0; + virtual uint32_t SetStereoAuto(const int32_t handle, const int32_t mode, const bool persist) = 0; + }; + +} // namespace dAudio +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dAudioImpl.h b/plugin/hal/dAudioImpl.h new file mode 100644 index 0000000..9712d96 --- /dev/null +++ b/plugin/hal/dAudioImpl.h @@ -0,0 +1,5469 @@ +/* + * 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. + */ +#pragma once + +#include "dAudio.h" +#include "DeviceSettingsTypes.h" + +#include + +#include "dsAudio.h" +#include "dsError.h" +#include "dsTypes.h" +#include "dsUtl.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +// Static global callback functions following HdmiIn pattern +static std::function g_AudioOutHotPlugCallback; +static std::function g_AudioFormatUpdateCallback; +static std::function g_DolbyAtmosCapabilitiesChangedCallback; +static std::function g_AssociatedAudioMixingChangedCallback; +static std::function g_AudioFaderControlChangedCallback; +static std::function g_AudioPrimaryLanguageChangedCallback; +static std::function g_AudioSecondaryLanguageChangedCallback; +static std::function g_AudioPortStateChangedCallback; +static std::function g_AudioLevelChangedCallback; +static std::function g_AudioModeChangedCallback; + +/* LE (Loudness Equivalent) enable state — mirrors m_LEEnabled in dsAudio.c. + * Loaded from persistence at init, updated on each EnableAudioLEConfig call. */ +static bool m_LEEnabled = false; + +using namespace WPEFramework::Exchange; + +class dAudioImpl : public hal::dAudio::IPlatform { + +private: + // delete copy constructor and assignment operator + dAudioImpl(const dAudioImpl&) = delete; + dAudioImpl& operator=(const dAudioImpl&) = delete; + + bool _isInitialized; + + // Audio ducking state management + bool _isDuckingInProgress; + int32_t _volumeDuckingLevel; + bool _muteStatus; + + // Audio port state tracking + bool _audioPortEnabled[dsAUDIOPORT_TYPE_MAX]; + + // Helper method implementations for enabling audio port + dsAudioPortType_t getAudioPortType(intptr_t handle) + { + intptr_t halHandle = 0; + + // Simplified approach - check common port types + const dsAudioPortType_t portTypes[] = { + dsAUDIOPORT_TYPE_HDMI, + dsAUDIOPORT_TYPE_SPDIF, + dsAUDIOPORT_TYPE_SPEAKER, + dsAUDIOPORT_TYPE_HDMI_ARC, + dsAUDIOPORT_TYPE_HEADPHONE + }; + + for (int i = 0; i < 5; i++) { + if (dsGetAudioPort(portTypes[i], 0, &halHandle) == dsERR_NONE) { + if (handle == halHandle) { + return portTypes[i]; + } + } + } + + LOGWARN("The requested audio port is not part of platform port configuration"); + return dsAUDIOPORT_TYPE_MAX; + } + + uint32_t setAudioDuckingAudioLevel(intptr_t handle) + { + float volume = 0; + + if (_isDuckingInProgress) { + volume = _volumeDuckingLevel; + } else { + // Use resolve function for dsGetAudioLevel + typedef dsError_t (*dsGetAudioLevel_t)(intptr_t handle, float* level); + static dsGetAudioLevel_t dsGetAudioLevelFunc = 0; + if (dsGetAudioLevelFunc == 0) { + dsGetAudioLevelFunc = (dsGetAudioLevel_t)resolve(RDK_DSHAL_NAME, "dsGetAudioLevel"); + if (dsGetAudioLevelFunc == 0) { + LOGERR("dsGetAudioLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioLevelFunc) { + ret = dsGetAudioLevelFunc(handle, &volume); + } + if (ret != dsERR_NONE) { + LOGERR("dsGetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + LOGINFO("Current audio level: %f", volume); + } + + // Use resolve function for dsSetAudioLevel + typedef dsError_t (*dsSetAudioLevel_t)(intptr_t handle, float level); + static dsSetAudioLevel_t dsSetAudioLevelFunc = 0; + if (dsSetAudioLevelFunc == 0) { + dsSetAudioLevelFunc = (dsSetAudioLevel_t)resolve(RDK_DSHAL_NAME, "dsSetAudioLevel"); + if (dsSetAudioLevelFunc == 0) { + LOGERR("dsSetAudioLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioLevelFunc) { + ret = dsSetAudioLevelFunc(handle, volume); + } + + if (ret != dsERR_NONE) { + LOGERR("dsSetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t getAudioDelayInternal(dsAudioPortType_t portType) + { + std::string audioDelayMs = "0"; + uint32_t returnAudioDelayMs = 0; + + switch(portType) { + case dsAUDIOPORT_TYPE_SPDIF: + { + try { + audioDelayMs = device::HostPersistence::getInstance().getProperty("SPDIF0.audio.Delay"); + } + catch(...) { + try { + LOGINFO("SPDIF0.audio.Delay not found in persistence store. Try system default"); + audioDelayMs = device::HostPersistence::getInstance().getDefaultProperty("SPDIF0.audio.Delay"); + } + catch(...) { + audioDelayMs = "0"; + } + } + } + break; + case dsAUDIOPORT_TYPE_HDMI: + { + try { + audioDelayMs = device::HostPersistence::getInstance().getProperty("HDMI0.audio.Delay"); + } + catch(...) { + try { + LOGINFO("HDMI0.audio.Delay not found in persistence store. Try system default"); + audioDelayMs = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.audio.Delay"); + } + catch(...) { + audioDelayMs = "0"; + } + } + } + break; + case dsAUDIOPORT_TYPE_SPEAKER: + { + try { + audioDelayMs = device::HostPersistence::getInstance().getProperty("SPEAKER0.audio.Delay"); + } + catch(...) { + try { + LOGINFO("SPEAKER0.audio.Delay not found in persistence store. Try system default"); + audioDelayMs = device::HostPersistence::getInstance().getDefaultProperty("SPEAKER0.audio.Delay"); + } + catch(...) { + audioDelayMs = "0"; + } + } + } + break; + case dsAUDIOPORT_TYPE_HDMI_ARC: + { + try { + audioDelayMs = device::HostPersistence::getInstance().getProperty("HDMI_ARC0.audio.Delay"); + } + catch(...) { + try { + LOGINFO("HDMI_ARC0.audio.Delay not found in persistence store. Try system default"); + audioDelayMs = device::HostPersistence::getInstance().getDefaultProperty("HDMI_ARC0.audio.Delay"); + } + catch(...) { + audioDelayMs = "0"; + } + } + } + break; + default: + LOGINFO("Port type: UNKNOWN, persist audio delay: %s : NOT SET", audioDelayMs.c_str()); + break; + } + + try { + returnAudioDelayMs = std::stoul(audioDelayMs); + LOGINFO("Audio delay value returnAudioDelayMs: %d", returnAudioDelayMs); + } + catch(...) { + LOGINFO("Exception in getting the audio delay from persistence storage, returning default value 0"); + returnAudioDelayMs = 0; + } + + return returnAudioDelayMs; + } + + bool setAudioDelayInternal(intptr_t handle, uint32_t audioDelay) + { + try { + // Use resolve function for dsSetAudioDelay + typedef dsError_t (*dsSetAudioDelay_t)(intptr_t handle, uint32_t audioDelay); + static dsSetAudioDelay_t dsSetAudioDelayFunc = 0; + if (dsSetAudioDelayFunc == 0) { + dsSetAudioDelayFunc = (dsSetAudioDelay_t)resolve(RDK_DSHAL_NAME, "dsSetAudioDelay"); + if (dsSetAudioDelayFunc == 0) { + LOGERR("dsSetAudioDelay is not defined"); + return false; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioDelayFunc) { + ret = dsSetAudioDelayFunc(handle, audioDelay); + } + + if (ret == dsERR_NONE) { + LOGINFO("Audio delay set successfully: handle=%ld, delay=%u", (long)handle, audioDelay); + return true; + } else { + if (ret == dsERR_OPERATION_NOT_SUPPORTED) + LOGWARN("dsSetAudioDelay not supported for this port (error=%d)", ret); + else + LOGERR("dsSetAudioDelay failed with error: %d", ret); + return false; + } + } catch (...) { + LOGERR("Exception in setAudioDelayInternal"); + return false; + } + } + + // HAL callback registration functions (internal) + dsError_t registerHALCallbacks() + { + ENTRY_LOG; + dsError_t ret = dsERR_NONE; + + try { + // Register audio output port connect callback + ret = dsAudioOutRegisterConnectCB(audioOutPortConnectCallback); + if (ret != dsERR_NONE) { + LOGWARN("dsAudioOutRegisterConnectCB failed with error: %d", ret); + } else { + LOGINFO("Audio output port connect callback registered successfully"); + } + + // Register audio format update callback + ret = dsAudioFormatUpdateRegisterCB(audioFormatUpdateCallback); + if (ret != dsERR_NONE) { + LOGWARN("dsAudioFormatUpdateRegisterCB failed with error: %d", ret); + } else { + LOGINFO("Audio format update callback registered successfully"); + } + + // Register atmos capability change callback + ret = dsAudioAtmosCapsChangeRegisterCB(audioAtmosCapsChangeCallback); + if (ret != dsERR_NONE) { + LOGWARN("dsAudioAtmosCapsChangeRegisterCB failed with error: %d", ret); + } else { + LOGINFO("Audio atmos caps change callback registered successfully"); + } + + } catch (...) { + LOGERR("Exception in registerHALCallbacks"); + ret = dsERR_GENERAL; + } + + EXIT_LOG; + return ret; + } + +public: + dAudioImpl() : _isInitialized(false), _isDuckingInProgress(false), _volumeDuckingLevel(0), _muteStatus(false) + { + for (int i = 0; i < dsAUDIOPORT_TYPE_MAX; i++) { + _audioPortEnabled[i] = false; + } + InitialiseHAL(); + } + + /** Called from DeviceSettingsImp::Configure() — deferred HAL initialisation. + * Mirrors old dsMgr pattern: load all persistence once, then init hardware. */ + void InitialiseHAL() + { + if (_isInitialized) return; + ENTRY_LOG; + LOGINFO("InitialiseHAL "); + try { + // Root cause fix #2: load ALL persistence into memory in ONE file read + // before audioConfigInit() makes 30-40 getProperty() calls. + // Mirrors dsMgr_init(): HostPersistence::getInstance().load() called once + // so all subsequent getProperty() are fast in-memory map lookups. + device::HostPersistence::getInstance().load(); + + dsError_t ret = dsAudioPortInit(); + if (ret != dsERR_NONE) { + LOGERR("dsAudioPortInit failed with error: %d", ret); + } else { + _isInitialized = true; + LOGINFO("Audio platform initialized successfully"); + initializeAudioSettings(); + audioConfigInit(); + registerHALCallbacks(); + notifyAudioPortStateChanged(AudioPortState::AUDIO_PORT_STATE_INITIALIZED); + } + } catch (...) { + LOGERR("Exception during Audio platform initialization"); + } + EXIT_LOG; + } + + virtual ~dAudioImpl() + { + ENTRY_LOG; + + if (_isInitialized) { + try { + dsError_t ret = dsAudioPortTerm(); + if (ret != dsERR_NONE) { + LOGERR("dsAudioPortTerm failed with error: %d", ret); + } + } catch (...) { + LOGERR("Exception during Audio platform termination"); + } + _isInitialized = false; + } + EXIT_LOG; + } + + // Type conversion methods + dsAudioPortType_t convertToDS(const AudioPortType type) + { + switch (type) { + case AudioPortType::AUDIO_PORT_TYPE_LR: return dsAUDIOPORT_TYPE_ID_LR; + case AudioPortType::AUDIO_PORT_TYPE_HDMI: return dsAUDIOPORT_TYPE_HDMI; + case AudioPortType::AUDIO_PORT_TYPE_SPDIF: return dsAUDIOPORT_TYPE_SPDIF; + case AudioPortType::AUDIO_PORT_TYPE_SPEAKER: return dsAUDIOPORT_TYPE_SPEAKER; + case AudioPortType::AUDIO_PORT_TYPE_HDMIARC: return dsAUDIOPORT_TYPE_HDMI_ARC; + case AudioPortType::AUDIO_PORT_TYPE_HEADPHONE: return dsAUDIOPORT_TYPE_HEADPHONE; + default: return dsAUDIOPORT_TYPE_MAX; + } + } + + dsAudioStereoMode_t convertToDS(const AudioStereoMode mode) + { + switch (mode) { + case AudioStereoMode::AUDIO_STEREO_UNKNOWN: return dsAUDIO_STEREO_UNKNOWN; + case AudioStereoMode::AUDIO_STEREO_MONO: return dsAUDIO_STEREO_MONO; + case AudioStereoMode::AUDIO_STEREO_STEREO: return dsAUDIO_STEREO_STEREO; + case AudioStereoMode::AUDIO_STEREO_SURROUND: return dsAUDIO_STEREO_SURROUND; + case AudioStereoMode::AUDIO_STEREO_PASSTHROUGH: return dsAUDIO_STEREO_PASSTHRU; + case AudioStereoMode::AUDIO_STEREO_DD: return dsAUDIO_STEREO_DD; + case AudioStereoMode::AUDIO_STEREO_DDPLUS: return dsAUDIO_STEREO_DDPLUS; + default: return dsAUDIO_STEREO_UNKNOWN; + } + } + + AudioStereoMode convertFromDS(const dsAudioStereoMode_t dsMode) + { + switch (dsMode) { + case dsAUDIO_STEREO_UNKNOWN: return AudioStereoMode::AUDIO_STEREO_UNKNOWN; + case dsAUDIO_STEREO_MONO: return AudioStereoMode::AUDIO_STEREO_MONO; + case dsAUDIO_STEREO_STEREO: return AudioStereoMode::AUDIO_STEREO_STEREO; + case dsAUDIO_STEREO_SURROUND: return AudioStereoMode::AUDIO_STEREO_SURROUND; + case dsAUDIO_STEREO_PASSTHRU: return AudioStereoMode::AUDIO_STEREO_PASSTHROUGH; + case dsAUDIO_STEREO_DD: return AudioStereoMode::AUDIO_STEREO_DD; + case dsAUDIO_STEREO_DDPLUS: return AudioStereoMode::AUDIO_STEREO_DDPLUS; + default: return AudioStereoMode::AUDIO_STEREO_UNKNOWN; + } + } + + // IPlatform interface implementation + uint32_t GetAudioPort(const AudioPortType type, const int32_t index, int32_t &handle) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + dsAudioPortType_t dsType = convertToDS(type); + intptr_t dsHandle; + + dsError_t ret = dsGetAudioPort(dsType, index, &dsHandle); + + if (ret == dsERR_NONE) { + handle = static_cast(dsHandle); + LOGINFO("GetAudioPort success: type=%d, index=%d, handle=%d", type, index, handle); + } else { + if (ret == dsERR_OPERATION_NOT_SUPPORTED) + LOGWARN("GetAudioPort: port type=%d not supported on this platform (error=%d)", type, ret); + else + LOGERR("dsGetAudioPort failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioPort"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioCapabilities(const int32_t handle, int32_t &capabilities) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int dsCapabilities; + + // Use resolve function for dsGetAudioCapabilities + typedef dsError_t (*dsGetAudioCapabilities_t)(intptr_t handle, int* capabilities); + static dsGetAudioCapabilities_t dsGetAudioCapabilitiesFunc = 0; + if (dsGetAudioCapabilitiesFunc == 0) { + dsGetAudioCapabilitiesFunc = (dsGetAudioCapabilities_t)resolve(RDK_DSHAL_NAME, "dsGetAudioCapabilities"); + if (dsGetAudioCapabilitiesFunc == 0) { + LOGERR("dsGetAudioCapabilities is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioCapabilitiesFunc) { + ret = dsGetAudioCapabilitiesFunc(dsHandle, &dsCapabilities); + } + + if (ret == dsERR_NONE) { + capabilities = dsCapabilities; + LOGINFO("GetAudioCapabilities success: handle=%d, capabilities=%d", handle, capabilities); + } else { + LOGERR("dsGetAudioCapabilities failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioCapabilities"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioMS12Capabilities(const int32_t handle, int32_t &capabilities) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int dsCapabilities; + dsError_t ret = dsGetMS12Capabilities(dsHandle, &dsCapabilities); + if (ret == dsERR_NONE) { + capabilities = dsCapabilities; + LOGINFO("GetAudioMS12Capabilities success: handle=%d, capabilities=%d", handle, capabilities); + } else { + LOGERR("dsGetMS12Capabilities failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioMS12Capabilities"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioFormat(const int32_t handle, AudioFormat &audioFormat) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioFormat_t dsFormat; + + // Use resolve function for dsGetAudioFormat + typedef dsError_t (*dsGetAudioFormat_t)(intptr_t handle, dsAudioFormat_t* format); + static dsGetAudioFormat_t dsGetAudioFormatFunc = 0; + if (dsGetAudioFormatFunc == 0) { + dsGetAudioFormatFunc = (dsGetAudioFormat_t)resolve(RDK_DSHAL_NAME, "dsGetAudioFormat"); + if (dsGetAudioFormatFunc == 0) { + LOGERR("dsGetAudioFormat is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioFormatFunc) { + ret = dsGetAudioFormatFunc(dsHandle, &dsFormat); + } + + if (ret == dsERR_NONE) { + audioFormat = static_cast(dsFormat); + LOGINFO("GetAudioFormat success: handle=%d, format=%d", handle, audioFormat); + } else { + LOGERR("dsGetAudioFormat failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioFormat"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioEncoding(const int32_t handle, AudioEncoding &encoding) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + // No dsGetAudioEncoding HAL API exists; encoding is derived from stereo mode (mirrors dsAudio.c _dsGetEncoding) + dsAudioStereoMode_t stereoMode = dsAUDIO_STEREO_UNKNOWN; + dsError_t ret = dsGetStereoMode(static_cast(handle), &stereoMode); + if (ret != dsERR_NONE) { + LOGERR("GetAudioEncoding: dsGetStereoMode failed: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + switch (stereoMode) { + case dsAUDIO_STEREO_STEREO: + encoding = AudioEncoding::AUDIO_ENCODING_PCM; + break; + case dsAUDIO_STEREO_DD: + encoding = AudioEncoding::AUDIO_ENCODING_AC3; + break; + case dsAUDIO_STEREO_DDPLUS: + encoding = AudioEncoding::AUDIO_ENCODING_EAC3; + break; + case dsAUDIO_STEREO_SURROUND: + case dsAUDIO_STEREO_PASSTHRU: + encoding = AudioEncoding::AUDIO_ENCODING_DISPLAY; + break; + case dsAUDIO_STEREO_UNKNOWN: + default: + encoding = AudioEncoding::AUDIO_ENCODING_NONE; + break; + } + LOGINFO("GetAudioEncoding: handle=%d stereoMode=%d encoding=%d", handle, stereoMode, static_cast(encoding)); + } catch (...) { + LOGERR("Exception in GetAudioEncoding"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetSupportedCompressions(const int32_t handle, IDeviceSettingsAudioCompressionIterator*& compressions) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + // Derive supported compressions from dsGetAudioCapabilities — no lib32-devicesettings dependency. + try { + intptr_t dsHandle = static_cast(handle); + + // Resolve dsGetAudioCapabilities via dlopen (same pattern as all other HAL calls). + typedef dsError_t (*dsGetAudioCapabilities_t)(intptr_t handle, int* capabilities); + static dsGetAudioCapabilities_t dsGetAudioCapabilitiesFunc = 0; + if (dsGetAudioCapabilitiesFunc == 0) { + dsGetAudioCapabilitiesFunc = (dsGetAudioCapabilities_t)resolve(RDK_DSHAL_NAME, "dsGetAudioCapabilities"); + } + + int caps = 0; + if (dsGetAudioCapabilitiesFunc != 0) { + dsGetAudioCapabilitiesFunc(dsHandle, &caps); + } + + // Build compression list based on capabilities bitmask. + // dsAUDIOSUPPORT_DD / DDPLUS indicate heavy/medium compression support. + std::vector compressionList; + compressionList.push_back(AudioCompression::AUDIO_COMPRESSION_NONE); + compressionList.push_back(AudioCompression::AUDIO_COMPRESSION_LIGHT); + if (caps & dsAUDIOSUPPORT_DD) { + compressionList.push_back(AudioCompression::AUDIO_COMPRESSION_MEDIUM); + } + if (caps & dsAUDIOSUPPORT_DDPLUS) { + compressionList.push_back(AudioCompression::AUDIO_COMPRESSION_HEAVY); + } + + using CompressionIterator = WPEFramework::RPC::IteratorType; + compressions = WPEFramework::Core::Service::Create(compressionList); + + LOGINFO("GetSupportedCompressions success: handle=%d, count=%zu, caps=0x%x", + handle, compressionList.size(), caps); + } catch (...) { + LOGERR("Exception in GetSupportedCompressions"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioCompression(const int32_t handle, AudioCompression &compression) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int dsCompression; + + // Use resolve function for dsGetAudioCompression + typedef dsError_t (*dsGetAudioCompression_t)(intptr_t handle, int* compression); + static dsGetAudioCompression_t dsGetAudioCompressionFunc = 0; + if (dsGetAudioCompressionFunc == 0) { + dsGetAudioCompressionFunc = (dsGetAudioCompression_t)resolve(RDK_DSHAL_NAME, "dsGetAudioCompression"); + if (dsGetAudioCompressionFunc == 0) { + LOGERR("dsGetAudioCompression is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioCompressionFunc) { + ret = dsGetAudioCompressionFunc(dsHandle, &dsCompression); + } + + if (ret == dsERR_NONE) { + compression = static_cast(dsCompression); + LOGINFO("GetAudioCompression success: handle=%d, compression=%d", handle, static_cast(compression)); + } else { + LOGERR("dsGetAudioCompression failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioCompression"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioCompression(const int32_t handle, const AudioCompression compression) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetAudioCompression + typedef dsError_t (*dsSetAudioCompression_t)(intptr_t handle, int compression); + static dsSetAudioCompression_t dsSetAudioCompressionFunc = 0; + if (dsSetAudioCompressionFunc == 0) { + dsSetAudioCompressionFunc = (dsSetAudioCompression_t)resolve(RDK_DSHAL_NAME, "dsSetAudioCompression"); + if (dsSetAudioCompressionFunc == 0) { + LOGERR("dsSetAudioCompression is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioCompressionFunc) { + ret = dsSetAudioCompressionFunc(dsHandle, static_cast(compression)); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioCompression success: handle=%d, compression=%d", handle, static_cast(compression)); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.Compression", std::to_string(static_cast(compression))); +#endif + } else { + LOGERR("dsSetAudioCompression failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioCompression"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioLevel(const int32_t handle, const float audioLevel) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetAudioLevel + typedef dsError_t (*dsSetAudioLevel_t)(intptr_t handle, float level); + static dsSetAudioLevel_t dsSetAudioLevelFunc = 0; + if (dsSetAudioLevelFunc == 0) { + dsSetAudioLevelFunc = (dsSetAudioLevel_t)resolve(RDK_DSHAL_NAME, "dsSetAudioLevel"); + if (dsSetAudioLevelFunc == 0) { + LOGERR("dsSetAudioLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioLevelFunc) { + // dsAudio.c: for SPEAKER port, if ducking is in progress, apply + // ducking level instead of the requested level (or skip if ducking is active). + dsAudioPortType_t portType = getAudioPortType(dsHandle); + if (portType == dsAUDIOPORT_TYPE_SPEAKER) { + float currentLevel = 0; + dsGetAudioLevel(dsHandle, ¤tLevel); + if (_isDuckingInProgress && currentLevel != static_cast(_volumeDuckingLevel)) { + // Ducking active and current level diverged — re-apply ducking level + LOGINFO("SetAudioLevel: ducking in progress, applying ducking level %d instead of %f", + _volumeDuckingLevel, audioLevel); + ret = dsSetAudioLevelFunc(dsHandle, static_cast(_volumeDuckingLevel)); + } else if (_isDuckingInProgress) { + // Already at ducking level — skip (dsAudio.c: returns SUCCESS without calling HAL) + LOGINFO("SetAudioLevel: ducking in progress, skipping level change for SPEAKER"); + ret = dsERR_NONE; + } else { + ret = dsSetAudioLevelFunc(dsHandle, audioLevel); + } + } else { + ret = dsSetAudioLevelFunc(dsHandle, audioLevel); + } + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioLevel success: handle=%d, level=%f", handle, audioLevel); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _audioLevel = std::to_string(audioLevel); + dsAudioPortType_t _portType = getAudioPortType(dsHandle); + switch (_portType) { + case dsAUDIOPORT_TYPE_SPDIF: device::HostPersistence::getInstance().persistHostProperty("SPDIF0.audio.Level", _audioLevel); break; + case dsAUDIOPORT_TYPE_HDMI: device::HostPersistence::getInstance().persistHostProperty("HDMI0.audio.Level", _audioLevel); break; + case dsAUDIOPORT_TYPE_SPEAKER: device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.audio.Level", _audioLevel); break; + case dsAUDIOPORT_TYPE_HEADPHONE: device::HostPersistence::getInstance().persistHostProperty("HEADPHONE0.audio.Level", _audioLevel); break; + default: break; + } +#endif + // Notify about audio level change + notifyAudioLevelChanged(static_cast(audioLevel)); + } else { + LOGERR("dsSetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioLevel"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioLevel(const int32_t handle, float &audioLevel) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + float dsLevel; + + // Use resolve function for dsGetAudioLevel + typedef dsError_t (*dsGetAudioLevel_t)(intptr_t handle, float* level); + static dsGetAudioLevel_t dsGetAudioLevelFunc = 0; + if (dsGetAudioLevelFunc == 0) { + dsGetAudioLevelFunc = (dsGetAudioLevel_t)resolve(RDK_DSHAL_NAME, "dsGetAudioLevel"); + if (dsGetAudioLevelFunc == 0) { + LOGERR("dsGetAudioLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioLevelFunc) { + ret = dsGetAudioLevelFunc(dsHandle, &dsLevel); + } + + if (ret == dsERR_NONE) { + audioLevel = dsLevel; + LOGINFO("GetAudioLevel success: handle=%d, level=%f", handle, audioLevel); + } else { + LOGERR("dsGetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioLevel"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioGain(const int32_t handle, const float gainLevel) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetAudioGain + typedef dsError_t (*dsSetAudioGain_t)(intptr_t handle, float gainLevel); + static dsSetAudioGain_t dsSetAudioGainFunc = 0; + if (dsSetAudioGainFunc == 0) { + dsSetAudioGainFunc = (dsSetAudioGain_t)resolve(RDK_DSHAL_NAME, "dsSetAudioGain"); + if (dsSetAudioGainFunc == 0) { + LOGERR("dsSetAudioGain is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioGainFunc) { + ret = dsSetAudioGainFunc(dsHandle, gainLevel); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioGain success: handle=%d, gain=%f", handle, gainLevel); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _gain = std::to_string(gainLevel); + dsAudioPortType_t _portType = getAudioPortType(dsHandle); + switch (_portType) { + case dsAUDIOPORT_TYPE_SPDIF: device::HostPersistence::getInstance().persistHostProperty("SPDIF0.audio.Gain", _gain); break; + case dsAUDIOPORT_TYPE_HDMI: device::HostPersistence::getInstance().persistHostProperty("HDMI0.audio.Gain", _gain); break; + case dsAUDIOPORT_TYPE_SPEAKER: device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.audio.Gain", _gain); break; + default: break; + } +#endif + } else { + LOGERR("dsSetAudioGain failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioGain"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioGain(const int32_t handle, float &gainLevel) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + float dsGain; + + // Use resolve function for dsGetAudioGain + typedef dsError_t (*dsGetAudioGain_t)(intptr_t handle, float* gain); + static dsGetAudioGain_t dsGetAudioGainFunc = 0; + if (dsGetAudioGainFunc == 0) { + dsGetAudioGainFunc = (dsGetAudioGain_t)resolve(RDK_DSHAL_NAME, "dsGetAudioGain"); + if (dsGetAudioGainFunc == 0) { + LOGERR("dsGetAudioGain is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioGainFunc) { + ret = dsGetAudioGainFunc(dsHandle, &dsGain); + } + + if (ret == dsERR_NONE) { + gainLevel = dsGain; + LOGINFO("GetAudioGain success: handle=%d, gain=%f", handle, gainLevel); + } else { + LOGERR("dsGetAudioGain failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioGain"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioMute(const int32_t handle, const bool mute) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // dsAudio.c: when unmuting SPEAKER port, restore ducking level first + dsAudioPortType_t portType = getAudioPortType(dsHandle); + if (!mute && portType == dsAUDIOPORT_TYPE_SPEAKER) { + if (setAudioDuckingAudioLevel(dsHandle) != WPEFramework::Core::ERROR_NONE) { + LOGERR("SetAudioMute: failed to restore audio ducking level for Speaker port"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsSetAudioMute(dsHandle, mute); + if (ret == dsERR_NONE) { + _muteStatus = mute; + LOGINFO("SetAudioMute success: handle=%d, mute=%d", handle, mute); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _mute = mute ? "TRUE" : "FALSE"; + dsAudioPortType_t _portType = getAudioPortType(dsHandle); + switch (_portType) { + case dsAUDIOPORT_TYPE_SPDIF: device::HostPersistence::getInstance().persistHostProperty("SPDIF0.audio.mute", _mute); break; + case dsAUDIOPORT_TYPE_HDMI: device::HostPersistence::getInstance().persistHostProperty("HDMI0.audio.mute", _mute); break; + case dsAUDIOPORT_TYPE_SPEAKER: device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.audio.mute", _mute); break; + case dsAUDIOPORT_TYPE_HEADPHONE: device::HostPersistence::getInstance().persistHostProperty("HEADPHONE0.audio.mute", _mute); break; + case dsAUDIOPORT_TYPE_HDMI_ARC: device::HostPersistence::getInstance().persistHostProperty("HDMI_ARC0.audio.mute", _mute); break; + default: break; + } +#endif + } else { + LOGERR("dsSetAudioMute failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioMute"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t IsAudioMuted(const int32_t handle, bool &muted) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + bool dsMuted; + + // Use resolve function for dsIsAudioMute + typedef dsError_t (*dsIsAudioMute_t)(intptr_t handle, bool* muted); + static dsIsAudioMute_t dsIsAudioMuteFunc = 0; + if (dsIsAudioMuteFunc == 0) { + dsIsAudioMuteFunc = (dsIsAudioMute_t)resolve(RDK_DSHAL_NAME, "dsIsAudioMute"); + if (dsIsAudioMuteFunc == 0) { + LOGERR("dsIsAudioMute is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsIsAudioMuteFunc) { + ret = dsIsAudioMuteFunc(dsHandle, &dsMuted); + } + + if (ret == dsERR_NONE) { + muted = dsMuted; + LOGINFO("IsAudioMuted success: handle=%d, muted=%d", handle, muted); + } else { + LOGERR("dsIsAudioMute failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioMuted"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDucking(const int32_t handle, const AudioDuckingType duckingType, const AudioDuckingAction duckingAction, const uint8_t level) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int32_t volume = 0; + float volumeLevel = 0; + bool portEnabled = false; + + LOGINFO("SetAudioDucking: action=%d, type=%d, level=%d", static_cast(duckingAction), static_cast(duckingType), level); + + // Check if audio port is enabled + dsError_t ret = dsIsAudioPortEnabled(dsHandle, &portEnabled); + if (ret != dsERR_NONE) { + LOGWARN("dsIsAudioPortEnabled failed with error: %d", ret); + } + + // Get current audio level + ret = dsGetAudioLevel(dsHandle, &volumeLevel); + if (ret != dsERR_NONE) { + LOGERR("dsGetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + + LOGINFO("Current volumeLevel: %f", volumeLevel); + + // Calculate ducking volume based on action and type + if (duckingAction == AudioDuckingAction::AUDIO_DUCKINGACTION_START) { + _isDuckingInProgress = true; + if (duckingType == AudioDuckingType::AUDIO_DUCKINGTYPE_RELATIVE) { + volume = (volumeLevel * level) / 100; + } else { + if (level > volumeLevel) { + volume = volumeLevel; + } else { + volume = level; + } + } + } else { + _isDuckingInProgress = false; + volume = volumeLevel; + } + + // If muted or port disabled, store volume but don't apply + if (_muteStatus || !portEnabled) { + LOGWARN("Mute on or port disabled, ignoring ducking request"); + _volumeDuckingLevel = volume; + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + LOGINFO("Adjusted volume: %d, previous ducking level: %d", volume, _volumeDuckingLevel); + + // Apply volume to HAL layer and send event if changed + if (volume != _volumeDuckingLevel) { + // Use resolve function for dsSetAudioLevel + typedef dsError_t (*dsSetAudioLevel_t)(intptr_t handle, float level); + static dsSetAudioLevel_t dsSetAudioLevelFunc = 0; + if (dsSetAudioLevelFunc == 0) { + dsSetAudioLevelFunc = (dsSetAudioLevel_t)resolve(RDK_DSHAL_NAME, "dsSetAudioLevel"); + if (dsSetAudioLevelFunc == 0) { + LOGERR("dsSetAudioLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioLevelFunc) { + ret = dsSetAudioLevelFunc(dsHandle, volume); + } + + if (ret == dsERR_NONE) { + _volumeDuckingLevel = volume; + LOGINFO("SetAudioDucking applied successfully: handle=%d, volume=%d", handle, volume); + + // Send audio level change event through callback if available + if (g_AudioLevelChangedCallback) { + g_AudioLevelChangedCallback(static_cast(volume)); + } + } else { + LOGERR("dsSetAudioLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + LOGINFO("SetAudioDucking success: handle=%d, type=%d, action=%d, level=%d, final_volume=%d", + handle, static_cast(duckingType), static_cast(duckingAction), level, volume); + } catch (...) { + LOGERR("Exception in SetAudioDucking"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetStereoMode(const int32_t handle, AudioStereoMode &mode) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioStereoMode_t dsMode; + + dsError_t ret = dsGetStereoMode(dsHandle, &dsMode); + + if (ret == dsERR_NONE) { + mode = convertFromDS(dsMode); + LOGINFO("GetStereoMode success: handle=%d, mode=%d", handle, static_cast(mode)); + } else { + LOGERR("dsGetStereoMode failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetStereoMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetStereoMode(const int32_t handle, const AudioStereoMode mode, const bool persist) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioStereoMode_t dsMode = convertToDS(mode); + + dsError_t ret = dsSetStereoMode(dsHandle, dsMode); + + if (ret == dsERR_NONE) { + LOGINFO("SetStereoMode success: handle=%d, mode=%d, persist=%s", handle, static_cast(mode), persist ? "true" : "false"); + + // Determine actual port type from handle + dsAudioPortType_t dsPortType = getAudioPortType(dsHandle); + AudioPortType portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; // Default + + // Convert dsAudioPortType_t to AudioPortType and handle persistence + std::string modeString; + switch (mode) { + case AudioStereoMode::AUDIO_STEREO_STEREO: + modeString = "STEREO"; + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + case AudioStereoMode::AUDIO_STEREO_SURROUND: + modeString = "SURROUND"; + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + case AudioStereoMode::AUDIO_STEREO_PASSTHROUGH: + modeString = "PASSTHRU"; + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + default: + modeString = "STEREO"; + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + } + + // Convert dsAudioPortType_t to AudioPortType for notification + switch (dsPortType) { + case dsAUDIOPORT_TYPE_HDMI: + portType = AudioPortType::AUDIO_PORT_TYPE_HDMI; + break; + case dsAUDIOPORT_TYPE_SPDIF: + portType = AudioPortType::AUDIO_PORT_TYPE_SPDIF; + break; + case dsAUDIOPORT_TYPE_SPEAKER: + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + case dsAUDIOPORT_TYPE_HDMI_ARC: + portType = AudioPortType::AUDIO_PORT_TYPE_HDMIARC; + break; + default: + portType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; + break; + } + + // Handle persistence based on port type and mode + if (persist) { + try { + LOGINFO("Setting Audio Mode %s with persistent value: %s", modeString.c_str(), persist ? "true" : "false"); + + switch (dsPortType) { + case dsAUDIOPORT_TYPE_HDMI: + device::HostPersistence::getInstance().persistHostProperty("HDMI0.AudioMode", modeString.c_str()); + break; + case dsAUDIOPORT_TYPE_SPDIF: + device::HostPersistence::getInstance().persistHostProperty("SPDIF0.AudioMode", modeString.c_str()); + break; + case dsAUDIOPORT_TYPE_HDMI_ARC: + device::HostPersistence::getInstance().persistHostProperty("HDMI_ARC0.AudioMode", modeString.c_str()); + break; + case dsAUDIOPORT_TYPE_SPEAKER: + device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.AudioMode", modeString.c_str()); + break; + default: + LOGWARN("Unknown port type %d, skipping persistence", dsPortType); + break; + } + } catch (...) { + LOGERR("Error in persisting audio mode setting"); + } + } + + // Notify about audio mode change + notifyAudioModeChanged(portType, mode); + } else { + if (ret == dsERR_OPERATION_NOT_SUPPORTED) + LOGWARN("dsSetStereoMode not supported on this port (error=%d)", ret); + else + LOGERR("dsSetStereoMode failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetStereoMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAssociatedAudioMixing(const int32_t handle, const bool mixing) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetAssociatedAudioMixing + typedef dsError_t (*dsSetAssociatedAudioMixing_t)(intptr_t handle, bool mixing); + static dsSetAssociatedAudioMixing_t dsSetAssociatedAudioMixingFunc = 0; + if (dsSetAssociatedAudioMixingFunc == 0) { + dsSetAssociatedAudioMixingFunc = (dsSetAssociatedAudioMixing_t)resolve(RDK_DSHAL_NAME, "dsSetAssociatedAudioMixing"); + if (dsSetAssociatedAudioMixingFunc == 0) { + LOGERR("dsSetAssociatedAudioMixing is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAssociatedAudioMixingFunc) { + ret = dsSetAssociatedAudioMixingFunc(dsHandle, mixing); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAssociatedAudioMixing success: handle=%d, mixing=%s", handle, mixing ? "true" : "false"); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.AssociatedAudioMixing", mixing ? "Enabled" : "Disabled"); +#endif + // Notify about associated audio mixing change + notifyAssociatedAudioMixingChanged(mixing); + } else { + LOGERR("dsSetAssociatedAudioMixing failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAssociatedAudioMixing"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAssociatedAudioMixing(const int32_t handle, bool &mixing) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + bool dsMixing; + + // Use resolve function for dsGetAssociatedAudioMixing + typedef dsError_t (*dsGetAssociatedAudioMixing_t)(intptr_t handle, bool* mixing); + static dsGetAssociatedAudioMixing_t dsGetAssociatedAudioMixingFunc = 0; + if (dsGetAssociatedAudioMixingFunc == 0) { + dsGetAssociatedAudioMixingFunc = (dsGetAssociatedAudioMixing_t)resolve(RDK_DSHAL_NAME, "dsGetAssociatedAudioMixing"); + if (dsGetAssociatedAudioMixingFunc == 0) { + LOGERR("dsGetAssociatedAudioMixing is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAssociatedAudioMixingFunc) { + ret = dsGetAssociatedAudioMixingFunc(dsHandle, &dsMixing); + } + + if (ret == dsERR_NONE) { + mixing = dsMixing; + LOGINFO("GetAssociatedAudioMixing success: handle=%d, mixing=%s", handle, mixing ? "true" : "false"); + } else { + LOGERR("dsGetAssociatedAudioMixing failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAssociatedAudioMixing"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioFaderControl(const int32_t handle, const int32_t mixerBalance) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetFaderControl + typedef dsError_t (*dsSetFaderControl_t)(intptr_t handle, int balance); + static dsSetFaderControl_t dsSetFaderControlFunc = 0; + if (dsSetFaderControlFunc == 0) { + dsSetFaderControlFunc = (dsSetFaderControl_t)resolve(RDK_DSHAL_NAME, "dsSetFaderControl"); + if (dsSetFaderControlFunc == 0) { + LOGERR("dsSetFaderControl is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetFaderControlFunc) { + ret = dsSetFaderControlFunc(dsHandle, mixerBalance); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioFaderControl success: handle=%d, balance=%d", handle, mixerBalance); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.FaderControl", std::to_string(mixerBalance)); +#endif + // Notify about fader control change + notifyAudioFaderControlChanged(mixerBalance); + } else { + LOGERR("dsSetFaderControl failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioFaderControl"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioFaderControl(const int32_t handle, int32_t &mixerBalance) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int dsBalance; + + // Use resolve function for dsGetFaderControl + typedef dsError_t (*dsGetFaderControl_t)(intptr_t handle, int* balance); + static dsGetFaderControl_t dsGetFaderControlFunc = 0; + if (dsGetFaderControlFunc == 0) { + dsGetFaderControlFunc = (dsGetFaderControl_t)resolve(RDK_DSHAL_NAME, "dsGetFaderControl"); + if (dsGetFaderControlFunc == 0) { + LOGERR("dsGetFaderControl is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetFaderControlFunc) { + ret = dsGetFaderControlFunc(dsHandle, &dsBalance); + } + + if (ret == dsERR_NONE) { + mixerBalance = dsBalance; + LOGINFO("GetAudioFaderControl success: handle=%d, balance=%d", handle, mixerBalance); + } else { + LOGERR("dsGetFaderControl failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioFaderControl"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioPrimaryLanguage(const int32_t handle, const std::string& primaryAudioLanguage) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetPrimaryLanguage + typedef dsError_t (*dsSetPrimaryLanguage_t)(intptr_t handle, const char* language); + static dsSetPrimaryLanguage_t dsSetPrimaryLanguageFunc = 0; + if (dsSetPrimaryLanguageFunc == 0) { + dsSetPrimaryLanguageFunc = (dsSetPrimaryLanguage_t)resolve(RDK_DSHAL_NAME, "dsSetPrimaryLanguage"); + if (dsSetPrimaryLanguageFunc == 0) { + LOGERR("dsSetPrimaryLanguage is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetPrimaryLanguageFunc) { + ret = dsSetPrimaryLanguageFunc(dsHandle, primaryAudioLanguage.c_str()); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioPrimaryLanguage success: handle=%d, language=%s", handle, primaryAudioLanguage.c_str()); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.PrimaryLanguage", primaryAudioLanguage); +#endif + // Notify about primary language change + notifyAudioPrimaryLanguageChanged(primaryAudioLanguage); + } else { + LOGERR("dsSetPrimaryLanguage failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioPrimaryLanguage"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioPrimaryLanguage(const int32_t handle, std::string &primaryAudioLanguage) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + char langStr[32] = {0}; + + // Use resolve function for dsGetPrimaryLanguage + typedef dsError_t (*dsGetPrimaryLanguage_t)(intptr_t handle, char* language); + static dsGetPrimaryLanguage_t dsGetPrimaryLanguageFunc = 0; + if (dsGetPrimaryLanguageFunc == 0) { + dsGetPrimaryLanguageFunc = (dsGetPrimaryLanguage_t)resolve(RDK_DSHAL_NAME, "dsGetPrimaryLanguage"); + if (dsGetPrimaryLanguageFunc == 0) { + LOGERR("dsGetPrimaryLanguage is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetPrimaryLanguageFunc) { + ret = dsGetPrimaryLanguageFunc(dsHandle, langStr); + } + + if (ret == dsERR_NONE) { + primaryAudioLanguage = std::string(langStr); + LOGINFO("GetAudioPrimaryLanguage success: handle=%d, language=%s", handle, primaryAudioLanguage.c_str()); + } else { + LOGERR("dsGetPrimaryLanguage failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioPrimaryLanguage"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioSecondaryLanguage(const int32_t handle, const std::string& secondaryAudioLanguage) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetSecondaryLanguage + typedef dsError_t (*dsSetSecondaryLanguage_t)(intptr_t handle, const char* language); + static dsSetSecondaryLanguage_t dsSetSecondaryLanguageFunc = 0; + if (dsSetSecondaryLanguageFunc == 0) { + dsSetSecondaryLanguageFunc = (dsSetSecondaryLanguage_t)resolve(RDK_DSHAL_NAME, "dsSetSecondaryLanguage"); + if (dsSetSecondaryLanguageFunc == 0) { + LOGERR("dsSetSecondaryLanguage is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetSecondaryLanguageFunc) { + ret = dsSetSecondaryLanguageFunc(dsHandle, secondaryAudioLanguage.c_str()); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioSecondaryLanguage success: handle=%d, language=%s", handle, secondaryAudioLanguage.c_str()); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.SecondaryLanguage", secondaryAudioLanguage); +#endif + // Notify about secondary language change + notifyAudioSecondaryLanguageChanged(secondaryAudioLanguage); + } else { + LOGERR("dsSetSecondaryLanguage failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioSecondaryLanguage"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioSecondaryLanguage(const int32_t handle, std::string &secondaryAudioLanguage) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + char langStr[32] = {0}; + + // Use resolve function for dsGetSecondaryLanguage + typedef dsError_t (*dsGetSecondaryLanguage_t)(intptr_t handle, char* language); + static dsGetSecondaryLanguage_t dsGetSecondaryLanguageFunc = 0; + if (dsGetSecondaryLanguageFunc == 0) { + dsGetSecondaryLanguageFunc = (dsGetSecondaryLanguage_t)resolve(RDK_DSHAL_NAME, "dsGetSecondaryLanguage"); + if (dsGetSecondaryLanguageFunc == 0) { + LOGERR("dsGetSecondaryLanguage is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetSecondaryLanguageFunc) { + ret = dsGetSecondaryLanguageFunc(dsHandle, langStr); + } + + if (ret == dsERR_NONE) { + secondaryAudioLanguage = std::string(langStr); + LOGINFO("GetAudioSecondaryLanguage success: handle=%d, language=%s", handle, secondaryAudioLanguage.c_str()); + } else { + LOGERR("dsGetSecondaryLanguage failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioSecondaryLanguage"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t IsAudioOutputConnected(const int32_t handle, bool &isConnected) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + bool dsConnected; + + // dsAudio.c uses dsAudioOutIsConnected (not dsIsAudioPortEnabled) + typedef dsError_t (*dsAudioOutIsConnected_t)(intptr_t handle, bool* isConnected); + static dsAudioOutIsConnected_t dsAudioOutIsConnectedFunc = 0; + if (dsAudioOutIsConnectedFunc == 0) { + dsAudioOutIsConnectedFunc = (dsAudioOutIsConnected_t)resolve(RDK_DSHAL_NAME, "dsAudioOutIsConnected"); + if (dsAudioOutIsConnectedFunc == 0) { + LOGERR("dsAudioOutIsConnected is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsAudioOutIsConnectedFunc) { + ret = dsAudioOutIsConnectedFunc(dsHandle, &dsConnected); + } + + if (ret == dsERR_NONE) { + isConnected = dsConnected; + LOGINFO("IsAudioOutputConnected success: handle=%d, connected=%s", handle, isConnected ? "true" : "false"); + } else { + LOGERR("dsAudioOutIsConnected failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioOutputConnected"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioSinkDeviceAtmosCapability(const int32_t handle, DolbyAtmosCapability &atmosCapability) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // dsAtmosCapability_t should be dsATMOSCapability_t + dsATMOSCapability_t dsCapability; + + // Use resolve function for dsGetSinkDeviceAtmosCapability + typedef dsError_t (*dsGetSinkDeviceAtmosCapability_t)(intptr_t handle, dsATMOSCapability_t* capability); + static dsGetSinkDeviceAtmosCapability_t dsGetSinkDeviceAtmosCapabilityFunc = 0; + if (dsGetSinkDeviceAtmosCapabilityFunc == 0) { + dsGetSinkDeviceAtmosCapabilityFunc = (dsGetSinkDeviceAtmosCapability_t)resolve(RDK_DSHAL_NAME, "dsGetSinkDeviceAtmosCapability"); + if (dsGetSinkDeviceAtmosCapabilityFunc == 0) { + LOGERR("dsGetSinkDeviceAtmosCapability is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetSinkDeviceAtmosCapabilityFunc) { + ret = dsGetSinkDeviceAtmosCapabilityFunc(dsHandle, &dsCapability); + } + + if (ret == dsERR_NONE) { + atmosCapability = static_cast(dsCapability); + LOGINFO("GetAudioSinkDeviceAtmosCapability success: handle=%d, capability=%d", handle, static_cast(atmosCapability)); + } else { + LOGERR("dsGetSinkDeviceAtmosCapability failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioSinkDeviceAtmosCapability"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioAtmosOutputMode(const int32_t handle, const bool enable) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetAudioAtmosOutputMode + typedef dsError_t (*dsSetAudioAtmosOutputMode_t)(intptr_t handle, bool enable); + static dsSetAudioAtmosOutputMode_t dsSetAudioAtmosOutputModeFunc = 0; + if (dsSetAudioAtmosOutputModeFunc == 0) { + dsSetAudioAtmosOutputModeFunc = (dsSetAudioAtmosOutputMode_t)resolve(RDK_DSHAL_NAME, "dsSetAudioAtmosOutputMode"); + if (dsSetAudioAtmosOutputModeFunc == 0) { + LOGERR("dsSetAudioAtmosOutputMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioAtmosOutputModeFunc) { + ret = dsSetAudioAtmosOutputModeFunc(dsHandle, enable); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioAtmosOutputMode success: handle=%d, enable=%s", handle, enable ? "true" : "false"); + } else { + LOGERR("dsSetAudioAtmosOutputMode failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioAtmosOutputMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + // Missing IDeviceSettingsAudio interface methods implementation + + uint32_t IsAudioPortEnabled(const int32_t handle, bool &enabled) override { + ENTRY_LOG; + try { + bool portEnabled = false; + + // Use resolve function for dsIsAudioPortEnabled + typedef dsError_t (*dsIsAudioPortEnabled_t)(intptr_t handle, bool* enabled); + static dsIsAudioPortEnabled_t dsIsAudioPortEnabledFunc = 0; + if (dsIsAudioPortEnabledFunc == 0) { + dsIsAudioPortEnabledFunc = (dsIsAudioPortEnabled_t)resolve(RDK_DSHAL_NAME, "dsIsAudioPortEnabled"); + if (dsIsAudioPortEnabledFunc == 0) { + LOGERR("dsIsAudioPortEnabled is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsIsAudioPortEnabledFunc) { + dsResult = dsIsAudioPortEnabledFunc(static_cast(handle), &portEnabled); + } + + if (dsResult == dsERR_NONE) { + enabled = portEnabled; + LOGINFO("IsAudioPortEnabled success: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + } else { + LOGERR("dsIsAudioPortEnabled failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioPortEnabled"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t EnableAudioPort(const int32_t handle, const bool enable) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioPortType_t portType = getAudioPortType(dsHandle); + + // Special handling for SPEAKER port - manage audio ducking level + if (portType == dsAUDIOPORT_TYPE_SPEAKER) { + bool muted = false; + dsError_t ret = dsIsAudioMute(dsHandle, &muted); + if (ret != dsERR_NONE) { + LOGWARN("Failed to get the mute status of Speaker port"); + } + + if (enable && !muted) { + if (setAudioDuckingAudioLevel(dsHandle) != WPEFramework::Core::ERROR_NONE) { + LOGERR("Failed to set audio ducking level for Speaker port"); + return WPEFramework::Core::ERROR_GENERAL; + } + } else { + LOGINFO("Not setting audio ducking level as mute status is %s", muted ? "true" : "false"); + } + } + + // Enable/disable the audio port + // Use resolve function for dsEnableAudioPort + typedef dsError_t (*dsEnableAudioPort_t)(intptr_t handle, bool enable); + static dsEnableAudioPort_t dsEnableAudioPortFunc = 0; + if (dsEnableAudioPortFunc == 0) { + dsEnableAudioPortFunc = (dsEnableAudioPort_t)resolve(RDK_DSHAL_NAME, "dsEnableAudioPort"); + if (dsEnableAudioPortFunc == 0) { + LOGERR("dsEnableAudioPort is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsEnableAudioPortFunc) { + dsResult = dsEnableAudioPortFunc(dsHandle, enable); + } + if (dsResult != dsERR_NONE) { + LOGERR("dsEnableAudioPort failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + + // Verify that the port was actually enabled/disabled + bool portEnabled = false; + dsResult = dsIsAudioPortEnabled(dsHandle, &portEnabled); + if (dsResult == dsERR_NONE) { + if (portEnabled != enable) { + LOGERR("Audio port enable verification failed. Expected: %s, Actual: %s", + enable ? "enabled" : "disabled", portEnabled ? "enabled" : "disabled"); + return WPEFramework::Core::ERROR_GENERAL; + } else { + LOGINFO("Audio port enable verification passed: %s", enable ? "enabled" : "disabled"); + + // Update port state tracking + if (portType < dsAUDIOPORT_TYPE_MAX) { + _audioPortEnabled[portType] = enable; + LOGINFO("Port type %d enabled status: %s", portType, enable ? "true" : "false"); + + // Set audio delay when enabling port + if (enable) { + uint32_t audioDelay = getAudioDelayInternal(portType); + bool delaySet = setAudioDelayInternal(dsHandle, audioDelay); + LOGINFO("Updated audio delay for port enable - port type: %d, delay: %u, success: %s", + portType, audioDelay, delaySet ? "true" : "false"); + } + } + } + } else { + LOGWARN("Audio port status verification failed - dsIsAudioPortEnabled call failed with error: %d", dsResult); + } + + LOGINFO("EnableAudioPort success: handle=%d, enable=%s", handle, enable ? "true" : "false"); + + } catch (...) { + LOGERR("Exception in EnableAudioPort"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetSupportedARCTypes(const int32_t handle, int32_t &types) override { + ENTRY_LOG; + try { + int arcTypes = 0; + + // Use resolve function for dsGetSupportedARCTypes + typedef dsError_t (*dsGetSupportedARCTypes_t)(intptr_t handle, int* types); + static dsGetSupportedARCTypes_t dsGetSupportedARCTypesFunc = 0; + if (dsGetSupportedARCTypesFunc == 0) { + dsGetSupportedARCTypesFunc = (dsGetSupportedARCTypes_t)resolve(RDK_DSHAL_NAME, "dsGetSupportedARCTypes"); + if (dsGetSupportedARCTypesFunc == 0) { + LOGERR("dsGetSupportedARCTypes is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetSupportedARCTypesFunc) { + dsResult = dsGetSupportedARCTypesFunc(static_cast(handle), &arcTypes); + } + + if (dsResult == dsERR_NONE) { + types = arcTypes; + } else { + LOGERR("dsGetSupportedARCTypes failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetSupportedARCTypes"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetSAD(const int32_t handle, const uint8_t sadList[], const uint8_t count) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // dsAudio.c uses dsAudioSetSAD (not dsSetSAD) + typedef dsError_t (*dsAudioSetSAD_t)(intptr_t handle, dsAudioSADList_t sad_list); + static dsAudioSetSAD_t dsAudioSetSADFunc = 0; + if (dsAudioSetSADFunc == 0) { + dsAudioSetSADFunc = (dsAudioSetSAD_t)resolve(RDK_DSHAL_NAME, "dsAudioSetSAD"); + if(dsAudioSetSADFunc == 0) { + LOGERR("dsAudioSetSAD is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsAudioSADList_t sadList_hal; + memcpy(sadList_hal.sad, sadList, count < 15 ? count : 15); + sadList_hal.count = count; + dsError_t ret = dsERR_GENERAL; + if (0 != dsAudioSetSADFunc) { + ret = dsAudioSetSADFunc(dsHandle, sadList_hal); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetSAD success: handle=%d, count=%d", handle, count); + } else { + LOGERR("dsSetSAD failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetSAD"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t EnableARC(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::AudioARCStatus arcStatus) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioARCStatus_t dsARCStatus; + dsARCStatus.type = static_cast(arcStatus.arcType); + dsARCStatus.status = arcStatus.status; + + // dsAudio.c uses dsAudioEnableARC (not dsEnableARC) + typedef dsError_t (*dsAudioEnableARC_t)(intptr_t handle, dsAudioARCStatus_t arcStatus); + static dsAudioEnableARC_t dsAudioEnableARCFunc = 0; + if (dsAudioEnableARCFunc == 0) { + dsAudioEnableARCFunc = (dsAudioEnableARC_t)resolve(RDK_DSHAL_NAME, "dsAudioEnableARC"); + if(dsAudioEnableARCFunc == 0) { + LOGERR("dsAudioEnableARC is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsAudioEnableARCFunc) { + ret = dsAudioEnableARCFunc(dsHandle, dsARCStatus); + } + + if (ret == dsERR_NONE) { + LOGINFO("EnableARC success: handle=%d, arcStatus type=%d status=%d", handle, static_cast(arcStatus.arcType), static_cast(arcStatus.status)); + } else { + LOGERR("dsEnableARC failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in EnableARC"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioEnablePersist(const int32_t handle, bool &enabled, string &portName) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + // By default all ports are enabled + enabled = true; + + std::string isEnabledAudioPortKey("audio."); + isEnabledAudioPortKey.append(portName); + isEnabledAudioPortKey.append(".isEnabled"); + std::string _AudioPortEnable("TRUE"); + + try { + _AudioPortEnable = device::HostPersistence::getInstance().getProperty(isEnabledAudioPortKey); + } + catch(...) { + try { + LOGINFO("GetAudioEnablePersist: %s port enable settings not found in persistence store. Try system default", isEnabledAudioPortKey.c_str()); + _AudioPortEnable = device::HostPersistence::getInstance().getDefaultProperty(isEnabledAudioPortKey); + } + catch(...) { + // By default enable all the ports + _AudioPortEnable = "TRUE"; + } + } + + if ("FALSE" == _AudioPortEnable) { + LOGINFO("GetAudioEnablePersist: persist dsEnableAudioPort value: %s", _AudioPortEnable.c_str()); + enabled = false; + } + else { + LOGINFO("GetAudioEnablePersist: persist dsEnableAudioPort value: %s", _AudioPortEnable.c_str()); + enabled = true; + } + + LOGINFO("GetAudioEnablePersist success: handle=%d, portName=%s, enabled=%s, key=%s, value=%s", + handle, portName.c_str(), enabled ? "TRUE" : "FALSE", isEnabledAudioPortKey.c_str(), _AudioPortEnable.c_str()); + } catch (...) { + LOGERR("Exception in GetAudioEnablePersist"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioEnablePersist(const int32_t handle, const bool enable, const string portName) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + std::string isEnabledAudioPortKey("audio."); + isEnabledAudioPortKey.append(portName); + isEnabledAudioPortKey.append(".isEnabled"); + + std::string enableValue = enable ? "TRUE" : "FALSE"; + device::HostPersistence::getInstance().persistHostProperty(isEnabledAudioPortKey.c_str(), enableValue.c_str()); + + LOGINFO("SetAudioEnablePersist success: handle=%d, portName=%s, enable=%s, key=%s", + handle, portName.c_str(), enableValue.c_str(), isEnabledAudioPortKey.c_str()); + } catch (...) { + LOGERR("Exception in SetAudioEnablePersist"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t IsAudioMSDecoded(const int32_t handle, bool &hasms11Decode) override { + ENTRY_LOG; + try { + bool ms11Decoded = false; + + // Use resolve function for dsIsAudioMSDecode + typedef dsError_t (*dsIsAudioMSDecode_t)(intptr_t handle, bool* decoded); + static dsIsAudioMSDecode_t dsIsAudioMSDecodeFunc = 0; + if (dsIsAudioMSDecodeFunc == 0) { + dsIsAudioMSDecodeFunc = (dsIsAudioMSDecode_t)resolve(RDK_DSHAL_NAME, "dsIsAudioMSDecode"); + if (dsIsAudioMSDecodeFunc == 0) { + LOGERR("dsIsAudioMSDecode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsIsAudioMSDecodeFunc) { + dsResult = dsIsAudioMSDecodeFunc(static_cast(handle), &ms11Decoded); + } + + if (dsResult == dsERR_NONE) { + hasms11Decode = ms11Decoded; + } else { + LOGERR("dsIsAudioMSDecode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioMSDecoded"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t IsAudioMS12Decoded(const int32_t handle, bool &hasms12Decode) override { + ENTRY_LOG; + try { + bool ms12Decoded = false; + dsError_t dsResult = dsIsAudioMS12Decode(static_cast(handle), &ms12Decoded); + if (dsResult == dsERR_NONE) { + hasms12Decode = ms12Decoded; + } else { + LOGERR("dsIsAudioMS12Decode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioMS12Decoded"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioLEConfig(const int32_t handle, bool &enabled) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + bool leEnabled; + // Use resolve function for dsGetLEConfig + typedef dsError_t (*dsGetLEConfig_t)(intptr_t handle, bool* enabled); + static dsGetLEConfig_t dsGetLEConfigFunc = 0; + if (dsGetLEConfigFunc == 0) { + dsGetLEConfigFunc = (dsGetLEConfig_t)resolve(RDK_DSHAL_NAME, "dsGetLEConfig"); + if (dsGetLEConfigFunc == 0) { + LOGERR("dsGetLEConfig is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetLEConfigFunc) { + ret = dsGetLEConfigFunc(dsHandle, &leEnabled); + } + if (ret == dsERR_NONE) { + enabled = leEnabled; + LOGINFO("GetAudioLEConfig success: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + } else { + LOGERR("dsGetLEConfig failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioLEConfig"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t EnableAudioLEConfig(const int32_t handle, const bool enable) override { + ENTRY_LOG; + try { + // dsAudio.c uses dsEnableLEConfig(handle, enable) — NOT dsEnableMS12Config + typedef dsError_t (*dsEnableLEConfig_t)(intptr_t handle, const bool enable); + static dsEnableLEConfig_t dsEnableLEConfigFunc = nullptr; + if (dsEnableLEConfigFunc == nullptr) { + dsEnableLEConfigFunc = (dsEnableLEConfig_t)resolve(RDK_DSHAL_NAME, "dsEnableLEConfig"); + if (dsEnableLEConfigFunc == nullptr) { + LOGERR("dsEnableLEConfig is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + /* Mirror dsAudio.c _dsEnableLEConfig: only call HAL and persist + * when the value actually changes — avoids redundant HAL calls. */ + if (enable != m_LEEnabled) { + m_LEEnabled = enable; +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.LEEnable", enable ? "TRUE" : "FALSE"); +#endif + dsError_t dsResult = dsEnableLEConfigFunc(static_cast(handle), enable); + if (dsResult != dsERR_NONE) { + LOGERR("dsEnableLEConfig failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } + } catch (...) { + LOGERR("Exception in EnableAudioLEConfig"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDelay(const int32_t handle, const uint32_t audioDelay) override { + ENTRY_LOG; + try { + // Use resolve function for dsSetAudioDelay + typedef dsError_t (*dsSetAudioDelay_t)(intptr_t handle, uint32_t audioDelay); + static dsSetAudioDelay_t dsSetAudioDelayFunc = 0; + if (dsSetAudioDelayFunc == 0) { + dsSetAudioDelayFunc = (dsSetAudioDelay_t)resolve(RDK_DSHAL_NAME, "dsSetAudioDelay"); + if (dsSetAudioDelayFunc == 0) { + LOGERR("dsSetAudioDelay is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsSetAudioDelayFunc) { + dsResult = dsSetAudioDelayFunc(static_cast(handle), audioDelay); + } + + if (dsResult == dsERR_NONE) { + LOGINFO("SetAudioDelay success: handle=%d, delay=%u", handle, audioDelay); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _delay = std::to_string(audioDelay); + dsAudioPortType_t _portType = getAudioPortType(static_cast(handle)); + switch (_portType) { + case dsAUDIOPORT_TYPE_SPDIF: device::HostPersistence::getInstance().persistHostProperty("SPDIF0.audio.Delay", _delay); break; + case dsAUDIOPORT_TYPE_HDMI: device::HostPersistence::getInstance().persistHostProperty("HDMI0.audio.Delay", _delay); break; + case dsAUDIOPORT_TYPE_SPEAKER: device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.audio.Delay", _delay); break; + case dsAUDIOPORT_TYPE_HDMI_ARC: device::HostPersistence::getInstance().persistHostProperty("HDMI_ARC0.audio.Delay", _delay); break; + default: break; + } +#endif + } else { + LOGERR("dsSetAudioDelay failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioDelay"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioDelay(const int32_t handle, uint32_t &audioDelay) override { + ENTRY_LOG; + try { + uint32_t delay = 0; + // Use resolve function for dsGetAudioDelay + typedef dsError_t (*dsGetAudioDelay_t)(intptr_t handle, uint32_t* delay); + static dsGetAudioDelay_t dsGetAudioDelayFunc = 0; + if (dsGetAudioDelayFunc == 0) { + dsGetAudioDelayFunc = (dsGetAudioDelay_t)resolve(RDK_DSHAL_NAME, "dsGetAudioDelay"); + if (dsGetAudioDelayFunc == 0) { + LOGERR("dsGetAudioDelay is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetAudioDelayFunc) { + dsResult = dsGetAudioDelayFunc(static_cast(handle), &delay); + } + if (dsResult == dsERR_NONE) { + audioDelay = delay; + LOGINFO("GetAudioDelay success: handle=%d, delay=%u", handle, audioDelay); + } else { + LOGERR("dsGetAudioDelay failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioDelay"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDelayOffset(const int32_t handle, const uint32_t delayOffset) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + typedef dsError_t (*dsSetAudioDelayOffset_t)(intptr_t handle, uint32_t delayOffset); + static dsSetAudioDelayOffset_t dsSetAudioDelayOffsetFunc = 0; + if (dsSetAudioDelayOffsetFunc == 0) { + dsSetAudioDelayOffsetFunc = (dsSetAudioDelayOffset_t)resolve(RDK_DSHAL_NAME, "dsSetAudioDelayOffset"); + if(dsSetAudioDelayOffsetFunc == 0) { + LOGERR("dsSetAudioDelayOffset is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetAudioDelayOffsetFunc) { + ret = dsSetAudioDelayOffsetFunc(dsHandle, delayOffset); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioDelayOffset success: handle=%d, offset=%u", handle, delayOffset); + } else { + LOGERR("dsSetAudioDelayOffset failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioDelayOffset"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioDelayOffset(const int32_t handle, uint32_t &delayOffset) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + uint32_t dsOffset; + + typedef dsError_t (*dsGetAudioDelayOffset_t)(intptr_t handle, uint32_t* delayOffset); + static dsGetAudioDelayOffset_t dsGetAudioDelayOffsetFunc = 0; + if (dsGetAudioDelayOffsetFunc == 0) { + dsGetAudioDelayOffsetFunc = (dsGetAudioDelayOffset_t)resolve(RDK_DSHAL_NAME, "dsGetAudioDelayOffset"); + if(dsGetAudioDelayOffsetFunc == 0) { + LOGERR("dsGetAudioDelayOffset is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetAudioDelayOffsetFunc) { + ret = dsGetAudioDelayOffsetFunc(dsHandle, &dsOffset); + } + + if (ret == dsERR_NONE) { + delayOffset = dsOffset; + LOGINFO("GetAudioDelayOffset success: handle=%d, offset=%u", handle, delayOffset); + } else { + LOGERR("dsGetAudioDelayOffset failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioDelayOffset"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioCompression(const int32_t handle, const int32_t compressionLevel) override { + ENTRY_LOG; + try { + // Use resolve function for dsSetAudioCompression + typedef dsError_t (*dsSetAudioCompression_t)(intptr_t handle, int compression); + static dsSetAudioCompression_t dsSetAudioCompressionFunc = 0; + if (dsSetAudioCompressionFunc == 0) { + dsSetAudioCompressionFunc = (dsSetAudioCompression_t)resolve(RDK_DSHAL_NAME, "dsSetAudioCompression"); + if (dsSetAudioCompressionFunc == 0) { + LOGERR("dsSetAudioCompression is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsSetAudioCompressionFunc) { + dsResult = dsSetAudioCompressionFunc(static_cast(handle), compressionLevel); + } + if (dsResult == dsERR_NONE) { + LOGINFO("SetAudioCompression success: handle=%d, level=%d", handle, compressionLevel); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.Compression", std::to_string(compressionLevel)); +#endif + } else { + LOGERR("dsSetAudioCompression failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioCompression"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioCompression(const int32_t handle, int32_t &compressionLevel) override { + ENTRY_LOG; + try { + int compression = 0; + // Use resolve function for dsGetAudioCompression + typedef dsError_t (*dsGetAudioCompression_t)(intptr_t handle, int* compression); + static dsGetAudioCompression_t dsGetAudioCompressionFunc = 0; + if (dsGetAudioCompressionFunc == 0) { + dsGetAudioCompressionFunc = (dsGetAudioCompression_t)resolve(RDK_DSHAL_NAME, "dsGetAudioCompression"); + if (dsGetAudioCompressionFunc == 0) { + LOGERR("dsGetAudioCompression is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetAudioCompressionFunc) { + dsResult = dsGetAudioCompressionFunc(static_cast(handle), &compression); + } + if (dsResult == dsERR_NONE) { + compressionLevel = compression; + LOGINFO("GetAudioCompression success: handle=%d, level=%d", handle, compressionLevel); + } else { + LOGERR("dsGetAudioCompression failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioCompression"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDialogEnhancement(const int32_t handle, const int32_t level) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Use resolve function for dsSetDialogEnhancement + typedef dsError_t (*dsSetDialogEnhancement_t)(intptr_t handle, int level); + static dsSetDialogEnhancement_t dsSetDialogEnhancementFunc = 0; + if (dsSetDialogEnhancementFunc == 0) { + dsSetDialogEnhancementFunc = (dsSetDialogEnhancement_t)resolve(RDK_DSHAL_NAME, "dsSetDialogEnhancement"); + if (dsSetDialogEnhancementFunc == 0) { + LOGERR("dsSetDialogEnhancement is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetDialogEnhancementFunc) { + ret = dsSetDialogEnhancementFunc(dsHandle, level); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioDialogEnhancement success: handle=%d, level=%d", handle, level); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("EnhancerLevel"), std::to_string(level)); +#endif + } else { + LOGERR("dsSetDialogEnhancement failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioDialogEnhancement"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioDialogEnhancement(const int32_t handle, int32_t &level) override { + ENTRY_LOG; + try { + int dialogLevel = 0; + // Use resolve function for dsGetDialogEnhancement + typedef dsError_t (*dsGetDialogEnhancement_t)(intptr_t handle, int* level); + static dsGetDialogEnhancement_t dsGetDialogEnhancementFunc = 0; + if (dsGetDialogEnhancementFunc == 0) { + dsGetDialogEnhancementFunc = (dsGetDialogEnhancement_t)resolve(RDK_DSHAL_NAME, "dsGetDialogEnhancement"); + if (dsGetDialogEnhancementFunc == 0) { + LOGERR("dsGetDialogEnhancement is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetDialogEnhancementFunc) { + dsResult = dsGetDialogEnhancementFunc(static_cast(handle), &dialogLevel); + } + if (dsResult == dsERR_NONE) { + level = dialogLevel; + LOGINFO("GetAudioDialogEnhancement success: handle=%d, level=%d", handle, level); + } else { + LOGERR("dsGetDialogEnhancement failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioDialogEnhancement"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDolbyVolumeMode(const int32_t handle, const bool enable) override { + ENTRY_LOG; + try { + // Use resolve function for dsSetDolbyVolumeMode + typedef dsError_t (*dsSetDolbyVolumeMode_t)(intptr_t handle, bool enable); + static dsSetDolbyVolumeMode_t dsSetDolbyVolumeModeFunc = 0; + if (dsSetDolbyVolumeModeFunc == 0) { + dsSetDolbyVolumeModeFunc = (dsSetDolbyVolumeMode_t)resolve(RDK_DSHAL_NAME, "dsSetDolbyVolumeMode"); + if (dsSetDolbyVolumeModeFunc == 0) { + LOGERR("dsSetDolbyVolumeMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsSetDolbyVolumeModeFunc) { + dsResult = dsSetDolbyVolumeModeFunc(static_cast(handle), enable); + } + if (dsResult == dsERR_NONE) { + LOGINFO("SetAudioDolbyVolumeMode success: handle=%d, enable=%s", handle, enable ? "true" : "false"); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.DolbyVolumeMode", enable ? "TRUE" : "FALSE"); +#endif + } else { + LOGERR("dsSetDolbyVolumeMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioDolbyVolumeMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioDolbyVolumeMode(const int32_t handle, bool &enabled) override { + ENTRY_LOG; + try { + bool dolbyMode = false; + // Use resolve function for dsGetDolbyVolumeMode + typedef dsError_t (*dsGetDolbyVolumeMode_t)(intptr_t handle, bool* mode); + static dsGetDolbyVolumeMode_t dsGetDolbyVolumeModeFunc = 0; + if (dsGetDolbyVolumeModeFunc == 0) { + dsGetDolbyVolumeModeFunc = (dsGetDolbyVolumeMode_t)resolve(RDK_DSHAL_NAME, "dsGetDolbyVolumeMode"); + if (dsGetDolbyVolumeModeFunc == 0) { + LOGERR("dsGetDolbyVolumeMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetDolbyVolumeModeFunc) { + dsResult = dsGetDolbyVolumeModeFunc(static_cast(handle), &dolbyMode); + } + if (dsResult == dsERR_NONE) { + enabled = dolbyMode; + LOGINFO("GetAudioDolbyVolumeMode success: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + } else { + LOGERR("dsGetDolbyVolumeMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioDolbyVolumeMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioIntelligentEqualizerMode(const int32_t handle, const int32_t mode) override { + ENTRY_LOG; + try { + // Use resolve function for dsSetIntelligentEqualizerMode + typedef dsError_t (*dsSetIntelligentEqualizerMode_t)(intptr_t handle, int mode); + static dsSetIntelligentEqualizerMode_t dsSetIntelligentEqualizerModeFunc = 0; + if (dsSetIntelligentEqualizerModeFunc == 0) { + dsSetIntelligentEqualizerModeFunc = (dsSetIntelligentEqualizerMode_t)resolve(RDK_DSHAL_NAME, "dsSetIntelligentEqualizerMode"); + if (dsSetIntelligentEqualizerModeFunc == 0) { + LOGERR("dsSetIntelligentEqualizerMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsSetIntelligentEqualizerModeFunc) { + dsResult = dsSetIntelligentEqualizerModeFunc(static_cast(handle), mode); + } + if (dsResult == dsERR_NONE) { + LOGINFO("SetAudioIntelligentEqualizerMode success: handle=%d, mode=%d", handle, mode); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.IntelligentEQ", std::to_string(mode)); +#endif + } else { + LOGERR("dsSetIntelligentEqualizerMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioIntelligentEqualizerMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioIntelligentEqualizerMode(const int32_t handle, int32_t &mode) override { + ENTRY_LOG; + try { + int eqMode = 0; + // Use resolve function for dsGetIntelligentEqualizerMode + typedef dsError_t (*dsGetIntelligentEqualizerMode_t)(intptr_t handle, int* mode); + static dsGetIntelligentEqualizerMode_t dsGetIntelligentEqualizerModeFunc = 0; + if (dsGetIntelligentEqualizerModeFunc == 0) { + dsGetIntelligentEqualizerModeFunc = (dsGetIntelligentEqualizerMode_t)resolve(RDK_DSHAL_NAME, "dsGetIntelligentEqualizerMode"); + if (dsGetIntelligentEqualizerModeFunc == 0) { + LOGERR("dsGetIntelligentEqualizerMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetIntelligentEqualizerModeFunc) { + dsResult = dsGetIntelligentEqualizerModeFunc(static_cast(handle), &eqMode); + } + if (dsResult == dsERR_NONE) { + mode = eqMode; + LOGINFO("GetAudioIntelligentEqualizerMode success: handle=%d, mode=%d", handle, mode); + } else { + LOGERR("dsGetIntelligentEqualizerMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioIntelligentEqualizerMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioVolumeLeveller(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::VolumeLeveller volumeLeveller) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsVolumeLeveller_t dsVolLeveller; + dsVolLeveller.mode = static_cast(volumeLeveller.mode); + dsVolLeveller.level = static_cast(volumeLeveller.level); + // Use resolve function for dsSetVolumeLeveller + typedef dsError_t (*dsSetVolumeLeveller_t)(intptr_t handle, dsVolumeLeveller_t leveller); + static dsSetVolumeLeveller_t dsSetVolumeLevellerFunc = 0; + if (dsSetVolumeLevellerFunc == 0) { + dsSetVolumeLevellerFunc = (dsSetVolumeLeveller_t)resolve(RDK_DSHAL_NAME, "dsSetVolumeLeveller"); + if (dsSetVolumeLevellerFunc == 0) { + LOGERR("dsSetVolumeLeveller is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetVolumeLevellerFunc) { + ret = dsSetVolumeLevellerFunc(dsHandle, dsVolLeveller); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioVolumeLeveller success: handle=%d, mode=%d, level=%d", handle, volumeLeveller.mode, volumeLeveller.level); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _PropertyMode = getCurrentProfileProperty("VolumeLeveller.mode"); + std::string _PropertyLevel = getCurrentProfileProperty("VolumeLeveller.level"); + device::HostPersistence::getInstance().persistHostProperty(_PropertyMode, std::to_string(volumeLeveller.mode)); + if ((volumeLeveller.mode == 0) || (volumeLeveller.mode == 1)) { + device::HostPersistence::getInstance().persistHostProperty(_PropertyLevel, std::to_string(volumeLeveller.level)); + } +#endif + } else { + LOGERR("dsSetVolumeLeveller failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioVolumeLeveller"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioVolumeLeveller(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::VolumeLeveller &volumeLeveller) override { + ENTRY_LOG; + try { + dsVolumeLeveller_t volLeveller; + // Use resolve function for dsGetVolumeLeveller + typedef dsError_t (*dsGetVolumeLeveller_t)(intptr_t handle, dsVolumeLeveller_t* leveller); + static dsGetVolumeLeveller_t dsGetVolumeLevellerFunc = 0; + if (dsGetVolumeLevellerFunc == 0) { + dsGetVolumeLevellerFunc = (dsGetVolumeLeveller_t)resolve(RDK_DSHAL_NAME, "dsGetVolumeLeveller"); + if (dsGetVolumeLevellerFunc == 0) { + LOGERR("dsGetVolumeLeveller is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetVolumeLevellerFunc) { + dsResult = dsGetVolumeLevellerFunc(static_cast(handle), &volLeveller); + } + if (dsResult == dsERR_NONE) { + // Convert dsVolumeLeveller_t to VolumeLeveller enum + volumeLeveller.mode = static_cast(volLeveller.mode); + volumeLeveller.level = static_cast(volLeveller.level); + } else { + LOGERR("dsGetVolumeLeveller failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioVolumeLeveller"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioBassEnhancer(const int32_t handle, const int32_t boost) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // Use resolve function for dsSetBassEnhancer + typedef dsError_t (*dsSetBassEnhancer_t)(intptr_t handle, int boost); + static dsSetBassEnhancer_t dsSetBassEnhancerFunc = 0; + if (dsSetBassEnhancerFunc == 0) { + dsSetBassEnhancerFunc = (dsSetBassEnhancer_t)resolve(RDK_DSHAL_NAME, "dsSetBassEnhancer"); + if (dsSetBassEnhancerFunc == 0) { + LOGERR("dsSetBassEnhancer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetBassEnhancerFunc) { + ret = dsSetBassEnhancerFunc(dsHandle, boost); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioBassEnhancer success: handle=%d, boost=%d", handle, boost); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.BassBoost", std::to_string(boost)); +#endif + } else { + LOGERR("dsSetBassEnhancer failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioBassEnhancer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioBassEnhancer(const int32_t handle, int32_t &boost) override { + ENTRY_LOG; + try { + int bassBoost = 0; + // Use resolve function for dsGetBassEnhancer + typedef dsError_t (*dsGetBassEnhancer_t)(intptr_t handle, int* boost); + static dsGetBassEnhancer_t dsGetBassEnhancerFunc = 0; + if (dsGetBassEnhancerFunc == 0) { + dsGetBassEnhancerFunc = (dsGetBassEnhancer_t)resolve(RDK_DSHAL_NAME, "dsGetBassEnhancer"); + if (dsGetBassEnhancerFunc == 0) { + LOGERR("dsGetBassEnhancer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetBassEnhancerFunc) { + dsResult = dsGetBassEnhancerFunc(static_cast(handle), &bassBoost); + } + if (dsResult == dsERR_NONE) { + boost = bassBoost; + } else { + LOGERR("dsGetBassEnhancer failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioBassEnhancer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t EnableAudioSurroudDecoder(const int32_t handle, const bool enable) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // Use resolve function for dsEnableSurroundDecoder + typedef dsError_t (*dsEnableSurroundDecoder_t)(intptr_t handle, bool enable); + static dsEnableSurroundDecoder_t dsEnableSurroundDecoderFunc = 0; + if (dsEnableSurroundDecoderFunc == 0) { + dsEnableSurroundDecoderFunc = (dsEnableSurroundDecoder_t)resolve(RDK_DSHAL_NAME, "dsEnableSurroundDecoder"); + if (dsEnableSurroundDecoderFunc == 0) { + LOGERR("dsEnableSurroundDecoder is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsEnableSurroundDecoderFunc) { + ret = dsEnableSurroundDecoderFunc(dsHandle, enable); + } + if (ret == dsERR_NONE) { + LOGINFO("EnableAudioSurroudDecoder success: handle=%d, enable=%s", handle, enable ? "true" : "false"); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.SurroundDecoderEnabled", enable ? "TRUE" : "FALSE"); +#endif + } else { + LOGERR("dsEnableSurroundDecoder failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in EnableAudioSurroudDecoder"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t IsAudioSurroudDecoderEnabled(const int32_t handle, bool &enabled) override { + ENTRY_LOG; + try { + bool decoderEnabled = false; + // Use resolve function for dsIsSurroundDecoderEnabled + typedef dsError_t (*dsIsSurroundDecoderEnabled_t)(intptr_t handle, bool* enabled); + static dsIsSurroundDecoderEnabled_t dsIsSurroundDecoderEnabledFunc = 0; + if (dsIsSurroundDecoderEnabledFunc == 0) { + dsIsSurroundDecoderEnabledFunc = (dsIsSurroundDecoderEnabled_t)resolve(RDK_DSHAL_NAME, "dsIsSurroundDecoderEnabled"); + if (dsIsSurroundDecoderEnabledFunc == 0) { + LOGERR("dsIsSurroundDecoderEnabled is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsIsSurroundDecoderEnabledFunc) { + dsResult = dsIsSurroundDecoderEnabledFunc(static_cast(handle), &decoderEnabled); + } + if (dsResult == dsERR_NONE) { + enabled = decoderEnabled; + } else { + LOGERR("dsIsSurroundDecoderEnabled failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in IsAudioSurroudDecoderEnabled"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioDRCMode(const int32_t handle, const int32_t drcMode) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // Use resolve function for dsSetDRCMode + typedef dsError_t (*dsSetDRCMode_t)(intptr_t handle, int mode); + static dsSetDRCMode_t dsSetDRCModeFunc = 0; + if (dsSetDRCModeFunc == 0) { + dsSetDRCModeFunc = (dsSetDRCMode_t)resolve(RDK_DSHAL_NAME, "dsSetDRCMode"); + if (dsSetDRCModeFunc == 0) { + LOGERR("dsSetDRCMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetDRCModeFunc) { + ret = dsSetDRCModeFunc(dsHandle, drcMode); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioDRCMode success: handle=%d, drcMode=%d", handle, drcMode); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.DRCMode", drcMode ? "RF" : "Line"); +#endif + } else { + LOGERR("dsSetDRCMode failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioDRCMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioDRCMode(const int32_t handle, int32_t &drcMode) override { + ENTRY_LOG; + try { + int mode = 0; + // Use resolve function for dsGetDRCMode + typedef dsError_t (*dsGetDRCMode_t)(intptr_t handle, int* mode); + static dsGetDRCMode_t dsGetDRCModeFunc = 0; + if (dsGetDRCModeFunc == 0) { + dsGetDRCModeFunc = (dsGetDRCMode_t)resolve(RDK_DSHAL_NAME, "dsGetDRCMode"); + if (dsGetDRCModeFunc == 0) { + LOGERR("dsGetDRCMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetDRCModeFunc) { + dsResult = dsGetDRCModeFunc(static_cast(handle), &mode); + } + if (dsResult == dsERR_NONE) { + drcMode = mode; + } else { + LOGERR("dsGetDRCMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioDRCMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioSurroudVirtualizer(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::SurroundVirtualizer surroundVirtualizer) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsSurroundVirtualizer_t dsSurVirtualizer; + dsSurVirtualizer.mode = static_cast(surroundVirtualizer.mode); + dsSurVirtualizer.boost = surroundVirtualizer.boost; + // Use resolve function for dsSetSurroundVirtualizer + typedef dsError_t (*dsSetSurroundVirtualizer_t)(intptr_t handle, dsSurroundVirtualizer_t virtualizer); + static dsSetSurroundVirtualizer_t dsSetSurroundVirtualizerFunc = 0; + if (dsSetSurroundVirtualizerFunc == 0) { + dsSetSurroundVirtualizerFunc = (dsSetSurroundVirtualizer_t)resolve(RDK_DSHAL_NAME, "dsSetSurroundVirtualizer"); + if (dsSetSurroundVirtualizerFunc == 0) { + LOGERR("dsSetSurroundVirtualizer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetSurroundVirtualizerFunc) { + ret = dsSetSurroundVirtualizerFunc(dsHandle, dsSurVirtualizer); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioSurroudVirtualizer success: handle=%d, mode=%d, boost=%d", handle, surroundVirtualizer.mode, surroundVirtualizer.boost); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _PropertyMode = getCurrentProfileProperty("SurroundVirtualizer.mode"); + std::string _PropertyBoost = getCurrentProfileProperty("SurroundVirtualizer.boost"); + device::HostPersistence::getInstance().persistHostProperty(_PropertyMode, std::to_string(surroundVirtualizer.mode)); + if ((surroundVirtualizer.mode >= 0) && (surroundVirtualizer.mode <= 2)) { + device::HostPersistence::getInstance().persistHostProperty(_PropertyBoost, std::to_string(surroundVirtualizer.boost)); + } +#endif + } else { + LOGERR("dsSetSurroundVirtualizer failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioSurroudVirtualizer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioSurroudVirtualizer(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::SurroundVirtualizer &surroundVirtualizer) override { + ENTRY_LOG; + try { + dsSurroundVirtualizer_t virtualizer; + // Use resolve function for dsGetSurroundVirtualizer + typedef dsError_t (*dsGetSurroundVirtualizer_t)(intptr_t handle, dsSurroundVirtualizer_t* virtualizer); + static dsGetSurroundVirtualizer_t dsGetSurroundVirtualizerFunc = 0; + if (dsGetSurroundVirtualizerFunc == 0) { + dsGetSurroundVirtualizerFunc = (dsGetSurroundVirtualizer_t)resolve(RDK_DSHAL_NAME, "dsGetSurroundVirtualizer"); + if (dsGetSurroundVirtualizerFunc == 0) { + LOGERR("dsGetSurroundVirtualizer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetSurroundVirtualizerFunc) { + dsResult = dsGetSurroundVirtualizerFunc(static_cast(handle), &virtualizer); + } + if (dsResult == dsERR_NONE) { + // Convert dsSurroundVirtualizer_t to SurroundVirtualizer enum + surroundVirtualizer.mode = static_cast(virtualizer.mode); + surroundVirtualizer.boost = static_cast(virtualizer.boost); + } else { + LOGERR("dsGetSurroundVirtualizer failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioSurroudVirtualizer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioMISteering(const int32_t handle, const bool enable) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // Use resolve function for dsSetMISteering + typedef dsError_t (*dsSetMISteering_t)(intptr_t handle, bool enable); + static dsSetMISteering_t dsSetMISteeringFunc = 0; + if (dsSetMISteeringFunc == 0) { + dsSetMISteeringFunc = (dsSetMISteering_t)resolve(RDK_DSHAL_NAME, "dsSetMISteering"); + if (dsSetMISteeringFunc == 0) { + LOGERR("dsSetMISteering is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetMISteeringFunc) { + ret = dsSetMISteeringFunc(dsHandle, enable); + } + if (ret == dsERR_NONE) { + LOGINFO("SetAudioMISteering success: handle=%d, enable=%s", handle, enable ? "true" : "false"); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.MISteering", enable ? "Enabled" : "Disabled"); +#endif + } else { + LOGERR("dsSetMISteering failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioMISteering"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioMISteering(const int32_t handle, bool &enable) override { + ENTRY_LOG; + try { + bool miSteering = false; + // Use resolve function for dsGetMISteering + typedef dsError_t (*dsGetMISteering_t)(intptr_t handle, bool* steering); + static dsGetMISteering_t dsGetMISteeringFunc = 0; + if (dsGetMISteeringFunc == 0) { + dsGetMISteeringFunc = (dsGetMISteering_t)resolve(RDK_DSHAL_NAME, "dsGetMISteering"); + if (dsGetMISteeringFunc == 0) { + LOGERR("dsGetMISteering is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetMISteeringFunc) { + dsResult = dsGetMISteeringFunc(static_cast(handle), &miSteering); + } + if (dsResult == dsERR_NONE) { + enable = miSteering; + } else { + LOGERR("dsGetMISteering failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioMISteering"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioGraphicEqualizerMode(const int32_t handle, const int32_t mode) override { + ENTRY_LOG; + try { + // Use resolve function for dsSetGraphicEqualizerMode + typedef dsError_t (*dsSetGraphicEqualizerMode_t)(intptr_t handle, int mode); + static dsSetGraphicEqualizerMode_t dsSetGraphicEqualizerModeFunc = 0; + if (dsSetGraphicEqualizerModeFunc == 0) { + dsSetGraphicEqualizerModeFunc = (dsSetGraphicEqualizerMode_t)resolve(RDK_DSHAL_NAME, "dsSetGraphicEqualizerMode"); + if (dsSetGraphicEqualizerModeFunc == 0) { + LOGERR("dsSetGraphicEqualizerMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsSetGraphicEqualizerModeFunc) { + dsResult = dsSetGraphicEqualizerModeFunc(static_cast(handle), mode); + } + if (dsResult == dsERR_NONE) { + LOGINFO("SetAudioGraphicEqualizerMode success: handle=%d, mode=%d", handle, mode); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.GraphicEQ", std::to_string(mode)); +#endif + } else { + LOGERR("dsSetGraphicEqualizerMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioGraphicEqualizerMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioGraphicEqualizerMode(const int32_t handle, int32_t &mode) override { + ENTRY_LOG; + try { + int eqMode = 0; + // Use resolve function for dsGetGraphicEqualizerMode + typedef dsError_t (*dsGetGraphicEqualizerMode_t)(intptr_t handle, int* mode); + static dsGetGraphicEqualizerMode_t dsGetGraphicEqualizerModeFunc = 0; + if (dsGetGraphicEqualizerModeFunc == 0) { + dsGetGraphicEqualizerModeFunc = (dsGetGraphicEqualizerMode_t)resolve(RDK_DSHAL_NAME, "dsGetGraphicEqualizerMode"); + if (dsGetGraphicEqualizerModeFunc == 0) { + LOGERR("dsGetGraphicEqualizerMode is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t dsResult = dsERR_GENERAL; + if (0 != dsGetGraphicEqualizerModeFunc) { + dsResult = dsGetGraphicEqualizerModeFunc(static_cast(handle), &eqMode); + } + if (dsResult == dsERR_NONE) { + mode = eqMode; + LOGINFO("GetAudioGraphicEqualizerMode success: handle=%d, mode=%d", handle, mode); + } else { + LOGERR("dsGetGraphicEqualizerMode failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioGraphicEqualizerMode"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioMS12ProfileList(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsAudio::IDeviceSettingsAudioMS12AudioProfileIterator*& ms12ProfileList) const override { + ENTRY_LOG; + ms12ProfileList = nullptr; + try { + // dsAudio.c: _dsGetMS12AudioProfileList resolves and calls dsGetMS12AudioProfileList + typedef dsError_t (*dsGetMS12AudioProfileList_t)(intptr_t handle, dsMS12AudioProfileList_t* profiles); + static dsGetMS12AudioProfileList_t dsGetMS12AudioProfileListFunc = 0; + if (dsGetMS12AudioProfileListFunc == 0) { + dsGetMS12AudioProfileListFunc = (dsGetMS12AudioProfileList_t)resolve(RDK_DSHAL_NAME, "dsGetMS12AudioProfileList"); + if (dsGetMS12AudioProfileListFunc == 0) { + LOGERR("GetAudioMS12ProfileList: dsGetMS12AudioProfileList is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsMS12AudioProfileList_t pList; + memset(&pList, 0, sizeof(pList)); + dsError_t dsResult = dsGetMS12AudioProfileListFunc(static_cast(handle), &pList); + if (dsResult != dsERR_NONE) { + LOGERR("GetAudioMS12ProfileList: dsGetMS12AudioProfileList failed, error=%d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + + LOGINFO("GetAudioMS12ProfileList: handle=%d, count=%d, profiles=%s", + handle, pList.audioProfileCount, pList.audioProfileList); + + // Parse the comma-separated audioProfileList string into MS12AudioProfile structs + // (matches dsAudio.c pattern: audioProfileList is comma-separated, audioProfileCount is count) + std::vector profileVec; + char profileBuffer[MAX_PROFILE_LIST_BUFFER_LEN]; + strncpy(profileBuffer, pList.audioProfileList, MAX_PROFILE_LIST_BUFFER_LEN - 1); + profileBuffer[MAX_PROFILE_LIST_BUFFER_LEN - 1] = '\0'; + + char* token = strtok(profileBuffer, ","); + while (token != nullptr) { + // Skip leading/trailing whitespace + while (*token == ' ') token++; + if (*token != '\0') { + WPEFramework::Exchange::IDeviceSettingsAudio::MS12AudioProfile profile; + profile.audioProfile = std::string(token); + profileVec.push_back(profile); + } + token = strtok(nullptr, ","); + } + + LOGINFO("GetAudioMS12ProfileList: parsed %zu profiles", profileVec.size()); + + // Create the COM-RPC iterator + using MS12ProfileIterator = WPEFramework::RPC::IteratorType; + ms12ProfileList = WPEFramework::Core::Service::Create(profileVec); + + } catch (...) { + LOGERR("Exception in GetAudioMS12ProfileList"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioMS12Profile(const int32_t handle, string &profile) override { + ENTRY_LOG; + try { + char profileStr[256] = {0}; + dsError_t dsResult = dsGetMS12AudioProfile(static_cast(handle), profileStr); + if (dsResult == dsERR_NONE) { + profile = std::string(profileStr); + } else { + LOGERR("dsGetMS12AudioProfile failed with error: %d", dsResult); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetAudioMS12Profile"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioMS12Profile(const int32_t handle, const string& profile) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsError_t ret = dsSetMS12AudioProfile(dsHandle, profile.c_str()); + if (ret == dsERR_NONE) { + LOGINFO("SetAudioMS12Profile success: handle=%d, profile=%s", handle, profile.c_str()); +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + device::HostPersistence::getInstance().persistHostProperty("audio.MS12Profile", profile); +#endif + } else { + LOGERR("dsSetMS12AudioProfile failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioMS12Profile"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioMixerLevels(const int32_t handle, const WPEFramework::Exchange::IDeviceSettingsAudio::AudioInput audioInput, const int32_t volume) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + dsAudioInput_t dsInput = static_cast(audioInput); + + typedef dsError_t (*dsSetMixerLevel_t)(intptr_t handle, dsAudioInput_t input, int32_t level); + static dsSetMixerLevel_t dsSetMixerLevelFunc = 0; + if (dsSetMixerLevelFunc == 0) { + dsSetMixerLevelFunc = (dsSetMixerLevel_t)resolve(RDK_DSHAL_NAME, "dsSetMixerLevel"); + if(dsSetMixerLevelFunc == 0) { + LOGERR("dsSetMixerLevel is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetMixerLevelFunc) { + ret = dsSetMixerLevelFunc(dsHandle, dsInput, volume); + } + + if (ret == dsERR_NONE) { + LOGINFO("SetAudioMixerLevels success: handle=%d, input=%d, volume=%d", handle, static_cast(audioInput), volume); + } else { + LOGERR("dsSetMixerLevel failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in SetAudioMixerLevels"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetAudioMS12SettingsOverride(const int32_t handle, const string profileName, + const string profileSettingsName, const string profileSettingValue, + const string profileState) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + // dsAudio.c: _dsSetMS12SetttingsOverride is pure in-process logic — no single HAL function. + // It orchestrates dsSetDialogEnhancement/dsSetBassEnhancer/dsSetVolumeLeveller/dsSetSurroundVirtualizer. +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + try { + intptr_t dsHandle = static_cast(handle); + std::string _AProfile("Off"); + try { _AProfile = device::HostPersistence::getInstance().getProperty("audio.MS12Profile"); } + catch(...) { try { _AProfile = device::HostPersistence::getInstance().getDefaultProperty("audio.MS12Profile"); } catch(...) { _AProfile = "Off"; } } + + if (profileName == _AProfile) { + // Active profile — apply the setting immediately via HAL + if (profileSettingsName == "DialogEnhance") { + typedef dsError_t (*dsSetDialogEnhancement_t)(intptr_t h, int level); + dsSetDialogEnhancement_t fn = (dsSetDialogEnhancement_t)resolve(RDK_DSHAL_NAME, "dsSetDialogEnhancement"); + if (fn) { + if (profileState == "ADD") { + int val = atoi(profileSettingValue.c_str()); + if (fn(dsHandle, val) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("EnhancerLevel"), profileSettingValue); + } else if (profileState == "REMOVE") { + std::string _p = getCurrentProfileProperty("EnhancerLevel"); + std::string _def("0"); try { _def = device::HostPersistence::getInstance().getDefaultProperty(_p); } catch(...) {} + if (fn(dsHandle, atoi(_def.c_str())) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty(_p, _def); + } + } + } else if (profileSettingsName == "VolumeLevellerMode") { + int m = atoi(profileSettingValue.c_str()); + if (m == 0 || m == 1) device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("VolumeLeveller.mode"), profileSettingValue); + } else if (profileSettingsName == "VolumeLevellerLevel") { + typedef dsError_t (*dsSetVolumeLeveller_t)(intptr_t h, dsVolumeLeveller_t vl); + dsSetVolumeLeveller_t fn = (dsSetVolumeLeveller_t)resolve(RDK_DSHAL_NAME, "dsSetVolumeLeveller"); + if (fn) { + if (profileState == "ADD") { + std::string _pMode = getCurrentProfileProperty("VolumeLeveller.mode"); + dsVolumeLeveller_t vl; + try { vl.mode = atoi(device::HostPersistence::getInstance().getProperty(_pMode).c_str()); } catch(...) { vl.mode = 0; } + vl.level = atoi(profileSettingValue.c_str()); + if (fn(dsHandle, vl) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("VolumeLeveller.level"), profileSettingValue); + } else if (profileState == "REMOVE") { + std::string _pm = getCurrentProfileProperty("VolumeLeveller.mode"), _pl = getCurrentProfileProperty("VolumeLeveller.level"); + std::string _dm("0"), _dl("0"); try { _dm = device::HostPersistence::getInstance().getDefaultProperty(_pm); } catch(...) {} try { _dl = device::HostPersistence::getInstance().getDefaultProperty(_pl); } catch(...) {} + dsVolumeLeveller_t vl; vl.mode = atoi(_dm.c_str()); vl.level = atoi(_dl.c_str()); + if (fn(dsHandle, vl) == dsERR_NONE) { device::HostPersistence::getInstance().persistHostProperty(_pm, _dm); device::HostPersistence::getInstance().persistHostProperty(_pl, _dl); } + } + } + } else if (profileSettingsName == "BassEnhancer") { + typedef dsError_t (*dsSetBassEnhancer_t)(intptr_t h, int boost); + dsSetBassEnhancer_t fn = (dsSetBassEnhancer_t)resolve(RDK_DSHAL_NAME, "dsSetBassEnhancer"); + if (fn) { + if (profileState == "ADD") { + if (fn(dsHandle, atoi(profileSettingValue.c_str())) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty("audio.BassBoost", profileSettingValue); + } else if (profileState == "REMOVE") { + std::string _p = getCurrentProfileProperty("BassBoost"); + std::string _def("0"); try { _def = device::HostPersistence::getInstance().getDefaultProperty(_p); } catch(...) {} + if (fn(dsHandle, atoi(_def.c_str())) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty("audio.BassBoost", _def); + } + } + } else if (profileSettingsName == "SurroundVirtualizerMode") { + int m = atoi(profileSettingValue.c_str()); + if (m >= 0 && m <= 2) device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("SurroundVirtualizer.mode"), profileSettingValue); + } else if (profileSettingsName == "SurroundVirtualizerLevel") { + typedef dsError_t (*dsSetSurroundVirtualizer_t)(intptr_t h, dsSurroundVirtualizer_t virt); + dsSetSurroundVirtualizer_t fn = (dsSetSurroundVirtualizer_t)resolve(RDK_DSHAL_NAME, "dsSetSurroundVirtualizer"); + if (fn) { + if (profileState == "ADD") { + std::string _pMode = getCurrentProfileProperty("SurroundVirtualizer.mode"); + dsSurroundVirtualizer_t virt; + try { virt.mode = atoi(device::HostPersistence::getInstance().getProperty(_pMode).c_str()); } catch(...) { virt.mode = 0; } + virt.boost = atoi(profileSettingValue.c_str()); + if (fn(dsHandle, virt) == dsERR_NONE) + device::HostPersistence::getInstance().persistHostProperty(getCurrentProfileProperty("SurroundVirtualizer.boost"), profileSettingValue); + } else if (profileState == "REMOVE") { + std::string _pm = getCurrentProfileProperty("SurroundVirtualizer.mode"), _pb = getCurrentProfileProperty("SurroundVirtualizer.boost"); + std::string _dm("0"), _db("0"); try { _dm = device::HostPersistence::getInstance().getDefaultProperty(_pm); } catch(...) {} try { _db = device::HostPersistence::getInstance().getDefaultProperty(_pb); } catch(...) {} + dsSurroundVirtualizer_t virt; virt.mode = atoi(_dm.c_str()); virt.boost = atoi(_db.c_str()); + if (fn(dsHandle, virt) == dsERR_NONE) { device::HostPersistence::getInstance().persistHostProperty(_pm, _dm); device::HostPersistence::getInstance().persistHostProperty(_pb, _db); } + } + } + } else { + LOGWARN("SetAudioMS12SettingsOverride: Unknown setting name: %s", profileSettingsName.c_str()); + return WPEFramework::Core::ERROR_GENERAL; + } + } else { + // Non-active profile — just persist the value for future use + std::string hostProperty; + if (profileSettingsName == "DialogEnhance") hostProperty = generateProfileProperty(profileName, "EnhancerLevel"); + else if (profileSettingsName == "VolumeLevellerMode") hostProperty = generateProfileProperty(profileName, "VolumeLeveller.mode"); + else if (profileSettingsName == "VolumeLevellerLevel") hostProperty = generateProfileProperty(profileName, "VolumeLeveller.level"); + else if (profileSettingsName == "BassEnhancer") hostProperty = "audio.BassBoost"; + else if (profileSettingsName == "SurroundVirtualizerMode") hostProperty = generateProfileProperty(profileName, "SurroundVirtualizer.mode"); + else if (profileSettingsName == "SurroundVirtualizerLevel")hostProperty = generateProfileProperty(profileName, "SurroundVirtualizer.boost"); + else { LOGWARN("SetAudioMS12SettingsOverride: Unknown setting name: %s", profileSettingsName.c_str()); return WPEFramework::Core::ERROR_GENERAL; } + + if (profileState == "ADD") { + device::HostPersistence::getInstance().persistHostProperty(hostProperty, profileSettingValue); + } else if (profileState == "REMOVE") { + std::string _def("0"); try { _def = device::HostPersistence::getInstance().getDefaultProperty(hostProperty); } catch(...) {} + device::HostPersistence::getInstance().persistHostProperty(hostProperty, _def); + } + } + LOGINFO("SetAudioMS12SettingsOverride success: handle=%d, profile=%s, setting=%s, state=%s", + handle, profileName.c_str(), profileSettingsName.c_str(), profileState.c_str()); + } catch (...) { + LOGERR("Exception in SetAudioMS12SettingsOverride"); + return WPEFramework::Core::ERROR_GENERAL; + } +#else + LOGINFO("SetAudioMS12SettingsOverride: DS_AUDIO_SETTINGS_PERSISTENCE not enabled"); +#endif + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t ResetAudioDialogEnhancement(const int32_t handle) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // dsAudio.c: _resetDialogEnhancerLevel reads default, calls dsSetDialogEnhancement, persists + typedef dsError_t (*dsSetDialogEnhancement_t)(intptr_t handle, int enhancerLevel); + static dsSetDialogEnhancement_t dsSetDialogEnhancementFunc = 0; + if (dsSetDialogEnhancementFunc == 0) { + dsSetDialogEnhancementFunc = (dsSetDialogEnhancement_t)resolve(RDK_DSHAL_NAME, "dsSetDialogEnhancement"); + if (dsSetDialogEnhancementFunc == 0) { + LOGERR("dsSetDialogEnhancement is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _Property = getCurrentProfileProperty("EnhancerLevel"); + std::string _EnhancerLevel("0"); + try { _EnhancerLevel = device::HostPersistence::getInstance().getDefaultProperty(_Property); } catch(...) { _EnhancerLevel = "0"; } + int m_enhancerLevel = atoi(_EnhancerLevel.c_str()); + if (dsSetDialogEnhancementFunc(dsHandle, m_enhancerLevel) == dsERR_NONE) { + LOGINFO("ResetAudioDialogEnhancement: handle=%d, default level=%d", handle, m_enhancerLevel); + device::HostPersistence::getInstance().persistHostProperty(_Property, _EnhancerLevel); + } else { + LOGERR("ResetAudioDialogEnhancement dsSetDialogEnhancement failed"); + return WPEFramework::Core::ERROR_GENERAL; + } +#else + if (dsSetDialogEnhancementFunc(dsHandle, 0) != dsERR_NONE) { + LOGERR("ResetAudioDialogEnhancement failed"); + return WPEFramework::Core::ERROR_GENERAL; + } + LOGINFO("ResetAudioDialogEnhancement success: handle=%d", handle); +#endif + } catch (...) { + LOGERR("Exception in ResetAudioDialogEnhancement"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t ResetAudioBassEnhancer(const int32_t handle) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // dsAudio.c: _resetBassEnhancer reads default, calls dsSetBassEnhancer, persists + typedef dsError_t (*dsSetBassEnhancer_t)(intptr_t handle, int boost); + static dsSetBassEnhancer_t dsSetBassEnhancerFunc = 0; + if (dsSetBassEnhancerFunc == 0) { + dsSetBassEnhancerFunc = (dsSetBassEnhancer_t)resolve(RDK_DSHAL_NAME, "dsSetBassEnhancer"); + if (dsSetBassEnhancerFunc == 0) { + LOGERR("dsSetBassEnhancer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _Property = getCurrentProfileProperty("BassBoost"); + std::string _BassBoost("0"); + try { _BassBoost = device::HostPersistence::getInstance().getDefaultProperty(_Property); } catch(...) { _BassBoost = "0"; } + int m_bassBoost = atoi(_BassBoost.c_str()); + if (dsSetBassEnhancerFunc(dsHandle, m_bassBoost) == dsERR_NONE) { + LOGINFO("ResetAudioBassEnhancer: handle=%d, default boost=%d", handle, m_bassBoost); + device::HostPersistence::getInstance().persistHostProperty("audio.BassBoost", _BassBoost); + } else { + LOGERR("ResetAudioBassEnhancer dsSetBassEnhancer failed"); + return WPEFramework::Core::ERROR_GENERAL; + } +#else + if (dsSetBassEnhancerFunc(dsHandle, 0) != dsERR_NONE) { + LOGERR("ResetAudioBassEnhancer failed"); + return WPEFramework::Core::ERROR_GENERAL; + } + LOGINFO("ResetAudioBassEnhancer success: handle=%d", handle); +#endif + } catch (...) { + LOGERR("Exception in ResetAudioBassEnhancer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t ResetAudioSurroundVirtualizer(const int32_t handle) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // dsAudio.c: _resetSurroundVirtualizer reads defaults for mode+boost, calls dsSetSurroundVirtualizer, persists + typedef dsError_t (*dsSetSurroundVirtualizer_t)(intptr_t handle, dsSurroundVirtualizer_t virtualizer); + static dsSetSurroundVirtualizer_t dsSetSurroundVirtualizerFunc = 0; + if (dsSetSurroundVirtualizerFunc == 0) { + dsSetSurroundVirtualizerFunc = (dsSetSurroundVirtualizer_t)resolve(RDK_DSHAL_NAME, "dsSetSurroundVirtualizer"); + if (dsSetSurroundVirtualizerFunc == 0) { + LOGERR("dsSetSurroundVirtualizer is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _PropertyMode = getCurrentProfileProperty("SurroundVirtualizer.mode"); + std::string _PropertyBoost = getCurrentProfileProperty("SurroundVirtualizer.boost"); + std::string _SVMode("0"), _SVBoost("0"); + try { _SVMode = device::HostPersistence::getInstance().getDefaultProperty(_PropertyMode); } catch(...) { _SVMode = "0"; } + try { _SVBoost = device::HostPersistence::getInstance().getDefaultProperty(_PropertyBoost); } catch(...) { _SVBoost = "0"; } + dsSurroundVirtualizer_t m_virtualizer; + m_virtualizer.mode = atoi(_SVMode.c_str()); + m_virtualizer.boost = atoi(_SVBoost.c_str()); + if (dsSetSurroundVirtualizerFunc(dsHandle, m_virtualizer) == dsERR_NONE) { + LOGINFO("ResetAudioSurroundVirtualizer: handle=%d, mode=%d boost=%d", handle, m_virtualizer.mode, m_virtualizer.boost); + device::HostPersistence::getInstance().persistHostProperty(_PropertyMode, _SVMode); + device::HostPersistence::getInstance().persistHostProperty(_PropertyBoost, _SVBoost); + } else { + LOGERR("ResetAudioSurroundVirtualizer dsSetSurroundVirtualizer failed"); + return WPEFramework::Core::ERROR_GENERAL; + } +#else + dsSurroundVirtualizer_t m_virt = {0, 0}; + if (dsSetSurroundVirtualizerFunc(dsHandle, m_virt) != dsERR_NONE) { + LOGERR("ResetAudioSurroundVirtualizer failed"); + return WPEFramework::Core::ERROR_GENERAL; + } + LOGINFO("ResetAudioSurroundVirtualizer success: handle=%d", handle); +#endif + } catch (...) { + LOGERR("Exception in ResetAudioSurroundVirtualizer"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t ResetAudioVolumeLeveller(const int32_t handle) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + // dsAudio.c: _resetVolumeLeveller reads defaults for mode+level, calls dsSetVolumeLeveller, persists + typedef dsError_t (*dsSetVolumeLeveller_t)(intptr_t handle, dsVolumeLeveller_t volLeveller); + static dsSetVolumeLeveller_t dsSetVolumeLevellerFunc = 0; + if (dsSetVolumeLevellerFunc == 0) { + dsSetVolumeLevellerFunc = (dsSetVolumeLeveller_t)resolve(RDK_DSHAL_NAME, "dsSetVolumeLeveller"); + if (dsSetVolumeLevellerFunc == 0) { + LOGERR("dsSetVolumeLeveller is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + +#ifdef DS_AUDIO_SETTINGS_PERSISTENCE + std::string _PropertyMode = getCurrentProfileProperty("VolumeLeveller.mode"); + std::string _PropertyLevel = getCurrentProfileProperty("VolumeLeveller.level"); + std::string _volLevellerMode("0"), _volLevellerLevel("0"); + try { _volLevellerMode = device::HostPersistence::getInstance().getDefaultProperty(_PropertyMode); } catch(...) { _volLevellerMode = "0"; } + try { _volLevellerLevel = device::HostPersistence::getInstance().getDefaultProperty(_PropertyLevel); } catch(...) { _volLevellerLevel = "0"; } + dsVolumeLeveller_t m_vl; + m_vl.mode = atoi(_volLevellerMode.c_str()); + m_vl.level = atoi(_volLevellerLevel.c_str()); + if (dsSetVolumeLevellerFunc(dsHandle, m_vl) == dsERR_NONE) { + LOGINFO("ResetAudioVolumeLeveller: handle=%d, mode=%d level=%d", handle, m_vl.mode, m_vl.level); + device::HostPersistence::getInstance().persistHostProperty(_PropertyMode, _volLevellerMode); + device::HostPersistence::getInstance().persistHostProperty(_PropertyLevel, _volLevellerLevel); + } else { + LOGERR("ResetAudioVolumeLeveller dsSetVolumeLeveller failed"); + return WPEFramework::Core::ERROR_GENERAL; + } +#else + dsVolumeLeveller_t m_vl = {0, 0}; + if (dsSetVolumeLevellerFunc(dsHandle, m_vl) != dsERR_NONE) { + LOGERR("ResetAudioVolumeLeveller failed"); + return WPEFramework::Core::ERROR_GENERAL; + } + LOGINFO("ResetAudioVolumeLeveller success: handle=%d", handle); +#endif + } catch (...) { + LOGERR("Exception in ResetAudioVolumeLeveller"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetAudioHDMIARCPortId(const int32_t handle, int32_t &portId) override { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + // Get HDMI ARC Port ID from device persistence (reference from dsAudio.c) + std::string hdmiARCPortId("0"); // Default value + try { + hdmiARCPortId = device::HostPersistence::getInstance().getDefaultProperty("HDMIARC.port.Id"); + } catch (...) { + LOGWARN("Failed to get HDMIARC.port.Id from persistence, using default value -1"); + hdmiARCPortId = "-1"; + } + + portId = atoi(hdmiARCPortId.c_str()); + LOGINFO("GetAudioHDMIARCPortId success: handle=%d, portId=%d", handle, portId); + } catch (...) { + LOGERR("Exception in GetAudioHDMIARCPortId"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetStereoAuto(const int32_t handle, int32_t &autoMode) override + { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + int dsAutoMode; + // Use resolve function for dsGetStereoAuto + typedef dsError_t (*dsGetStereoAuto_t)(intptr_t handle, int* autoMode); + static dsGetStereoAuto_t dsGetStereoAutoFunc = 0; + if (dsGetStereoAutoFunc == 0) { + dsGetStereoAutoFunc = (dsGetStereoAuto_t)resolve(RDK_DSHAL_NAME, "dsGetStereoAuto"); + if (dsGetStereoAutoFunc == 0) { + LOGERR("dsGetStereoAuto is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsGetStereoAutoFunc) { + ret = dsGetStereoAutoFunc(dsHandle, &dsAutoMode); + } + if (ret == dsERR_NONE) { + autoMode = dsAutoMode; + LOGINFO("GetStereoAuto success: handle=%d, autoMode=%d", handle, autoMode); + } else { + LOGERR("dsGetStereoAuto failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } catch (...) { + LOGERR("Exception in GetStereoAuto"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t SetStereoAuto(const int32_t handle, const int32_t autoMode, const bool persist) override + { + ENTRY_LOG; + if (!_isInitialized) { + LOGERR("Audio platform not initialized"); + return WPEFramework::Core::ERROR_GENERAL; + } + + try { + intptr_t dsHandle = static_cast(handle); + + // Handle persistence similar to dsAudio.c _dsSetStereoAuto implementation + if (persist) { + dsAudioPortType_t portType = getAudioPortType(dsHandle); + switch (portType) { + case dsAUDIOPORT_TYPE_HDMI: + device::HostPersistence::getInstance().persistHostProperty("HDMI0.AudioMode.AUTO", autoMode ? "TRUE" : "FALSE"); + LOGINFO("Persisted HDMI stereo auto mode: autoMode=%d", autoMode); + break; + + case dsAUDIOPORT_TYPE_HDMI_ARC: + device::HostPersistence::getInstance().persistHostProperty("HDMI_ARC0.AudioMode.AUTO", autoMode ? "TRUE" : "FALSE"); + LOGINFO("Persisted HDMI_ARC stereo auto mode: autoMode=%d", autoMode); + break; + + case dsAUDIOPORT_TYPE_SPDIF: + device::HostPersistence::getInstance().persistHostProperty("SPDIF0.AudioMode.AUTO", autoMode ? "TRUE" : "FALSE"); + LOGINFO("Persisted SPDIF stereo auto mode: autoMode=%d", autoMode); + break; + + case dsAUDIOPORT_TYPE_SPEAKER: + device::HostPersistence::getInstance().persistHostProperty("SPEAKER0.AudioMode.AUTO", autoMode ? "TRUE" : "FALSE"); + LOGINFO("Persisted SPEAKER stereo auto mode: autoMode=%d", autoMode); + break; + + default: + LOGWARN("SetStereoAuto persistence not supported for port type: %d", portType); + break; + } + } + + // Call the HAL function - only for HDMI_ARC and SPDIF ports as per dsAudio.c logic + dsAudioPortType_t portType = getAudioPortType(dsHandle); + if ((portType == dsAUDIOPORT_TYPE_HDMI_ARC) || (portType == dsAUDIOPORT_TYPE_SPDIF)) { + // Use resolve function for dsSetStereoAuto + typedef dsError_t (*dsSetStereoAuto_t)(intptr_t handle, int autoMode); + static dsSetStereoAuto_t dsSetStereoAutoFunc = 0; + if (dsSetStereoAutoFunc == 0) { + dsSetStereoAutoFunc = (dsSetStereoAuto_t)resolve(RDK_DSHAL_NAME, "dsSetStereoAuto"); + if (dsSetStereoAutoFunc == 0) { + LOGERR("dsSetStereoAuto is not defined"); + return WPEFramework::Core::ERROR_GENERAL; + } + } + + dsError_t ret = dsERR_GENERAL; + if (0 != dsSetStereoAutoFunc) { + ret = dsSetStereoAutoFunc(dsHandle, autoMode); + } + if (ret == dsERR_NONE) { + LOGINFO("SetStereoAuto success: handle=%d, autoMode=%d, persist=%s", + handle, autoMode, persist ? "true" : "false"); + } else { + LOGERR("dsSetStereoAuto failed with error: %d", ret); + return WPEFramework::Core::ERROR_GENERAL; + } + } else { + LOGINFO("SetStereoAuto HAL call skipped for port type %d (only HDMI_ARC/SPDIF supported): handle=%d, autoMode=%d", + portType, handle, autoMode); + } + } catch (...) { + LOGERR("Exception in SetStereoAuto"); + return WPEFramework::Core::ERROR_GENERAL; + } + EXIT_LOG; + return WPEFramework::Core::ERROR_NONE; + } + +private: + // Implementation of audio settings initialization from dsAudioMgr_init + void initializeAudioSettings() + { + ENTRY_LOG; + try { + // Initialize audio configuration settings from persistence + // This is adapted from dsAudioMgr_init logic in dsAudio.c + + LOGINFO("Initializing comprehensive audio settings from persistence and platform defaults..."); + + // Initialize audio port settings for all supported audio port types + initializeAudioPortSettings(); + + // Initialize MS12 audio processing features if supported + initializeMS12Settings(); + + LOGINFO("Audio platform and settings initialization completed successfully"); + + } catch (...) { + LOGERR("Exception in initializing audio settings"); + } + EXIT_LOG; + } + + // Audio configuration initialization from AudioConfigInit function + void audioConfigInit() + { + ENTRY_LOG; + try { + LOGINFO("Starting comprehensive audio configuration initialization..."); + + void *dllib = nullptr; + intptr_t handle = 0; + + // 1. Initialize LE (Loudness Equivalence) Configuration + typedef dsError_t (*dsEnableLEConfig_t)(intptr_t handle, const bool enable); + dsEnableLEConfig_t dsEnableLEConfigFunc = nullptr; + + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + dsEnableLEConfigFunc = (dsEnableLEConfig_t) resolve(RDK_DSHAL_NAME, "dsEnableLEConfig"); + if (dsEnableLEConfigFunc) { + LOGINFO("dsEnableLEConfig(int, bool) is defined and loaded"); + std::string leEnable("FALSE"); + try { + leEnable = device::HostPersistence::getInstance().getProperty("audio.LEEnable"); + } catch(...) { + #ifndef DS_LE_DEFAULT_DISABLED + leEnable = "TRUE"; + #endif + LOGINFO("LE : Persisting default LE status: %s", leEnable.c_str()); + device::HostPersistence::getInstance().persistHostProperty("audio.LEEnable", leEnable); + } + + bool leEnabled = (leEnable == "TRUE"); + dsEnableLEConfigFunc(handle, leEnabled); + m_LEEnabled = leEnabled; // sync static state with what was applied to HAL + LOGINFO("LE (Loudness Equivalence) initialized: %s", leEnabled ? "enabled" : "disabled"); + } else { + LOGINFO("dsEnableLEConfig(int, bool) is not available in HAL"); + } + } else { + LOGERR("dsEnableLEConfig failed - HDMI port 0 not available"); + } + + #ifdef DS_AUDIO_SETTINGS_PERSISTENCE + // 2. Initialize Audio Gain for SPEAKER and HDMI ports + typedef dsError_t (*dsSetAudioGain_t)(intptr_t handle, float gain); + dsSetAudioGain_t dsSetAudioGainFunc = nullptr; + + dsSetAudioGainFunc = (dsSetAudioGain_t) resolve(RDK_DSHAL_NAME, "dsSetAudioGain"); + if (dsSetAudioGainFunc) { + LOGINFO("dsSetAudioGain_t(int, float) is defined and loaded"); + std::string audioGain("0"); + float audioGainValue = 0; + + // SPEAKER init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + try { + audioGain = device::HostPersistence::getInstance().getProperty("SPEAKER0.audio.Gain"); + } catch(...) { + try { + LOGINFO("SPEAKER0.audio.Gain not found in persistence store. Try system default"); + audioGain = device::HostPersistence::getInstance().getDefaultProperty("SPEAKER0.audio.Gain"); + } catch(...) { + audioGain = "0"; + } + } + audioGainValue = atof(audioGain.c_str()); + if (dsSetAudioGainFunc(handle, audioGainValue) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized audio gain: %f", audioGainValue); + } + } + + // HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + try { + audioGain = device::HostPersistence::getInstance().getProperty("HDMI0.audio.Gain"); + } catch(...) { + try { + LOGINFO("HDMI0.audio.Gain not found in persistence store. Try system default"); + audioGain = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.audio.Gain"); + } catch(...) { + audioGain = "0"; + } + } + audioGainValue = atof(audioGain.c_str()); + if (dsSetAudioGainFunc(handle, audioGainValue) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized audio gain: %f", audioGainValue); + } + } + // SPDIF init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPDIF, 0, &handle) == dsERR_NONE) { + try { + audioGain = device::HostPersistence::getInstance().getProperty("SPDIF0.audio.Gain"); + } catch(...) { + try { + LOGINFO("SPDIF0.audio.Gain not found in persistence store. Try system default"); + audioGain = device::HostPersistence::getInstance().getDefaultProperty("SPDIF0.audio.Gain"); + } catch(...) { + audioGain = "0"; + } + } + audioGainValue = atof(audioGain.c_str()); + if (dsSetAudioGainFunc(handle, audioGainValue) == dsERR_NONE) { + LOGINFO("Port SPDIF0: Initialized audio gain: %f", audioGainValue); + } + } + } else { + LOGINFO("dsSetAudioGain_t(int, float) is not available in HAL"); + } + + // 3. Initialize Audio Level for SPDIF, SPEAKER, HEADPHONE, and HDMI ports + typedef dsError_t (*dsSetAudioLevel_t)(intptr_t handle, float level); + static dsSetAudioLevel_t dsSetAudioLevelFunc = nullptr; + + if (dsSetAudioLevelFunc == nullptr) { + dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + dsSetAudioLevelFunc = (dsSetAudioLevel_t) dlsym(dllib, "dsSetAudioLevel"); + if (dsSetAudioLevelFunc) { + LOGINFO("dsSetAudioLevel_t(int, float) is defined and loaded"); + std::string audioLevel("0"); + float audioLevelValue = 0; + + // SPDIF init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPDIF, 0, &handle) == dsERR_NONE) { + try { + audioLevel = device::HostPersistence::getInstance().getProperty("SPDIF0.audio.Level"); + } catch(...) { + try { + LOGINFO("SPDIF0.audio.Level not found in persistence store. Try system default"); + audioLevel = device::HostPersistence::getInstance().getDefaultProperty("SPDIF0.audio.Level"); + } catch(...) { + audioLevel = "40"; + } + } + audioLevelValue = atof(audioLevel.c_str()); + if (dsSetAudioLevelFunc(handle, audioLevelValue) == dsERR_NONE) { + LOGINFO("Port SPDIF0: Initialized audio level: %f", audioLevelValue); + } + } + + // SPEAKER init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + try { + audioLevel = device::HostPersistence::getInstance().getProperty("SPEAKER0.audio.Level"); + } catch(...) { + try { + LOGINFO("SPEAKER0.audio.Level not found in persistence store. Try system default"); + audioLevel = device::HostPersistence::getInstance().getDefaultProperty("SPEAKER0.audio.Level"); + } catch(...) { + audioLevel = "40"; + } + } + audioLevelValue = atof(audioLevel.c_str()); + if (dsSetAudioLevelFunc(handle, audioLevelValue) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized audio level: %f", audioLevelValue); + } + } + + // HEADPHONE init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HEADPHONE, 0, &handle) == dsERR_NONE) { + try { + audioLevel = device::HostPersistence::getInstance().getProperty("HEADPHONE0.audio.Level"); + } catch(...) { + try { + LOGINFO("HEADPHONE0.audio.Level not found in persistence store. Try system default"); + audioLevel = device::HostPersistence::getInstance().getDefaultProperty("HEADPHONE0.audio.Level"); + } catch(...) { + audioLevel = "40"; + } + } + audioLevelValue = atof(audioLevel.c_str()); + if (dsSetAudioLevelFunc(handle, audioLevelValue) == dsERR_NONE) { + LOGINFO("Port HEADPHONE0: Initialized audio level: %f", audioLevelValue); + } + } + + // HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + try { + audioLevel = device::HostPersistence::getInstance().getProperty("HDMI0.audio.Level"); + } catch(...) { + try { + LOGINFO("HDMI0.audio.Level not found in persistence store. Try system default"); + audioLevel = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.audio.Level"); + } catch(...) { + audioLevel = "40"; + } + } + audioLevelValue = atof(audioLevel.c_str()); + if (dsSetAudioLevelFunc(handle, audioLevelValue) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized audio level: %f", audioLevelValue); + } + } + } else { + LOGINFO("dsSetAudioLevel_t(int, float) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + + // 4. Initialize Audio Delay for SPEAKER, HDMI, and HDMI_ARC ports + typedef dsError_t (*dsSetAudioDelay_t)(intptr_t handle, uint32_t audioDelayMs); + static dsSetAudioDelay_t dsSetAudioDelayFunc = nullptr; + + if (dsSetAudioDelayFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetAudioDelayFunc = (dsSetAudioDelay_t) dlsym(dllib, "dsSetAudioDelay"); + if (dsSetAudioDelayFunc) { + LOGINFO("dsSetAudioDelay_t(int, uint32_t) is defined and loaded"); + std::string audioDelay("0"); + int audioDelayValue = 0; + + // SPEAKER init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + try { + audioDelay = device::HostPersistence::getInstance().getProperty("SPEAKER0.audio.Delay"); + } catch(...) { + try { + LOGINFO("SPEAKER0.audio.Delay not found in persistence store. Try system default"); + audioDelay = device::HostPersistence::getInstance().getDefaultProperty("SPEAKER0.audio.Delay"); + } catch(...) { + audioDelay = "0"; + } + } + audioDelayValue = atoi(audioDelay.c_str()); + if (dsSetAudioDelayFunc(handle, audioDelayValue) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized audio delay: %d ms", audioDelayValue); + } + } + + // HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + try { + audioDelay = device::HostPersistence::getInstance().getProperty("HDMI0.audio.Delay"); + } catch(...) { + try { + LOGINFO("HDMI0.audio.Delay not found in persistence store. Try system default"); + audioDelay = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.audio.Delay"); + } catch(...) { + audioDelay = "0"; + } + } + audioDelayValue = atoi(audioDelay.c_str()); + if (dsSetAudioDelayFunc(handle, audioDelayValue) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized audio delay: %d ms", audioDelayValue); + } + } + + // HDMI ARC init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI_ARC, 0, &handle) == dsERR_NONE) { + try { + audioDelay = device::HostPersistence::getInstance().getProperty("HDMI_ARC0.audio.Delay"); + } catch(...) { + try { + LOGINFO("HDMI_ARC0.audio.Delay not found in persistence store. Try system default"); + audioDelay = device::HostPersistence::getInstance().getDefaultProperty("HDMI_ARC0.audio.Delay"); + } catch(...) { + audioDelay = "0"; + } + } + audioDelayValue = atoi(audioDelay.c_str()); + if (dsSetAudioDelayFunc(handle, audioDelayValue) == dsERR_NONE) { + LOGINFO("Port HDMI_ARC0: Initialized audio delay: %d ms", audioDelayValue); + } + } + } else { + LOGINFO("dsSetAudioDelay_t(int, uint32_t) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + + // 5. Initialize Primary Language + typedef dsError_t (*dsSetPrimaryLanguage_t)(intptr_t handle, const char* pLang); + static dsSetPrimaryLanguage_t dsSetPrimaryLanguageFunc = nullptr; + + if (dsSetPrimaryLanguageFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetPrimaryLanguageFunc = (dsSetPrimaryLanguage_t) dlsym(dllib, "dsSetPrimaryLanguage"); + if (dsSetPrimaryLanguageFunc) { + LOGINFO("dsSetPrimaryLanguage_t(int, char*) is defined and loaded"); + std::string primaryLanguage("eng"); + handle = 0; + + try { + primaryLanguage = device::HostPersistence::getInstance().getProperty("audio.PrimaryLanguage"); + } catch(...) { + try { + LOGINFO("audio.PrimaryLanguage not found in persistence store. Try system default"); + primaryLanguage = device::HostPersistence::getInstance().getDefaultProperty("audio.PrimaryLanguage"); + } catch(...) { + primaryLanguage = "eng"; + } + } + + if (dsSetPrimaryLanguageFunc(handle, primaryLanguage.c_str()) == dsERR_NONE) { + LOGINFO("Initialized Primary Language: %s", primaryLanguage.c_str()); + } + } else { + LOGINFO("dsSetPrimaryLanguage_t(int, char*) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + + // 6. Initialize Secondary Language + typedef dsError_t (*dsSetSecondaryLanguage_t)(intptr_t handle, const char* sLang); + static dsSetSecondaryLanguage_t dsSetSecondaryLanguageFunc = nullptr; + + if (dsSetSecondaryLanguageFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetSecondaryLanguageFunc = (dsSetSecondaryLanguage_t) dlsym(dllib, "dsSetSecondaryLanguage"); + if (dsSetSecondaryLanguageFunc) { + LOGINFO("dsSetSecondaryLanguage_t(int, char*) is defined and loaded"); + std::string secondaryLanguage("eng"); + handle = 0; + + try { + secondaryLanguage = device::HostPersistence::getInstance().getProperty("audio.SecondaryLanguage"); + } catch(...) { + try { + LOGINFO("audio.SecondaryLanguage not found in persistence store. Try system default"); + secondaryLanguage = device::HostPersistence::getInstance().getDefaultProperty("audio.SecondaryLanguage"); + } catch(...) { + secondaryLanguage = "eng"; + } + } + + if (dsSetSecondaryLanguageFunc(handle, secondaryLanguage.c_str()) == dsERR_NONE) { + LOGINFO("Initialized Secondary Language: %s", secondaryLanguage.c_str()); + } + } else { + LOGINFO("dsSetSecondaryLanguage_t(int, char*) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + + // 7. Initialize Fader Control + typedef dsError_t (*dsSetFaderControl_t)(intptr_t handle, int mixerbalance); + static dsSetFaderControl_t dsSetFaderControlFunc = nullptr; + + if (dsSetFaderControlFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetFaderControlFunc = (dsSetFaderControl_t) dlsym(dllib, "dsSetFaderControl"); + if (dsSetFaderControlFunc) { + LOGINFO("dsSetFaderControl_t(int, int) is defined and loaded"); + std::string faderControl("0"); + int faderControlValue = 0; + handle = 0; + + try { + faderControl = device::HostPersistence::getInstance().getProperty("audio.FaderControl"); + } catch(...) { + try { + LOGINFO("audio.FaderControl not found in persistence store. Try system default"); + faderControl = device::HostPersistence::getInstance().getDefaultProperty("audio.FaderControl"); + } catch(...) { + faderControl = "0"; + } + } + + faderControlValue = atoi(faderControl.c_str()); + if (dsSetFaderControlFunc(handle, faderControlValue) == dsERR_NONE) { + LOGINFO("Initialized Fader Control, mixing: %d", faderControlValue); + } + } else { + LOGINFO("dsSetFaderControl_t(int, int) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + + // 8. Initialize Associated Audio Mixing + typedef dsError_t (*dsSetAssociatedAudioMixing_t)(intptr_t handle, bool mixing); + static dsSetAssociatedAudioMixing_t dsSetAssociatedAudioMixingFunc = nullptr; + + if (dsSetAssociatedAudioMixingFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetAssociatedAudioMixingFunc = (dsSetAssociatedAudioMixing_t) dlsym(dllib, "dsSetAssociatedAudioMixing"); + if (dsSetAssociatedAudioMixingFunc) { + LOGINFO("dsSetAssociatedAudioMixing_t (intptr_t handle, bool mixing) is defined and loaded"); + std::string associatedAudioMixing("Disabled"); + bool associatedAudioMixingValue = false; + handle = 0; + + try { + associatedAudioMixing = device::HostPersistence::getInstance().getProperty("audio.AssociatedAudioMixing"); + } catch(...) { + try { + LOGINFO("audio.AssociatedAudioMixing not found in persistence store. Try system default"); + associatedAudioMixing = device::HostPersistence::getInstance().getDefaultProperty("audio.AssociatedAudioMixing"); + } catch(...) { + associatedAudioMixing = "Disabled"; + } + } + + associatedAudioMixingValue = (associatedAudioMixing == "Enabled"); + if (dsSetAssociatedAudioMixingFunc(handle, associatedAudioMixingValue) == dsERR_NONE) { + LOGINFO("Initialized AssociatedAudioMixingFunc: %s", associatedAudioMixingValue ? "enabled" : "disabled"); + } + } else { + LOGINFO("dsSetAssociatedAudioMixing_t (intptr_t handle, bool enable) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + #endif // DS_AUDIO_SETTINGS_PERSISTENCE + + // 9. Initialize MS12 Audio Profile Support + std::string ms12ProfileSupport("FALSE"); + std::string ms12Profile("Off"); + + try { + ms12ProfileSupport = device::HostPersistence::getInstance().getDefaultProperty("audio.MS12Profile.supported"); + } catch(...) { + ms12ProfileSupport = "FALSE"; + LOGINFO("audio.MS12Profile.supported setting not found in hostDataDefault"); + } + LOGINFO("audio.MS12Profile.supported = %s", ms12ProfileSupport.c_str()); + + if (ms12ProfileSupport == "TRUE") { + // MS12 Profile is supported - initialize MS12 Audio Profile + typedef dsError_t (*dsSetMS12AudioProfile_t)(intptr_t handle, const char* profile); + static dsSetMS12AudioProfile_t dsSetMS12AudioProfileFunc = nullptr; + + if (dsSetMS12AudioProfileFunc == nullptr) { + dllib = dlopen("libdshal.so", RTLD_LAZY); + if (dllib) { + dsSetMS12AudioProfileFunc = (dsSetMS12AudioProfile_t) dlsym(dllib, "dsSetMS12AudioProfile"); + if (dsSetMS12AudioProfileFunc) { + LOGINFO("dsSetMS12AudioProfile_t(int, const char*) is defined and loaded"); + + try { + ms12Profile = device::HostPersistence::getInstance().getProperty("audio.MS12Profile"); + } catch(...) { + try { + LOGINFO("audio.MS12Profile not found in persistence store. Try system default"); + ms12Profile = device::HostPersistence::getInstance().getDefaultProperty("audio.MS12Profile"); + } catch(...) { + ms12Profile = "Off"; + } + } + + // SPEAKER init for MS12 profile + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetMS12AudioProfileFunc(handle, ms12Profile.c_str()) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized MS12 Audio Profile: %s", ms12Profile.c_str()); + device::HostPersistence::getInstance().persistHostProperty("audio.MS12Profile", ms12Profile.c_str()); + } else { + LOGINFO("Port SPEAKER0: Initialization failed !!! MS12 Audio Profile: %s", ms12Profile.c_str()); + } + } + } else { + LOGINFO("dsSetMS12AudioProfile_t(int, const char*) is not defined"); + } + dlclose(dllib); + dllib = nullptr; + } else { + LOGERR("Opening libdshal.so failed"); + } + } + } + + // Initialize individual MS12 settings based on profile support and override settings + if ((ms12ProfileSupport == "TRUE") && (ms12Profile != "Off")) { + // MS12 Profile supported and active - check for individual overrides + initializeMS12ProfileOverrides(); + } else if (ms12ProfileSupport == "FALSE") { + // MS12 Profile not supported - initialize individual settings from persistence + initializeIndividualMS12Settings(); + } + + LOGINFO("Comprehensive audio configuration initialization completed successfully"); + + } catch (...) { + LOGERR("Exception in audioConfigInit"); + } + EXIT_LOG; + } + + // Initialize MS12 profile override settings when profile is active + void initializeMS12ProfileOverrides() + { + ENTRY_LOG; + try { + intptr_t handle = 0; + std::string profileOverride = "FALSE"; + + // Audio Compression Profile Override + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.Compression.ms12ProfileOverride"); + } catch(...) { + profileOverride = "FALSE"; + } + + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetAudioCompression_t)(intptr_t handle, int compressionLevel); + dsSetAudioCompression_t dsSetAudioCompressionFunc = nullptr; + + dsSetAudioCompressionFunc = (dsSetAudioCompression_t) resolve(RDK_DSHAL_NAME, "dsSetAudioCompression"); + if (dsSetAudioCompressionFunc) { + try { + std::string audioCompression = device::HostPersistence::getInstance().getProperty("audio.Compression"); + int compressionLevel = atoi(audioCompression.c_str()); + + // SPEAKER and HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetAudioCompressionFunc(handle, compressionLevel) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized audio compression: %d", compressionLevel); + } + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetAudioCompressionFunc(handle, compressionLevel) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized audio compression: %d", compressionLevel); + } + } + } catch(...) { + LOGINFO("audio.Compression not found in persistence store. System Default configured through profiles"); + } + } + } + + // Dialog Enhancement Profile Override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.DialogEnhancer.ms12ProfileOverride"); + } catch(...) { + profileOverride = "FALSE"; + } + + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetDialogEnhancement_t)(intptr_t handle, int enhancerLevel); + dsSetDialogEnhancement_t dsSetDialogEnhancementFunc = nullptr; + + dsSetDialogEnhancementFunc = (dsSetDialogEnhancement_t) resolve(RDK_DSHAL_NAME, "dsSetDialogEnhancement"); + if (dsSetDialogEnhancementFunc) { + try { + std::string currentProfile = getCurrentProfileProperty("EnhancerLevel"); + std::string enhancerLevel = device::HostPersistence::getInstance().getProperty(currentProfile); + int enhancerValue = atoi(enhancerLevel.c_str()); + + // SPEAKER and HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDialogEnhancementFunc(handle, enhancerValue) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized dialog enhancement level: %d", enhancerValue); + } + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDialogEnhancementFunc(handle, enhancerValue) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized dialog enhancement level: %d", enhancerValue); + } + } + } catch(...) { + LOGINFO("audio.EnhancerLevel not found in persistence store. System Default configured through profiles"); + } + } + } + + // DolbyVolumeMode override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.DolbyVolumeMode.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetDolbyVolumeMode_ov_t)(intptr_t handle, bool enable); + dsSetDolbyVolumeMode_ov_t dsSetDolbyVolumeModeFunc = (dsSetDolbyVolumeMode_ov_t) resolve(RDK_DSHAL_NAME, "dsSetDolbyVolumeMode"); + if (dsSetDolbyVolumeModeFunc) { + try { + std::string dolbyMode = device::HostPersistence::getInstance().getProperty("audio.DolbyVolumeMode"); + bool m_dolbyVolumeMode = (dolbyMode == "TRUE"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDolbyVolumeModeFunc(handle, m_dolbyVolumeMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Dolby Volume Mode: %d", m_dolbyVolumeMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDolbyVolumeModeFunc(handle, m_dolbyVolumeMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Dolby Volume Mode: %d", m_dolbyVolumeMode); + } + } catch(...) { LOGINFO("audio.DolbyVolumeMode not found. System Default configured through profiles"); } + } + } + + // IntelligentEQ override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.IntelligentEQ.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetIEQMode_ov_t)(intptr_t handle, int mode); + dsSetIEQMode_ov_t dsSetIEQModeFunc = (dsSetIEQMode_ov_t) resolve(RDK_DSHAL_NAME, "dsSetIntelligentEqualizerMode"); + if (dsSetIEQModeFunc) { + try { + int m_IEQMode = atoi(device::HostPersistence::getInstance().getProperty("audio.IntelligentEQ").c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetIEQModeFunc(handle, m_IEQMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Intelligent Equalizer mode: %d", m_IEQMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetIEQModeFunc(handle, m_IEQMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Intelligent Equalizer mode: %d", m_IEQMode); + } + } catch(...) { LOGINFO("audio.IntelligentEQ not found. System Default configured through profiles"); } + } + } + + // VolumeLeveller override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.VolumeLeveller.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetVolLev_ov_t)(intptr_t handle, dsVolumeLeveller_t volLeveller); + dsSetVolLev_ov_t dsSetVolLevFunc = (dsSetVolLev_ov_t) resolve(RDK_DSHAL_NAME, "dsSetVolumeLeveller"); + if (dsSetVolLevFunc) { + std::string _pMode = getCurrentProfileProperty("VolumeLeveller.mode"); + std::string _pLevel = getCurrentProfileProperty("VolumeLeveller.level"); + try { + dsVolumeLeveller_t m_vl; + m_vl.mode = atoi(device::HostPersistence::getInstance().getProperty(_pMode).c_str()); + m_vl.level = atoi(device::HostPersistence::getInstance().getProperty(_pLevel).c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetVolLevFunc(handle, m_vl) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Volume Leveller: Mode: %d, Level: %d", m_vl.mode, m_vl.level); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetVolLevFunc(handle, m_vl) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Volume Leveller: Mode: %d, Level: %d", m_vl.mode, m_vl.level); + } + } catch(...) { LOGINFO("audio.VolumeLeveller not found. System Default configured through profiles"); } + } + } + + // BassBoost override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.BassBoost.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetBass_ov_t)(intptr_t handle, int boost); + dsSetBass_ov_t dsSetBassFunc = (dsSetBass_ov_t) resolve(RDK_DSHAL_NAME, "dsSetBassEnhancer"); + if (dsSetBassFunc) { + try { + int m_bassBoost = atoi(device::HostPersistence::getInstance().getProperty("audio.BassBoost").c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetBassFunc(handle, m_bassBoost) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Bass Boost: %d", m_bassBoost); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetBassFunc(handle, m_bassBoost) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Bass Boost: %d", m_bassBoost); + } + } catch(...) { LOGINFO("audio.BassBoost not found. System Default configured through profiles"); } + } + } + + // SurroundDecoder override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.SurroundDecoder.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsEnableSurrDec_ov_t)(intptr_t handle, bool enabled); + dsEnableSurrDec_ov_t dsEnableSurrDecFunc = (dsEnableSurrDec_ov_t) resolve(RDK_DSHAL_NAME, "dsEnableSurroundDecoder"); + if (dsEnableSurrDecFunc) { + try { + std::string sd = device::HostPersistence::getInstance().getProperty("audio.SurroundDecoderEnabled"); + bool m_surroundDecoder = (sd == "TRUE"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsEnableSurrDecFunc(handle, m_surroundDecoder) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Surround Decoder: %d", m_surroundDecoder); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsEnableSurrDecFunc(handle, m_surroundDecoder) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Surround Decoder: %d", m_surroundDecoder); + } + } catch(...) { LOGINFO("audio.SurroundDecoderEnabled not found. System Default configured through profiles"); } + } + } + + // DRCMode override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.DRCMode.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetDRC_ov_t)(intptr_t handle, int mode); + dsSetDRC_ov_t dsSetDRCFunc = (dsSetDRC_ov_t) resolve(RDK_DSHAL_NAME, "dsSetDRCMode"); + if (dsSetDRCFunc) { + try { + std::string drc = device::HostPersistence::getInstance().getProperty("audio.DRCMode"); + int m_DRCMode = (drc == "RF") ? 1 : 0; + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDRCFunc(handle, m_DRCMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized DRCMode: %d", m_DRCMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDRCFunc(handle, m_DRCMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized DRCMode: %d", m_DRCMode); + } + } catch(...) { LOGINFO("audio.DRCMode not found. System Default configured through profiles"); } + } + } + + // SurroundVirtualizer override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.SurroundVirtualizer.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetSurrVirt_ov_t)(intptr_t handle, dsSurroundVirtualizer_t virtualizer); + dsSetSurrVirt_ov_t dsSetSurrVirtFunc = (dsSetSurrVirt_ov_t) resolve(RDK_DSHAL_NAME, "dsSetSurroundVirtualizer"); + if (dsSetSurrVirtFunc) { + std::string _pMode = getCurrentProfileProperty("SurroundVirtualizer.mode"); + std::string _pBoost = getCurrentProfileProperty("SurroundVirtualizer.boost"); + try { + dsSurroundVirtualizer_t m_virt; + m_virt.mode = atoi(device::HostPersistence::getInstance().getProperty(_pMode).c_str()); + m_virt.boost = atoi(device::HostPersistence::getInstance().getProperty(_pBoost).c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetSurrVirtFunc(handle, m_virt) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Surround Virtualizer: Mode: %d, Boost: %d", m_virt.mode, m_virt.boost); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetSurrVirtFunc(handle, m_virt) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Surround Virtualizer: Mode: %d, Boost: %d", m_virt.mode, m_virt.boost); + } + } catch(...) { LOGINFO("audio.SurroundVirtualizer not found. System Default configured through profiles"); } + } + } + + // MISteering override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.MISteering.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetMISteering_ov_t)(intptr_t handle, bool enabled); + dsSetMISteering_ov_t dsSetMIFunc = (dsSetMISteering_ov_t) resolve(RDK_DSHAL_NAME, "dsSetMISteering"); + if (dsSetMIFunc) { + try { + std::string mi = device::HostPersistence::getInstance().getProperty("audio.MISteering"); + bool m_MISteering = (mi == "Enabled"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetMIFunc(handle, m_MISteering) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized MI Steering: %d", m_MISteering); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetMIFunc(handle, m_MISteering) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized MI Steering: %d", m_MISteering); + } + } catch(...) { LOGINFO("audio.MISteering not found. System Default configured through profiles"); } + } + } + + // GraphicEQ override + profileOverride = "FALSE"; + try { + profileOverride = device::HostPersistence::getInstance().getDefaultProperty("audio.GraphicEQ.ms12ProfileOverride"); + } catch(...) { profileOverride = "FALSE"; } + if (profileOverride == "TRUE") { + typedef dsError_t (*dsSetGEQ_ov_t)(intptr_t handle, int mode); + dsSetGEQ_ov_t dsSetGEQFunc = (dsSetGEQ_ov_t) resolve(RDK_DSHAL_NAME, "dsSetGraphicEqualizerMode"); + if (dsSetGEQFunc) { + try { + int m_GEQMode = atoi(device::HostPersistence::getInstance().getProperty("audio.GraphicEQ").c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetGEQFunc(handle, m_GEQMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Graphic Equalizer mode: %d", m_GEQMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetGEQFunc(handle, m_GEQMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Graphic Equalizer mode: %d", m_GEQMode); + } + } catch(...) { LOGINFO("audio.GraphicEQ not found. System Default configured through profiles"); } + } + } + + } catch (...) { + LOGERR("Exception in initializeMS12ProfileOverrides"); + } + EXIT_LOG; + } + + // Initialize individual MS12 settings when profile is not supported + void initializeIndividualMS12Settings() + { + ENTRY_LOG; + try { + intptr_t handle = 0; + + // Initialize Audio Compression + typedef dsError_t (*dsSetAudioCompression_t)(intptr_t handle, int compressionLevel); + dsSetAudioCompression_t dsSetAudioCompressionFunc = nullptr; + + dsSetAudioCompressionFunc = (dsSetAudioCompression_t) resolve(RDK_DSHAL_NAME, "dsSetAudioCompression"); + if (dsSetAudioCompressionFunc) { + std::string audioCompression("0"); + try { + audioCompression = device::HostPersistence::getInstance().getProperty("audio.Compression"); + } catch(...) { + try { + audioCompression = device::HostPersistence::getInstance().getDefaultProperty("audio.Compression"); + } catch(...) { + audioCompression = "0"; + } + } + + int compressionLevel = atoi(audioCompression.c_str()); + + // SPEAKER and HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetAudioCompressionFunc(handle, compressionLevel) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized audio compression: %d", compressionLevel); + } + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetAudioCompressionFunc(handle, compressionLevel) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized audio compression: %d", compressionLevel); + } + } + } + + // Initialize Dialog Enhancement + typedef dsError_t (*dsSetDialogEnhancement_t)(intptr_t handle, int enhancerLevel); + dsSetDialogEnhancement_t dsSetDialogEnhancementFunc = nullptr; + + dsSetDialogEnhancementFunc = (dsSetDialogEnhancement_t) resolve(RDK_DSHAL_NAME, "dsSetDialogEnhancement"); + if (dsSetDialogEnhancementFunc) { + std::string enhancerLevel("0"); + try { + enhancerLevel = device::HostPersistence::getInstance().getProperty("audio.EnhancerLevel"); + } catch(...) { + try { + enhancerLevel = device::HostPersistence::getInstance().getDefaultProperty("audio.EnhancerLevel"); + } catch(...) { + enhancerLevel = "0"; + } + } + + int enhancerValue = atoi(enhancerLevel.c_str()); + + // SPEAKER and HDMI init + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDialogEnhancementFunc(handle, enhancerValue) == dsERR_NONE) { + LOGINFO("Port SPEAKER0: Initialized dialog enhancement level: %d", enhancerValue); + } + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDialogEnhancementFunc(handle, enhancerValue) == dsERR_NONE) { + LOGINFO("Port HDMI0: Initialized dialog enhancement level: %d", enhancerValue); + } + } + } + + // DolbyVolumeMode (with bDolbyVolumeOverrideCheck: VolumeLeveller overrides DolbyVolumeMode) + typedef dsError_t (*dsSetDolbyVolumeMode_ind_t)(intptr_t handle, bool enable); + dsSetDolbyVolumeMode_ind_t dsSetDolbyVolumeModeIndFunc = nullptr; + bool bDolbyVolumeOverrideCheck = true; + dsSetDolbyVolumeModeIndFunc = (dsSetDolbyVolumeMode_ind_t) resolve(RDK_DSHAL_NAME, "dsSetDolbyVolumeMode"); + if (dsSetDolbyVolumeModeIndFunc) { + std::string dolbyMode("FALSE"); + bool m_dolbyVolumeMode = false; + try { + dolbyMode = device::HostPersistence::getInstance().getProperty("audio.DolbyVolumeMode"); + bDolbyVolumeOverrideCheck = false; + } catch(...) { + try { + LOGINFO("audio.DolbyVolumeMode not found in persistence store. Try system default"); + dolbyMode = device::HostPersistence::getInstance().getDefaultProperty("audio.DolbyVolumeMode"); + } catch(...) { dolbyMode = "FALSE"; } + } + m_dolbyVolumeMode = (dolbyMode == "TRUE"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDolbyVolumeModeIndFunc(handle, m_dolbyVolumeMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Dolby Volume Mode: %d", m_dolbyVolumeMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDolbyVolumeModeIndFunc(handle, m_dolbyVolumeMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Dolby Volume Mode: %d", m_dolbyVolumeMode); + } + } + + // IntelligentEQ + typedef dsError_t (*dsSetIEQMode_ind_t)(intptr_t handle, int mode); + dsSetIEQMode_ind_t dsSetIEQModeIndFunc = nullptr; + dsSetIEQModeIndFunc = (dsSetIEQMode_ind_t) resolve(RDK_DSHAL_NAME, "dsSetIntelligentEqualizerMode"); + if (dsSetIEQModeIndFunc) { + std::string ieqMode("0"); + try { + ieqMode = device::HostPersistence::getInstance().getProperty("audio.IntelligentEQ"); + } catch(...) { + try { + LOGINFO("audio.IntelligentEQ not found in persistence store. Try system default"); + ieqMode = device::HostPersistence::getInstance().getDefaultProperty("audio.IntelligentEQ"); + } catch(...) { ieqMode = "0"; } + } + int m_IEQMode = atoi(ieqMode.c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetIEQModeIndFunc(handle, m_IEQMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Intelligent Equalizer mode: %d", m_IEQMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetIEQModeIndFunc(handle, m_IEQMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Intelligent Equalizer mode: %d", m_IEQMode); + } + } + + // VolumeLeveller (bDolbyVolumeOverrideCheck: set true if found, then apply instead of DolbyVolumeMode) + typedef dsError_t (*dsSetVolLev_ind_t)(intptr_t handle, dsVolumeLeveller_t volLeveller); + dsSetVolLev_ind_t dsSetVolLevIndFunc = nullptr; + dsSetVolLevIndFunc = (dsSetVolLev_ind_t) resolve(RDK_DSHAL_NAME, "dsSetVolumeLeveller"); + if (dsSetVolLevIndFunc) { + std::string volMode("0"), volLevel("0"); + dsVolumeLeveller_t m_vl; + try { + volMode = device::HostPersistence::getInstance().getProperty("audio.VolumeLeveller.mode"); + volLevel = device::HostPersistence::getInstance().getProperty("audio.VolumeLeveller.level"); + bDolbyVolumeOverrideCheck = true; + } catch(...) { + try { + LOGINFO("audio.VolumeLeveller not found in persistence store. Try system default"); + volMode = device::HostPersistence::getInstance().getDefaultProperty("audio.VolumeLeveller.mode"); + volLevel = device::HostPersistence::getInstance().getDefaultProperty("audio.VolumeLeveller.level"); + } catch(...) { volMode = "0"; volLevel = "0"; } + } + m_vl.mode = atoi(volMode.c_str()); + m_vl.level = atoi(volLevel.c_str()); + LOGINFO("bDolbyVolumeOverrideCheck value: %d", (int)bDolbyVolumeOverrideCheck); + handle = 0; + if (bDolbyVolumeOverrideCheck && dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetVolLevIndFunc(handle, m_vl) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Volume Leveller: Mode: %d, Level: %d", m_vl.mode, m_vl.level); + } + handle = 0; + if (bDolbyVolumeOverrideCheck && dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetVolLevIndFunc(handle, m_vl) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Volume Leveller: Mode: %d, Level: %d", m_vl.mode, m_vl.level); + } + } + + // BassBoost + typedef dsError_t (*dsSetBass_ind_t)(intptr_t handle, int boost); + dsSetBass_ind_t dsSetBassIndFunc = nullptr; + dsSetBassIndFunc = (dsSetBass_ind_t) resolve(RDK_DSHAL_NAME, "dsSetBassEnhancer"); + if (dsSetBassIndFunc) { + std::string bassBoost("0"); + try { + bassBoost = device::HostPersistence::getInstance().getProperty("audio.BassBoost"); + } catch(...) { + try { + LOGINFO("audio.BassBoost not found in persistence store. Try system default"); + bassBoost = device::HostPersistence::getInstance().getDefaultProperty("audio.BassBoost"); + } catch(...) { bassBoost = "0"; } + } + int m_bassBoost = atoi(bassBoost.c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetBassIndFunc(handle, m_bassBoost) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Bass Boost: %d", m_bassBoost); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetBassIndFunc(handle, m_bassBoost) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Bass Boost: %d", m_bassBoost); + } + } + + // SurroundDecoder + typedef dsError_t (*dsEnableSurrDec_ind_t)(intptr_t handle, bool enabled); + dsEnableSurrDec_ind_t dsEnableSurrDecIndFunc = nullptr; + dsEnableSurrDecIndFunc = (dsEnableSurrDec_ind_t) resolve(RDK_DSHAL_NAME, "dsEnableSurroundDecoder"); + if (dsEnableSurrDecIndFunc) { + std::string sd("FALSE"); + try { + sd = device::HostPersistence::getInstance().getProperty("audio.SurroundDecoderEnabled"); + } catch(...) { + try { + LOGINFO("audio.SurroundDecoderEnabled not found in persistence store. Try system default"); + sd = device::HostPersistence::getInstance().getDefaultProperty("audio.SurroundDecoderEnabled"); + } catch(...) { sd = "FALSE"; } + } + bool m_surroundDecoder = (sd == "TRUE"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsEnableSurrDecIndFunc(handle, m_surroundDecoder) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Surround Decoder: %d", m_surroundDecoder); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsEnableSurrDecIndFunc(handle, m_surroundDecoder) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Surround Decoder: %d", m_surroundDecoder); + } + } + + // DRCMode + typedef dsError_t (*dsSetDRC_ind_t)(intptr_t handle, int mode); + dsSetDRC_ind_t dsSetDRCIndFunc = nullptr; + dsSetDRCIndFunc = (dsSetDRC_ind_t) resolve(RDK_DSHAL_NAME, "dsSetDRCMode"); + if (dsSetDRCIndFunc) { + std::string drcMode("Line"); + try { + drcMode = device::HostPersistence::getInstance().getProperty("audio.DRCMode"); + } catch(...) { + try { + LOGINFO("audio.DRCMode not found in persistence store. Try system default"); + drcMode = device::HostPersistence::getInstance().getDefaultProperty("audio.DRCMode"); + } catch(...) { drcMode = "Line"; } + } + int m_DRCMode = (drcMode == "RF") ? 1 : 0; + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetDRCIndFunc(handle, m_DRCMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized DRCMode: %d", m_DRCMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetDRCIndFunc(handle, m_DRCMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized DRCMode: %d", m_DRCMode); + } + } + + // SurroundVirtualizer + typedef dsError_t (*dsSetSurrVirt_ind_t)(intptr_t handle, dsSurroundVirtualizer_t virtualizer); + dsSetSurrVirt_ind_t dsSetSurrVirtIndFunc = nullptr; + dsSetSurrVirtIndFunc = (dsSetSurrVirt_ind_t) resolve(RDK_DSHAL_NAME, "dsSetSurroundVirtualizer"); + if (dsSetSurrVirtIndFunc) { + std::string svMode("0"), svBoost("0"); + dsSurroundVirtualizer_t m_virt; + try { + svMode = device::HostPersistence::getInstance().getProperty("audio.SurroundVirtualizer.mode"); + svBoost = device::HostPersistence::getInstance().getProperty("audio.SurroundVirtualizer.boost"); + m_virt.mode = atoi(svMode.c_str()); + m_virt.boost = atoi(svBoost.c_str()); + } catch(...) { + try { + LOGINFO("audio.SurroundVirtualizer.mode/boost not found in persistence store. Try system default"); + svMode = device::HostPersistence::getInstance().getDefaultProperty("audio.SurroundVirtualizer.mode"); + svBoost = device::HostPersistence::getInstance().getDefaultProperty("audio.SurroundVirtualizer.boost"); + } catch(...) { svMode = "0"; svBoost = "0"; } + } + m_virt.mode = atoi(svMode.c_str()); + m_virt.boost = atoi(svBoost.c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetSurrVirtIndFunc(handle, m_virt) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Surround Virtualizer: Mode: %d, Boost: %d", m_virt.mode, m_virt.boost); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetSurrVirtIndFunc(handle, m_virt) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Surround Virtualizer: Mode: %d, Boost: %d", m_virt.mode, m_virt.boost); + } + } + + // MISteering + typedef dsError_t (*dsSetMISteering_ind_t)(intptr_t handle, bool enabled); + dsSetMISteering_ind_t dsSetMIIndFunc = nullptr; + dsSetMIIndFunc = (dsSetMISteering_ind_t) resolve(RDK_DSHAL_NAME, "dsSetMISteering"); + if (dsSetMIIndFunc) { + std::string miSteering("Disabled"); + try { + miSteering = device::HostPersistence::getInstance().getProperty("audio.MISteering"); + } catch(...) { + try { + LOGINFO("audio.MISteering not found in persistence store. Try system default"); + miSteering = device::HostPersistence::getInstance().getDefaultProperty("audio.MISteering"); + } catch(...) { miSteering = "Disabled"; } + } + bool m_MISteering = (miSteering == "Enabled"); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetMIIndFunc(handle, m_MISteering) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized MI Steering: %d", m_MISteering); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetMIIndFunc(handle, m_MISteering) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized MI Steering: %d", m_MISteering); + else + LOGINFO("Port HDMI0: Initialization MI Steering: %d failed. Port not available", m_MISteering); + } + } + + // GraphicEQ + typedef dsError_t (*dsSetGEQ_ind_t)(intptr_t handle, int mode); + dsSetGEQ_ind_t dsSetGEQIndFunc = nullptr; + dsSetGEQIndFunc = (dsSetGEQ_ind_t) resolve(RDK_DSHAL_NAME, "dsSetGraphicEqualizerMode"); + if (dsSetGEQIndFunc) { + std::string geqMode("0"); + try { + geqMode = device::HostPersistence::getInstance().getProperty("audio.GraphicEQ"); + } catch(...) { + try { + LOGINFO("audio.GraphicEQ not found in persistence store. Try system default"); + geqMode = device::HostPersistence::getInstance().getDefaultProperty("audio.GraphicEQ"); + } catch(...) { geqMode = "0"; } + } + int m_GEQMode = atoi(geqMode.c_str()); + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetGEQIndFunc(handle, m_GEQMode) == dsERR_NONE) + LOGINFO("Port SPEAKER0: Initialized Graphic Equalizer mode: %d", m_GEQMode); + } + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetGEQIndFunc(handle, m_GEQMode) == dsERR_NONE) + LOGINFO("Port HDMI0: Initialized Graphic Equalizer mode: %d", m_GEQMode); + } + } + + } catch (...) { + LOGERR("Exception in initializeIndividualMS12Settings"); + } + EXIT_LOG; + } + + // Helper method to get current profile property + std::string getCurrentProfileProperty(const std::string& property) + { + std::string currentProfile = "Off"; + try { + currentProfile = device::HostPersistence::getInstance().getProperty("audio.MS12Profile"); + } catch(...) { + currentProfile = "Off"; + } + + return generateProfileProperty(currentProfile, property); + } + + // Helper method to generate profile property string + std::string generateProfileProperty(const std::string& profile, const std::string& property) + { + return "audio." + profile + "." + property; + } + + // Resolve function - exactly like HDMI implementation + static void* resolve(const std::string& libName, const std::string& symbolName) { + void* handle = dlopen(libName.c_str(), RTLD_LAZY); + if (!handle) { + std::cerr << "dlopen failed for " << libName << ": " << dlerror() << std::endl; + return nullptr; + } + void* symbol = dlsym(handle, symbolName.c_str()); + if (!symbol) { + std::cerr << "dlsym failed for " << symbolName << ": " << dlerror() << std::endl; + } + dlclose(handle); // Fix resource leak + return symbol; + } + + // Initialize audio port settings (from dsAudioMgr_init) + void initializeAudioPortSettings() + { + ENTRY_LOG; + try { + LOGINFO("Starting comprehensive audio port settings initialization from persistence..."); + + // Initialize HDMI Audio Mode Settings from Persistence + #ifdef IGNORE_EDID_LOGIC + std::string hdmiAudioModeSettings("SURROUND"); + #else + std::string hdmiAudioModeSettings("STEREO"); + #endif + + dsAudioStereoMode_t hdmiAudioMode; + + LOGINFO("Checking Host persistence for HDMI audio settings"); + try { + hdmiAudioModeSettings = device::HostPersistence::getInstance().getProperty("HDMI0.AudioMode"); + } catch(...) { + LOGINFO("HDMI0.AudioMode not in host persistence. Checking default."); + try { + hdmiAudioModeSettings = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.AudioMode"); + } catch(...) { + LOGINFO("HDMI0.AudioMode not in default host persistence."); + } + } + + LOGINFO("The HDMI Audio Mode Setting on startup is %s", hdmiAudioModeSettings.c_str()); + + // Parse HDMI audio mode string to enum + if (hdmiAudioModeSettings.compare("SURROUND") == 0) { + hdmiAudioMode = dsAUDIO_STEREO_SURROUND; + } else if (hdmiAudioModeSettings.compare("PASSTHRU") == 0) { + hdmiAudioMode = dsAUDIO_STEREO_PASSTHRU; + } else if (hdmiAudioModeSettings.compare("DOLBYDIGITAL") == 0) { + hdmiAudioMode = dsAUDIO_STEREO_DD; + } else if (hdmiAudioModeSettings.compare("DOLBYDIGITALPLUS") == 0) { + hdmiAudioMode = dsAUDIO_STEREO_DDPLUS; + } else if (hdmiAudioModeSettings.compare("STEREO") == 0) { + hdmiAudioMode = dsAUDIO_STEREO_STEREO; + } else { + #ifdef IGNORE_EDID_LOGIC + hdmiAudioMode = dsAUDIO_STEREO_SURROUND; + #else + hdmiAudioMode = dsAUDIO_STEREO_STEREO; + #endif + } + + // Initialize Audio Auto Mode Settings from Persistence + std::string hdmiAudioModeAuto("FALSE"); + bool hdmiAutoMode = false; + + try { + hdmiAudioModeAuto = device::HostPersistence::getInstance().getProperty("HDMI0.AudioMode.AUTO"); + } catch(...) { + LOGINFO("HDMI0.AudioMode.AUTO not found in persistence store. Try system default"); + try { + hdmiAudioModeAuto = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.AudioMode.AUTO"); + } catch(...) { + #ifdef IGNORE_EDID_LOGIC + hdmiAudioModeAuto = "TRUE"; + #else + hdmiAudioModeAuto = "FALSE"; + #endif + } + } + + // Initialize ARC Audio Auto Mode Settings + std::string arcAudioModeAuto("FALSE"); + bool arcAutoMode = false; + + try { + arcAudioModeAuto = device::HostPersistence::getInstance().getProperty("HDMI_ARC0.AudioMode.AUTO"); + } catch(...) { + try { + LOGINFO("HDMI_ARC0.AudioMode.AUTO not found in persistence store. Try system default"); + arcAudioModeAuto = device::HostPersistence::getInstance().getDefaultProperty("HDMI_ARC0.AudioMode.AUTO"); + } catch(...) { + arcAudioModeAuto = "FALSE"; + } + } + + // Initialize SPDIF Audio Auto Mode Settings + std::string spdifAudioModeAuto("FALSE"); + bool spdifAutoMode = false; + + try { + spdifAudioModeAuto = device::HostPersistence::getInstance().getProperty("SPDIF0.AudioMode.AUTO"); + } catch(...) { + try { + LOGINFO("SPDIF0.AudioMode.AUTO not found in persistence store. Try system default"); + spdifAudioModeAuto = device::HostPersistence::getInstance().getDefaultProperty("SPDIF0.AudioMode.AUTO"); + } catch(...) { + spdifAudioModeAuto = "FALSE"; + } + } + + // Initialize SPEAKER Audio Auto Mode Settings + std::string speakerAudioModeAuto("TRUE"); + bool speakerAutoMode = true; + + try { + speakerAudioModeAuto = device::HostPersistence::getInstance().getProperty("SPEAKER0.AudioMode.AUTO"); + } catch(...) { + try { + LOGINFO("SPEAKER0.AudioMode.AUTO not found in persistence store. Try system default"); + speakerAudioModeAuto = device::HostPersistence::getInstance().getDefaultProperty("SPEAKER0.AudioMode.AUTO"); + } catch(...) { + speakerAudioModeAuto = "TRUE"; + } + } + + // Parse auto mode settings + hdmiAutoMode = (hdmiAudioModeAuto.compare("TRUE") == 0); + arcAutoMode = (arcAudioModeAuto.compare("TRUE") == 0); + spdifAutoMode = (spdifAudioModeAuto.compare("TRUE") == 0); + speakerAutoMode = (speakerAudioModeAuto.compare("TRUE") == 0); + + LOGINFO("The HDMI Audio Auto Setting on startup is %s", hdmiAudioModeAuto.c_str()); + LOGINFO("The HDMI ARC Audio Auto Setting on startup is %s", arcAudioModeAuto.c_str()); + LOGINFO("The SPDIF Audio Auto Setting on startup is %s", spdifAudioModeAuto.c_str()); + LOGINFO("The SPEAKER Audio Auto Setting on startup is %s", speakerAudioModeAuto.c_str()); + + // Initialize SPDIF Audio Mode Settings + std::string spdifModeSettings("STEREO"); + dsAudioStereoMode_t spdifAudioMode; + + spdifModeSettings = device::HostPersistence::getInstance().getProperty("SPDIF0.AudioMode", spdifModeSettings); + LOGINFO("The SPDIF Audio Mode Setting on startup is %s", spdifModeSettings.c_str()); + + if (spdifModeSettings.compare("SURROUND") == 0) { + spdifAudioMode = dsAUDIO_STEREO_SURROUND; + } else if (spdifModeSettings.compare("PASSTHRU") == 0) { + spdifAudioMode = dsAUDIO_STEREO_PASSTHRU; + } else { + spdifAudioMode = dsAUDIO_STEREO_STEREO; + } + + // Initialize HDMI ARC Audio Mode Settings + std::string arcModeSettings("STEREO"); + dsAudioStereoMode_t arcAudioMode; + + arcModeSettings = device::HostPersistence::getInstance().getProperty("HDMI_ARC0.AudioMode", arcModeSettings); + LOGINFO("The HDMI ARC Audio Mode Setting on startup is %s", arcModeSettings.c_str()); + + if (arcModeSettings.compare("SURROUND") == 0) { + arcAudioMode = dsAUDIO_STEREO_SURROUND; + } else if (arcModeSettings.compare("PASSTHRU") == 0) { + arcAudioMode = dsAUDIO_STEREO_PASSTHRU; + } else { + arcAudioMode = dsAUDIO_STEREO_STEREO; + } + + // Initialize SPEAKER Audio Mode Settings + std::string speakerModeSettings("SURROUND"); + dsAudioStereoMode_t speakerAudioMode; + + try { + speakerModeSettings = device::HostPersistence::getInstance().getProperty("SPEAKER0.AudioMode", speakerModeSettings); + LOGINFO("The SPEAKER Audio Mode Setting on startup is %s", speakerModeSettings.c_str()); + } catch(...) { + speakerModeSettings = "SURROUND"; + } + + if (speakerModeSettings.compare("SURROUND") == 0) { + speakerAudioMode = dsAUDIO_STEREO_SURROUND; + } else if (speakerModeSettings.compare("PASSTHRU") == 0) { + speakerAudioMode = dsAUDIO_STEREO_PASSTHRU; + } else if (speakerModeSettings.compare("STEREO") == 0) { + speakerAudioMode = dsAUDIO_STEREO_STEREO; + } else { + speakerAudioMode = dsAUDIO_STEREO_SURROUND; + } + + // Apply audio port settings using HAL functions + intptr_t handle = 0; + + // Set HDMI port audio mode + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI, 0, &handle) == dsERR_NONE) { + if (dsSetStereoMode(handle, hdmiAudioMode) == dsERR_NONE) { + LOGINFO("HDMI0: Applied audio mode: %d", hdmiAudioMode); + } + if (dsSetStereoAuto(handle, hdmiAutoMode ? 1 : 0) == dsERR_NONE) { + LOGINFO("HDMI0: Applied auto mode: %s", hdmiAutoMode ? "TRUE" : "FALSE"); + } + } + + // Set SPDIF port audio mode + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPDIF, 0, &handle) == dsERR_NONE) { + if (dsSetStereoMode(handle, spdifAudioMode) == dsERR_NONE) { + LOGINFO("SPDIF0: Applied audio mode: %d", spdifAudioMode); + } + if (dsSetStereoAuto(handle, spdifAutoMode ? 1 : 0) == dsERR_NONE) { + LOGINFO("SPDIF0: Applied auto mode: %s", spdifAutoMode ? "TRUE" : "FALSE"); + } + } + + // Set HDMI ARC port audio mode + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_HDMI_ARC, 0, &handle) == dsERR_NONE) { + if (dsSetStereoMode(handle, arcAudioMode) == dsERR_NONE) { + LOGINFO("HDMI_ARC0: Applied audio mode: %d", arcAudioMode); + } + if (dsSetStereoAuto(handle, arcAutoMode ? 1 : 0) == dsERR_NONE) { + LOGINFO("HDMI_ARC0: Applied auto mode: %s", arcAutoMode ? "TRUE" : "FALSE"); + } + } + + // Set SPEAKER port audio mode + handle = 0; + if (dsGetAudioPort(dsAUDIOPORT_TYPE_SPEAKER, 0, &handle) == dsERR_NONE) { + if (dsSetStereoMode(handle, speakerAudioMode) == dsERR_NONE) { + LOGINFO("SPEAKER0: Applied audio mode: %d", speakerAudioMode); + } + if (dsSetStereoAuto(handle, speakerAutoMode ? 1 : 0) == dsERR_NONE) { + LOGINFO("SPEAKER0: Applied auto mode: %s", speakerAutoMode ? "TRUE" : "FALSE"); + } + } + + LOGINFO("Comprehensive audio port settings initialization completed successfully"); + + } catch (...) { + LOGERR("Exception in initializeAudioPortSettings"); + } + EXIT_LOG; + } + + // Initialize MS12 audio processing settings + void initializeMS12Settings() + { + ENTRY_LOG; + try { + intptr_t handle = 0; + + // Initialize basic audio compression for all profiles + typedef dsError_t (*dsSetAudioCompression_t)(intptr_t handle, int compressionLevel); + dsSetAudioCompression_t dsSetAudioCompressionFunc = nullptr; + + dsSetAudioCompressionFunc = (dsSetAudioCompression_t) resolve(RDK_DSHAL_NAME, "dsSetAudioCompression"); + if (dsSetAudioCompressionFunc) { + int defaultCompression = 0; + + // Initialize compression for SPEAKER and HDMI ports + const dsAudioPortType_t compressionPorts[] = {dsAUDIOPORT_TYPE_SPEAKER, dsAUDIOPORT_TYPE_HDMI}; + const char* portNames[] = {"SPEAKER0", "HDMI0"}; + + for (int i = 0; i < 2; i++) { + handle = 0; + if (dsGetAudioPort(compressionPorts[i], 0, &handle) == dsERR_NONE) { + if (dsSetAudioCompressionFunc(handle, defaultCompression) == dsERR_NONE) { + LOGINFO("%s: Initialized audio compression: %d", portNames[i], defaultCompression); + } + } + } + } + + LOGINFO("MS12 audio settings initialization completed"); + + } catch (...) { + LOGERR("Exception in initializeMS12Settings"); + } + EXIT_LOG; + } + + // audioOutPortConnectCallback implementation + static void audioOutPortConnectCallback(dsAudioPortType_t portType, unsigned int uiPortNo, bool isPortConnected) + { + LOGINFO("Audio port hotplug event: portType=%d, portNo=%d, connected=%s", + portType, uiPortNo, isPortConnected ? "true" : "false"); + + // Convert dsAudioPortType_t to AudioPortType + AudioPortType wpePortType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; // default + switch (portType) { + case dsAUDIOPORT_TYPE_ID_LR: wpePortType = AudioPortType::AUDIO_PORT_TYPE_LR; break; + case dsAUDIOPORT_TYPE_HDMI: wpePortType = AudioPortType::AUDIO_PORT_TYPE_HDMI; break; + case dsAUDIOPORT_TYPE_SPDIF: wpePortType = AudioPortType::AUDIO_PORT_TYPE_SPDIF; break; + case dsAUDIOPORT_TYPE_SPEAKER: wpePortType = AudioPortType::AUDIO_PORT_TYPE_SPEAKER; break; + case dsAUDIOPORT_TYPE_HDMI_ARC: wpePortType = AudioPortType::AUDIO_PORT_TYPE_HDMIARC; break; + case dsAUDIOPORT_TYPE_HEADPHONE: wpePortType = AudioPortType::AUDIO_PORT_TYPE_HEADPHONE; break; + default: break; + } + + // Call Audio event handler through global callback if available + if (g_AudioOutHotPlugCallback) { + g_AudioOutHotPlugCallback(wpePortType, static_cast(uiPortNo), isPortConnected); + } + } + + // audioFormatUpdateCallback implementation + static void audioFormatUpdateCallback(dsAudioFormat_t audioFormat) + { + LOGINFO("Audio format update event: audioFormat=%d", audioFormat); + + // Convert dsAudioFormat_t to AudioFormat + AudioFormat wpeFormat = static_cast(audioFormat); + + // Call Audio event handler through global callback if available + if (g_AudioFormatUpdateCallback) { + g_AudioFormatUpdateCallback(wpeFormat); + } + } + + // audioAtmosCapsChangeCallback implementation + static void audioAtmosCapsChangeCallback(dsATMOSCapability_t atmosCaps, bool status) + { + LOGINFO("Audio atmos caps change event: atmosCaps=%d, status=%s", atmosCaps, status ? "true" : "false"); + + // Convert dsATMOSCapability_t to DolbyAtmosCapability + DolbyAtmosCapability wpeAtmosCaps = static_cast(atmosCaps); + + // Call Audio event handler through global callback if available + if (g_DolbyAtmosCapabilitiesChangedCallback) { + g_DolbyAtmosCapabilitiesChangedCallback(wpeAtmosCaps, status); + } + } + + // State Change Notification Functions using global callbacks + // notifyAssociatedAudioMixingChanged implementation + void notifyAssociatedAudioMixingChanged(bool mixing) + { + LOGINFO("Associated audio mixing changed: %s", mixing ? "enabled" : "disabled"); + // Call Audio event handler using global callback if available + if (g_AssociatedAudioMixingChangedCallback) { + g_AssociatedAudioMixingChangedCallback(mixing); + } + } + + // notifyAudioFaderControlChanged implementation + void notifyAudioFaderControlChanged(int32_t mixerBalance) + { + LOGINFO("Audio fader control changed: mixerBalance=%d", mixerBalance); + // Call Audio event handler using global callback if available + if (g_AudioFaderControlChangedCallback) { + g_AudioFaderControlChangedCallback(mixerBalance); + } + } + + // notifyAudioPrimaryLanguageChanged implementation + void notifyAudioPrimaryLanguageChanged(const std::string& primaryLanguage) + { + LOGINFO("Audio primary language changed: %s", primaryLanguage.c_str()); + // Call Audio event handler using global callback if available + if (g_AudioPrimaryLanguageChangedCallback) { + g_AudioPrimaryLanguageChangedCallback(primaryLanguage); + } + } + + // notifyAudioSecondaryLanguageChanged implementation + void notifyAudioSecondaryLanguageChanged(const std::string& secondaryLanguage) + { + LOGINFO("Audio secondary language changed: %s", secondaryLanguage.c_str()); + // Call Audio event handler using global callback if available + if (g_AudioSecondaryLanguageChangedCallback) { + g_AudioSecondaryLanguageChangedCallback(secondaryLanguage); + } + } + + // notifyAudioPortStateChanged implementation + void notifyAudioPortStateChanged(AudioPortState audioPortState) + { + LOGINFO("Audio port state changed: state=%d", static_cast(audioPortState)); + // Call Audio event handler using global callback if available + if (g_AudioPortStateChangedCallback) { + g_AudioPortStateChangedCallback(audioPortState); + } + } + + // notifyAudioLevelChanged implementation + void notifyAudioLevelChanged(int32_t audioLevel) + { + LOGINFO("Audio level changed: audioLevel=%d", audioLevel); + // Call Audio event handler using global callback if available + if (g_AudioLevelChangedCallback) { + g_AudioLevelChangedCallback(static_cast(audioLevel)); + } + } + + // notifyAudioModeChanged implementation + void notifyAudioModeChanged(AudioPortType portType, AudioStereoMode mode) + { + LOGINFO("Audio mode changed: portType=%d, mode=%d", static_cast(portType), static_cast(mode)); + // Call Audio event handler using global callback if available + if (g_AudioModeChangedCallback) { + g_AudioModeChangedCallback(portType, mode); + } + } + + // Callback management implementation following HdmiIn pattern + void setAllCallbacks(const CallbackBundle bundle) override + { + ENTRY_LOG; + + // Register audio callbacks following HdmiIn pattern + if (bundle.OnAudioOutHotPlug) { + LOGINFO("Audio Output Hot Plug Event Callback Registered"); + g_AudioOutHotPlugCallback = bundle.OnAudioOutHotPlug; + } + + if (bundle.OnAudioFormatUpdate) { + LOGINFO("Audio Format Update Event Callback Registered"); + g_AudioFormatUpdateCallback = bundle.OnAudioFormatUpdate; + } + + if (bundle.OnDolbyAtmosCapabilitiesChanged) { + LOGINFO("Dolby Atmos Capabilities Changed Event Callback Registered"); + g_DolbyAtmosCapabilitiesChangedCallback = bundle.OnDolbyAtmosCapabilitiesChanged; + } + + if (bundle.OnAssociatedAudioMixingChanged) { + LOGINFO("Associated Audio Mixing Changed Event Callback Registered"); + g_AssociatedAudioMixingChangedCallback = bundle.OnAssociatedAudioMixingChanged; + } + + if (bundle.OnAudioFaderControlChanged) { + LOGINFO("Audio Fader Control Changed Event Callback Registered"); + g_AudioFaderControlChangedCallback = bundle.OnAudioFaderControlChanged; + } + + if (bundle.OnAudioPrimaryLanguageChanged) { + LOGINFO("Audio Primary Language Changed Event Callback Registered"); + g_AudioPrimaryLanguageChangedCallback = bundle.OnAudioPrimaryLanguageChanged; + } + + if (bundle.OnAudioSecondaryLanguageChanged) { + LOGINFO("Audio Secondary Language Changed Event Callback Registered"); + g_AudioSecondaryLanguageChangedCallback = bundle.OnAudioSecondaryLanguageChanged; + } + + if (bundle.OnAudioPortStateChanged) { + LOGINFO("Audio Port State Changed Event Callback Registered"); + g_AudioPortStateChangedCallback = bundle.OnAudioPortStateChanged; + } + + if (bundle.OnAudioLevelChanged) { + LOGINFO("Audio Level Changed Event Callback Registered"); + g_AudioLevelChangedCallback = bundle.OnAudioLevelChanged; + } + + if (bundle.OnAudioModeChanged) { + LOGINFO("Audio Mode Changed Event Callback Registered"); + g_AudioModeChangedCallback = bundle.OnAudioModeChanged; + } + + LOGINFO("Audio callbacks set successfully"); + EXIT_LOG; + } + + void getPersistenceValue() override + { + ENTRY_LOG; + // Initialize persistence-related values if needed + LOGINFO("Audio persistence values loaded"); + EXIT_LOG; + } +}; diff --git a/plugin/hal/dCompositeIn.h b/plugin/hal/dCompositeIn.h new file mode 100644 index 0000000..6193636 --- /dev/null +++ b/plugin/hal/dCompositeIn.h @@ -0,0 +1,56 @@ +/* + * 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. + */ +#pragma once + +#include "dsCompositeIn.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +using namespace WPEFramework; + +namespace hal { +namespace dCompositeIn { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle& bundle) = 0; + virtual void getPersistenceValue() = 0; + + // CompositeIn Platform interface methods - all pure virtual + virtual uint32_t GetNrOfCompositeInputs(int32_t& nrCompositeInputs) = 0; + virtual uint32_t GetCompositeInStatus(CompositeInStatus& status) = 0; + virtual uint32_t SelectCompositeInPort(const CompositeInPort port) = 0; + virtual uint32_t ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect) = 0; + + }; +} // namespace dCompositeIn +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dCompositeInImpl.h b/plugin/hal/dCompositeInImpl.h new file mode 100644 index 0000000..8c5e593 --- /dev/null +++ b/plugin/hal/dCompositeInImpl.h @@ -0,0 +1,492 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "dCompositeIn.h" +#include "dsCompositeIn.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" +#include "dsError.h" +#include "dsCompositeIn.h" +#include "dsDisplay.h" + + +#include +#include "DeviceSettingsTypes.h" + +#ifndef RDK_DSHAL_NAME +#warning "RDK_DSHAL_NAME is not defined" +#define RDK_DSHAL_NAME "RDK_DSHAL_NAME is not defined" +#endif + +#include +#include +#include +#include +#include +#include +#include + +static int compositeIn_isInitialized = 0; +static int compositeIn_isPlatInitialized = 0; +static pthread_mutex_t dsCompositeInLock = PTHREAD_MUTEX_INITIALIZER; + +// Static global callback functions for CompositeIn events - using WPE Framework types +static std::function g_CompositeInHotPlugCallback; +static std::function g_CompositeInSignalStatusCallback; +static std::function g_CompositeInStatusCallback; +static std::function g_CompositeInVideoModeUpdateCallback; + +class dCompositeInImpl : public hal::dCompositeIn::IPlatform { + + // delete copy constructor and assignment operator + dCompositeInImpl(const dCompositeInImpl&) = delete; + dCompositeInImpl& operator=(const dCompositeInImpl&) = delete; + +public: + dCompositeInImpl() + { + LOGINFO("dCompositeInImpl Constructor"); + getInstance() = this; // Set static instance for callback access + InitialiseHAL(); + } + + virtual ~dCompositeInImpl() + { + LOGINFO("dCompositeInImpl Destructor"); + DeInitialiseHAL(); + getInstance() = nullptr; // Clear static instance + } + + // Resolve method for dynamic library loading - following dHdmiInImpl.h pattern + static void* resolve(const std::string& libName, const std::string& symbolName) { + return WPEFramework::Plugin::DeviceSettingsHALLoader::ResolveSymbol(libName, symbolName); + } + + // Singleton getInstance method - following VideoPort pattern + static dCompositeInImpl*& getInstance() + { + static dCompositeInImpl* instance = nullptr; + return instance; + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + + // Check TV profile - following dHdmiInImpl.h pattern + profileType = searchRdkProfile(); + LOGINFO("profileType %d", profileType); + + if (TV != profileType) { + LOGINFO("InitialiseHAL: Not TV profile - profileType=%d", static_cast(profileType)); + return; + } + + if (!compositeIn_isPlatInitialized) { + LOGINFO("InitialiseHAL - TV Profile"); + + // Initialize DS HAL CompositeIn using resolve() method - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInInit_t)(void); + static dsCompositeInInit_t initFunc = nullptr; + + if (initFunc == nullptr) { + initFunc = (dsCompositeInInit_t) resolve(RDK_DSHAL_NAME, "dsCompositeInInit"); + } + + if (initFunc) { + LOGINFO("Invoking dsCompositeInInit()"); + dsError_t eError = initFunc(); + if (dsERR_NONE != eError) { + LOGERR("InitialiseHAL: dsCompositeInInit failed with error: %d", eError); + return; + } + LOGINFO("InitialiseHAL: dsCompositeInInit succeeded"); + } else { + LOGERR("InitialiseHAL: dsCompositeInInit function not available"); + return; + } + + // Load persistence values after successful initialization + getPersistenceValue(); + + compositeIn_isPlatInitialized = 1; + LOGINFO("InitialiseHAL completed: compositeIn_isPlatInitialized=%d, compositeIn_isInitialized=%d", + compositeIn_isPlatInitialized, compositeIn_isInitialized); + } + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + + if (TV != profileType) { + LOGINFO("DeInitialiseHAL: Not TV profile - profileType=%d", static_cast(profileType)); + return; + } + + if (compositeIn_isPlatInitialized) { + compositeIn_isPlatInitialized--; + if (!compositeIn_isPlatInitialized) { + // Use resolve method for dsCompositeInTerm - matches dsCompositeIn.c _dsCompositeInTerm pattern + typedef dsError_t (*dsCompositeInTerm_t)(void); + static dsCompositeInTerm_t termFunc = nullptr; + + if (termFunc == nullptr) { + termFunc = (dsCompositeInTerm_t) resolve(RDK_DSHAL_NAME, "dsCompositeInTerm"); + } + + if (termFunc) { + LOGINFO("Invoking dsCompositeInTerm()"); + dsError_t eError = termFunc(); + if (dsERR_NONE != eError) { + LOGERR("DeInitialiseHAL: dsCompositeInTerm failed with error: %d", eError); + } + } else { + LOGERR("DeInitialiseHAL: dsCompositeInTerm function not available"); + } + } + } + compositeIn_isInitialized = 0; + } + + void setAllCallbacks(const CallbackBundle& bundle) override + { + LOGINFO("dCompositeInImpl setAllCallbacks"); + + if (!compositeIn_isInitialized) { + // Set the global callback function pointers from CallbackBundle + g_CompositeInHotPlugCallback = bundle.OnCompositeInHotPlug; + g_CompositeInSignalStatusCallback = bundle.OnCompositeInSignalStatus; + g_CompositeInStatusCallback = bundle.OnCompositeInStatus; + g_CompositeInVideoModeUpdateCallback = bundle.OnCompositeInVideoModeUpdate; + + // Register HAL callbacks + registerCompositeInEventCallbacks(); + + compositeIn_isInitialized = 1; + LOGINFO("dCompositeInImpl setAllCallbacks: CompositeIn callbacks registered successfully"); + } else { + LOGINFO("dCompositeInImpl setAllCallbacks: CompositeIn already initialized, skipping callback registration"); + } + } + + void getPersistenceValue() override + { + LOGINFO("dCompositeInImpl getPersistenceValue - CompositeIn persistence loading"); + // Load any CompositeIn-specific persistence values here + // This would be similar to VideoPort persistence loading but for CompositeIn settings + } + + // Implementation of CompositeIn Platform interface methods + uint32_t GetNrOfCompositeInputs(int32_t& nrCompositeInputs) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetNrOfCompositeInputs"); + + pthread_mutex_lock(&dsCompositeInLock); + + // Use resolve method for dsCompositeInGetNumberOfInputs - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInGetNumberOfInputs_t)(uint8_t *nrCompositeInputs); + static dsCompositeInGetNumberOfInputs_t func = 0; + if (func == 0) { + func = (dsCompositeInGetNumberOfInputs_t) resolve(RDK_DSHAL_NAME, "dsCompositeInGetNumberOfInputs"); + } + + if (func != 0) { + uint8_t nrInputs = 0; + dsError_t eError = func(&nrInputs); + if (eError == dsERR_NONE) { + nrCompositeInputs = static_cast(nrInputs); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetNrOfCompositeInputs: SUCCESS - nrCompositeInputs=%d", nrCompositeInputs); + } else { + LOGERR("GetNrOfCompositeInputs: FAILED - dsCompositeInGetNumberOfInputs error=%d", eError); + } + } else { + LOGERR("GetNrOfCompositeInputs: FAILED - dsCompositeInGetNumberOfInputs not available"); + } + + pthread_mutex_unlock(&dsCompositeInLock); + return retCode; + } + + uint32_t GetCompositeInStatus(CompositeInStatus& status) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetCompositeInStatus"); + + pthread_mutex_lock(&dsCompositeInLock); + + // Use resolve method for dsCompositeInGetStatus - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInGetStatus_t)(dsCompositeInStatus_t *inputStatus); + static dsCompositeInGetStatus_t func = 0; + if (func == 0) { + func = (dsCompositeInGetStatus_t) resolve(RDK_DSHAL_NAME, "dsCompositeInGetStatus"); + } + + if (func != 0) { + dsCompositeInStatus_t dsStatus; + dsError_t eError = func(&dsStatus); + if (eError == dsERR_NONE) { + // Convert from DS types to WPE Framework types + status.activePort = static_cast(dsStatus.activePort); + status.isPresented = dsStatus.isPresented; + + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetCompositeInStatus: SUCCESS - activePort=%d, isPresented=%s", + static_cast(status.activePort), status.isPresented ? "true" : "false"); + } else { + LOGERR("GetCompositeInStatus: FAILED - dsCompositeInGetStatus error=%d", eError); + } + } else { + LOGERR("GetCompositeInStatus: FAILED - dsCompositeInGetStatus not available"); + } + + pthread_mutex_unlock(&dsCompositeInLock); + return retCode; + } + + uint32_t SelectCompositeInPort(const CompositeInPort port) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SelectCompositeInPort: port=%d", static_cast(port)); + + pthread_mutex_lock(&dsCompositeInLock); + + // Use resolve method for dsCompositeInSelectPort - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInSelectPort_t)(dsCompositeInPort_t port); + static dsCompositeInSelectPort_t func = 0; + if (func == 0) { + func = (dsCompositeInSelectPort_t) resolve(RDK_DSHAL_NAME, "dsCompositeInSelectPort"); + } + + if (func != 0) { + dsCompositeInPort_t dsPort = static_cast(port); + dsError_t eError = func(dsPort); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SelectCompositeInPort: SUCCESS - port=%d", static_cast(port)); + } else { + LOGERR("SelectCompositeInPort: FAILED - dsCompositeInSelectPort error=%d", eError); + } + } else { + LOGERR("SelectCompositeInPort: FAILED - dsCompositeInSelectPort not available"); + } + + pthread_mutex_unlock(&dsCompositeInLock); + return retCode; + } + + uint32_t ScaleCompositeInVideo(const CompositeInVideoRectangle videoRect) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("ScaleCompositeInVideo: x=%d, y=%d, width=%d, height=%d", + videoRect.x, videoRect.y, videoRect.width, videoRect.height); + + pthread_mutex_lock(&dsCompositeInLock); + + // Use resolve method for dsCompositeInScaleVideo - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInScaleVideo_t)(int x, int y, int width, int height); + static dsCompositeInScaleVideo_t func = 0; + if (func == 0) { + func = (dsCompositeInScaleVideo_t) resolve(RDK_DSHAL_NAME, "dsCompositeInScaleVideo"); + } + + if (func != 0) { + dsError_t eError = func(videoRect.x, videoRect.y, videoRect.width, videoRect.height); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("ScaleCompositeInVideo: SUCCESS"); + } else { + LOGERR("ScaleCompositeInVideo: FAILED - dsCompositeInScaleVideo error=%d", eError); + } + } else { + LOGERR("ScaleCompositeInVideo: FAILED - dsCompositeInScaleVideo not available"); + } + + pthread_mutex_unlock(&dsCompositeInLock); + return retCode; + } + + // Type conversion methods between DS HAL types and WPE Framework types + static WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort convertToWPECompositeInPort(const CompositeInPort port) + { + return static_cast(port); + } + + static CompositeInPort convertFromWPECompositeInPort(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort port) + { + return static_cast(port); + } + + static WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus convertToWPECompositeInSignalStatus(const CompositeInSignalStatus signalStatus) + { + return static_cast(signalStatus); + } + + static CompositeInSignalStatus convertFromWPECompositeInSignalStatus(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus signalStatus) + { + return static_cast(signalStatus); + } + + static WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution convertToWPEDisplayVideoPortResolution(const DisplayVideoPortResolution resolution) + { + WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution wpeResolution; + wpeResolution.name = resolution.name; + wpeResolution.pixelResolution = static_cast(resolution.pixelResolution); + wpeResolution.aspectRatio = static_cast(resolution.aspectRatio); + wpeResolution.frameRate = static_cast(resolution.frameRate); + wpeResolution.interlaced = resolution.interlaced; + return wpeResolution; + } + + static DisplayVideoPortResolution convertFromWPEDisplayVideoPortResolution(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution resolution) + { + DisplayVideoPortResolution halResolution; + halResolution.name = resolution.name; + halResolution.pixelResolution = static_cast(resolution.pixelResolution); + halResolution.aspectRatio = static_cast(resolution.aspectRatio); + halResolution.frameRate = static_cast(resolution.frameRate); + halResolution.interlaced = resolution.interlaced; + return halResolution; + } + + static WPEFramework::Exchange::IDeviceSettingsCompositeIn::VideoRectangle convertToWPEVideoRectangle(const CompositeInVideoRectangle rectangle) + { + WPEFramework::Exchange::IDeviceSettingsCompositeIn::VideoRectangle wpeRectangle; + wpeRectangle.x = rectangle.x; + wpeRectangle.y = rectangle.y; + wpeRectangle.width = rectangle.width; + wpeRectangle.height = rectangle.height; + return wpeRectangle; + } + + static CompositeInVideoRectangle convertFromWPEVideoRectangle(const WPEFramework::Exchange::IDeviceSettingsCompositeIn::VideoRectangle rectangle) + { + CompositeInVideoRectangle halRectangle; + halRectangle.x = rectangle.x; + halRectangle.y = rectangle.y; + halRectangle.width = rectangle.width; + halRectangle.height = rectangle.height; + return halRectangle; + } + +private: + void registerCompositeInEventCallbacks() + { + LOGINFO("registerCompositeInEventCallbacks"); + + // Register CompositeIn event callbacks using resolve method - matches dsCompositeIn.c pattern + typedef dsError_t (*dsCompositeInRegisterConnectCB_t)(dsCompositeInConnectCB_t callback); + typedef dsError_t (*dsCompositeInRegisterSignalChangeCB_t)(dsCompositeInSignalChangeCB_t callback); + typedef dsError_t (*dsCompositeInRegisterStatusChangeCB_t)(dsCompositeInStatusChangeCB_t callback); + typedef dsError_t (*dsCompositeInRegisterVideoModeUpdateCB_t)(dsCompositeInVideoModeUpdateCB_t callback); + + static dsCompositeInRegisterConnectCB_t funcConnect = 0; + static dsCompositeInRegisterSignalChangeCB_t funcSignal = 0; + static dsCompositeInRegisterStatusChangeCB_t funcStatus = 0; + static dsCompositeInRegisterVideoModeUpdateCB_t funcVideoMode = 0; + + if (funcConnect == 0) { + funcConnect = (dsCompositeInRegisterConnectCB_t) resolve(RDK_DSHAL_NAME, "dsCompositeInRegisterConnectCB"); + funcSignal = (dsCompositeInRegisterSignalChangeCB_t) resolve(RDK_DSHAL_NAME, "dsCompositeInRegisterSignalChangeCB"); + funcStatus = (dsCompositeInRegisterStatusChangeCB_t) resolve(RDK_DSHAL_NAME, "dsCompositeInRegisterStatusChangeCB"); + funcVideoMode = (dsCompositeInRegisterVideoModeUpdateCB_t) resolve(RDK_DSHAL_NAME, "dsCompositeInRegisterVideoModeUpdateCB"); + } + + if (funcConnect && funcSignal && funcStatus && funcVideoMode) { + funcConnect(dsCompositeInConnectCallback); + funcSignal(dsCompositeInSignalChangeCallback); + funcStatus(dsCompositeInStatusChangeCallback); + funcVideoMode(dsCompositeInVideoModeUpdateCallback); + LOGINFO("registerCompositeInEventCallbacks: SUCCESS"); + } else { + LOGERR("registerCompositeInEventCallbacks: FAILED - callbacks not available"); + } + } + + // Static callback functions to handle CompositeIn events from HAL + static void dsCompositeInConnectCallback(dsCompositeInPort_t port, bool isPortConnected) + { + LOGINFO("dsCompositeInConnectCallback: port=%d, isPortConnected=%s", static_cast(port), isPortConnected ? "true" : "false"); + + if (g_CompositeInHotPlugCallback) { + // Convert DS HAL type directly to WPE Framework type + WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort wpePort = convertToWPECompositeInPort(static_cast(port)); + g_CompositeInHotPlugCallback(wpePort, isPortConnected); + } + } + + static void dsCompositeInSignalChangeCallback(dsCompositeInPort_t port, dsCompInSignalStatus_t sigStatus) + { + LOGINFO("dsCompositeInSignalChangeCallback: port=%d, sigStatus=%d", static_cast(port), static_cast(sigStatus)); + + if (g_CompositeInSignalStatusCallback) { + // Convert DS HAL types directly to WPE Framework types + WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort wpePort = convertToWPECompositeInPort(static_cast(port)); + WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInSignalStatus wpeSignalStatus = convertToWPECompositeInSignalStatus(static_cast(sigStatus)); + g_CompositeInSignalStatusCallback(wpePort, wpeSignalStatus); + } + } + + static void dsCompositeInStatusChangeCallback(dsCompositeInStatus_t inputStatus) + { + LOGINFO("dsCompositeInStatusChangeCallback: activePort=%d, isPresented=%s", + static_cast(inputStatus.activePort), inputStatus.isPresented ? "true" : "false"); + + if (g_CompositeInStatusCallback) { + // Convert DS HAL type directly to WPE Framework type + WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort wpePort = convertToWPECompositeInPort(static_cast(inputStatus.activePort)); + g_CompositeInStatusCallback(wpePort, inputStatus.isPresented); + } + } + + static void dsCompositeInVideoModeUpdateCallback(dsCompositeInPort_t port, dsVideoPortResolution_t videoResolution) + { + LOGINFO("dsCompositeInVideoModeUpdateCallback: port=%d", static_cast(port)); + LOGINFO("Video Mode: %s pixelResolution %d aspectRatio %d stereoScopicMode %d frameRate %d", + videoResolution.name, videoResolution.pixelResolution, videoResolution.aspectRatio, + videoResolution.stereoScopicMode, videoResolution.frameRate); + + if (g_CompositeInVideoModeUpdateCallback) { + // Convert DS HAL types to WPE Framework types + WPEFramework::Exchange::IDeviceSettingsCompositeIn::CompositeInPort wpePort = convertToWPECompositeInPort(static_cast(port)); + + // Convert DS HAL dsVideoPortResolution_t to DisplayVideoPortResolution + DisplayVideoPortResolution halResolution; + halResolution.name = std::string(videoResolution.name); + halResolution.pixelResolution = static_cast(videoResolution.pixelResolution); + halResolution.aspectRatio = static_cast(videoResolution.aspectRatio); + halResolution.stereoScopicMode = static_cast(videoResolution.stereoScopicMode); + halResolution.frameRate = static_cast(videoResolution.frameRate); + halResolution.interlaced = videoResolution.interlaced; + + WPEFramework::Exchange::IDeviceSettingsCompositeIn::DisplayVideoPortResolution wpeResolution = convertToWPEDisplayVideoPortResolution(halResolution); + g_CompositeInVideoModeUpdateCallback(wpePort, wpeResolution); + } + } +}; \ No newline at end of file diff --git a/plugin/hal/dDisplay.h b/plugin/hal/dDisplay.h new file mode 100644 index 0000000..13f3678 --- /dev/null +++ b/plugin/hal/dDisplay.h @@ -0,0 +1,62 @@ +/* + * 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. + */ +#pragma once + +#include "dsDisplay.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dDisplay { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle& bundle) = 0; + virtual void getPersistenceValue() = 0; + + // Display Platform interface methods - all pure virtual + virtual uint32_t GetConnectedVideoDisplay(const int32_t videoPortHandle, bool& isConnected) = 0; + virtual uint32_t GetDisplaySurroundMode(const int32_t videoPortHandle, VideoPortSurroundMode& surroundMode) = 0; + virtual uint32_t GetDisplayEDID(const int32_t videoPortHandle, uint8_t edidBytes[], const uint16_t edidBytesLength) = 0; + + // New Display Platform interface methods for the 5 required functions + virtual uint32_t GetDisplay(const int32_t type, const int32_t index, int32_t &handle) = 0; + virtual uint32_t GetDisplayAspectRatio(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsDisplay::DisplayVideoAspectRatio &aspectRatio) = 0; + virtual uint32_t GetDisplayEdid(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsDisplay::DisplayEDID &edId) = 0; + virtual uint32_t GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength) = 0; + virtual uint32_t SetAllmEnabled(const int32_t handle, const bool enabled) = 0; + virtual uint32_t SetAVIContentType(const int32_t handle, const int32_t contentType) = 0; + virtual uint32_t SetAVIScanInformation(const int32_t handle, const int32_t scanInfo) = 0; + + }; +} // namespace dDisplay +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dDisplayImpl.h b/plugin/hal/dDisplayImpl.h new file mode 100644 index 0000000..a7e3199 --- /dev/null +++ b/plugin/hal/dDisplayImpl.h @@ -0,0 +1,702 @@ +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "dDisplay.h" +#include "dsDisplay.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include +#include "DeviceSettingsTypes.h" + +#ifndef RDK_DSHAL_NAME +#warning "RDK_DSHAL_NAME is not defined" +#define RDK_DSHAL_NAME "RDK_DSHAL_NAME is not defined" +#endif + +#include +#include +#include +#include +#include + +static int display_isInitialized = 0; +static int display_isPlatInitialized = 0; +/* EDID caches — mirrors isEdidCached / isEdidBytesCached in dsDisplay.c. + * Populated on first successful HAL read; reset to false on + * dsDISPLAY_EVENT_DISCONNECTED (matching dsDisplay.c _dsDisplayEventCallback). */ +static bool isEdidCached = false; +static bool isEdidBytesCached = false; +static dsDisplayEDID_t s_edidStructCache; // cache for GetDisplayEdid +static unsigned char s_edidBytesCache[1024] = {0}; // cache for GetDisplayEdidBytes +static int s_edidBytesCacheLength = 0; +static pthread_mutex_t dsDisplayLock = PTHREAD_MUTEX_INITIALIZER; + +// Static global callback functions for Display events +static std::function g_DisplayRxSenseCallback; +static std::function g_DisplayHDCPStatusCallback; +static std::function g_DisplayHDMIHotPlugCallback; + +class dDisplayImpl : public hal::dDisplay::IPlatform { + + // delete copy constructor and assignment operator + dDisplayImpl(const dDisplayImpl&) = delete; + dDisplayImpl& operator=(const dDisplayImpl&) = delete; + +public: + dDisplayImpl() + { + LOGINFO("dDisplayImpl Constructor"); + getInstance() = this; // Set static instance for callback access + InitialiseHAL(); + } + + virtual ~dDisplayImpl() + { + LOGINFO("dDisplayImpl Destructor"); + DeInitialiseHAL(); + getInstance() = nullptr; // Clear static instance + } + + // Resolve method for dynamic library loading - following dHdmiInImpl.h pattern + static void* resolve(const std::string& libName, const std::string& symbolName) { + void* handle = dlopen(libName.c_str(), RTLD_LAZY); + if (!handle) { + LOGERR("dlopen failed for %s: %s", libName.c_str(), dlerror()); + return nullptr; + } + void* symbol = dlsym(handle, symbolName.c_str()); + if (!symbol) { + LOGERR("dlsym failed for %s: %s", symbolName.c_str(), dlerror()); + } + dlclose(handle); + return symbol; + } + + // Singleton getInstance method - following VideoPort pattern + static dDisplayImpl*& getInstance() + { + static dDisplayImpl* instance = nullptr; + return instance; + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + + if (!display_isPlatInitialized) { + LOGINFO("InitialiseHAL "); + dsError_t eError = dsDisplayInit(); + if (dsERR_NONE != eError) { + LOGERR("InitialiseHAL: dsDisplayInit failed with error: %d", eError); + return; + } + LOGINFO("InitialiseHAL: dsDisplayInit succeeded"); + + // Load persistence values after successful initialization + getPersistenceValue(); + + display_isPlatInitialized = 1; + LOGINFO("InitialiseHAL completed: display_isPlatInitialized=%d, display_isInitialized=%d", + display_isPlatInitialized, display_isInitialized); + } + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + if (display_isPlatInitialized) + { + dsDisplayTerm(); + display_isPlatInitialized = 0; + } + display_isInitialized = 0; + } + + void setAllCallbacks(const CallbackBundle& bundle) override + { + LOGINFO("dDisplayImpl setAllCallbacks"); + + if (!display_isInitialized) { + // Set the global callback function pointers + g_DisplayRxSenseCallback = bundle.OnDisplayRxSense; + g_DisplayHDCPStatusCallback = bundle.OnDisplayHDCPStatus; + g_DisplayHDMIHotPlugCallback = bundle.OnDisplayHDMIHotPlug; + + // Register HAL callbacks + registerDisplayEventCallbacks(); + + display_isInitialized = 1; + LOGINFO("dDisplayImpl setAllCallbacks: Display callbacks registered successfully"); + } else { + LOGINFO("dDisplayImpl setAllCallbacks: Display already initialized, skipping callback registration"); + } + } + + void getPersistenceValue() override + { + LOGINFO("dDisplayImpl getPersistenceValue - Display persistence loading"); + // Load any display-specific persistence values here + // This would be similar to VideoPort persistence loading but for Display settings + } + + // Implementation of Display Platform interface methods + uint32_t GetConnectedVideoDisplay(const int32_t videoPortHandle, bool& isConnected) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetConnectedVideoDisplay: videoPortHandle=%d", videoPortHandle); + + pthread_mutex_lock(&dsDisplayLock); + + // Use dynamic library loading for dsIsDisplayConnected + typedef dsError_t (*dsIsDisplayConnected_t)(intptr_t handle, bool *connected); + static dsIsDisplayConnected_t func = 0; + if (func == 0) { + void *dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + func = (dsIsDisplayConnected_t) dlsym(dllib, "dsIsDisplayConnected"); + dlclose(dllib); + } + } + + if (func != 0) { + dsError_t eError = func(static_cast(videoPortHandle), &isConnected); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetConnectedVideoDisplay: SUCCESS - isConnected=%s", isConnected ? "true" : "false"); + } else { + LOGERR("GetConnectedVideoDisplay: FAILED - dsIsDisplayConnected error=%d", eError); + } + } else { + LOGERR("GetConnectedVideoDisplay: FAILED - dsIsDisplayConnected not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t GetDisplaySurroundMode(const int32_t videoPortHandle, VideoPortSurroundMode& surroundMode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplaySurroundMode: videoPortHandle=%d", videoPortHandle); + + pthread_mutex_lock(&dsDisplayLock); + + // Use dynamic library loading for dsGetDisplaySurroundMode + typedef dsError_t (*dsGetDisplaySurroundMode_t)(intptr_t handle, int *surroundMode); + static dsGetDisplaySurroundMode_t func = 0; + if (func == 0) { + void *dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + func = (dsGetDisplaySurroundMode_t) dlsym(dllib, "dsGetDisplaySurroundMode"); + dlclose(dllib); + } + } + + if (func != 0) { + int dsSurroundMode = 0; + dsError_t eError = func(static_cast(videoPortHandle), &dsSurroundMode); + if (eError == dsERR_NONE) { + surroundMode = static_cast(dsSurroundMode); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplaySurroundMode: SUCCESS - surroundMode=%d", static_cast(surroundMode)); + } else { + LOGERR("GetDisplaySurroundMode: FAILED - dsGetDisplaySurroundMode error=%d", eError); + } + } else { + LOGERR("GetDisplaySurroundMode: FAILED - dsGetDisplaySurroundMode not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t GetDisplayEDID(const int32_t videoPortHandle, uint8_t edidBytes[], const uint16_t edidBytesLength) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplayEDID: videoPortHandle=%d, edidBytesLength=%d", videoPortHandle, edidBytesLength); + + if (!edidBytes || edidBytesLength <= 0) { + LOGERR("GetDisplayEDID: FAILED - Invalid parameters"); + return WPEFramework::Core::ERROR_BAD_REQUEST; + } + + pthread_mutex_lock(&dsDisplayLock); + + // Use dynamic library loading for dsGetEDIDBytes + typedef dsError_t (*dsGetEDIDBytes_t)(intptr_t handle, unsigned char *edid, int *length); + static dsGetEDIDBytes_t func = 0; + if (func == 0) { + void *dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + func = (dsGetEDIDBytes_t) dlsym(dllib, "dsGetEDIDBytes"); + dlclose(dllib); + } + } + + if (func != 0) { + int actualLength = edidBytesLength; + dsError_t eError = func(static_cast(videoPortHandle), edidBytes, &actualLength); + if (eError == dsERR_NONE && actualLength <= edidBytesLength) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplayEDID: SUCCESS - actualLength=%d", actualLength); + } else { + LOGERR("GetDisplayEDID: FAILED - dsGetEDIDBytes error=%d, actualLength=%d", eError, actualLength); + } + } else { + LOGERR("GetDisplayEDID: FAILED - dsGetEDIDBytes not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t GetDisplayEdidBytes(const int32_t handle, uint8_t edIdBytes[], const uint16_t edidLength) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplayEdidBytes: handle=%d, edidLength=%d", handle, edidLength); + + if (edIdBytes == nullptr || edidLength == 0) { + LOGERR("GetDisplayEdidBytes: FAILED - Invalid parameters"); + return retCode; + } + + /* Mirror dsDisplay.c _dsGetEDIDBytes: serve from cache if available + * (reset to false on dsDISPLAY_EVENT_DISCONNECTED). */ + if (isEdidBytesCached && s_edidBytesCacheLength > 0 && + s_edidBytesCacheLength <= static_cast(edidLength)) { + memcpy(edIdBytes, s_edidBytesCache, s_edidBytesCacheLength); + LOGINFO("GetDisplayEdidBytes: returning cached EDID bytes, length=%d", s_edidBytesCacheLength); + return WPEFramework::Core::ERROR_NONE; + } + + pthread_mutex_lock(&dsDisplayLock); + + // Use resolve method for dsGetEDIDBytes (matches dsDisplay.c pattern) + typedef dsError_t (*dsGetEDIDBytes_t)(intptr_t handle, uint8_t *edidBytes, int *actualLength); + static dsGetEDIDBytes_t func = 0; + if (func == 0) { + func = (dsGetEDIDBytes_t) resolve(RDK_DSHAL_NAME, "dsGetEDIDBytes"); + } + + if (func != 0) { + int actualLength = 0; + dsError_t eError = func(handle, edIdBytes, &actualLength); + if (eError == dsERR_NONE && actualLength > 0 && + actualLength <= static_cast(edidLength) && + actualLength <= static_cast(sizeof(s_edidBytesCache))) { + /* Populate cache — mirrors dsDisplay.c isEdidBytesCached = true */ + memcpy(s_edidBytesCache, edIdBytes, actualLength); + s_edidBytesCacheLength = actualLength; + isEdidBytesCached = true; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplayEdidBytes: SUCCESS - actualLength=%d (cached)", actualLength); + } else { + LOGERR("GetDisplayEdidBytes: FAILED - dsGetEDIDBytes error=%d, actualLength=%d", eError, actualLength); + } + } else { + LOGERR("GetDisplayEdidBytes: FAILED - dsGetEDIDBytes not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + // New Display HAL methods implementation + uint32_t GetDisplay(const int32_t type, const int32_t index, int32_t &handle) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplay: type=%d, index=%d", type, index); + + // Validate input parameters + if (type < 0 || index < 0) { + LOGERR("GetDisplay: FAILED - Invalid parameters, type=%d, index=%d", type, index); + return WPEFramework::Core::ERROR_BAD_REQUEST; + } + + // Initialize handle to safe value + handle = -1; + + // Add safety check for mutex lock + int lock_result = pthread_mutex_lock(&dsDisplayLock); + if (lock_result != 0) { + LOGERR("GetDisplay: FAILED - Could not acquire mutex lock, error=%d", lock_result); + return WPEFramework::Core::ERROR_GENERAL; + } + + // Use direct call for dsGetDisplay (matches dsDisplay.c _dsGetDisplay pattern) + intptr_t halHandle = 0; + LOGINFO("GetDisplay: Calling dsGetDisplay with type=%d, index=%d", type, index); + + dsError_t eError = dsGetDisplay(static_cast(type), index, &halHandle); + + if (eError == dsERR_NONE) { + handle = static_cast(halHandle); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplay: SUCCESS - handle=%d", handle); + } else { + if (eError == dsERR_OPERATION_NOT_SUPPORTED) + LOGWARN("GetDisplay: not supported for portType=%d (error=%d)", type, eError); + else + LOGERR("GetDisplay: FAILED - dsGetDisplay error=%d", eError); + handle = -1; + } + + int unlock_result = pthread_mutex_unlock(&dsDisplayLock); + if (unlock_result != 0) { + LOGERR("GetDisplay: WARNING - Could not release mutex lock, error=%d", unlock_result); + } + + return retCode; + } + + uint32_t GetDisplayAspectRatio(const int32_t handle, DisplayVideoAspectRatio &aspectRatio) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplayAspectRatio: handle=%d", handle); + + pthread_mutex_lock(&dsDisplayLock); + + // Use direct call for dsGetDisplayAspectRatio (matches dsDisplay.c _dsGetDisplayAspectRatio pattern) + dsVideoAspectRatio_t halAspectRatio; + dsError_t eError = dsGetDisplayAspectRatio(handle, &halAspectRatio); + if (eError == dsERR_NONE) { + // Convert DS HAL type to WPE Framework type + aspectRatio = (halAspectRatio == dsVIDEO_ASPECT_RATIO_4x3) ? + DisplayVideoAspectRatio::DS_DISPLAY_ASPECT_RATIO_4X3 : + DisplayVideoAspectRatio::DS_DISPLAY_ASPECT_RATIO_16X9; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplayAspectRatio: SUCCESS - aspectRatio=%d", static_cast(aspectRatio)); + } else { + LOGERR("GetDisplayAspectRatio: FAILED - dsGetDisplayAspectRatio error=%d", eError); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t GetDisplayEdid(const int32_t handle, WPEFramework::Exchange::IDeviceSettingsDisplay::DisplayEDID &edId) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetDisplayEdid: handle=%d", handle); + + /* Mirror dsDisplay.c _dsGetEDID: serve from cache when available. + * Cache is reset to false on dsDISPLAY_EVENT_DISCONNECTED. */ + if (isEdidCached) { + edId.productCode = s_edidStructCache.productCode; + edId.serialNumber = s_edidStructCache.serialNumber; + edId.manufactureYear = s_edidStructCache.manufactureYear; + edId.manufactureWeek = s_edidStructCache.manufactureWeek; + edId.hdmiDeviceType = s_edidStructCache.hdmiDeviceType; + edId.isRepeater = s_edidStructCache.isRepeater; + edId.physicalAddressA = s_edidStructCache.physicalAddressA; + edId.physicalAddressB = s_edidStructCache.physicalAddressB; + edId.physicalAddressC = s_edidStructCache.physicalAddressC; + edId.physicalAddressD = s_edidStructCache.physicalAddressD; + edId.numOfSupportedResolution = s_edidStructCache.numOfSupportedResolution; + edId.monitorName = std::string(s_edidStructCache.monitorName); + LOGINFO("GetDisplayEdid: returning cached EDID"); + return WPEFramework::Core::ERROR_NONE; + } + + pthread_mutex_lock(&dsDisplayLock); + + // Use direct call for dsGetEDID (matches dsDisplay.c _dsGetEDID pattern) + dsDisplayEDID_t halEdid; + memset(&halEdid, 0, sizeof(halEdid)); + dsError_t eError = dsGetEDID(handle, &halEdid); + if (eError == dsERR_NONE) { + /* Populate cache and dump EDID info — mirrors dsDisplay.c pattern */ + memcpy(&s_edidStructCache, &halEdid, sizeof(dsDisplayEDID_t)); + isEdidCached = true; + dumpEDIDInformation(&halEdid); + + // Convert DS HAL type to WPE Framework type + edId.productCode = halEdid.productCode; + edId.serialNumber = halEdid.serialNumber; + edId.manufactureYear = halEdid.manufactureYear; + edId.manufactureWeek = halEdid.manufactureWeek; + edId.hdmiDeviceType = halEdid.hdmiDeviceType; + edId.isRepeater = halEdid.isRepeater; + edId.physicalAddressA = halEdid.physicalAddressA; + edId.physicalAddressB = halEdid.physicalAddressB; + edId.physicalAddressC = halEdid.physicalAddressC; + edId.physicalAddressD = halEdid.physicalAddressD; + edId.numOfSupportedResolution = halEdid.numOfSupportedResolution; + edId.monitorName = std::string(halEdid.monitorName); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetDisplayEdid: SUCCESS (cached for next call)"); + } else { + LOGERR("GetDisplayEdid: FAILED - dsGetEDID error=%d", eError); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + /* Mirror dsDisplay.c dumpEDIDInformation — logs EDID product/serial/year/ + * week/monitorName/deviceType/repeater, matching the IARM server output. */ + static void dumpEDIDInformation(dsDisplayEDID_t *edid) + { + if (!edid) return; + LOGINFO("[DsMgr]dumpEDIDInformation values:%x,%x,%d,%d,%s,%s,%x", + edid->productCode, edid->serialNumber, + edid->manufactureYear, edid->manufactureWeek, + edid->monitorName, + edid->hdmiDeviceType ? "HDMI" : "DVI", + edid->isRepeater); + LOGINFO("[DsMgr]numOfSupportedResolution=%d", edid->numOfSupportedResolution); + } + + uint32_t SetAllmEnabled(const int32_t handle, const bool enabled) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetAllmEnabled: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + + pthread_mutex_lock(&dsDisplayLock); + + // Use resolve method for dsSetAllmEnabled (matches dsDisplay.c pattern) + typedef dsError_t (*dsSetAllmEnabled_t)(intptr_t handle, bool enabled); + typedef dsError_t (*dsGetAllmEnabled_t)(intptr_t handle, bool *enabled); + static dsSetAllmEnabled_t func_dsSetAllmEnabled = 0; + static dsGetAllmEnabled_t func_dsGetAllmEnabled = 0; + if (func_dsGetAllmEnabled == 0 && func_dsSetAllmEnabled == 0) { + func_dsGetAllmEnabled = (dsGetAllmEnabled_t) resolve(RDK_DSHAL_NAME, "dsGetAllmEnabled"); + func_dsSetAllmEnabled = (dsSetAllmEnabled_t) resolve(RDK_DSHAL_NAME, "dsSetAllmEnabled"); + } + + if (func_dsGetAllmEnabled != 0 && func_dsSetAllmEnabled != 0) { + bool currentALLMState = false; + dsError_t eError = func_dsGetAllmEnabled(handle, ¤tALLMState); + if (eError == dsERR_NONE) { + if (currentALLMState == enabled) { + LOGINFO("SetAllmEnabled: ALLM mode already %s", enabled ? "Enabled" : "Disabled"); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGINFO("SetAllmEnabled: Current ALLM state %s, Requested to %s", + currentALLMState ? "Enabled" : "Disabled", enabled ? "Enabled" : "Disabled"); + eError = func_dsSetAllmEnabled(handle, enabled); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetAllmEnabled: SUCCESS"); + } else { + LOGERR("SetAllmEnabled: FAILED - dsSetAllmEnabled error=%d", eError); + } + } + } else { + LOGERR("SetAllmEnabled: FAILED - dsGetAllmEnabled error=%d", eError); + } + } else { + LOGERR("SetAllmEnabled: FAILED - dsSetAllmEnabled/dsGetAllmEnabled not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t SetAVIContentType(const int32_t handle, const int32_t contentType) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetAVIContentType: handle=%d, contentType=%d", handle, contentType); + + pthread_mutex_lock(&dsDisplayLock); + + // Use resolve method for dsSetAVIContentType (matches dsDisplay.c pattern) + typedef dsError_t (*dsSetAVIContentType_t)(intptr_t handle, dsAviContentType_t contentType); + typedef dsError_t (*dsGetAVIContentType_t)(intptr_t handle, dsAviContentType_t* contentType); + static dsSetAVIContentType_t func_dsSetAVIContentType = 0; + static dsGetAVIContentType_t func_dsGetAVIContentType = 0; + if (func_dsGetAVIContentType == 0 && func_dsSetAVIContentType == 0) { + func_dsSetAVIContentType = (dsSetAVIContentType_t) resolve(RDK_DSHAL_NAME, "dsSetAVIContentType"); + func_dsGetAVIContentType = (dsGetAVIContentType_t) resolve(RDK_DSHAL_NAME, "dsGetAVIContentType"); + } + + if (func_dsGetAVIContentType != 0 && func_dsSetAVIContentType != 0) { + dsAviContentType_t currentContentType = dsAVICONTENT_TYPE_NOT_SIGNALLED; + dsError_t eError = func_dsGetAVIContentType(handle, ¤tContentType); + if (eError == dsERR_NONE) { + if (currentContentType == static_cast(contentType)) { + LOGINFO("SetAVIContentType: HDMI AVI content type already set to %d", contentType); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGINFO("SetAVIContentType: Current AVI content type %d, requested content type %d", + currentContentType, contentType); + eError = func_dsSetAVIContentType(handle, static_cast(contentType)); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetAVIContentType: SUCCESS"); + } else { + LOGERR("SetAVIContentType: FAILED - dsSetAVIContentType error=%d", eError); + } + } + } else { + LOGERR("SetAVIContentType: FAILED - dsGetAVIContentType error=%d", eError); + } + } else { + LOGERR("SetAVIContentType: FAILED - dsSetAVIContentType/dsGetAVIContentType not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + + uint32_t SetAVIScanInformation(const int32_t handle, const int32_t scanInfo) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetAVIScanInformation: handle=%d, scanInfo=%d", handle, scanInfo); + + pthread_mutex_lock(&dsDisplayLock); + + // Use resolve method for dsSetAVIScanInformation (matches dsDisplay.c pattern) + typedef dsError_t (*dsSetAVIScanInfo_t)(intptr_t handle, dsAVIScanInformation_t scanInfo); + typedef dsError_t (*dsGetAVIScanInfo_t)(intptr_t handle, dsAVIScanInformation_t* scanInfo); + static dsSetAVIScanInfo_t func_dsSetAVIScanInfo = 0; + static dsGetAVIScanInfo_t func_dsGetAVIScanInfo = 0; + if (func_dsGetAVIScanInfo == 0 && func_dsSetAVIScanInfo == 0) { + func_dsSetAVIScanInfo = (dsSetAVIScanInfo_t) resolve(RDK_DSHAL_NAME, "dsSetAVIScanInformation"); + func_dsGetAVIScanInfo = (dsGetAVIScanInfo_t) resolve(RDK_DSHAL_NAME, "dsGetAVIScanInformation"); + } + + if (func_dsGetAVIScanInfo != 0 && func_dsSetAVIScanInfo != 0) { + dsAVIScanInformation_t currentScanInfo = dsAVI_SCAN_TYPE_NO_DATA; + dsError_t eError = func_dsGetAVIScanInfo(handle, ¤tScanInfo); + if (eError == dsERR_NONE) { + if (currentScanInfo == static_cast(scanInfo)) { + LOGINFO("SetAVIScanInformation: HDMI AVI scan Info already set to %d", scanInfo); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGINFO("SetAVIScanInformation: Current AVI scan Info %d, requested scan Info %d", + currentScanInfo, scanInfo); + eError = func_dsSetAVIScanInfo(handle, static_cast(scanInfo)); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetAVIScanInformation: SUCCESS"); + } else { + LOGERR("SetAVIScanInformation: FAILED - dsSetAVIScanInformation error=%d", eError); + } + } + } else { + LOGERR("SetAVIScanInformation: FAILED - dsGetAVIScanInformation error=%d", eError); + } + } else { + LOGERR("SetAVIScanInformation: FAILED - dsSetAVIScanInformation/dsGetAVIScanInformation not available"); + } + + pthread_mutex_unlock(&dsDisplayLock); + return retCode; + } + +private: + void registerDisplayEventCallbacks() + { + LOGINFO("registerDisplayEventCallbacks"); + + // Use direct calls matching dsDisplay.c _dsDisplayInit pattern + intptr_t handle = 0; + dsError_t eReturn = dsGetDisplay(dsVIDEOPORT_TYPE_HDMI, 0, &handle); + if (dsERR_NONE != eReturn) { + LOGINFO("registerDisplayEventCallbacks: dsGetDisplay for HDMI failed, trying INTERNAL"); + eReturn = dsGetDisplay(dsVIDEOPORT_TYPE_INTERNAL, 0, &handle); + if (dsERR_NONE != eReturn) { + LOGERR("registerDisplayEventCallbacks: FAILED - dsGetDisplay for INTERNAL also failed, error=%d", eReturn); + return; + } + } + + // Register display event callback using wrapper that adapts int to intptr_t + dsError_t eError = dsRegisterDisplayEventCallback(handle, dsDisplayEventCallbackWrapper); + if (eError == dsERR_NONE) { + LOGINFO("registerDisplayEventCallbacks: SUCCESS - registered with handle=%d", static_cast(handle)); + } else { + LOGERR("registerDisplayEventCallbacks: FAILED - error=%d", eError); + } + } + + // Wrapper callback that adapts int handle to intptr_t (for legacy devicesettings API compatibility) + static void dsDisplayEventCallbackWrapper(int handle, dsDisplayEvent_t dsDisplayEvent, void* eventData) + { + // Cast int handle to intptr_t and call the implementation + dsDisplayEventCallbackImpl(static_cast(handle), dsDisplayEvent, eventData); + } + + // Static callback function to handle display events from HAL + static void dsDisplayEventCallbackImpl(intptr_t handle, dsDisplayEvent_t dsDisplayEvent, void* eventData) + { + LOGINFO("dsDisplayEventCallbackImpl: handle=%d, event=%d", static_cast(handle), static_cast(dsDisplayEvent)); + + dDisplayImpl* instance = getInstance(); + if (!instance) { + LOGERR("dsDisplayEventCallbackImpl: No Display instance available"); + return; + } + + uint8_t port = static_cast(handle & 0xFF); // Extract port from handle + + switch (dsDisplayEvent) { + case dsDISPLAY_RXSENSE_ON: // DS_DISPLAY_RXSENSE_ON equivalent + if (g_DisplayRxSenseCallback) { + g_DisplayRxSenseCallback(port, true); + } + break; + + case dsDISPLAY_RXSENSE_OFF: // DS_DISPLAY_RXSENSE_OFF equivalent + if (g_DisplayRxSenseCallback) { + g_DisplayRxSenseCallback(port, false); + } + break; + + case dsDISPLAY_HDCPPROTOCOL_CHANGE: // DS_DISPLAY_HDCPPROTOCOL_CHANGE equivalent + if (g_DisplayHDCPStatusCallback && eventData) { + bool isAuthenticated = *static_cast(eventData); + g_DisplayHDCPStatusCallback(port, isAuthenticated); + } + break; + + case dsDISPLAY_EVENT_CONNECTED: // DS_DISPLAY_EVENT_CONNECTED equivalent + if (g_DisplayHDMIHotPlugCallback) { + g_DisplayHDMIHotPlugCallback(port, true); + } + break; + + case dsDISPLAY_EVENT_DISCONNECTED: // DS_DISPLAY_EVENT_DISCONNECTED equivalent + /* Mirror dsDisplay.c _dsDisplayEventCallback: reset EDID caches + * so next GetDisplayEdid/GetDisplayEdidBytes re-reads from HAL. */ + isEdidCached = false; + isEdidBytesCached = false; + s_edidBytesCacheLength = 0; + LOGINFO("dsDisplayEventCallbackImpl: DISCONNECTED — EDID caches invalidated"); + if (g_DisplayHDMIHotPlugCallback) { + g_DisplayHDMIHotPlugCallback(port, false); + } + break; + + default: + LOGWARN("dsDisplayEventCallbackImpl: Unknown event=%d", static_cast(dsDisplayEvent)); + break; + } + } +}; \ No newline at end of file diff --git a/plugin/hal/dFPD.h b/plugin/hal/dFPD.h new file mode 100644 index 0000000..fdc6212 --- /dev/null +++ b/plugin/hal/dFPD.h @@ -0,0 +1,65 @@ +/* + * 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. + */ +#pragma once + +#include "dsHdmiIn.h" +#include "dsError.h" +#include "dsHdmiInTypes.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dFPD { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + + // FPD Platform interface methods - all pure virtual + virtual uint32_t SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) = 0; + virtual uint32_t SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) = 0; + virtual uint32_t SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) = 0; + virtual uint32_t SetFPDBrightness(const FPDIndicator indicator , const uint32_t brightNess , const bool persist ) = 0; + virtual uint32_t GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) = 0; + virtual uint32_t SetFPDState(const FPDIndicator indicator, const FPDState state) = 0; + virtual uint32_t GetFPDState(const FPDIndicator indicator, FPDState &state) = 0; + virtual uint32_t GetFPDColor(const FPDIndicator indicator, uint32_t &color) = 0; + virtual uint32_t SetFPDColor(const FPDIndicator indicator, const uint32_t color) = 0; + virtual uint32_t SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) = 0; + virtual uint32_t GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) = 0; + virtual uint32_t EnableFPDClockDisplay(const bool enable) = 0; + virtual uint32_t GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) = 0; + virtual uint32_t SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) = 0; + virtual uint32_t SetFPDMode(const FPDMode fpdMode) = 0; + + }; +} // namespace dFPD +} // namespace hal + diff --git a/plugin/hal/dFPDImpl.h b/plugin/hal/dFPDImpl.h new file mode 100644 index 0000000..811de80 --- /dev/null +++ b/plugin/hal/dFPDImpl.h @@ -0,0 +1,470 @@ +// Out-of-line virtual destructor definition for RTTI/typeinfo +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include "dFPD.h" +#include "dsHdmiIn.h" +#include "dsError.h" +#include "dsHdmiInTypes.h" +#include "dsUtl.h" +#include "dsTypes.h" +#include "dsFPD.h" +#include "dsFPDTypes.h" + +#include +#include "DeviceSettingsTypes.h" + +static int fpd_isInitialized = 0; +static int fpd_isPlatInitialized = 0; +static std::mutex fpd_initMutex; + +/** Structure that defines internal data base for the FP */ +typedef struct _dsFPDSettings_t_ +{ + dsFPDBrightness_t brightness; + dsFPDState_t state; + dsFPDColor_t color; +}_FPDSettings_t; + +static _FPDSettings_t srvFPDSettings[dsFPD_INDICATOR_MAX]; + +#ifndef dsFPD_BRIGHTNESS_DEFAULT +#define dsFPD_BRIGHTNESS_DEFAULT dsFPD_BRIGHTNESS_MAX +#endif + +static dsFPDBrightness_t _dsPowerBrightness = dsFPD_BRIGHTNESS_MAX; +static dsFPDBrightness_t _dsTextBrightness = dsFPD_BRIGHTNESS_MAX; +static dsFPDColor_t _dsPowerLedColor = dsFPD_COLOR_BLUE; + +class dFPDImpl : public hal::dFPD::IPlatform { + + // delete copy constructor and assignment operator + dFPDImpl(const dFPDImpl&) = delete; + dFPDImpl& operator=(const dFPDImpl&) = delete; + +public: + dFPDImpl() + { + LOGINFO("dFPDImpl Constructor"); + InitialiseHAL(); + } + + virtual ~dFPDImpl() + { + LOGINFO("dFPDImpl Destructor"); + DeInitialiseHAL(); + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + if (!fpd_isInitialized) { + for (int i = dsFPD_INDICATOR_MESSAGE; i < dsFPD_INDICATOR_MAX; i++) + { + srvFPDSettings[i].brightness = dsFPD_BRIGHTNESS_MAX; + srvFPDSettings[i].state = dsFPD_STATE_OFF; + srvFPDSettings[i].color = dsFPD_COLOR_BLUE; + } + + fpd_isInitialized = 1; + + } + // HAL dsFPInit() is deferred to first use via EnsurePlatInit() + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + std::lock_guard lock(fpd_initMutex); + if (fpd_isPlatInitialized) + { + dsFPTerm(); + fpd_isPlatInitialized = 0; + } + fpd_isInitialized = 0; + } + + // Mirrors FrontPanelConfig::getInstance(): retry dsFPInit() up to 20 times on first HAL use. + bool EnsurePlatInit() + { + std::lock_guard lock(fpd_initMutex); + if (fpd_isPlatInitialized) + return true; + + dsError_t errorCode = dsERR_NONE; + unsigned int retryCount = 1; + do { + errorCode = dsFPInit(); + if (dsERR_NONE == errorCode) { + fpd_isPlatInitialized = 1; + LOGINFO("EnsurePlatInit: dsFPInit succeeded"); + } else { + LOGERR("EnsurePlatInit: dsFPInit failed with error[%d]. Retrying... (%d/20)", errorCode, retryCount); + usleep(50000); + } + } while ((!fpd_isPlatInitialized) && (retryCount++ < 20)); + + if (!fpd_isPlatInitialized) { + LOGERR("EnsurePlatInit: dsFPInit failed after 20 retries"); + return false; + } + + try { + int maxBrightness = dsFPD_BRIGHTNESS_DEFAULT; + std::string value; + + try { + value = device::HostPersistence::getInstance().getProperty("Power.brightness"); + } catch (...) { + value = std::to_string(maxBrightness); + device::HostPersistence::getInstance().persistHostProperty("Power.brightness", value); + } + _dsPowerBrightness = static_cast(atoi(value.c_str())); + + try { + value = device::HostPersistence::getInstance().getProperty("Text.brightness"); + } catch (...) { + value = std::to_string(maxBrightness); + device::HostPersistence::getInstance().persistHostProperty("Text.brightness", value); + } + _dsTextBrightness = static_cast(atoi(value.c_str())); + +#if (dsFPD_BRIGHTNESS_DEFAULT != dsFPD_BRIGHTNESS_MAX) + if (_dsPowerBrightness == dsFPD_BRIGHTNESS_MAX) + _dsPowerBrightness = dsFPD_BRIGHTNESS_DEFAULT; + if (_dsTextBrightness == dsFPD_BRIGHTNESS_MAX) + _dsTextBrightness = dsFPD_BRIGHTNESS_DEFAULT; +#endif + + std::string colorStr; + try { + colorStr = device::HostPersistence::getInstance().getProperty("Power.Color"); + } catch (...) { + colorStr = "BLUE"; + } + if (colorStr == "GREEN") _dsPowerLedColor = dsFPD_COLOR_GREEN; + else if (colorStr == "RED") _dsPowerLedColor = dsFPD_COLOR_RED; + else if (colorStr == "YELLOW") _dsPowerLedColor = dsFPD_COLOR_YELLOW; + else if (colorStr == "ORANGE") _dsPowerLedColor = dsFPD_COLOR_ORANGE; + else _dsPowerLedColor = dsFPD_COLOR_BLUE; + + LOGINFO("EnsurePlatInit: Power.brightness=%d Text.brightness=%d Power.Color=%s", + _dsPowerBrightness, _dsTextBrightness, colorStr.c_str()); + } catch (...) { + LOGERR("EnsurePlatInit: Error reading FPD persistence, using defaults"); + } + return true; + } + + // Implementation of all FPD Platform interface methods + uint32_t SetFPDTime(const FPDTimeFormat timeFormat, const uint32_t minutes, const uint32_t seconds) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("SetFPDTime is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t SetFPDScroll(const uint32_t scrollHoldDuration, const uint32_t nHorizontalScrollIterations, const uint32_t nVerticalScrollIterations) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("SetFPDScroll is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t SetFPDBlink(const FPDIndicator indicator, const uint32_t blinkDuration, const uint32_t blinkIterations) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("SetFPDBlink is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t SetFPDBrightness(const FPDIndicator indicator, const uint32_t brightNess, const bool persist) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetFPDBrightness: indicator %d, brightNess %d, persist %d", static_cast(indicator), brightNess, persist); + if (!EnsurePlatInit()) { + LOGERR("SetFPDBrightness: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX && brightNess <= dsFPD_BRIGHTNESS_MAX) { + dsError_t eError = dsSetFPBrightness(static_cast(indicator), static_cast(brightNess)); + LOGINFO("SetFPDBrightness: dsSetFPBrightness returned %d", eError); + if (eError == dsERR_NONE) { + srvFPDSettings[static_cast(indicator)].brightness = brightNess; + + // Update global power brightness when POWER indicator is set + if (static_cast(indicator) == dsFPD_INDICATOR_POWER) { + LOGINFO("SetFPDBrightness: Power Brightness From App is %d", brightNess); + if (persist) { + _dsPowerBrightness = brightNess; + /* Mirror dsFPD.c _dsSetFPBrightness: persist Power.brightness */ + try { + device::HostPersistence::getInstance().persistHostProperty( + "Power.brightness", std::to_string(_dsPowerBrightness)); + LOGINFO("SetFPDBrightness: Persisted Power.brightness=%d", _dsPowerBrightness); + } catch (...) { + LOGERR("SetFPDBrightness: Error persisting Power.brightness"); + } + } + } + + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("SetFPDBrightness: dsSetFPBrightness failed with error %d", eError); + } + } else { + LOGERR("SetFPDBrightness: Invalid parameters - indicator %d, brightness %d", static_cast(indicator), brightNess); + } + return retCode; + } + + uint32_t GetFPDBrightness(const FPDIndicator indicator, uint32_t &brightNess) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetFPDBrightness: indicator %d", static_cast(indicator)); + if (!EnsurePlatInit()) { + LOGERR("GetFPDBrightness: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX) { + dsFPDBrightness_t halBrightness = 0; + dsGetFPBrightness(static_cast(indicator), &halBrightness); + + brightNess = static_cast(_dsPowerBrightness); + LOGINFO("GetFPDBrightness: indicator %d brightness %d (hal=%d _dsPowerBrightness=%d)", + static_cast(indicator), brightNess, + static_cast(halBrightness), static_cast(_dsPowerBrightness)); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("GetFPDBrightness: Invalid indicator %d", static_cast(indicator)); + } + return retCode; + } + + uint32_t SetFPDState(const FPDIndicator indicator, const FPDState state) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetFPDState: indicator %d, state %d", static_cast(indicator), static_cast(state)); + if (!EnsurePlatInit()) { + LOGERR("SetFPDState: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX) { + dsError_t eError = dsERR_NONE; + + // Match RPC layer approach - use dsSetFPBrightness based on state + if (state == FPDState::DS_FPD_STATE_ON) { + // Power LED Indicator Brightness is the Global LED brightness for all indicators + eError = dsSetFPBrightness(static_cast(indicator), _dsPowerBrightness); + if (static_cast(indicator) == dsFPD_INDICATOR_POWER) { + LOGINFO("SetFPDState: Setting Power LED to ON with Brightness %d", _dsPowerBrightness); + } + } else if (state == FPDState::DS_FPD_STATE_OFF) { + eError = dsSetFPBrightness(static_cast(indicator), 0); + if (static_cast(indicator) == dsFPD_INDICATOR_POWER) { + LOGINFO("SetFPDState: Setting Power LED to OFF with Brightness 0"); + } + } + + LOGINFO("SetFPDState: dsSetFPBrightness returned %d", eError); + if (eError == dsERR_NONE) { + srvFPDSettings[static_cast(indicator)].state = static_cast(state); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("SetFPDState: dsSetFPBrightness failed with error %d", eError); + } + } else { + LOGERR("SetFPDState: Invalid indicator %d", static_cast(indicator)); + } + return retCode; + } + + uint32_t GetFPDState(const FPDIndicator indicator, FPDState &state) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetFPDState: indicator %d", static_cast(indicator)); + if (!EnsurePlatInit()) { + LOGERR("GetFPDState: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX) { + // Match RPC layer approach - read from internal cache instead of hardware call + state = static_cast(srvFPDSettings[static_cast(indicator)].state); + LOGINFO("GetFPDState: indicator %d state %d (from cache)", static_cast(indicator), static_cast(state)); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("GetFPDState: Invalid indicator %d", static_cast(indicator)); + } + return retCode; + } + + uint32_t GetFPDColor(const FPDIndicator indicator, uint32_t &color) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetFPDColor: indicator %d", static_cast(indicator)); + if (!EnsurePlatInit()) { + LOGERR("GetFPDColor: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX) { + dsFPDColor_t halColor = 0; + dsError_t eError = dsGetFPColor(static_cast(indicator), &halColor); + LOGINFO("GetFPDColor: dsGetFPColor returned %d", eError); + if (eError == dsERR_NONE) { + color = static_cast(halColor); + srvFPDSettings[static_cast(indicator)].color = halColor; + LOGINFO("GetFPDColor: indicator %d color %d", static_cast(indicator), color); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("GetFPDColor: dsGetFPColor failed with error %d", eError); + // Fallback to cached value + color = srvFPDSettings[static_cast(indicator)].color; + LOGINFO("GetFPDColor: indicator %d color %d (cached)", static_cast(indicator), color); + retCode = WPEFramework::Core::ERROR_NONE; + } + } else { + LOGERR("GetFPDColor: Invalid indicator %d", static_cast(indicator)); + } + return retCode; + } + + uint32_t SetFPDColor(const FPDIndicator indicator, const uint32_t color) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetFPDColor: indicator %d, color %d", static_cast(indicator), color); + if (!EnsurePlatInit()) { + LOGERR("SetFPDColor: FPD HAL not initialised"); + return retCode; + } + + if (static_cast(indicator) < dsFPD_INDICATOR_MAX && dsFPDColor_isValid(color)) { + dsError_t eError = dsSetFPColor(static_cast(indicator), static_cast(color)); + LOGINFO("SetFPDColor: dsSetFPColor returned %d", eError); + if (eError == dsERR_NONE) { + /* Mask to 24-bit RGB — mirrors _dsSetFPColor in dsFPD.c */ + uint32_t maskedColor = color & 0x00FFFFFF; + srvFPDSettings[static_cast(indicator)].color = static_cast(maskedColor); + + /* Persist Power.Color for POWER indicator + * Mirrors dsFPD.c _dsSetFPColor + enumToColor helper. */ + if (static_cast(indicator) == dsFPD_INDICATOR_POWER) { + _dsPowerLedColor = static_cast(maskedColor); + try { + const char* colorStr = "BLUE"; + switch (_dsPowerLedColor) { + case dsFPD_COLOR_GREEN: colorStr = "GREEN"; break; + case dsFPD_COLOR_RED: colorStr = "RED"; break; + case dsFPD_COLOR_YELLOW: colorStr = "YELLOW"; break; + case dsFPD_COLOR_ORANGE: colorStr = "RED"; break; // dsFPD.c enumToColor maps ORANGE→RED + default: break; + } + device::HostPersistence::getInstance().persistHostProperty("Power.Color", colorStr); + LOGINFO("SetFPDColor: Persisted Power.Color=%s", colorStr); + } catch (...) { + LOGERR("SetFPDColor: Error persisting Power.Color"); + } + } + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("SetFPDColor: dsSetFPColor failed with error %d", eError); + } + } else { + LOGERR("SetFPDColor: Invalid parameters - indicator %d, color 0x%x", static_cast(indicator), color); + } + return retCode; + } + + uint32_t SetFPDTextBrightness(const FPDTextDisplay textDisplay, const uint32_t brightNess) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("SetFPDTextBrightness is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t GetFPDTextBrightness(const FPDTextDisplay textDisplay, uint32_t &brightNess) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("GetFPDTextBrightness is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t EnableFPDClockDisplay(const bool enable) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("EnableFPDClockDisplay is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t GetFPDTimeFormat(FPDTimeFormat &fpdTimeFormat) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("GetFPDTimeFormat is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t SetFPDTimeFormat(const FPDTimeFormat fpdTimeFormat) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + + LOGERR("SetFPDTimeFormat is DEPRECATED and not IMPLEMENTED"); + + return retCode; + } + + uint32_t SetFPDMode(const FPDMode fpdMode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetFPDMode: fpdMode %d", static_cast(fpdMode)); + + dsError_t eError = dsSetFPDMode(static_cast(fpdMode)); + LOGINFO("SetFPDMode: dsSetFPDMode returned %d", eError); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("SetFPDMode: dsSetFPDMode failed with error %d", eError); + } + return retCode; + } + + private: +}; diff --git a/plugin/hal/dHdmiIn.h b/plugin/hal/dHdmiIn.h new file mode 100644 index 0000000..0603e92 --- /dev/null +++ b/plugin/hal/dHdmiIn.h @@ -0,0 +1,79 @@ +/* + * 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. + */ +#pragma once + +#include "dsHdmiIn.h" +#include "dsError.h" +#include "dsHdmiInTypes.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dHdmiIn { + + class IPlatform { + + public: + virtual ~IPlatform(); + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle bundle) = 0; + virtual void getPersistenceValue() = 0; + //virtual void deinit(); + + static void DS_OnHDMIInHotPlugEvent(const dsHdmiInPort_t port, const bool isConnected); + static void DS_OnHDMIInSignalStatusEvent(const dsHdmiInPort_t port, const dsHdmiInSignalStatus_t signalStatus); + static void DS_OnHDMIInStatusEvent(const dsHdmiInStatus_t status); + static void DS_OnHDMIInVideoModeUpdateEvent(const dsHdmiInPort_t port, const dsVideoPortResolution_t videoPortResolution); + static void DS_OnHDMIInAllmStatusEvent(const dsHdmiInPort_t port, const bool allmStatus); + static void DS_OnHDMIInAVIContentTypeEvent(const dsHdmiInPort_t port, const dsAviContentType_t aviContentType); + static void DS_OnHDMIInAVLatencyEvent(const int32_t audioDelay, const int32_t videoDelay); + static void DS_OnHDMIInVRRStatusEvent(const dsHdmiInPort_t port, const dsVRRType_t vrrType); + + virtual uint32_t GetHDMIInNumberOfInputs(int32_t &count) = 0; + virtual uint32_t GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) = 0; + virtual uint32_t SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) = 0; + virtual uint32_t ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) = 0; + virtual uint32_t SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) = 0; + virtual uint32_t GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) = 0; + virtual uint32_t GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) = 0; + virtual uint32_t GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) = 0; + virtual uint32_t GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) = 0; + virtual uint32_t SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) = 0; + virtual uint32_t GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) = 0; + virtual uint32_t GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) = 0; + virtual uint32_t GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) = 0; + virtual uint32_t SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) = 0; + virtual uint32_t GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) = 0; + virtual uint32_t GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) = 0; + virtual uint32_t SetVRRSupport(const HDMIInPort port, const bool vrrSupport) = 0; + virtual uint32_t GetVRRSupport(const HDMIInPort port, bool &vrrSupport) = 0; + virtual uint32_t GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) = 0; + }; +} // namespace power +} // namespace hal + diff --git a/plugin/hal/dHdmiInImpl.h b/plugin/hal/dHdmiInImpl.h new file mode 100644 index 0000000..f51bbe1 --- /dev/null +++ b/plugin/hal/dHdmiInImpl.h @@ -0,0 +1,1268 @@ +// Out-of-line virtual destructor definition for RTTI/typeinfo +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dHdmiIn.h" +#include "dsHdmiIn.h" +#include "dsError.h" +#include "dsHdmiInTypes.h" +#include "dsVideoDeviceTypes.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include +#include "DeviceSettingsTypes.h" + +static int m_hdmiInInitialized = 0; +static int m_hdmiInPlatInitialized = 0; +static bool isDalsEnabled = 0; +static dsHdmiInCap_t hdmiInCap_gs; +static bool m_edidallmsupport[dsHDMI_IN_PORT_MAX]; +static bool m_vrrsupport[dsHDMI_IN_PORT_MAX]; +static bool m_hdmiPortVrrCaps[dsHDMI_IN_PORT_MAX]; + +static tv_hdmi_edid_version_t m_edidversion[dsHDMI_IN_PORT_MAX]; + +static std::function g_HdmiInHotPlugCallback; +static std::function g_HdmiInSignalStatusCallback; +static std::function g_HdmiInVideoModeUpdateCallback; +static std::function g_HdmiInAllmStatusCallback; +static std::function g_HdmiInAviContentTypeCallback; +static std::function g_HdmiInAVLatencyCallback; +static std::function g_HdmiInVRRStatusCallback; +static std::function g_HdmiInStatusCallback; + +class dHdmiInImpl : public hal::dHdmiIn::IPlatform { + + // delete copy constructor and assignment operator + dHdmiInImpl(const dHdmiInImpl&) = delete; + dHdmiInImpl& operator=(const dHdmiInImpl&) = delete; + +public: + dHdmiInImpl() + { + LOGINFO("dHdmiInImpl Constructor"); + InitialiseHAL(); + } + + virtual ~dHdmiInImpl() + { + LOGERR("dHdmiInImpl Destructor"); + DeInitialiseHAL(); + } + + void InitialiseHAL() + { + getDynamicAutoLatencyConfig(); + + profileType = searchRdkProfile(); + LOGINFO("profileType %d", profileType); + + if (TV == profileType) + { + if (!m_hdmiInPlatInitialized) + { + dsError_t eError = dsHdmiInInit(); + if (eError != dsERR_NONE) { + LOGERR("dsHdmiInInit failed: %d", eError); + } else { + LOGINFO("dsHdmiInInit succeeded: %d", eError); + } + } + m_hdmiInPlatInitialized++; + } + } + + void DeInitialiseHAL() + { + // profileType is already initialized in DeviceSettingsImplementation.cpp + LOGINFO("profileType %d", profileType); + getDynamicAutoLatencyConfig(); + + if (TV == profileType) + { + if (m_hdmiInPlatInitialized) + { + m_hdmiInPlatInitialized--; + if (!m_hdmiInPlatInitialized) + { + dsHdmiInTerm(); + } + m_hdmiInPlatInitialized = 0; + } + } + } + + static void* resolve(const std::string& libName, const std::string& symbolName) { + return WPEFramework::Plugin::DeviceSettingsHALLoader::ResolveSymbol(libName, symbolName); + } + + bool getHdmiInPortPersistValue(const std::string& propertyName, int portIndex) { + try { + // Use HostPersistence from DeviceSettingsTypes.h with default value support + std::string value = device::HostPersistence::getInstance().getProperty(propertyName, "TRUE"); + bool support = (value == "TRUE"); + LOGINFO("Port property %s: Value: %s, Parsed: %d", propertyName.c_str(), value.c_str(), support); + return support; + } catch(...) { + LOGERR("Port property %s: Exception in getting property from persistence storage, using default TRUE", propertyName.c_str()); + return true; + } + } + + static dsError_t getVRRSupport (dsHdmiInPort_t iHdmiPort, bool *vrrSupport) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsHdmiInGetVRRSupport_t)(dsHdmiInPort_t iHdmiPort, bool *vrrSupport); + static dsHdmiInGetVRRSupport_t dsHdmiInGetVRRSupportFunc = 0; + + if (dsHdmiInGetVRRSupportFunc == 0) { + dsHdmiInGetVRRSupportFunc = (dsHdmiInGetVRRSupport_t)resolve(RDK_DSHAL_NAME, "dsHdmiInGetVRRSupport"); + if(dsHdmiInGetVRRSupportFunc == 0) { + LOGWARN("dsHdmiInGetVRRSupport is not defined"); + } + else { + LOGINFO("dsHdmiInGetVRRSupport loaded"); + } + } + if (0 != dsHdmiInGetVRRSupportFunc) { + eRet = dsHdmiInGetVRRSupportFunc (iHdmiPort, vrrSupport); + LOGINFO("dsHdmiInGetVRRSupportFunc eRet: %d", eRet); + } + else { + LOGINFO("dsHdmiInGetVRRSupportFunc = %p", dsHdmiInGetVRRSupportFunc); + } + return eRet; + } + + static dsError_t setVRRSupport (dsHdmiInPort_t iHdmiPort, bool vrrSupport) { + dsError_t eRet = dsERR_GENERAL; + if (!m_hdmiPortVrrCaps[iHdmiPort]) { + return dsERR_OPERATION_NOT_SUPPORTED; + } + typedef dsError_t (*dsHdmiInSetVRRSupport_t)(dsHdmiInPort_t iHdmiPort, bool vrrSupport); + static dsHdmiInSetVRRSupport_t dsHdmiInSetVRRSupportFunc = 0; + + if (dsHdmiInSetVRRSupportFunc == 0) { + dsHdmiInSetVRRSupportFunc = (dsHdmiInSetVRRSupport_t)resolve(RDK_DSHAL_NAME, "dsHdmiInSetVRRSupport"); + if(dsHdmiInSetVRRSupportFunc == 0) { + LOGERR("dsHdmiInSetVRRSupport is not defined"); + } + else { + LOGINFO("dsHdmiInSetVRRSupport loaded"); + } + } + LOGINFO("setVRRSupport to ds-hal: EDID VRR Bit: %d", vrrSupport); + if (0 != dsHdmiInSetVRRSupportFunc) { + eRet = dsHdmiInSetVRRSupportFunc (iHdmiPort, vrrSupport); + LOGINFO("[srv] %s: dsHdmiInSetVRRSupportFunc eRet: %d", __FUNCTION__, eRet); + } + else { + LOGINFO("%s: dsHdmiInSetVRRSupportFunc = %p\n", __FUNCTION__, dsHdmiInSetVRRSupportFunc); + } + LOGINFO("setVRRSupport to ds-hal: EDID VRR Bit: %d\n", vrrSupport); + if (0 != dsHdmiInSetVRRSupportFunc) { + eRet = dsHdmiInSetVRRSupportFunc (iHdmiPort, vrrSupport); + LOGINFO("dsHdmiInSetVRRSupportFunc eRet: %d", eRet); + } + else { + LOGINFO("dsHdmiInSetVRRSupportFunc = %p", dsHdmiInSetVRRSupportFunc); + } + return eRet; + } + + static dsError_t setEdid2AllmSupport (dsHdmiInPort_t iHdmiPort, bool allmSupport) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsSetEdid2AllmSupport_t)(dsHdmiInPort_t iHdmiPort, bool allmSupport); + static dsSetEdid2AllmSupport_t dsSetEdid2AllmSupportFunc = 0; + + if (dsSetEdid2AllmSupportFunc == 0) { + dsSetEdid2AllmSupportFunc = (dsSetEdid2AllmSupport_t)resolve(RDK_DSHAL_NAME, "dsSetEdid2AllmSupport"); + if(dsSetEdid2AllmSupportFunc == 0) { + LOGERR("dsSetEdid2AllmSupport is not defined"); + } + else { + LOGINFO("dsSetEdid2AllmSupport loaded"); + } + } + LOGINFO("setEdid2AllmSupport to ds-hal: EDID Allm Bit: %d", allmSupport); + if (0 != dsSetEdid2AllmSupportFunc) { + eRet = dsSetEdid2AllmSupportFunc (iHdmiPort, allmSupport); + LOGINFO("dsSetEdid2AllmSupportFunc eRet: %d", eRet); + } + else { + LOGINFO("dsSetEdid2AllmSupportFunc = %p", dsSetEdid2AllmSupportFunc); + } + return eRet; + } + + static dsError_t isHdmiARCPort (int iPort, bool* isArcEnabled) { + dsError_t eRet = dsERR_GENERAL; + + typedef bool (*dsIsHdmiARCPort_t)(int iPortArg, bool *boolArg); + static dsIsHdmiARCPort_t dsIsHdmiARCPortFunc = 0; + if (dsIsHdmiARCPortFunc == 0) { + dsIsHdmiARCPortFunc = (dsIsHdmiARCPort_t)resolve(RDK_DSHAL_NAME, "dsIsHdmiARCPort"); + if(dsIsHdmiARCPortFunc == 0) { + LOGERR("dsIsHdmiARCPort is not defined"); + eRet = dsERR_GENERAL; + } + else { + LOGINFO("dsIsHdmiARCPort loaded"); + } + } + if (0 != dsIsHdmiARCPortFunc) { + dsIsHdmiARCPortFunc (iPort, isArcEnabled); + LOGINFO("dsIsHdmiARCPort port %d isArcEnabled:%d", iPort, *isArcEnabled); + } + else { + LOGINFO("dsIsHdmiARCPort dsIsHdmiARCPortFunc = %p", dsIsHdmiARCPortFunc); + } + return eRet; + } + + static dsError_t setEdidVersion (dsHdmiInPort_t iHdmiPort, tv_hdmi_edid_version_t iEdidVersion) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsSetEdidVersion_t)(dsHdmiInPort_t iHdmiPort, tv_hdmi_edid_version_t iEdidVersion); + static dsSetEdidVersion_t dsSetEdidVersionFunc = 0; + char edidVer[2]; + sprintf(edidVer,"%d",iEdidVersion); + + if (dsSetEdidVersionFunc == 0) { + dsSetEdidVersionFunc = (dsSetEdidVersion_t)resolve(RDK_DSHAL_NAME, "dsSetEdidVersion"); + if(dsSetEdidVersionFunc == 0) { + LOGERR("dsSetEdidVersion is not defined"); + } + else { + LOGINFO("dsSetEdidVersion loaded"); + } + } + + if (0 != dsSetEdidVersionFunc) { + eRet = dsSetEdidVersionFunc (iHdmiPort, iEdidVersion); + if (eRet == dsERR_NONE) { + switch (iHdmiPort) { + case dsHDMI_IN_PORT_0: + device::HostPersistence::getInstance().persistHostProperty("HDMI0.edidversion", edidVer); + LOGINFO("Port %s: Persist EDID Version: %d", "HDMI0", iEdidVersion); + break; + case dsHDMI_IN_PORT_1: + device::HostPersistence::getInstance().persistHostProperty("HDMI1.edidversion", edidVer); + LOGINFO("Port %s: Persist EDID Version: %d", "HDMI1", iEdidVersion); + break; + case dsHDMI_IN_PORT_2: + device::HostPersistence::getInstance().persistHostProperty("HDMI2.edidversion", edidVer); + LOGINFO("Port %s: Persist EDID Version: %d", "HDMI2", iEdidVersion); + break; + case dsHDMI_IN_PORT_3: + device::HostPersistence::getInstance().persistHostProperty("HDMI3.edidversion", edidVer); + LOGINFO("Port %s: Persist EDID Version: %d", "HDMI3", iEdidVersion); + break; + case dsHDMI_IN_PORT_NONE: + case dsHDMI_IN_PORT_4: + case dsHDMI_IN_PORT_MAX: + break; + } + // Whenever there is a change in edid version to 2.0, ensure the edid allm support and edid vrr support is updated with latest value + if(iEdidVersion == HDMI_EDID_VER_20) + { + LOGINFO("As the version is changed to 2.0, we are updating the allm bit and the vrr bit in edid"); + setEdid2AllmSupport(iHdmiPort,m_edidallmsupport[iHdmiPort]); + setVRRSupport(iHdmiPort,m_vrrsupport[iHdmiPort]); + } + } + LOGINFO("dsSetEdidVersionFunc eRet: %d", eRet); + } + else { + LOGINFO("dsSetEdidVersionFunc = %p", dsSetEdidVersionFunc); + } + return eRet; + } + + static dsError_t getEdidVersion (dsHdmiInPort_t iHdmiPort, int *iEdidVersion) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetEdidVersion_t)(dsHdmiInPort_t iHdmiPort, tv_hdmi_edid_version_t *iEdidVersion); + static dsGetEdidVersion_t dsGetEdidVersionFunc = 0; + if (dsGetEdidVersionFunc == 0) { + dsGetEdidVersionFunc = (dsGetEdidVersion_t)resolve(RDK_DSHAL_NAME, "dsGetEdidVersion"); + if(dsGetEdidVersionFunc == 0) { + LOGERR("dsGetEdidVersion is not defined"); + } + else { + LOGINFO("dsGetEdidVersion loaded"); + } + } + if (0 != dsGetEdidVersionFunc) { + tv_hdmi_edid_version_t EdidVersion; + eRet = dsGetEdidVersionFunc (iHdmiPort, &EdidVersion); + int EdidVer = static_cast(EdidVersion); + *iEdidVersion = EdidVer; + LOGINFO("dsGetEdidVersionFunc eRet: %d", eRet); + } + else { + LOGINFO("%s: dsGetEdidVersionFunc = %p", __FUNCTION__, dsGetEdidVersionFunc); + } + return eRet; + } + + static dsError_t getAllmStatus (dsHdmiInPort_t iHdmiPort, bool *allmStatus) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetAllmStatus_t)(dsHdmiInPort_t iHdmiPort, bool *allmStatus); + static dsGetAllmStatus_t dsGetAllmStatusFunc = 0; + if (dsGetAllmStatusFunc == 0) { + dsGetAllmStatusFunc = (dsGetAllmStatus_t)resolve(RDK_DSHAL_NAME, "dsGetAllmStatus"); + if(dsGetAllmStatusFunc == 0) { + LOGERR("dsGetAllmStatus is not defined"); + } + else { + LOGINFO("dsGetAllmStatus loaded"); + } + } + if (0 != dsGetAllmStatusFunc) { + eRet = dsGetAllmStatusFunc (iHdmiPort, allmStatus); + LOGINFO("dsGetAllmStatusFunc eRet: %d", eRet); + } + else { + LOGINFO("dsGetAllmStatusFunc = %p", dsGetAllmStatusFunc); + } + return eRet; + } + + static dsError_t getSupportedGameFeaturesList (dsSupportedGameFeatureList_t *fList) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetSupportedGameFeaturesList_t)(dsSupportedGameFeatureList_t *fList); + static dsGetSupportedGameFeaturesList_t dsGetSupportedGameFeaturesListFunc = 0; + if (dsGetSupportedGameFeaturesListFunc == 0) { + dsGetSupportedGameFeaturesListFunc = (dsGetSupportedGameFeaturesList_t)resolve(RDK_DSHAL_NAME, "dsGetSupportedGameFeaturesList"); + if(dsGetSupportedGameFeaturesListFunc == 0) { + LOGERR("dsGetSupportedGameFeaturesList is not defined"); + } + else { + LOGINFO("dsGetSupportedGameFeaturesList loaded"); + } + } + if (0 != dsGetSupportedGameFeaturesListFunc) { + eRet = dsGetSupportedGameFeaturesListFunc (fList); + LOGINFO("dsGetSupportedGameFeaturesListFunc eRet: %d", eRet); + } + else { + LOGINFO("dsGetSupportedGameFeaturesListFunc = %p", dsGetSupportedGameFeaturesListFunc); + } + return eRet; + } + + static dsError_t getAVLatency_hal (int *audio_latency, int *video_latency) + { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetAVLatency_t)(int *audio_latency, int *video_latency); + static dsGetAVLatency_t dsGetAVLatencyFunc = 0; + if (dsGetAVLatencyFunc == 0) { + dsGetAVLatencyFunc = (dsGetAVLatency_t)resolve(RDK_DSHAL_NAME, "dsGetAVLatency"); + if(dsGetAVLatencyFunc == 0) { + LOGERR("dsGetAVLatency is not defined"); + } + else { + LOGINFO("dsGetAVLatency loaded"); + } + } + if (0 != dsGetAVLatencyFunc) { + eRet = dsGetAVLatencyFunc (audio_latency, video_latency); + LOGINFO("dsGetAVLatencyFunc eRet: %d", eRet); + } + else { + LOGINFO("dsGetAVLatencyFunc = %p", dsGetAVLatencyFunc); + } + return eRet; + } + + static dsError_t getHdmiVersion (dsHdmiInPort_t iHdmiPort, dsHdmiMaxCapabilityVersion_t *capversion) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetHdmiVersion_t)(dsHdmiInPort_t iHdmiPort, dsHdmiMaxCapabilityVersion_t *capversion); + static dsGetHdmiVersion_t dsGetHdmiVersionFunc = 0; + if (dsGetHdmiVersionFunc == 0) { + dsGetHdmiVersionFunc = (dsGetHdmiVersion_t)resolve(RDK_DSHAL_NAME, "dsGetHdmiVersion"); + if(dsGetHdmiVersionFunc == 0) { + LOGERR("dsGetHdmiVersion is not defined"); + eRet = dsERR_GENERAL; + } + else { + LOGINFO("dsGetHdmiVersion loaded"); + } + } + if (0 != dsGetHdmiVersionFunc) { + eRet = dsGetHdmiVersionFunc (iHdmiPort, capversion); + LOGINFO("dsGetHdmiVersionFunc eRet: %d", eRet); + } + return eRet; + } + + void setAllCallbacks(const CallbackBundle bundle) override + { + ENTRY_LOG; + LOGINFO("setAllCallbacks: profileType %d", profileType); + if (!m_hdmiInInitialized && m_hdmiInPlatInitialized) { + LOGINFO("HdmiIn platform callback Initialization"); + if (TV == profileType) + { + LOGINFO("setAllCallbacks: its TV Profile"); + if (bundle.OnHDMIInHotPlugEvent) { + LOGINFO("HDMI In Hot Plug Event Callback Registered"); + g_HdmiInHotPlugCallback = bundle.OnHDMIInHotPlugEvent; + dsHdmiInRegisterConnectCB(DS_OnHDMIInHotPlugEvent); + } + + typedef dsError_t (*dsHdmiInRegisterSignalChangeCB_t)(dsHdmiInSignalChangeCB_t CBFunc); + static dsHdmiInRegisterSignalChangeCB_t signalChangeCBFunc = 0; + if (bundle.OnHDMIInSignalStatusEvent) { + LOGINFO("HDMI In Signal Status Event Callback Registered"); + g_HdmiInSignalStatusCallback = bundle.OnHDMIInSignalStatusEvent; + if (!signalChangeCBFunc) { + signalChangeCBFunc = (dsHdmiInRegisterSignalChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterSignalChangeCB"); + } + if (signalChangeCBFunc) { + signalChangeCBFunc(DS_OnHDMIInSignalStatusEvent); + } else { + LOGERR("Failed to resolve dsHdmiInRegisterSignalChangeCB"); + } + } + + typedef dsError_t (*dsHdmiInRegisterStatusChangeCB_t)(dsHdmiInStatusChangeCB_t CBFunc); + static dsHdmiInRegisterStatusChangeCB_t StatusCBFunc = 0; + if (bundle.OnHDMIInStatusEvent) { + LOGINFO("HDMI In Status Event Callback Registered"); + g_HdmiInStatusCallback = bundle.OnHDMIInStatusEvent; + if (!StatusCBFunc) { + StatusCBFunc = (dsHdmiInRegisterStatusChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterStatusChangeCB"); + } + if (StatusCBFunc) { + StatusCBFunc(DS_OnHDMIInStatusEvent); + } else { + LOGERR("Failed to resolve dsHdmiInRegisterStatusChangeCB"); + } + } + + typedef dsError_t (*dsHdmiInRegisterVideoModeUpdateCB_t)(dsHdmiInVideoModeUpdateCB_t CBFunc); + static dsHdmiInRegisterVideoModeUpdateCB_t videoModeUpdateCBFunc = 0; + if (bundle.OnHDMIInVideoModeUpdateEvent) { + LOGINFO("HDMI In Video Mode Update Event Callback Registered"); + g_HdmiInVideoModeUpdateCallback = bundle.OnHDMIInVideoModeUpdateEvent; + if (!videoModeUpdateCBFunc) { + videoModeUpdateCBFunc = (dsHdmiInRegisterVideoModeUpdateCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterVideoModeUpdateCB"); + } + if (videoModeUpdateCBFunc) { + videoModeUpdateCBFunc(DS_OnHDMIInVideoModeUpdateEvent); + } else { + LOGERR("Failed to resolve dsHdmiInRegisterVideoModeUpdateCB"); + } + } + + typedef dsError_t (*dsHdmiInRegisterAllmChangeCB_t)(dsHdmiInAllmChangeCB_t CBFunc); + static dsHdmiInRegisterAllmChangeCB_t allmChangeCBFunc = 0; + if (bundle.OnHDMIInAllmStatusEvent) { + LOGINFO("HDMI In ALLM Status Event Callback Registered"); + g_HdmiInAllmStatusCallback = bundle.OnHDMIInAllmStatusEvent; + if (!allmChangeCBFunc) { + allmChangeCBFunc = (dsHdmiInRegisterAllmChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterAllmChangeCB"); + } + if (allmChangeCBFunc) { + allmChangeCBFunc(DS_OnHDMIInAllmStatusEvent); + } else { + LOGERR("Failed to resolve dsHdmiInRegisterALLMChangeCB"); + } + } + + typedef dsError_t (*dsHdmiInRegisterVRRChangeCB_t)(dsHdmiInVRRChangeCB_t CBFunc); + static dsHdmiInRegisterVRRChangeCB_t vrrChangeCBFunc = 0; + if (bundle.OnHDMIInVRRStatusEvent) { + LOGINFO("HDMI In VRR Status Event Callback Registered"); + g_HdmiInVRRStatusCallback = bundle.OnHDMIInVRRStatusEvent; + if (!vrrChangeCBFunc) { + vrrChangeCBFunc = (dsHdmiInRegisterVRRChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterVRRChangeCB"); + } + if (vrrChangeCBFunc) { + vrrChangeCBFunc(DS_OnHDMIInVRRStatusEvent); + } else { + LOGWARN("dsHdmiInRegisterVRRChangeCB not supported on this platform"); + } + } + + typedef dsError_t (*dsHdmiInRegisterAviContentTypeChangeCB_t)(dsHdmiInAviContentTypeChangeCB_t CBFunc); + static dsHdmiInRegisterAviContentTypeChangeCB_t AviContentTypeChangeCBFunc = 0; + if (bundle.OnHDMIInAVIContentTypeEvent) { + LOGINFO("HDMI In AVI Content Type Event Callback Registered"); + g_HdmiInAviContentTypeCallback = bundle.OnHDMIInAVIContentTypeEvent; + if (!AviContentTypeChangeCBFunc) { + AviContentTypeChangeCBFunc = (dsHdmiInRegisterAviContentTypeChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterAviContentTypeChangeCB"); + } + if (AviContentTypeChangeCBFunc) { + AviContentTypeChangeCBFunc(DS_OnHDMIInAVIContentTypeEvent); + } else { + LOGERR("Failed to resolve dsHdmiInRegisterAviContentTypeChangeCB"); + } + } + + typedef dsError_t (*dsHdmiInRegisterAVLatencyChangeCB_t)(dsAVLatencyChangeCB_t CBFunc); + static dsHdmiInRegisterAVLatencyChangeCB_t AVLatencyChangeCBFunc = 0; + if (bundle.OnHDMIInAVLatencyEvent) { + LOGINFO("HDMI In AV Latency Event Callback Registered"); + g_HdmiInAVLatencyCallback = bundle.OnHDMIInAVLatencyEvent; + if (!AVLatencyChangeCBFunc) { + AVLatencyChangeCBFunc = (dsHdmiInRegisterAVLatencyChangeCB_t)resolve(RDK_DSHAL_NAME, "dsHdmiInRegisterAVLatencyChangeCB"); + } + if (AVLatencyChangeCBFunc && isDalsEnabled) { + AVLatencyChangeCBFunc(DS_OnHDMIInAVLatencyEvent); + } else { + LOGWARN("dsHdmiInRegisterAVLatencyChangeCB not supported or DALS disabled"); + } + } + } + } + EXIT_LOG; + } + + void getPersistenceValue() override + { + if (!m_hdmiInInitialized && m_hdmiInPlatInitialized) { + int itr = 0; + bool isARCCapable = false; + for (itr = 0; itr < dsHDMI_IN_PORT_MAX; itr++) { + isARCCapable = false; + isHdmiARCPort (itr, &isARCCapable); + hdmiInCap_gs.isPortArcCapable[itr] = isARCCapable; + } + + std::string _EdidAllmSupport("TRUE"); + m_edidallmsupport[dsHDMI_IN_PORT_0] = getHdmiInPortPersistValue("HDMI0.edidallmEnable", dsHDMI_IN_PORT_0); + m_edidallmsupport[dsHDMI_IN_PORT_1] = getHdmiInPortPersistValue("HDMI1.edidallmEnable", dsHDMI_IN_PORT_1); + m_edidallmsupport[dsHDMI_IN_PORT_2] = getHdmiInPortPersistValue("HDMI2.edidallmEnable", dsHDMI_IN_PORT_2); + m_edidallmsupport[dsHDMI_IN_PORT_3] = getHdmiInPortPersistValue("HDMI3.edidallmEnable", dsHDMI_IN_PORT_3); + + std::string _VRRSupport("TRUE"); + m_vrrsupport[dsHDMI_IN_PORT_0] = getHdmiInPortPersistValue("HDMI0.vrrEnable", dsHDMI_IN_PORT_0); + m_vrrsupport[dsHDMI_IN_PORT_1] = getHdmiInPortPersistValue("HDMI1.vrrEnable", dsHDMI_IN_PORT_1); + m_vrrsupport[dsHDMI_IN_PORT_2] = getHdmiInPortPersistValue("HDMI2.vrrEnable", dsHDMI_IN_PORT_2); + m_vrrsupport[dsHDMI_IN_PORT_3] = getHdmiInPortPersistValue("HDMI3.vrrEnable", dsHDMI_IN_PORT_3); + + std::string _EdidVersion("1"); + try { + _EdidVersion = device::HostPersistence::getInstance().getProperty("HDMI0.edidversion"); + m_edidversion[dsHDMI_IN_PORT_0] = static_cast(atoi (_EdidVersion.c_str())); + } catch(...) { + try { + LOGERR("Port %s: Exception in Getting the HDMI0 EDID version from persistence storage. Try system default...", "HDMI0"); + _EdidVersion = device::HostPersistence::getInstance().getDefaultProperty("HDMI0.edidversion"); + m_edidversion[dsHDMI_IN_PORT_0] = static_cast(atoi (_EdidVersion.c_str())); + } + catch(...) { + LOGERR("Port %s: Exception in Getting the HDMI0 EDID version from system default.....", "HDMI0"); + m_edidversion[dsHDMI_IN_PORT_0] = HDMI_EDID_VER_20; + } + } + + try { + _EdidVersion = device::HostPersistence::getInstance().getProperty("HDMI1.edidversion"); + m_edidversion[dsHDMI_IN_PORT_1] = static_cast(atoi (_EdidVersion.c_str())); + } catch(...) { + try { + LOGERR("Port %s: Exception in Getting the HDMI1 EDID version from persistence storage. Try system default...", "HDMI1"); + _EdidVersion = device::HostPersistence::getInstance().getDefaultProperty("HDMI1.edidversion"); + m_edidversion[dsHDMI_IN_PORT_1] = static_cast(atoi (_EdidVersion.c_str())); + } + catch(...) { + LOGERR("Port %s: Exception in Getting the HDMI1 EDID version from system default.....", "HDMI1"); + m_edidversion[dsHDMI_IN_PORT_1] = HDMI_EDID_VER_20; + } + } + + try { + _EdidVersion = device::HostPersistence::getInstance().getProperty("HDMI2.edidversion"); + m_edidversion[dsHDMI_IN_PORT_2] = static_cast(atoi (_EdidVersion.c_str())); + } catch(...) { + try { + LOGERR("Port %s: Exception in Getting the HDMI2 EDID version from persistence storage. Try system default...", "HDMI2"); + _EdidVersion = device::HostPersistence::getInstance().getDefaultProperty("HDMI2.edidversion"); + m_edidversion[dsHDMI_IN_PORT_2] = static_cast(atoi (_EdidVersion.c_str())); + } + catch(...) { + LOGERR("Port %s: Exception in Getting the HDMI2 EDID version from system default.....", "HDMI2"); + m_edidversion[dsHDMI_IN_PORT_2] = HDMI_EDID_VER_20; + } + } + + for (itr = 0; itr < dsHDMI_IN_PORT_MAX; itr++) { + if (getVRRSupport(static_cast(itr), &m_hdmiPortVrrCaps[itr]) >= 0) { + LOGINFO("Port HDMI%d: VRR capability : %d", itr, m_hdmiPortVrrCaps[itr]); + } + } + for (itr = 0; itr < dsHDMI_IN_PORT_MAX; itr++) { + if (setEdidVersion (static_cast(itr), m_edidversion[itr]) >= 0) { + LOGINFO("Port HDMI%d: Initialized EDID Version : %d", itr, m_edidversion[itr]); + } + } + m_hdmiInInitialized = 1; + } + + LOGINFO("Set Callbacks"); + } + + #if 0 + profile_t searchRdkProfile(void) { + LOGINFO("Entering searchRdkProfile"); + const char* devPropPath = "/etc/device.properties"; + char line[256], *rdkProfile = NULL; + profile_t ret = PROFILE_INVALID; + FILE* file; + + file = fopen(devPropPath, "r"); + if (file == NULL) { + LOGINFO("searchRdkProfile: device.properties file not found."); + return PROFILE_INVALID; + } + + while (fgets(line, sizeof(line), file)) { + rdkProfile = strstr(line, RDK_PROFILE); + if (rdkProfile != NULL) { + rdkProfile = strchr(line, '='); + LOGINFO("searchRdkProfile: Found RDK_PROFILE"); + break; + } + } + if(rdkProfile != NULL) + { + rdkProfile++; // Move past the '=' character + if(0 == strncmp(rdkProfile, PROFILE_STR_TV, strlen(PROFILE_STR_TV))) { + ret = PROFILE_TV; + } else if (0 == strncmp(rdkProfile, PROFILE_STR_STB, strlen(PROFILE_STR_STB))) { + ret = PROFILE_STB; + } + } + else + { + LOGINFO("searchRdkProfile: NOT FOUND RDK_PROFILE in device properties file"); + ret = PROFILE_INVALID; + } + + fclose(file); + LOGINFO("Exit searchRdkProfile: RDK_PROFILE = %d", ret); + return ret; + } + #endif + + void getDynamicAutoLatencyConfig() + { + RFC_ParamData_t param = {0}; + WDMP_STATUS status = getRFCParameter((char*)"dssrv", TVSETTINGS_DALS_RFC_PARAM, ¶m); + LOGINFO("DALS Feature Enable = [ %s ]", param.value); + if(WDMP_SUCCESS == status && (strncasecmp(param.value,"true",4) == 0)) { + isDalsEnabled = true; + LOGINFO("Value of isDalsEnabled = [ %d ]", isDalsEnabled); + } + else { + LOGERR("Fetching RFC for DALS failed or DALS is disabled: %d", status); + } + } + + // Missing functions from dsHdmiIn.c + void updateEdidAllmBitValuesInPersistence(dsHdmiInPort_t iHdmiPort, bool allmSupport) + { + LOGINFO("Updating values of edid allm bit in persistence"); + switch(iHdmiPort){ + case dsHDMI_IN_PORT_0: + device::HostPersistence::getInstance().persistHostProperty("HDMI0.edidallmEnable", allmSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID Allm Bit: %d", "HDMI0", allmSupport); + break; + case dsHDMI_IN_PORT_1: + device::HostPersistence::getInstance().persistHostProperty("HDMI1.edidallmEnable", allmSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID Allm Bit: %d", "HDMI1", allmSupport); + break; + case dsHDMI_IN_PORT_2: + device::HostPersistence::getInstance().persistHostProperty("HDMI2.edidallmEnable", allmSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID Allm Bit: %d", "HDMI2", allmSupport); + break; + case dsHDMI_IN_PORT_3: + device::HostPersistence::getInstance().persistHostProperty("HDMI3.edidallmEnable", allmSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID Allm Bit: %d", "HDMI3", allmSupport); + break; + default: + LOGWARN("Invalid HDMI port %d for ALLM persistence update", iHdmiPort); + break; + } + } + + void updateVRRBitValuesInPersistence(dsHdmiInPort_t iHdmiPort, bool vrrSupport) + { + LOGINFO("Updating values of vrr bit in persistence"); + switch(iHdmiPort){ + case dsHDMI_IN_PORT_0: + device::HostPersistence::getInstance().persistHostProperty("HDMI0.vrrEnable", vrrSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID VRR Bit: %d", "HDMI0", vrrSupport); + break; + case dsHDMI_IN_PORT_1: + device::HostPersistence::getInstance().persistHostProperty("HDMI1.vrrEnable", vrrSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID VRR Bit: %d", "HDMI1", vrrSupport); + break; + case dsHDMI_IN_PORT_2: + device::HostPersistence::getInstance().persistHostProperty("HDMI2.vrrEnable", vrrSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID VRR Bit: %d", "HDMI2", vrrSupport); + break; + case dsHDMI_IN_PORT_3: + device::HostPersistence::getInstance().persistHostProperty("HDMI3.vrrEnable", vrrSupport ? "TRUE" : "FALSE"); + LOGINFO("Port %s: Persist EDID VRR Bit: %d", "HDMI3", vrrSupport); + break; + default: + LOGWARN("Invalid HDMI port %d for VRR persistence update", iHdmiPort); + break; + } + } + + static void DS_OnHDMIInHotPlugEvent(const dsHdmiInPort_t port, const bool isConnected) + { + LOGINFO("DS_OnHDMIInHotPlugEvent event Received: port=%d, isConnected=%s", port, isConnected ? "true" : "false"); + if (g_HdmiInHotPlugCallback) { + g_HdmiInHotPlugCallback(static_cast(port), isConnected); + } + } + + static void DS_OnHDMIInSignalStatusEvent(const dsHdmiInPort_t port, const dsHdmiInSignalStatus_t signalStatus) + { + LOGINFO("DS_OnHDMIInSignalStatusEvent event Received: port=%d, signalStatus=%d", port, signalStatus); + if (g_HdmiInSignalStatusCallback) { + g_HdmiInSignalStatusCallback(static_cast(port), static_cast(signalStatus)); + } + } + + static void DS_OnHDMIInStatusEvent(const dsHdmiInStatus_t status) + { + LOGINFO("DS_OnHDMIInStatusEvent event Received: Port=%d, isPresented=%s", status.activePort, status.isPresented ? "true" : "false"); + + if (g_HdmiInStatusCallback) { + g_HdmiInStatusCallback(static_cast(status.activePort), status.isPresented); + } + } + + static void DS_OnHDMIInVideoModeUpdateEvent(const dsHdmiInPort_t port, const dsVideoPortResolution_t videoPortResolution) + { + LOGINFO("DS_OnHDMIInVideoModeUpdateEvent event Received: port=%d", port); // adjust as needed + LOGINFO("Video Mode: %s pixelResolution %d aspectRatio %d stereoScopicMode %d frameRate %d", videoPortResolution.name, videoPortResolution.pixelResolution, videoPortResolution.aspectRatio, videoPortResolution.stereoScopicMode, videoPortResolution.frameRate); + + if (g_HdmiInVideoModeUpdateCallback) { + HDMIVideoPortResolution res; + res.name = std::string(videoPortResolution.name); + res.pixelResolution = static_cast(videoPortResolution.pixelResolution); + res.aspectRatio = static_cast(videoPortResolution.aspectRatio); + res.stereoScopicMode = static_cast(videoPortResolution.stereoScopicMode); + res.frameRate = static_cast(videoPortResolution.frameRate); + res.interlaced = videoPortResolution.interlaced; + g_HdmiInVideoModeUpdateCallback(static_cast(port), res); + } + } + + static void DS_OnHDMIInAllmStatusEvent(const dsHdmiInPort_t port, const bool allmStatus) + { + LOGINFO("DS_OnHDMIInAllmStatusEvent event Received: port=%d, allmStatus=%s", port, allmStatus ? "true" : "false"); + if (g_HdmiInAllmStatusCallback) { + g_HdmiInAllmStatusCallback(static_cast(port), allmStatus); + } + } + + static void DS_OnHDMIInAVIContentTypeEvent(const dsHdmiInPort_t port, const dsAviContentType_t aviContentType) + { + LOGINFO("DS_OnHDMIInAVIContentTypeEvent event Received: port=%d, aviContentType=%d", port, aviContentType); + if (g_HdmiInAviContentTypeCallback) { + g_HdmiInAviContentTypeCallback(static_cast(port), static_cast(aviContentType)); + } + } + + static void DS_OnHDMIInAVLatencyEvent(const int32_t audioDelay, const int32_t videoDelay) + { + LOGINFO("DS_OnHDMIInAVLatencyEvent event Received: audioDelay=%d, videoDelay=%d", audioDelay, videoDelay); + if (g_HdmiInAVLatencyCallback) { + g_HdmiInAVLatencyCallback(audioDelay, videoDelay); + } + } + + static void DS_OnHDMIInVRRStatusEvent(const dsHdmiInPort_t port, const dsVRRType_t vrrType) + { + LOGINFO("DS_OnHDMIInVRRStatusEvent event Received: port=%d, vrrType=%d", port, vrrType); + if (g_HdmiInVRRStatusCallback) { + g_HdmiInVRRStatusCallback(static_cast(port), static_cast(vrrType)); + } + } + + virtual uint32_t GetHDMIInNumberOfInputs(int32_t &count) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + uint8_t NumberofInputs = 0; + + if (dsHdmiInGetNumberOfInputs(&NumberofInputs) == dsERR_NONE) { + count = static_cast(NumberofInputs); + retCode = WPEFramework::Core::ERROR_NONE; + } + LOGINFO("GetHDMIInNumberOfInputs: count=%d, retCode=%d", count, retCode); + return retCode; + } + + uint32_t GetHDMIInStatus(HDMIInStatus &hdmiStatus, IHDMIInPortConnectionStatusIterator*& portConnectionStatus) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInStatus_t status; + if (dsHdmiInGetStatus(&status) == dsERR_NONE) { + hdmiStatus.activePort = static_cast(status.activePort); + hdmiStatus.isPresented = status.isPresented; + LOGINFO("GetHDMIInStatus: activePort=%d, isPresented=%s", status.activePort, status.isPresented ? "true" : "false"); + + /* Build per-port connection status iterator from dsHdmiInStatus_t.isPortConnected[]. */ + std::vector portStatuses; + for (int p = 0; p < dsHDMI_IN_PORT_MAX; p++) { + DeviceSettingsHDMIIn::HDMIPortConnectionStatus ps; + ps.isPortConnected = status.isPortConnected[p]; + portStatuses.push_back(ps); + LOGINFO("GetHDMIInStatus: port[%d] isPortConnected=%s", p, ps.isPortConnected ? "true" : "false"); + } + portConnectionStatus = WPEFramework::Core::Service>::Create(portStatuses); + + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t GetHDMIInAVLatency(uint32_t &videoLatency, uint32_t &audioLatency) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + int vLatency = 0; + int aLatency = 0; + if (getAVLatency_hal(&aLatency, &vLatency) == dsERR_NONE) { + audioLatency = static_cast(aLatency); + videoLatency = static_cast(vLatency); + LOGINFO("GetHDMIInAVLatency: audioLatency=%d, videoLatency=%d", audioLatency, videoLatency); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t GetHDMIInAllmStatus(const HDMIInPort port, bool &allmStatus) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + bool status = false; + if (getAllmStatus(hdmiPort, &status) == dsERR_NONE) { + allmStatus = status; + LOGINFO("GetHDMIInAllmStatus: port=%d, allmStatus=%s", hdmiPort, allmStatus ? "true" : "false"); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t GetHDMIInEdid2AllmSupport(const HDMIInPort port, bool &allmSupport) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + if (hdmiPort < dsHDMI_IN_PORT_MAX) { + allmSupport = m_edidallmsupport[hdmiPort]; + LOGINFO("GetHDMIInEdid2AllmSupport: port=%d, allmSupport=%s", hdmiPort, allmSupport ? "true" : "false"); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t SetHDMIInEdid2AllmSupport(const HDMIInPort port, bool allmSupport) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + if (hdmiPort < dsHDMI_IN_PORT_MAX) { + LOGINFO("In SetHDMIInEdid2AllmSupport, checking m_edidversion of port %d : %d", hdmiPort, m_edidversion[hdmiPort]); + if(m_edidversion[hdmiPort] == HDMI_EDID_VER_20) { // if the edidver is 2.0, then only set the allm bit in edid + if (setEdid2AllmSupport(hdmiPort, allmSupport) == dsERR_NONE) { + updateEdidAllmBitValuesInPersistence(hdmiPort, allmSupport); + m_edidallmsupport[hdmiPort] = allmSupport; + LOGINFO("SetHDMIInEdid2AllmSupport: port=%d, allmSupport=%s", hdmiPort, allmSupport ? "true" : "false"); + retCode = WPEFramework::Core::ERROR_NONE; + } + } else { + LOGINFO("EDID version is not 2.0, cannot set ALLM support for port %d", hdmiPort); + retCode = WPEFramework::Core::ERROR_UNAVAILABLE; + } + } + return retCode; + } + + uint32_t GetSupportedGameFeaturesList(IHDMIInGameFeatureListIterator *& gameFeatureList) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsSupportedGameFeatureList_t fList; + + // Initialize the structure + memset(&fList, 0, sizeof(fList)); + + dsError_t dsResult = getSupportedGameFeaturesList(&fList); + LOGINFO("GetSupportedGameFeaturesList: dsGetSupportedGameFeaturesList returned: %d", dsResult); + + if (dsResult == dsERR_NONE) { + LOGINFO("GetSupportedGameFeaturesList: Raw HAL data - gameFeatureList='%s', count=%d", + fList.gameFeatureList, fList.gameFeatureCount); + + try { + // Parse the comma-separated game features string + std::vector features; + + if (strlen(fList.gameFeatureList) > 0) { + std::string featureStr(fList.gameFeatureList); + std::stringstream ss(featureStr); + std::string feature; + + // Split by comma and create feature entries + while (std::getline(ss, feature, ',')) { + // Remove quotes and whitespace + feature.erase(std::remove(feature.begin(), feature.end(), '"'), feature.end()); + feature.erase(std::remove(feature.begin(), feature.end(), ' '), feature.end()); + + if (!feature.empty()) { + DeviceSettingsHDMIIn::HDMIInGameFeatureList gameFeature; + gameFeature.gameFeature = feature; + features.push_back(gameFeature); + LOGINFO("GetSupportedGameFeaturesList: Added feature: '%s'", feature.c_str()); + } + } + } + + LOGINFO("GetSupportedGameFeaturesList: Parsed %zu features from HAL data", features.size()); + + // Create iterator using the GameFeatureListIteratorImpl type already defined in dHdmiIn.h + // This uses WPEFramework's standard iterator pattern with explicit interface template parameter + //gameFeatureList = GameFeatureListIteratorImpl::Create(features); + + if (gameFeatureList != nullptr) { + LOGINFO("GetSupportedGameFeaturesList: Successfully created iterator with %zu features", features.size()); + retCode = WPEFramework::Core::ERROR_NONE; + + // Log all parsed features for debugging + LOGINFO("GetSupportedGameFeaturesList: Feature summary:"); + for (size_t i = 0; i < features.size(); i++) { + LOGINFO(" Feature[%zu]: '%s'", i, features[i].gameFeature.c_str()); + } + } else { + // Empty feature list or RPC iterator allocation failure — treat as no features + LOGWARN("GetSupportedGameFeaturesList: iterator creation failed (features=%zu), returning empty list", features.size()); + retCode = WPEFramework::Core::ERROR_NONE; + gameFeatureList = nullptr; + } + } catch (const std::exception& e) { + LOGERR("GetSupportedGameFeaturesList: Exception while parsing features: %s", e.what()); + gameFeatureList = nullptr; + retCode = WPEFramework::Core::ERROR_GENERAL; + } + } else { + LOGERR("GetSupportedGameFeaturesList: dsGetSupportedGameFeaturesList failed with error: %d", dsResult); + gameFeatureList = nullptr; + } + + return retCode; + } + + uint32_t SelectHDMIInPort(const HDMIInPort port, const bool requestAudioMix, const bool topMostPlane, const HDMIVideoPlaneType videoPlaneType) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + dsVideoPlaneType_t videoType = static_cast(videoPlaneType); + if (dsHdmiInSelectPort(hdmiPort, requestAudioMix, videoType, topMostPlane) == dsERR_NONE) { + LOGINFO("SelectHDMIInPort: port=%d, requestAudioMix=%s, topMostPlane=%s, videoPlaneType=%d", hdmiPort, requestAudioMix ? "true" : "false", topMostPlane ? "true" : "false", videoPlaneType); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t ScaleHDMIInVideo(const HDMIInVideoRectangle videoPosition) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + if (dsHdmiInScaleVideo(videoPosition.x, videoPosition.y, videoPosition.width, videoPosition.height) == dsERR_NONE) { + LOGINFO("Successfully set the video position x=%d, y=%d, width=%d, height=%d", + videoPosition.x, videoPosition.y, videoPosition.width, videoPosition.height); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t SelectHDMIZoomMode(const HDMIInVideoZoom zoomMode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsVideoZoom_t zoom = static_cast(zoomMode); + if ((retCode = dsHdmiInSelectZoomMode(zoom)) == dsERR_NONE) { + LOGINFO("Successfully set the zoom mode: %d", zoom); + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGINFO("Failed to select zoom %d and return errorcode %d", zoom, retCode); + } + return retCode; + } + + static dsError_t getEDIDBytesInfo (dsHdmiInPort_t iHdmiPort, unsigned char *edid, int *length) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetEDIDBytesInfo_t)(dsHdmiInPort_t iHdmiPort, unsigned char *edid, int *length); + static dsGetEDIDBytesInfo_t dsGetEDIDBytesInfoFunc = 0; + if (dsGetEDIDBytesInfoFunc == 0) { + dsGetEDIDBytesInfoFunc = (dsGetEDIDBytesInfo_t)resolve(RDK_DSHAL_NAME, "dsGetEDIDBytesInfo"); + if(dsGetEDIDBytesInfoFunc == 0) { + LOGERR("dsGetEDIDBytesInfo is not defined"); + eRet = dsERR_GENERAL; + } else { + LOGINFO("dsGetEDIDBytesInfo loaded"); + } + } + if (0 != dsGetEDIDBytesInfoFunc) { + LOGINFO("Entering dsGetEDIDBytesInfoFunc"); + eRet = dsGetEDIDBytesInfoFunc (iHdmiPort, edid, length); + LOGINFO("dsGetEDIDBytesInfoFunc eRet: %d data len: %d", eRet, *length); + } + return eRet; + } + + uint32_t GetEdidBytes(const HDMIInPort port, const uint16_t edidBytesLength, uint8_t edidBytes[]) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + int length = static_cast(edidBytesLength); + LOGINFO("GetEdidBytes"); + if (getEDIDBytesInfo(hdmiPort, edidBytes, &length) == dsERR_NONE) { + LOGINFO("GetEdidBytes: port=%d, edidBytesLength=%d, actualLength=%d", hdmiPort, edidBytesLength, length); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + static dsError_t getHDMISPDInfo (dsHdmiInPort_t iHdmiPort, unsigned char *spd) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsGetHDMISPDInfo_t)(dsHdmiInPort_t iHdmiPort, unsigned char *data); + static dsGetHDMISPDInfo_t dsGetHDMISPDInfoFunc = 0; + if (dsGetHDMISPDInfoFunc == 0) { + dsGetHDMISPDInfoFunc = (dsGetHDMISPDInfo_t)resolve(RDK_DSHAL_NAME, "dsGetHDMISPDInfo"); + if(dsGetHDMISPDInfoFunc == 0) { + LOGERR("dsGetHDMISPDInfo is not defined"); + eRet = dsERR_GENERAL; + } else { + LOGINFO("dsGetHDMISPDInfo loaded"); + } + } + if (0 != dsGetHDMISPDInfoFunc) { + eRet = dsGetHDMISPDInfoFunc (iHdmiPort, spd); + LOGINFO("dsGetHDMISPDInfoFunc eRet: %d", eRet); + } + else { + LOGINFO("dsGetHDMISPDInfoFunc = %p", dsGetHDMISPDInfoFunc); + } + return eRet; + } + + uint32_t GetHDMISPDInformation(const HDMIInPort port, const uint16_t spdBytesLength, uint8_t spdBytes[]) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + + if (getHDMISPDInfo(hdmiPort, spdBytes) == dsERR_NONE) { + LOGINFO("GetHDMISPDInformation: port=%d, spdBytesLength=%d", hdmiPort, spdBytesLength); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t GetHDMIEdidVersion(const HDMIInPort port, HDMIInEdidVersion &edidVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + int edidVer = 0; + if (getEdidVersion(hdmiPort, &edidVer) == dsERR_NONE) { + edidVersion = static_cast(edidVer); + LOGINFO("GetHDMIEdidVersion: port=%d, edidVersion=%d", hdmiPort, edidVer); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t SetHDMIEdidVersion(const HDMIInPort port, const HDMIInEdidVersion edidVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + tv_hdmi_edid_version_t edidVer = static_cast(edidVersion); + if (setEdidVersion(hdmiPort, edidVer) == dsERR_NONE) { + m_edidversion[hdmiPort] = edidVer; + LOGINFO("SetHDMIEdidVersion: port=%d, edidVersion=%d", hdmiPort, edidVer); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t GetHDMIVideoMode(HDMIVideoPortResolution &videoPortResolution) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsVideoPortResolution_t videoRes; + + memset(&videoRes, 0, sizeof(videoRes)); + + if (dsHdmiInGetCurrentVideoMode(&videoRes) == dsERR_NONE) { + // Validate that we have reasonable data before logging + LOGINFO("GetHDMIVideoMode: Raw HAL data - name=, pixelRes=%u, aspectRatio=%u, stereoScopicMode=%u, frameRate=%u, interlaced=%d", + videoRes.pixelResolution, videoRes.aspectRatio, videoRes.stereoScopicMode, videoRes.frameRate, videoRes.interlaced); + + if (videoRes.name[0] != '\0' && strlen(videoRes.name) < sizeof(videoRes.name)) { + videoPortResolution.name = std::string(videoRes.name); + } else { + videoPortResolution.name = "UNKNOWN"; + LOGWARN("GetHDMIVideoMode: Invalid video mode name, using 'UNKNOWN'"); + } + + videoPortResolution.pixelResolution = static_cast(videoRes.pixelResolution); + videoPortResolution.aspectRatio = static_cast(videoRes.aspectRatio); + videoPortResolution.stereoScopicMode = static_cast(videoRes.stereoScopicMode); + videoPortResolution.frameRate = static_cast(videoRes.frameRate); + videoPortResolution.interlaced = videoRes.interlaced; + + // Debug print all the assigned data + LOGINFO("GetHDMIVideoMode: Assigned data - name='%s', pixelResolution=%u, aspectRatio=%u, stereoScopicMode=%u, frameRate=%u, interlaced=%d", + videoPortResolution.name.c_str(), + videoPortResolution.pixelResolution, + videoPortResolution.aspectRatio, + videoPortResolution.stereoScopicMode, + videoPortResolution.frameRate, + videoPortResolution.interlaced); + + retCode = WPEFramework::Core::ERROR_NONE; + } else { + LOGERR("GetHDMIVideoMode: dsHdmiInGetCurrentVideoMode failed"); + // Initialize output with safe defaults + videoPortResolution.name = "ERROR"; + videoPortResolution.pixelResolution = static_cast(0); + videoPortResolution.aspectRatio = static_cast(0); + videoPortResolution.stereoScopicMode = static_cast(0); + videoPortResolution.frameRate = static_cast(0); + videoPortResolution.interlaced = false; + } + return retCode; + } + + uint32_t GetHDMIVersion(const HDMIInPort port, HDMIInCapabilityVersion &capabilityVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + dsHdmiMaxCapabilityVersion_t capversion; + if (getHdmiVersion(hdmiPort, &capversion) == dsERR_NONE) { + capabilityVersion = static_cast(capversion); + LOGINFO("GetHDMIVersion: port=%d, capabilityVersion=%d", hdmiPort, capversion); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + uint32_t SetVRRSupport(const HDMIInPort port, const bool vrrSupport) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + if (hdmiPort < dsHDMI_IN_PORT_MAX) { + LOGINFO("In SetVRRSupport, checking m_edidversion of port %d : %d", hdmiPort, m_edidversion[hdmiPort]); + if(m_edidversion[hdmiPort] == HDMI_EDID_VER_20) { // if the edidver is 2.0, then only set the vrr bit in edid + if (setVRRSupport(hdmiPort, vrrSupport) == dsERR_NONE) { + updateVRRBitValuesInPersistence(hdmiPort, vrrSupport); + m_vrrsupport[hdmiPort] = vrrSupport; + LOGINFO("SetVRRSupport: port=%d, vrrSupport=%d", hdmiPort, vrrSupport); + retCode = WPEFramework::Core::ERROR_NONE; + } + } else { + LOGINFO("EDID version is not 2.0, cannot set VRR support for port %d", hdmiPort); + retCode = WPEFramework::Core::ERROR_UNAVAILABLE; + } + } + return retCode; + } + + uint32_t GetVRRSupport(const HDMIInPort port, bool &vrrSupport) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + if (hdmiPort < dsHDMI_IN_PORT_MAX) { + vrrSupport = m_vrrsupport[hdmiPort]; + LOGINFO("GetVRRSupport: port=%d, vrrSupport=%d", hdmiPort, vrrSupport); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + static dsError_t getVRRStatus (dsHdmiInPort_t iHdmiPort, dsHdmiInVrrStatus_t *vrrStatus) { + dsError_t eRet = dsERR_GENERAL; + typedef dsError_t (*dsHdmiInGetVRRStatus_t)(dsHdmiInPort_t iHdmiPort, dsHdmiInVrrStatus_t *vrrStatus); + static dsHdmiInGetVRRStatus_t dsHdmiInGetVRRStatusFunc = 0; + if (dsHdmiInGetVRRStatusFunc == 0) { + dsHdmiInGetVRRStatusFunc = (dsHdmiInGetVRRStatus_t)resolve(RDK_DSHAL_NAME, "dsHdmiInGetVRRStatus"); + if(dsHdmiInGetVRRStatusFunc == 0) { + LOGERR("dsHdmiInGetVRRStatus is not defined"); + } else { + LOGINFO("dsHdmiInGetVRRStatus loaded"); + } + } + if (0 != dsHdmiInGetVRRStatusFunc) { + eRet = dsHdmiInGetVRRStatusFunc (iHdmiPort, vrrStatus); + LOGINFO("dsHdmiInGetVRRStatusFunc eRet: %d", eRet); + } + else { + LOGINFO("dsHdmiInGetVRRStatusFunc = %p", dsHdmiInGetVRRStatusFunc); + } + return eRet; + } + + uint32_t GetVRRStatus(const HDMIInPort port, HDMIInVRRStatus &vrrStatus) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + dsHdmiInPort_t hdmiPort = static_cast(port); + dsHdmiInVrrStatus_t status; + if (getVRRStatus(hdmiPort, &status) == dsERR_NONE) { + vrrStatus.vrrType = static_cast(status.vrrType); + vrrStatus.vrrFreeSyncFramerateHz = status.vrrAmdfreesyncFramerate_Hz; + LOGINFO("GetVRRStatus: port=%d, vrrType=%d, vrrFreeSyncFramerateHz=%f", hdmiPort, vrrStatus.vrrType, vrrStatus.vrrFreeSyncFramerateHz); + retCode = WPEFramework::Core::ERROR_NONE; + } + return retCode; + } + + // Helper function to convert dsError_t to WPEFramework error codes + static uint32_t convertDsErrorToWPEError(dsError_t dsErr) { + switch (dsErr) { + case dsERR_NONE: + return WPEFramework::Core::ERROR_NONE; + case dsERR_GENERAL: + return WPEFramework::Core::ERROR_GENERAL; + case dsERR_INVALID_PARAM: + return WPEFramework::Core::ERROR_BAD_REQUEST; + case dsERR_INVALID_STATE: + return WPEFramework::Core::ERROR_ILLEGAL_STATE; + case dsERR_OPERATION_NOT_SUPPORTED: + return WPEFramework::Core::ERROR_UNAVAILABLE; + default: + return WPEFramework::Core::ERROR_GENERAL; + } + } + + private: +}; diff --git a/plugin/hal/dHost.h b/plugin/hal/dHost.h new file mode 100644 index 0000000..fbfabfc --- /dev/null +++ b/plugin/hal/dHost.h @@ -0,0 +1,52 @@ +/* + * 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. + */ +#pragma once + +#include "dsHost.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dHost { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle& bundle) = 0; + virtual void getPersistenceValue() = 0; + + // Host Platform interface methods - only what remains in IDeviceSettingsHost + virtual uint32_t GetEDID(uint8_t edId[], const uint16_t edIdLength) = 0; + virtual uint32_t GetMS12ConfigType(string &ms12Config) = 0; + + }; +} // namespace dHost +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dHostImpl.h b/plugin/hal/dHostImpl.h new file mode 100644 index 0000000..48bd7cc --- /dev/null +++ b/plugin/hal/dHostImpl.h @@ -0,0 +1,206 @@ +// Out-of-line virtual destructor definition for RTTI/typeinfo +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include "dHost.h" +#include "dsHost.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include +#include "DeviceSettingsTypes.h" + + + +// Static global variables from dsHost.cpp conversion +static int host_isInitialized = 0; +static int host_isPlatInitialized = 0; + +// MS12 Configuration constants +#ifndef MS12_CONFIG_BUF_SIZE +#define MS12_CONFIG_BUF_SIZE 256 +#endif + +// EDID constants +#ifndef EDID_MAX_DATA_SIZE +#define EDID_MAX_DATA_SIZE 1024 +#endif + +// DS HAL function type definitions +typedef dsError_t (*dsGetHostEDIDFunc_t)(unsigned char *edid, int *length); + +class dHostImpl : public hal::dHost::IPlatform { + + // delete copy constructor and assignment operator + dHostImpl(const dHostImpl&) = delete; + dHostImpl& operator=(const dHostImpl&) = delete; + +public: + dHostImpl() + { + LOGINFO("dHostImpl Constructor"); + getInstance() = this; // Set static instance for callback access + InitialiseHAL(); + } + + virtual ~dHostImpl() + { + LOGINFO("dHostImpl Destructor"); + DeInitialiseHAL(); + getInstance() = nullptr; // Clear static instance + } + + // Singleton getInstance method - following VideoPort/HDMIIn pattern + static dHostImpl*& getInstance() + { + static dHostImpl* instance = nullptr; + return instance; + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + // Note: host_isInitialized should only be set in setAllCallbacks after callback registration + // Don't set it here as it prevents callback registration condition from working + + if (!host_isPlatInitialized) { + LOGINFO("InitialiseHAL "); + dsError_t eError = dsHostInit(); + if (dsERR_NONE != eError) { + LOGERR("InitialiseHAL: dsHostInit failed with error: %d", eError); + return; + } + host_isPlatInitialized = 1; + LOGINFO("InitialiseHAL: dsHost HAL initialized successfully"); + + // Load persistence values - following dsHost.cpp dsHostMgr_init pattern + getPersistenceValue(); + } + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + if (host_isPlatInitialized) { + dsError_t eError = dsHostTerm(); + if (dsERR_NONE != eError) { + LOGERR("DeInitialiseHAL: dsHostTerm failed with error: %d", eError); + } + host_isPlatInitialized = 0; + LOGINFO("DeInitialiseHAL: dsHost HAL de-initialized successfully"); + } + } + + uint32_t GetEDID(uint8_t edId[], const uint16_t edIdLength) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetEDID: edIdLength=%u", edIdLength); + + // Use resolve function following dHdmiInImpl.h pattern + typedef dsError_t (*dsGetHostEDIDFunc_t)(unsigned char *edid, int *length); + dsGetHostEDIDFunc_t func = (dsGetHostEDIDFunc_t)resolve(RDK_DSHAL_NAME, "dsGetHostEDID"); + + if (func != nullptr) { + unsigned char edidBytes[EDID_MAX_DATA_SIZE]; + int length = 0; + dsError_t eError = func(edidBytes, &length); + if (eError == dsERR_NONE && length <= static_cast(edIdLength)) { + memcpy(edId, edidBytes, length); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetEDID: SUCCESS - copied %d bytes", length); + } else if (eError == dsERR_NONE && length > static_cast(edIdLength)) { + LOGERR("GetEDID: Buffer too small - required %d bytes, provided %u", length, edIdLength); + retCode = WPEFramework::Core::ERROR_BAD_REQUEST; + } else { + LOGERR("GetEDID: dsGetHostEDID failed with error: %d", eError); + } + } else { + retCode = WPEFramework::Core::ERROR_UNAVAILABLE; + LOGERR("GetEDID: Function not available"); + } + + return retCode; + } + + uint32_t GetMS12ConfigType(string &ms12Config) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetMS12ConfigType"); + + // Following dsHost.cpp pattern + try { + ms12Config = device::HostPersistence::getInstance().getDefaultProperty("MS12.Config.Type"); + LOGINFO("GetMS12ConfigType: SUCCESS - ms12Config='%s'", ms12Config.c_str()); + retCode = WPEFramework::Core::ERROR_NONE; + } catch (const std::exception& e) { + LOGWARN("GetMS12ConfigType: Failed to retrieve config from default persistence: %s", e.what()); + ms12Config = "CONFIG_NONE"; + retCode = WPEFramework::Core::ERROR_NONE; + } catch (...) { + LOGWARN("GetMS12ConfigType: Unknown error retrieving config from default persistence"); + ms12Config = "CONFIG_NONE"; + retCode = WPEFramework::Core::ERROR_NONE; + } + + return retCode; + } + + void setAllCallbacks(const CallbackBundle& bundle) override + { + ENTRY_LOG; + LOGINFO("Host::setAllCallbacks"); + if (host_isPlatInitialized && !host_isInitialized) { + host_isInitialized = 1; + LOGINFO("Host platform callback Initialization done"); + } + EXIT_LOG; + } + + void getPersistenceValue() override + { + ENTRY_LOG; + LOGINFO("Host::getPersistenceValue"); + EXIT_LOG; + } + +private: + // Dynamic loading helper - following dHdmiInImpl.h pattern + static void* resolve(const std::string& libName, const std::string& symbolName) { + void* handle = dlopen(libName.c_str(), RTLD_LAZY); + if (!handle) { + std::cerr << "dlopen failed for " << libName << ": " << dlerror() << std::endl; + return nullptr; + } + void* symbol = dlsym(handle, symbolName.c_str()); + if (!symbol) { + std::cerr << "dlsym failed for " << symbolName << ": " << dlerror() << std::endl; + } + dlclose(handle); + return symbol; + } +}; \ No newline at end of file diff --git a/plugin/hal/dVideoDevice.h b/plugin/hal/dVideoDevice.h new file mode 100644 index 0000000..4f9b4ea --- /dev/null +++ b/plugin/hal/dVideoDevice.h @@ -0,0 +1,67 @@ +/* + * 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. + */ +#pragma once + +#include "dsVideoDevice.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dVideoDevice { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle& bundle) = 0; + virtual void getPersistenceValue() = 0; + + // VideoDevice Platform interface methods - all pure virtual + virtual uint32_t GetVideoDeviceHandle(const int32_t index, int32_t& handle) = 0; + virtual uint32_t SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting) = 0; + virtual uint32_t GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom& zoomSetting) = 0; + virtual uint32_t GetHDRCapabilities(const int32_t handle, int32_t& capabilities) = 0; + virtual uint32_t GetSupportedVideoCodingFormats(const int32_t handle, int32_t& supportedFormats) = 0; + virtual uint32_t GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo) = 0; + virtual uint32_t DisableHDR(const int32_t handle, const bool disable) = 0; + virtual uint32_t SetFRFMode(const int32_t handle, const int32_t frfmode) = 0; + virtual uint32_t GetFRFMode(const int32_t handle, int32_t& frfmode) = 0; + virtual uint32_t GetCurrentDisplayFrameRate(const int32_t handle, string& framerate) = 0; + virtual uint32_t SetDisplayFrameRate(const int32_t handle, const string framerate) = 0; + }; + + // CallbackBundle structure to hold all VideoDevice event callbacks + struct CallbackBundle { + std::function OnZoomSettingsChanged; + std::function OnDisplayFrameratePreChange; + std::function OnDisplayFrameratePostChange; + }; +} // namespace dVideoDevice +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dVideoDeviceImpl.h b/plugin/hal/dVideoDeviceImpl.h new file mode 100644 index 0000000..64c24e6 --- /dev/null +++ b/plugin/hal/dVideoDeviceImpl.h @@ -0,0 +1,816 @@ +/* + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dVideoDevice.h" +#include "dsVideoDevice.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" +#include "dsHdmiIn.h" + + + +#include +#include "DeviceSettingsTypes.h" + +// Static global variables from dsVideoDevice.c conversion +static int videoDevice_isInitialized = 0; +static int videoDevice_isPlatInitialized = 0; +static dsVideoZoom_t srv_dfc = dsVIDEO_ZOOM_FULL; +static bool force_disable_hdr = true; + +// Static global callbacks for VideoDevice events - following HdmiIn pattern +static std::function g_VideoDeviceZoomSettingsChangedCallback; +static std::function g_VideoDeviceDisplayFrameratePreChangeCallback; +static std::function g_VideoDeviceDisplayFrameratePostChangeCallback; + +class dVideoDeviceImpl : public hal::dVideoDevice::IPlatform { + + // delete copy constructor and assignment operator + dVideoDeviceImpl(const dVideoDeviceImpl&) = delete; + dVideoDeviceImpl& operator=(const dVideoDeviceImpl&) = delete; + +public: + dVideoDeviceImpl() + { + LOGINFO("dVideoDeviceImpl Constructor"); + InitialiseHAL(); + } + + virtual ~dVideoDeviceImpl() + { + LOGERR("dVideoDeviceImpl Destructor"); + DeInitialiseHAL(); + } + + // Singleton getInstance method - following HdmiIn pattern + static dVideoDeviceImpl*& getInstance() + { + static dVideoDeviceImpl* instance = new dVideoDeviceImpl(); + return instance; + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + // Note: videoDevice_isInitialized should only be set in setAllCallbacks after callback registration + // Don't set it here as it prevents callback registration condition from working + + if (!videoDevice_isPlatInitialized) { + LOGINFO("InitialiseHAL "); + dsError_t eError = dsVideoDeviceInit(); + if (dsERR_NONE != eError) { + LOGERR("InitialiseHAL: dsVideoDeviceInit failed with error: %d", eError); + return; + } + LOGINFO("InitialiseHAL: dsVideoDeviceInit succeeded"); + + // Load persistence values after successful initialization - following dsVideoDevice.c pattern + getPersistenceValue(); + + videoDevice_isPlatInitialized = 1; + LOGINFO("InitialiseHAL completed: videoDevice_isPlatInitialized=%d, videoDevice_isInitialized=%d", + videoDevice_isPlatInitialized, videoDevice_isInitialized); + } + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + if (videoDevice_isPlatInitialized) + { + dsVideoDeviceTerm(); + videoDevice_isPlatInitialized = 0; + } + videoDevice_isInitialized = 0; + } + + static void* resolve(const std::string& libName, const std::string& symbolName) { + void* handle = dlopen(libName.c_str(), RTLD_LAZY); + if (!handle) { + LOGERR("resolve: Failed to load library %s: %s", libName.c_str(), dlerror()); + return nullptr; + } + + void* symbol = dlsym(handle, symbolName.c_str()); + if (!symbol) { + LOGERR("resolve: Failed to find symbol %s in %s: %s", symbolName.c_str(), libName.c_str(), dlerror()); + dlclose(handle); + return nullptr; + } + + LOGINFO("resolve: Successfully resolved %s from %s", symbolName.c_str(), libName.c_str()); + dlclose(handle); + return symbol; + } + + // Implementation of all VideoDevice Platform interface methods + uint32_t GetVideoDeviceHandle(const int32_t index, int32_t& handle) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoDeviceHandle: index=%d", index); + + // Use intptr_t locally for HAL call - dsGetVideoDevice expects intptr_t* + intptr_t halHandle = 0; + dsError_t eError = dsGetVideoDevice(index, &halHandle); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + handle = static_cast(halHandle); // Cast result back to int32_t for interface + LOGINFO("GetVideoDeviceHandle: SUCCESS - handle=%d", handle); + } else { + LOGERR("GetVideoDeviceHandle: dsGetVideoDevice failed with error: %d", eError); + } + + return retCode; + } + + uint32_t SetVideoDeviceDFC(const int32_t handle, const VideoDeviceZoom zoomSetting) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetVideoDeviceDFC: handle=%d, zoomSetting=%d", handle, static_cast(zoomSetting)); + + // Convert VideoDeviceZoom to dsVideoZoom_t + dsVideoZoom_t dsZoom = convertVideoDeviceZoom(zoomSetting); + + try { + if (dsZoom == dsVIDEO_ZOOM_NONE) { + LOGINFO("Call Zoom setting NONE"); + dsError_t eError = dsSetDFC(handle, dsZoom); + if (eError == dsERR_NONE) { + srv_dfc = dsZoom; + retCode = WPEFramework::Core::ERROR_NONE; + device::HostPersistence::getInstance().persistHostProperty("VideoDevice.DFC", "None"); + + // Trigger zoom settings changed callback + if (g_VideoDeviceZoomSettingsChangedCallback) { + g_VideoDeviceZoomSettingsChangedCallback(VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_NONE); + } + + LOGINFO("SetVideoDeviceDFC: SUCCESS (NONE)"); + } else { + LOGERR("SetVideoDeviceDFC: dsSetDFC failed with error: %d", eError); + } + } else if (dsZoom == dsVIDEO_ZOOM_FULL) { + LOGINFO("Call Zoom setting FULL"); + dsError_t eError = dsSetDFC(handle, dsZoom); + if (eError == dsERR_NONE) { + srv_dfc = dsZoom; + retCode = WPEFramework::Core::ERROR_NONE; + device::HostPersistence::getInstance().persistHostProperty("VideoDevice.DFC", "Full"); + + // Trigger zoom settings changed callback + if (g_VideoDeviceZoomSettingsChangedCallback) { + g_VideoDeviceZoomSettingsChangedCallback(VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_FULL); + } + + LOGINFO("SetVideoDeviceDFC: SUCCESS (FULL)"); + } else { + LOGERR("SetVideoDeviceDFC: dsSetDFC failed with error: %d", eError); + } + } else if (dsZoom == dsVIDEO_ZOOM_16_9_ZOOM) { + LOGINFO("Call Zoom setting dsVIDEO_ZOOM_16_9_ZOOM"); + dsError_t eError = dsSetDFC(handle, dsZoom); + if (eError == dsERR_NONE) { + srv_dfc = dsZoom; + retCode = WPEFramework::Core::ERROR_NONE; + device::HostPersistence::getInstance().persistHostProperty("VideoDevice.DFC", "Full"); + + // Trigger zoom settings changed callback + if (g_VideoDeviceZoomSettingsChangedCallback) { + g_VideoDeviceZoomSettingsChangedCallback(VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_16_9_ZOOM); + } + + LOGINFO("SetVideoDeviceDFC: SUCCESS (16_9_ZOOM)"); + } else { + LOGERR("SetVideoDeviceDFC: dsSetDFC failed with error: %d", eError); + } + } else { + LOGERR("ERROR: unsupported Zoom setting %d", static_cast(zoomSetting)); + } + + if (profileType == TV) { + LOGINFO("TV Profile - setting HDMI In zoom mode"); + dsHdmiInSelectZoomMode(srv_dfc); + } + } catch (...) { + LOGERR("Error in Setting the Video Device DFC"); + } + + return retCode; + } + + uint32_t GetVideoDeviceDFC(const int32_t handle, VideoDeviceZoom& zoomSetting) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoDeviceDFC: handle=%d", handle); + + // Return the cached zoom setting + zoomSetting = convertDSVideoZoom(srv_dfc); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoDeviceDFC: SUCCESS - zoomSetting=%d", static_cast(zoomSetting)); + + return retCode; + } + + uint32_t GetHDRCapabilities(const int32_t handle, int32_t& capabilities) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetHDRCapabilities: handle=%d", handle); + + typedef dsError_t (*dsGetHDRCapabilitiesFunc_t)(intptr_t handle, int *capabilities); + static dsGetHDRCapabilitiesFunc_t func = 0; + if (func == 0) { + func = (dsGetHDRCapabilitiesFunc_t)resolve(RDK_DSHAL_NAME, "dsGetHDRCapabilities"); + if (func) { + LOGINFO("dsGetHDRCapabilities() is defined and loaded"); + } else { + LOGINFO("dsGetHDRCapabilities() is not defined"); + } + } + + if ((0 != func) && (false == force_disable_hdr)) { + int dsCapabilities = 0; + dsError_t eError = func(handle, &dsCapabilities); + if (eError == dsERR_NONE) { + capabilities = static_cast(dsCapabilities); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDRCapabilities: SUCCESS - capabilities=0x%x", capabilities); + } else { + LOGERR("GetHDRCapabilities: dsGetHDRCapabilities failed with error: %d", eError); + } + } else { + capabilities = dsHDRSTANDARD_NONE; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDRCapabilities: HDR disabled or function not available - capabilities=0x%x", capabilities); + } + + return retCode; + } + + uint32_t GetSupportedVideoCodingFormats(const int32_t handle, int32_t& supportedFormats) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetSupportedVideoCodingFormats: handle=%d", handle); + + typedef dsError_t (*dsGetSupportedVideoCodingFormatsFunc_t)(intptr_t handle, unsigned int *supported_formats); + static dsGetSupportedVideoCodingFormatsFunc_t func = 0; + if (func == 0) { + func = (dsGetSupportedVideoCodingFormatsFunc_t)resolve(RDK_DSHAL_NAME, "dsGetSupportedVideoCodingFormats"); + if (func) { + LOGINFO("dsGetSupportedVideoCodingFormats() is defined and loaded"); + } else { + LOGINFO("dsGetSupportedVideoCodingFormats() is not defined"); + } + } + + if (0 != func) { + unsigned int dsFormats = 0; + dsError_t eError = func(handle, &dsFormats); + if (eError == dsERR_NONE) { + supportedFormats = static_cast(dsFormats); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetSupportedVideoCodingFormats: SUCCESS - supportedFormats=0x%x", supportedFormats); + } else { + LOGERR("GetSupportedVideoCodingFormats: dsGetSupportedVideoCodingFormats failed with error: %d", eError); + } + } else { + supportedFormats = 0x0; // Safe default: no formats supported + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetSupportedVideoCodingFormats: Function not available - supportedFormats=0x%x", supportedFormats); + } + + return retCode; + } + + uint32_t GetCodecInfo(const int32_t handle, const VideoDeviceCodec videoCodec, IDeviceSettingsVideoCodecProfileSupportIterator*& codecInfo) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetCodecInfo: handle=%d, videoCodec=%d", handle, static_cast(videoCodec)); + + typedef dsError_t (*dsGetVideoCodecInfoFunc_t)(intptr_t handle, dsVideoCodingFormat_t codec, dsVideoCodecInfo_t * info); + static dsGetVideoCodecInfoFunc_t func = 0; + if (func == 0) { + func = (dsGetVideoCodecInfoFunc_t)resolve(RDK_DSHAL_NAME, "dsGetVideoCodecInfo"); + if (func) { + LOGINFO("dsGetVideoCodecInfo() is defined and loaded"); + } else { + LOGINFO("dsGetVideoCodecInfo() is not defined"); + } + } + + if (0 != func) { + dsVideoCodingFormat_t dsFormat = convertVideoCodecToDSFormat(videoCodec); + dsVideoCodecInfo_t info; + dsError_t eError = func(handle, dsFormat, &info); + if (eError == dsERR_NONE) { + // Convert dsVideoCodecInfo_t to iterator + codecInfo = createCodecInfoIterator(info); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetCodecInfo: SUCCESS - codecInfo created"); + } else { + LOGERR("GetCodecInfo: dsGetVideoCodecInfo failed with error: %d", eError); + } + } else { + retCode = WPEFramework::Core::ERROR_UNAVAILABLE; + LOGERR("GetCodecInfo: Function not available"); + } + + return retCode; + } + + uint32_t DisableHDR(const int32_t handle, const bool disable) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("DisableHDR: handle=%d, disable=%s", handle, disable ? "true" : "false"); + + typedef dsError_t (*dsDisableHDRSupportFunc_t)(intptr_t handle, bool enable); + static dsDisableHDRSupportFunc_t func = 0; + if (func == 0) { + func = (dsDisableHDRSupportFunc_t)resolve(RDK_DSHAL_NAME, "dsForceDisableHDRSupport"); + if (func) { + LOGINFO("dsForceDisableHDRSupport() is defined and loaded"); + } else { + LOGINFO("dsForceDisableHDRSupport() is not defined"); + } + } + + retCode = WPEFramework::Core::ERROR_NONE; + + if (0 != func) { + dsError_t eError = func(handle, disable); + if (eError != dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_GENERAL; + LOGERR("DisableHDR: dsForceDisableHDRSupport failed with error: %d", eError); + } else { + LOGINFO("DisableHDR: dsForceDisableHDRSupport succeeded - disable=%s", disable ? "true" : "false"); + } + } + + force_disable_hdr = disable; + if (force_disable_hdr) { + device::HostPersistence::getInstance().persistHostProperty("VideoDevice.forceHDRDisabled", "true"); + } else { + device::HostPersistence::getInstance().persistHostProperty("VideoDevice.forceHDRDisabled", "false"); + } + + return retCode; + } + + uint32_t SetFRFMode(const int32_t handle, const int32_t frfmode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetFRFMode: handle=%d, frfmode=%d", handle, frfmode); + + typedef dsError_t (*dsSetFRFModeFunc_t)(intptr_t handle, int frfmode); + static dsSetFRFModeFunc_t func = 0; + if (func == 0) { + func = (dsSetFRFModeFunc_t)resolve(RDK_DSHAL_NAME, "dsSetFRFMode"); + if (func) { + LOGINFO("dsSetFRFMode is defined and loaded"); + } else { + LOGINFO("dsSetFRFMode is not defined"); + } + } + + if (0 != func) { + dsError_t eError = func(handle, frfmode); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetFRFMode: SUCCESS"); + } else { + LOGERR("SetFRFMode: dsSetFRFMode failed with error: %d", eError); + } + } + + return retCode; + } + + uint32_t GetFRFMode(const int32_t handle, int32_t& frfmode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetFRFMode: handle=%d", handle); + + typedef dsError_t (*dsGetFRFModeFunc_t)(intptr_t handle, int *frfmode); + static dsGetFRFModeFunc_t func = 0; + if (func == 0) { + func = (dsGetFRFModeFunc_t)resolve(RDK_DSHAL_NAME, "dsGetFRFMode"); + if (func) { + LOGINFO("dsGetFRFMode() is defined and loaded"); + } else { + LOGINFO("dsGetFRFMode() is not defined"); + } + } + + if (0 != func) { + int dsFrfMode = 0; + dsError_t eError = func(handle, &dsFrfMode); + if (eError == dsERR_NONE) { + frfmode = static_cast(dsFrfMode); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetFRFMode: SUCCESS - frfmode=%d", frfmode); + } else { + LOGERR("GetFRFMode: dsGetFRFMode failed with error: %d", eError); + } + } + + return retCode; + } + + uint32_t GetCurrentDisplayFrameRate(const int32_t handle, string& framerate) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetCurrentDisplayFrameRate: handle=%d", handle); + + typedef dsError_t (*dsGetCurrentDisframerateFunc_t)(intptr_t handle, char *framerate); + static dsGetCurrentDisframerateFunc_t func = 0; + if (func == 0) { + func = (dsGetCurrentDisframerateFunc_t)resolve(RDK_DSHAL_NAME, "dsGetCurrentDisplayframerate"); + if (func) { + LOGINFO("dsGetCurrentDisframerate() is defined and loaded"); + } else { + LOGINFO("dsGetCurrentDisframerate() is not defined"); + } + } + + if (0 != func) { + char dsFramerate[32] = ""; + dsError_t eError = func(handle, dsFramerate); + if (eError == dsERR_NONE) { + framerate = string(dsFramerate); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetCurrentDisplayFrameRate: SUCCESS - framerate=%s", framerate.c_str()); + } else { + LOGERR("GetCurrentDisplayFrameRate: dsGetCurrentDisplayframerate failed with error: %d", eError); + } + } + + return retCode; + } + + uint32_t SetDisplayFrameRate(const int32_t handle, const string framerate) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetDisplayFrameRate: handle=%d, framerate=%s", handle, framerate.c_str()); + + typedef dsError_t (*dsSetDisplayframerateFunc_t)(intptr_t handle, char *frfmode); + static dsSetDisplayframerateFunc_t func = 0; + if (func == 0) { + func = (dsSetDisplayframerateFunc_t)resolve(RDK_DSHAL_NAME, "dsSetDisplayframerate"); + if (func) { + LOGINFO("dsSetDisplayframerate() is defined and loaded"); + } else { + LOGINFO("dsSetDisplayframerate() is not defined"); + } + } + + dsError_t result = dsERR_NONE; + + // Validate framerate parameter + if (framerate.empty()) { + result = dsERR_INVALID_PARAM; + return WPEFramework::Core::ERROR_BAD_REQUEST; + } + + // Send pre-change callback + if (g_VideoDeviceDisplayFrameratePreChangeCallback) { + g_VideoDeviceDisplayFrameratePreChangeCallback(framerate); + } + + if (0 != func) { + char dsFramerate[32]; + strncpy(dsFramerate, framerate.c_str(), sizeof(dsFramerate) - 1); + dsFramerate[sizeof(dsFramerate) - 1] = '\0'; + + result = func(handle, dsFramerate); + if (result == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetDisplayFrameRate: SUCCESS"); + } else { + LOGERR("SetDisplayFrameRate: dsSetDisplayframerate failed with error: %d", result); + } + } + + // Send post-change callback + if (g_VideoDeviceDisplayFrameratePostChangeCallback) { + g_VideoDeviceDisplayFrameratePostChangeCallback(framerate); + } + + return retCode; + } + + // VideoDevice Event Handling Infrastructure - following HdmiIn singleton pattern + void setAllCallbacks(const CallbackBundle& bundle) override + { + ENTRY_LOG; + LOGINFO("VideoDevice::setAllCallbacks - Registering event callbacks with DS HAL"); + + // Debug logging to diagnose condition failure + LOGINFO("VideoDevice callback registration check: videoDevice_isInitialized=%d, videoDevice_isPlatInitialized=%d", + videoDevice_isInitialized, videoDevice_isPlatInitialized); + + if (videoDevice_isPlatInitialized && !videoDevice_isInitialized) { + LOGINFO("VideoDevice platform callback Initialization"); + + // Register Zoom Settings Changed Callback + if (bundle.OnZoomSettingsChanged) { + LOGINFO("VideoDevice Zoom Settings Changed Event Callback Registered"); + g_VideoDeviceZoomSettingsChangedCallback = bundle.OnZoomSettingsChanged; + // Zoom callbacks are triggered manually during DFC setting + } + + // Register Display Framerate Pre-Change Callback - following dsVideoDevice.c pattern + if (bundle.OnDisplayFrameratePreChange) { + LOGINFO("VideoDevice Display Framerate Pre-Change Event Callback Registered"); + g_VideoDeviceDisplayFrameratePreChangeCallback = bundle.OnDisplayFrameratePreChange; + + // Register framerate pre-change callback with DS HAL - exact pattern from dsVideoDevice.c + dsError_t eRet = VideoDeviceRegisterFrameratePreChangeCB(VideoDeviceFramerateStatusPreChangeCB); + if (dsERR_NONE != eRet) { + LOGERR("VideoDeviceRegisterFrameratePreChangeCB failed with error: %d", eRet); + } else { + LOGINFO("Framerate pre-change callback registered successfully with DS HAL"); + } + } + + // Register Display Framerate Post-Change Callback - following dsVideoDevice.c pattern + if (bundle.OnDisplayFrameratePostChange) { + LOGINFO("VideoDevice Display Framerate Post-Change Event Callback Registered"); + g_VideoDeviceDisplayFrameratePostChangeCallback = bundle.OnDisplayFrameratePostChange; + + // Register framerate post-change callback with DS HAL - exact pattern from dsVideoDevice.c + dsError_t eRet = VideoDeviceRegisterFrameratePostChangeCB(VideoDeviceFramerateStatusPostChangeCB); + if (dsERR_NONE != eRet) { + LOGERR("VideoDeviceRegisterFrameratePostChangeCB failed with error: %d", eRet); + } else { + LOGINFO("Framerate post-change callback registered successfully with DS HAL"); + } + } + + videoDevice_isInitialized = 1; + LOGINFO("VideoDevice platform callback Initialization done"); + } else { + if (!videoDevice_isPlatInitialized) { + LOGERR("VideoDevice callback registration FAILED: Platform not initialized (videoDevice_isPlatInitialized=%d)", + videoDevice_isPlatInitialized); + } + if (videoDevice_isInitialized) { + LOGWARN("VideoDevice callback registration SKIPPED: Callbacks already initialized (videoDevice_isInitialized=%d)", + videoDevice_isInitialized); + } + } + + EXIT_LOG; + } + + void getPersistenceValue() + { + ENTRY_LOG; + LOGINFO("VideoDevice::getPersistenceValue - Loading persistence settings"); + + try { + std::string _ZoomSettings("Full"); + /* Get the Zoom from Persistence */ + _ZoomSettings = device::HostPersistence::getInstance().getProperty("VideoDevice.DFC", _ZoomSettings); + if (_ZoomSettings.compare("None") == 0) { + srv_dfc = dsVIDEO_ZOOM_NONE; + } + LOGINFO("Persistent VideoDevice DFC read: %s", _ZoomSettings.c_str()); + + if (profileType == TV) { + LOGINFO("TV Profile - setting persistent zoom mode"); + dsHdmiInSelectZoomMode(srv_dfc); + } + } catch (...) { + LOGINFO("Exception in Getting the Zoom settings on Startup"); + } + + try { + std::string _hdr_setting("false"); + _hdr_setting = device::HostPersistence::getInstance().getProperty("VideoDevice.forceHDRDisabled", _hdr_setting); + if (_hdr_setting.compare("false") == 0) { + force_disable_hdr = false; + } else { + force_disable_hdr = true; + LOGINFO("HDR support in disabled configuration"); + } + } catch (...) { + LOGINFO("Exception in getting force-disable-HDR setting at start up"); + } + + EXIT_LOG; + } + + // Static callback functions for DS HAL integration - following HdmiIn pattern + static void VideoDeviceFramerateStatusPreChangeCB(unsigned int inputStatus) + { + LOGINFO("VideoDeviceFramerateStatusPreChangeCB: inputStatus=%u", inputStatus); + + // Call the stored global callback if available + if (g_VideoDeviceDisplayFrameratePreChangeCallback) { + std::string framerate = std::to_string(inputStatus); + g_VideoDeviceDisplayFrameratePreChangeCallback(framerate); + } + } + + static void VideoDeviceFramerateStatusPostChangeCB(unsigned int inputStatus) + { + LOGINFO("VideoDeviceFramerateStatusPostChangeCB: inputStatus=%u", inputStatus); + + // Call the stored global callback if available + if (g_VideoDeviceDisplayFrameratePostChangeCallback) { + std::string framerate = std::to_string(inputStatus); + g_VideoDeviceDisplayFrameratePostChangeCallback(framerate); + } + } + + // DS HAL Callback Registration Functions - following exact pattern from dsVideoDevice.c + static dsError_t VideoDeviceRegisterFrameratePreChangeCB(dsRegisterFrameratePreChangeCB_t cbFunc) + { + dsError_t eRet = dsERR_GENERAL; + LOGINFO("VideoDeviceRegisterFrameratePreChangeCB: Registering framerate pre-change callback"); + + typedef dsError_t (*_dsFramerateStatusPreChangeCB_t)(dsRegisterFrameratePreChangeCB_t CBFunc); + static _dsFramerateStatusPreChangeCB_t frameratePreChangeCB = 0; + + if (frameratePreChangeCB == 0) { + void* dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + frameratePreChangeCB = (_dsFramerateStatusPreChangeCB_t) dlsym(dllib, "dsRegisterFrameratePreChangeCB"); + if (frameratePreChangeCB == 0) { + LOGINFO("dsRegisterFrameratePreChangeCB is not defined"); + } else { + LOGINFO("dsRegisterFrameratePreChangeCB loaded"); + } + dlclose(dllib); + } else { + LOGERR("Failed to open RDK_DSHAL_NAME [%s]: %s", RDK_DSHAL_NAME, dlerror()); + eRet = dsERR_GENERAL; + } + } + + if (frameratePreChangeCB) { + eRet = frameratePreChangeCB(cbFunc); + if (dsERR_NONE == eRet) { + LOGINFO("Framerate pre-change callback registered successfully"); + } else { + LOGERR("Failed to register framerate pre-change callback: %d", eRet); + } + } + + return eRet; + } + + static dsError_t VideoDeviceRegisterFrameratePostChangeCB(dsRegisterFrameratePostChangeCB_t cbFunc) + { + dsError_t eRet = dsERR_GENERAL; + LOGINFO("VideoDeviceRegisterFrameratePostChangeCB: Registering framerate post-change callback"); + + typedef dsError_t (*_dsFramerateStatusPostChangeCB_t)(dsRegisterFrameratePostChangeCB_t CBFunc); + static _dsFramerateStatusPostChangeCB_t frameratePostChangeCB = 0; + + if (frameratePostChangeCB == 0) { + void* dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + frameratePostChangeCB = (_dsFramerateStatusPostChangeCB_t) dlsym(dllib, "dsRegisterFrameratePostChangeCB"); + if (frameratePostChangeCB == 0) { + LOGINFO("dsRegisterFrameratePostChangeCB is not defined"); + } else { + LOGINFO("dsRegisterFrameratePostChangeCB loaded"); + } + dlclose(dllib); + } else { + LOGERR("Failed to open RDK_DSHAL_NAME [%s]: %s", RDK_DSHAL_NAME, dlerror()); + eRet = dsERR_GENERAL; + } + } + + if (frameratePostChangeCB) { + eRet = frameratePostChangeCB(cbFunc); + if (dsERR_NONE == eRet) { + LOGINFO("Framerate post-change callback registered successfully"); + } else { + LOGERR("Failed to register framerate post-change callback: %d", eRet); + } + } + + return eRet; + } + +private: + + // Helper methods for DS VideoDevice HAL conversion + dsVideoZoom_t convertVideoDeviceZoom(const VideoDeviceZoom zoom) + { + switch (zoom) { + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_NONE: + return dsVIDEO_ZOOM_NONE; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_FULL: + return dsVIDEO_ZOOM_FULL; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_16_9: + return dsVIDEO_ZOOM_LB_16_9; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_14_9: + return dsVIDEO_ZOOM_LB_14_9; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_CCO: + return dsVIDEO_ZOOM_CCO; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PAN_SCAN: + return dsVIDEO_ZOOM_PAN_SCAN; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_2_21_1_ON_4_3: + return dsVIDEO_ZOOM_LB_2_21_1_ON_4_3; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_2_21_1_ON_16_9: + return dsVIDEO_ZOOM_LB_2_21_1_ON_16_9; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PLATFORM: + return dsVIDEO_ZOOM_PLATFORM; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_16_9_ZOOM: + return dsVIDEO_ZOOM_16_9_ZOOM; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PILLARBOX_4_3: + return dsVIDEO_ZOOM_PILLARBOX_4_3; + case VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_WIDE_4_3: + return dsVIDEO_ZOOM_WIDE_4_3; + default: + return dsVIDEO_ZOOM_FULL; + } + } + + VideoDeviceZoom convertDSVideoZoom(const dsVideoZoom_t dsZoom) + { + switch (dsZoom) { + case dsVIDEO_ZOOM_NONE: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_NONE; + case dsVIDEO_ZOOM_FULL: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_FULL; + case dsVIDEO_ZOOM_LB_16_9: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_16_9; + case dsVIDEO_ZOOM_LB_14_9: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_14_9; + case dsVIDEO_ZOOM_CCO: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_CCO; + case dsVIDEO_ZOOM_PAN_SCAN: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PAN_SCAN; + case dsVIDEO_ZOOM_LB_2_21_1_ON_4_3: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_2_21_1_ON_4_3; + case dsVIDEO_ZOOM_LB_2_21_1_ON_16_9: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_LB_2_21_1_ON_16_9; + case dsVIDEO_ZOOM_PLATFORM: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PLATFORM; + case dsVIDEO_ZOOM_16_9_ZOOM: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_16_9_ZOOM; + case dsVIDEO_ZOOM_PILLARBOX_4_3: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_PILLARBOX_4_3; + case dsVIDEO_ZOOM_WIDE_4_3: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_WIDE_4_3; + default: + return VideoDeviceZoom::DS_VIDEO_DEVICE_ZOOM_FULL; + } + } + + dsVideoCodingFormat_t convertVideoCodecToDSFormat(const VideoDeviceCodec codec) + { + switch (codec) { + case VideoDeviceCodec::DS_VIDEO_CODEC_MPEGHPART2: + return dsVIDEO_CODEC_MPEGHPART2; + case VideoDeviceCodec::DS_VIDEO_CODEC_MPEG4PART10: + return dsVIDEO_CODEC_MPEG4PART10; + case VideoDeviceCodec::DS_VIDEO_CODEC_MPEG2: + return dsVIDEO_CODEC_MPEG2; + default: + return dsVIDEO_CODEC_MPEGHPART2; + } + } + + IDeviceSettingsVideoCodecProfileSupportIterator* createCodecInfoIterator(const dsVideoCodecInfo_t& info) + { + // This is a placeholder implementation + // In a real implementation, this would create an iterator from the codec info + LOGWARN("createCodecInfoIterator: Not implemented - returning nullptr"); + return nullptr; + } +}; \ No newline at end of file diff --git a/plugin/hal/dVideoPort.h b/plugin/hal/dVideoPort.h new file mode 100644 index 0000000..f1b1295 --- /dev/null +++ b/plugin/hal/dVideoPort.h @@ -0,0 +1,89 @@ +/* + * 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. + */ +#pragma once + +#include "dsVideoPort.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include "rfcapi.h" + +#include +#include +#include "DeviceSettingsTypes.h" + +#include + +namespace hal { +namespace dVideoPort { + + class IPlatform { + + public: + virtual ~IPlatform() {} + void InitialiseHAL(); + void DeInitialiseHAL(); + virtual void setAllCallbacks(const CallbackBundle& bundle) = 0; + virtual void getPersistenceValue() = 0; + + // VideoPort Platform interface methods - all pure virtual + virtual uint32_t GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t& handle) = 0; + virtual uint32_t IsVideoPortEnabled(const int32_t handle, bool& enabled) = 0; + virtual uint32_t EnableVideoPort(const int32_t handle, const bool enabled) = 0; + virtual uint32_t IsVideoPortDisplayConnected(const int32_t handle, bool& connected) = 0; + virtual uint32_t IsVideoPortActive(const int32_t handle, bool& active) = 0; + virtual uint32_t GetVideoPortResolution(const int32_t handle, VideoPortResolution& resolution) = 0; + virtual uint32_t SetVideoPortResolution(const int32_t handle, const VideoPortResolution resolution, const bool persist, const bool forceCompatibility) = 0; + virtual uint32_t GetColorDepth(const int32_t handle, uint32_t& colorDepth) = 0; + virtual uint32_t SetVideoPortColorDepth(const int32_t handle, const uint32_t colorDepth) = 0; + virtual uint32_t GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange& quantizationRange) = 0; + virtual uint32_t SetVideoPortQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange) = 0; + virtual uint32_t GetColorSpace(const int32_t handle, VideoPortColorSpace& colorSpace) = 0; + virtual uint32_t SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace) = 0; + virtual uint32_t GetVideoPortFrameRate(const int32_t handle, uint32_t& frameRate) = 0; + virtual uint32_t SetVideoPortFrameRate(const int32_t handle, const uint32_t frameRate) = 0; + virtual uint32_t GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus& hdcpStatus) = 0; + virtual uint32_t GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) = 0; + virtual uint32_t GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) = 0; + virtual uint32_t GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) = 0; + virtual uint32_t EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t* hdcpKey, const uint16_t hdcpKeySize) = 0; + virtual uint32_t IsHDCPEnabledOnVideoPort(const int32_t handle, bool& hdcpEnabled) = 0; + virtual uint32_t GetTVHDRCapabilities(const int32_t handle, int32_t& capabilities) = 0; + virtual uint32_t GetTVSupportedResolutions(const int32_t handle, int32_t& resolutions) = 0; + virtual uint32_t SetForceDisable4K(const int32_t handle, const bool disable) = 0; + virtual uint32_t GetForceDisable4K(const int32_t handle, bool& disabled) = 0; + virtual uint32_t IsVideoPortOutputHDR(const int32_t handle, bool& isHDR) = 0; + virtual uint32_t ResetVideoPortOutputToSDR() = 0; + virtual uint32_t GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) = 0; + virtual uint32_t SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) = 0; + virtual uint32_t GetVideoEOTF(const int32_t handle, HDRStandard& hdrStandard) = 0; + virtual uint32_t GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients& matrixCoefficients) = 0; + virtual uint32_t IsVideoPortDisplaySurround(const int32_t handle, bool& surround) = 0; + virtual uint32_t GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode& surroundMode) = 0; + virtual uint32_t GetCurrentOutputSettings(const int32_t handle, DSOutputSettings& outputSettings) = 0; + virtual uint32_t SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor) = 0; + virtual uint32_t SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode) = 0; + virtual uint32_t GetColorDepthCapabilities(const int32_t handle, uint32_t& colorDepthCapabilities) = 0; + virtual uint32_t GetPreferredColorDepth(const int32_t handle, DisplayColorDepth& colorDepth, const bool persist) = 0; + virtual uint32_t SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist) = 0; + + }; +} // namespace dVideoPort +} // namespace hal \ No newline at end of file diff --git a/plugin/hal/dVideoPortImpl.h b/plugin/hal/dVideoPortImpl.h new file mode 100644 index 0000000..ddea29a --- /dev/null +++ b/plugin/hal/dVideoPortImpl.h @@ -0,0 +1,1987 @@ +// Out-of-line virtual destructor definition for RTTI/typeinfo +/* + * 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. + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "dVideoPort.h" +#include "dsVideoPort.h" +#include "dsError.h" +#include "dsUtl.h" +#include "dsTypes.h" + +#include +#include "DeviceSettingsTypes.h" + +// Resolution defaults — matches dsVideoPort.c naming +#define DS_VP_DEFAULT_RESOLUTION "720p" +#define DS_VP_DEFAULT_RESOLUTION_1080P "1080p" +#define DS_VP_DEFAULT_RESOLUTION_2160P "2160p" + +static int videoPort_isInitialized = 0; +static int videoPort_isPlatInitialized = 0; + +// Persistent resolution settings — initialised in getPersistenceValue() based on profileType. +// TV profile (profileType=1) defaults to DS_VP_DEFAULT_RESOLUTION_2160P; STB defaults to DS_VP_DEFAULT_RESOLUTION_1080P. +static std::string _dsHDMIResolution = DS_VP_DEFAULT_RESOLUTION_1080P; +static std::string _dsCompResolution = DS_VP_DEFAULT_RESOLUTION_1080P; +static std::string _dsRFResolution = DS_VP_DEFAULT_RESOLUTION_1080P; +static std::string _dsBBResolution = DS_VP_DEFAULT_RESOLUTION_1080P; + +// Color depth settings - following dsVideoPort.c pattern +static const dsDisplayColorDepth_t DEFAULT_COLOR_DEPTH = dsDISPLAY_COLORDEPTH_AUTO; +// static dsDisplayColorDepth_t hdmiColorDepth = DEFAULT_COLOR_DEPTH; // Unused variable - commented out + +// Static global callback functions for VideoPort events - following HdmiIn pattern +static std::function g_VideoPortResolutionPreChangeCallback; +static std::function g_VideoPortResolutionPostChangeCallback; +static std::function g_VideoPortHDCPStatusChangeCallback; +static std::function g_VideoPortVideoFormatUpdateCallback; + +class dVideoPortImpl : public hal::dVideoPort::IPlatform { + + // delete copy constructor and assignment operator + dVideoPortImpl(const dVideoPortImpl&) = delete; + dVideoPortImpl& operator=(const dVideoPortImpl&) = delete; + +public: + dVideoPortImpl() + { + LOGINFO("dVideoPortImpl Constructor"); + getInstance() = this; // Set static instance for callback access + InitialiseHAL(); + } + + virtual ~dVideoPortImpl() + { + LOGINFO("dVideoPortImpl Destructor"); + DeInitialiseHAL(); + getInstance() = nullptr; // Clear static instance + } + + // Singleton getInstance method - following HdmiIn pattern + static dVideoPortImpl*& getInstance() + { + static dVideoPortImpl* instance = nullptr; + return instance; + } + + void InitialiseHAL() + { + LOGINFO("InitialiseHAL"); + // Note: videoPort_isInitialized should only be set in setAllCallbacks after callback registration + // Don't set it here as it prevents callback registration condition from working + + if (!videoPort_isPlatInitialized) { + LOGINFO("InitialiseHAL "); + dsError_t eError = dsVideoPortInit(); + if (dsERR_NONE != eError) { + LOGERR("InitialiseHAL: dsVideoPortInit failed with error: %d", eError); + return; + } + LOGINFO("InitialiseHAL: dsVideoPortInit succeeded"); + + // Load persistence values after successful initialization - following dsVideoPort.c pattern + getPersistenceValue(); + + videoPort_isPlatInitialized = 1; + LOGINFO("InitialiseHAL completed: videoPort_isPlatInitialized=%d, videoPort_isInitialized=%d", + videoPort_isPlatInitialized, videoPort_isInitialized); + } + } + + void DeInitialiseHAL() + { + LOGINFO("DeInitialiseHAL"); + if (videoPort_isPlatInitialized) + { + dsVideoPortTerm(); + videoPort_isPlatInitialized = 0; + } + videoPort_isInitialized = 0; + } + + static void* resolve(const std::string& libName, const std::string& symbolName) { + return WPEFramework::Plugin::DeviceSettingsHALLoader::ResolveSymbol(libName, symbolName); + } + + // Implementation of all VideoPort Platform interface methods + uint32_t GetVideoPort(const VideoPortType videoPort, const int32_t index, int32_t& handle) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoPort: videoPort=%d, index=%d", static_cast(videoPort), index); + + dsVideoPortType_t dsVideoPort = convertVideoPortType(videoPort); + intptr_t dsHandle; + + dsError_t eError = dsGetVideoPort(dsVideoPort, index, &dsHandle); + if (eError == dsERR_NONE) { + handle = static_cast(dsHandle); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoPort: SUCCESS - handle=%d", handle); + } else { + LOGERR("GetVideoPort: dsGetVideoPort failed with error: %d", eError); + } + + return retCode; + } + + uint32_t IsVideoPortEnabled(const int32_t handle, bool& enabled) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsVideoPortEnabled: handle=%d", handle); + + bool dsEnabled = false; + dsError_t eError = dsIsVideoPortEnabled(handle, &dsEnabled); + if (eError == dsERR_NONE) { + enabled = dsEnabled; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsVideoPortEnabled: SUCCESS - enabled=%s", enabled ? "true" : "false"); + } else { + LOGERR("IsVideoPortEnabled: dsIsVideoPortEnabled failed with error: %d", eError); + } + + return retCode; + } + + uint32_t EnableVideoPort(const int32_t handle, const bool enabled) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("EnableVideoPort: handle=%d, enabled=%s", handle, enabled ? "true" : "false"); + + dsError_t eError = dsEnableVideoPort(handle, enabled); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("EnableVideoPort: SUCCESS"); + } else { + LOGERR("EnableVideoPort: dsEnableVideoPort failed with error: %d", eError); + } + + return retCode; + } + + uint32_t IsVideoPortDisplayConnected(const int32_t handle, bool& connected) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsVideoPortDisplayConnected: handle=%d", handle); + + bool dsConnected = false; + dsError_t eError = dsIsDisplayConnected(handle, &dsConnected); + if (eError == dsERR_NONE) { + connected = dsConnected; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsVideoPortDisplayConnected: SUCCESS - connected=%s", connected ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplayConnected: dsIsDisplayConnected failed with error: %d", eError); + } + + return retCode; + } + + uint32_t IsVideoPortActive(const int32_t handle, bool& active) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsVideoPortActive: handle=%d", handle); + + bool dsActive = false; + dsError_t eError = dsIsVideoPortActive(handle, &dsActive); + if (eError == dsERR_NONE) { + active = dsActive; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsVideoPortActive: SUCCESS - active=%s", active ? "true" : "false"); + } else { + LOGERR("IsVideoPortActive: dsIsVideoPortActive failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetVideoPortResolution(const int32_t handle, VideoPortResolution& resolution) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoPortResolution: handle=%d", handle); + + dsVideoPortResolution_t dsResolution; + dsError_t eError = dsGetResolution(handle, &dsResolution); + if (eError == dsERR_NONE) { + resolution = convertVideoPortResolution(dsResolution); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoPortResolution: SUCCESS"); + } else { + LOGERR("GetVideoPortResolution: dsGetResolution failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetColorDepth(const int32_t handle, uint32_t& colorDepth) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetColorDepth: handle=%d", handle); + + typedef dsError_t (*dsGetColorDepth_t)(intptr_t handle, unsigned int* color_depth); + static dsGetColorDepth_t dsGetColorDepthFunc = 0; + + if (dsGetColorDepthFunc == 0) { + dsGetColorDepthFunc = (dsGetColorDepth_t)resolve(RDK_DSHAL_NAME, "dsGetColorDepth"); + if (dsGetColorDepthFunc == 0) { + LOGERR("GetColorDepth: dsGetColorDepth_t(int, unsigned int*) is not defined"); + } + else { + LOGINFO("GetColorDepth: dsGetColorDepth_t(int, unsigned int*) is defined and loaded"); + } + } + + if (dsGetColorDepthFunc != 0) { + unsigned int dsColorDepth = 0; + dsError_t eError = dsGetColorDepthFunc(handle, &dsColorDepth); + if (eError == dsERR_NONE) { + colorDepth = dsColorDepth; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetColorDepth: SUCCESS - colorDepth=%u", colorDepth); + } else { + LOGERR("GetColorDepth: dsGetColorDepth failed with error: %d", eError); + colorDepth = 0; // Default value on error + } + } else { + LOGERR("GetColorDepth: not able to load function dsGetColorDepthFunc:%p", dsGetColorDepthFunc); + colorDepth = 0; // Default value + } + + return retCode; + } + + uint32_t SetVideoPortColorDepth(const int32_t handle, const uint32_t colorDepth) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetVideoPortColorDepth: handle=%d, colorDepth=%u", handle, colorDepth); + + // Use dsSetPreferredColorDepth instead since dsSetVideoPortColorDepth may not exist + dsDisplayColorDepth_t dsColorDepth = static_cast(colorDepth); + dsError_t eError = dsSetPreferredColorDepth(handle, dsColorDepth); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetVideoPortColorDepth: SUCCESS (via dsSetPreferredColorDepth)"); + } else { + LOGERR("SetVideoPortColorDepth: dsSetPreferredColorDepth failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetQuantizationRange(const int32_t handle, VideoPortQuantizationRange& quantizationRange) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetQuantizationRange: handle=%d", handle); + + typedef dsError_t (*dsGetQuantizationRange_t)(intptr_t handle, dsDisplayQuantizationRange_t* quantization_range); + static dsGetQuantizationRange_t dsGetQuantizationRangeFunc = 0; + + if (dsGetQuantizationRangeFunc == 0) { + dsGetQuantizationRangeFunc = (dsGetQuantizationRange_t)resolve(RDK_DSHAL_NAME, "dsGetQuantizationRange"); + if(dsGetQuantizationRangeFunc == 0) { + LOGERR("dsGetQuantizationRange is not defined"); + } + else { + LOGINFO("dsGetQuantizationRange loaded"); + } + } + + if (dsGetQuantizationRangeFunc != 0) { + dsDisplayQuantizationRange_t dsQuantizationRange; + dsError_t eError = dsGetQuantizationRangeFunc(handle, &dsQuantizationRange); + if (eError == dsERR_NONE) { + quantizationRange = convertQuantizationRange(dsQuantizationRange); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetQuantizationRange: SUCCESS"); + } else { + LOGERR("GetQuantizationRange: dsGetQuantizationRange failed with error: %d", eError); + } + } else { + LOGERR("GetQuantizationRange: dsGetQuantizationRange function not available"); + quantizationRange = static_cast(dsDISPLAY_QUANTIZATIONRANGE_UNKNOWN); + } + + return retCode; + } + + uint32_t SetVideoPortQuantizationRange(const int32_t handle, const VideoPortQuantizationRange quantizationRange) override + { + // dsVideoPort.c has no dsSetQuantizationRange; quantization range is a read-only sink attribute + LOGWARN("SetVideoPortQuantizationRange: not supported by DS HAL (read-only sink property)"); + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetColorSpace(const int32_t handle, VideoPortColorSpace& colorSpace) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetColorSpace: handle=%d", handle); + + typedef dsError_t (*dsGetColorSpace_t)(intptr_t handle, dsDisplayColorSpace_t* color_space); + static dsGetColorSpace_t dsGetColorSpaceFunc = 0; + + if (dsGetColorSpaceFunc == 0) { + dsGetColorSpaceFunc = (dsGetColorSpace_t)resolve(RDK_DSHAL_NAME, "dsGetColorSpace"); + if(dsGetColorSpaceFunc == 0) { + LOGERR("dsGetColorSpace is not defined"); + } + else { + LOGINFO("dsGetColorSpace loaded"); + } + } + + if (dsGetColorSpaceFunc != 0) { + dsDisplayColorSpace_t dsColorSpace; + dsError_t eError = dsGetColorSpaceFunc(handle, &dsColorSpace); + if (eError == dsERR_NONE) { + colorSpace = static_cast(dsColorSpace); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetColorSpace: SUCCESS - colorSpace=%d", static_cast(colorSpace)); + } else { + LOGERR("GetColorSpace: dsGetColorSpace failed with error: %d", eError); + } + } else { + LOGERR("GetColorSpace: dsGetColorSpace function not available"); + colorSpace = static_cast(dsDISPLAY_COLORSPACE_RGB); // Default fallback + } + + return retCode; + } + + uint32_t SetColorSpace(const int32_t handle, const VideoPortColorSpace colorSpace) override + { + // dsVideoPort.c has no dsSetColorSpace; color space is a read-only EDID-negotiated property + LOGWARN("SetColorSpace: not supported by DS HAL (read-only sink property)"); + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetVideoPortFrameRate(const int32_t handle, uint32_t& frameRate) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoPortFrameRate: handle=%d", handle); + + // No standalone dsGetFrameRate API; frame rate is embedded in the resolution name (e.g. "1080p60", "2160p30") + dsVideoPortResolution_t dsResolution; + dsError_t eError = dsGetResolution(handle, &dsResolution); + if (eError == dsERR_NONE) { + switch (dsResolution.frameRate) { + case dsVIDEO_FRAMERATE_24: frameRate = 24; break; + case dsVIDEO_FRAMERATE_25: frameRate = 25; break; + case dsVIDEO_FRAMERATE_30: frameRate = 30; break; + case dsVIDEO_FRAMERATE_50: frameRate = 50; break; + case dsVIDEO_FRAMERATE_60: frameRate = 60; break; + case dsVIDEO_FRAMERATE_23dot98: frameRate = 24; break; + case dsVIDEO_FRAMERATE_29dot97: frameRate = 30; break; + case dsVIDEO_FRAMERATE_59dot94: frameRate = 60; break; + default: frameRate = 60; break; + } + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoPortFrameRate: SUCCESS - frameRate=%u (from resolution %s)", frameRate, dsResolution.name); + } else { + LOGERR("GetVideoPortFrameRate: dsGetResolution failed: %d", eError); + frameRate = 60; + } + + return retCode; + } + + uint32_t SetVideoPortFrameRate(const int32_t handle, const uint32_t frameRate) override + { + // No standalone dsSetFrameRate API; frame rate is set via dsSetResolution as part of the resolution name + LOGWARN("SetVideoPortFrameRate: not a separate HAL operation — frame rate is implicit in SetVideoPortResolution"); + return WPEFramework::Core::ERROR_NONE; + } + + uint32_t GetVideoPortHDCPStatus(const int32_t handle, VideoPortHdcpStatus& hdcpStatus) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoPortHDCPStatus: handle=%d", handle); + + dsHdcpStatus_t dsHdcpStatus; + dsError_t eError = dsGetHDCPStatus(handle, &dsHdcpStatus); + if (eError == dsERR_NONE) { + hdcpStatus = convertHdcpStatus(dsHdcpStatus); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoPortHDCPStatus: SUCCESS"); + } else if (eError == dsERR_INVALID_PARAM || eError == dsERR_OPERATION_NOT_SUPPORTED) { + // Internal/non-HDMI port — HDCP not applicable on this port type + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_UNPOWERED; + retCode = WPEFramework::Core::ERROR_NONE; + LOGWARN("GetVideoPortHDCPStatus: HDCP not supported on this port (error=%d)", eError); + } else { + LOGERR("GetVideoPortHDCPStatus: dsGetHDCPStatus failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetHDCPProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetHDCPProtocolVersionOnVideoPort: handle=%d", handle); + + typedef dsError_t (*dsGetHDCPProtocol_t)(intptr_t handle, dsHdcpProtocolVersion_t* protocolVersion); + static dsGetHDCPProtocol_t dsGetHDCPProtocolFunc = 0; + + if (dsGetHDCPProtocolFunc == 0) { + dsGetHDCPProtocolFunc = (dsGetHDCPProtocol_t)resolve(RDK_DSHAL_NAME, "dsGetHDCPProtocol"); + if(dsGetHDCPProtocolFunc == 0) { + LOGERR("dsGetHDCPProtocol is not defined"); + } + else { + LOGINFO("dsGetHDCPProtocol loaded"); + } + } + + if (dsGetHDCPProtocolFunc != 0) { + dsHdcpProtocolVersion_t dsHdcpVersion; + dsError_t eError = dsGetHDCPProtocolFunc(handle, &dsHdcpVersion); + if (eError == dsERR_NONE) { + hdcpVersion = convertHdcpProtocolVersion(dsHdcpVersion); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDCPProtocolVersionOnVideoPort: SUCCESS"); + } else { + LOGERR("GetHDCPProtocolVersionOnVideoPort: dsGetHDCPProtocol failed with error: %d", eError); + } + } else { + LOGERR("GetHDCPProtocolVersionOnVideoPort: dsGetHDCPProtocol function not available"); + } + + return retCode; + } + + uint32_t GetHDCPReceiverProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetHDCPReceiverProtocolVersionOnVideoPort: handle=%d", handle); + + typedef dsError_t (*dsGetHDCPReceiverProtocol_t)(intptr_t handle, dsHdcpProtocolVersion_t* protocolVersion); + static dsGetHDCPReceiverProtocol_t dsGetHDCPReceiverProtocolFunc = 0; + + if (dsGetHDCPReceiverProtocolFunc == 0) { + dsGetHDCPReceiverProtocolFunc = (dsGetHDCPReceiverProtocol_t)resolve(RDK_DSHAL_NAME, "dsGetHDCPReceiverProtocol"); + if(dsGetHDCPReceiverProtocolFunc == 0) { + LOGERR("dsGetHDCPReceiverProtocol is not defined"); + } + else { + LOGINFO("dsGetHDCPReceiverProtocol loaded"); + } + } + + if (dsGetHDCPReceiverProtocolFunc != 0) { + dsHdcpProtocolVersion_t dsHdcpVersion; + dsError_t eError = dsGetHDCPReceiverProtocolFunc(handle, &dsHdcpVersion); + if (eError == dsERR_NONE) { + hdcpVersion = convertHdcpProtocolVersion(dsHdcpVersion); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDCPReceiverProtocolVersionOnVideoPort: SUCCESS"); + } else { + LOGERR("GetHDCPReceiverProtocolVersionOnVideoPort: dsGetHDCPReceiverProtocol failed with error: %d", eError); + } + } else { + LOGERR("GetHDCPReceiverProtocolVersionOnVideoPort: dsGetHDCPReceiverProtocol function not available"); + } + + return retCode; + } + + uint32_t GetHDCPCurrentProtocolVersionOnVideoPort(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetHDCPCurrentProtocolVersionOnVideoPort: handle=%d", handle); + + typedef dsError_t (*dsGetHDCPCurrentProtocol_t)(intptr_t handle, dsHdcpProtocolVersion_t* protocolVersion); + static dsGetHDCPCurrentProtocol_t dsGetHDCPCurrentProtocolFunc = 0; + + if (dsGetHDCPCurrentProtocolFunc == 0) { + dsGetHDCPCurrentProtocolFunc = (dsGetHDCPCurrentProtocol_t)resolve(RDK_DSHAL_NAME, "dsGetHDCPCurrentProtocol"); + if(dsGetHDCPCurrentProtocolFunc == 0) { + LOGERR("dsGetHDCPCurrentProtocol is not defined"); + } + else { + LOGINFO("dsGetHDCPCurrentProtocol loaded"); + } + } + + if (dsGetHDCPCurrentProtocolFunc != 0) { + dsHdcpProtocolVersion_t dsHdcpVersion; + dsError_t eError = dsGetHDCPCurrentProtocolFunc(handle, &dsHdcpVersion); + if (eError == dsERR_NONE) { + hdcpVersion = convertHdcpProtocolVersion(dsHdcpVersion); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDCPCurrentProtocolVersionOnVideoPort: SUCCESS"); + } else { + LOGERR("GetHDCPCurrentProtocolVersionOnVideoPort: dsGetHDCPCurrentProtocol failed with error: %d", eError); + } + } else { + LOGERR("GetHDCPCurrentProtocolVersionOnVideoPort: dsGetHDCPCurrentProtocol function not available"); + } + + return retCode; + } + + uint32_t GetVideoEOTF(const int32_t handle, HDRStandard& hdrStandard) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoEOTF: handle=%d", handle); + + typedef dsError_t (*dsGetVideoEOTF_t)(intptr_t handle, dsHDRStandard_t* video_eotf); + static dsGetVideoEOTF_t dsGetVideoEOTFFunc = 0; + + if (dsGetVideoEOTFFunc == 0) { + dsGetVideoEOTFFunc = (dsGetVideoEOTF_t)resolve(RDK_DSHAL_NAME, "dsGetVideoEOTF"); + if(dsGetVideoEOTFFunc == 0) { + LOGERR("dsGetVideoEOTF is not defined"); + } + else { + LOGINFO("dsGetVideoEOTF loaded"); + } + } + + if (dsGetVideoEOTFFunc != 0) { + dsHDRStandard_t dsVideoEotf; + dsError_t eError = dsGetVideoEOTFFunc(handle, &dsVideoEotf); + if (eError == dsERR_NONE) { + hdrStandard = static_cast(dsVideoEotf); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoEOTF: SUCCESS - hdrStandard=%d", static_cast(hdrStandard)); + } else { + LOGERR("GetVideoEOTF: dsGetVideoEOTF failed with error: %d", eError); + } + } else { + LOGERR("GetVideoEOTF: dsGetVideoEOTF function not available"); + hdrStandard = static_cast(dsHDRSTANDARD_NONE); + } + + return retCode; + } + + uint32_t GetMatrixCoefficients(const int32_t handle, DisplayMatrixCoefficients& matrixCoefficients) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetMatrixCoefficients: handle=%d", handle); + + typedef dsError_t (*dsGetMatrixCoefficients_t)(intptr_t handle, dsDisplayMatrixCoefficients_t* matrix_coefficients); + static dsGetMatrixCoefficients_t dsGetMatrixCoefficientsFunc = 0; + + if (dsGetMatrixCoefficientsFunc == 0) { + dsGetMatrixCoefficientsFunc = (dsGetMatrixCoefficients_t)resolve(RDK_DSHAL_NAME, "dsGetMatrixCoefficients"); + if(dsGetMatrixCoefficientsFunc == 0) { + LOGERR("dsGetMatrixCoefficients is not defined"); + } + else { + LOGINFO("dsGetMatrixCoefficients loaded"); + } + } + + if (dsGetMatrixCoefficientsFunc != 0) { + dsDisplayMatrixCoefficients_t dsMatrixCoefficients; + dsError_t eError = dsGetMatrixCoefficientsFunc(handle, &dsMatrixCoefficients); + if (eError == dsERR_NONE) { + matrixCoefficients = static_cast(dsMatrixCoefficients); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetMatrixCoefficients: SUCCESS - matrixCoefficients=%d", static_cast(matrixCoefficients)); + } else { + LOGERR("GetMatrixCoefficients: dsGetMatrixCoefficients failed with error: %d", eError); + } + } else { + LOGERR("GetMatrixCoefficients: dsGetMatrixCoefficients function not available"); + matrixCoefficients = static_cast(dsDISPLAY_MATRIXCOEFFICIENT_UNKNOWN); + } + + return retCode; + } + + uint32_t IsVideoPortDisplaySurround(const int32_t handle, bool& surround) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsVideoPortDisplaySurround: handle=%d", handle); + + typedef dsError_t (*dsIsDisplaySurround_t)(intptr_t handle, bool *surround); + static dsIsDisplaySurround_t dsIsDisplaySurroundFunc = 0; + + if (dsIsDisplaySurroundFunc == 0) { + dsIsDisplaySurroundFunc = (dsIsDisplaySurround_t)resolve(RDK_DSHAL_NAME, "dsIsDisplaySurround"); + if(dsIsDisplaySurroundFunc == 0) { + LOGERR("dsIsDisplaySurround is not defined"); + } + else { + LOGINFO("dsIsDisplaySurround loaded"); + } + } + + if (dsIsDisplaySurroundFunc != 0) { + bool dsSurround = false; + dsError_t eError = dsIsDisplaySurroundFunc(handle, &dsSurround); + if (eError == dsERR_NONE) { + surround = dsSurround; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsVideoPortDisplaySurround: SUCCESS - surround=%s", surround ? "true" : "false"); + } else { + LOGERR("IsVideoPortDisplaySurround: dsIsDisplaySurround failed with error: %d", eError); + } + } else { + LOGERR("IsVideoPortDisplaySurround: dsIsDisplaySurround function not available"); + surround = false; + } + + return retCode; + } + + uint32_t GetVideoPortDisplaySurroundMode(const int32_t handle, VideoPortSurroundMode& surroundMode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetVideoPortDisplaySurroundMode: handle=%d", handle); + + typedef dsError_t (*dsGetSurroundMode_t)(intptr_t handle, int *surround); + static dsGetSurroundMode_t dsGetSurroundModeFunc = 0; + + if (dsGetSurroundModeFunc == 0) { + dsGetSurroundModeFunc = (dsGetSurroundMode_t)resolve(RDK_DSHAL_NAME, "dsGetSurroundMode"); + if(dsGetSurroundModeFunc == 0) { + LOGERR("dsGetSurroundMode is not defined"); + } + else { + LOGINFO("dsGetSurroundMode loaded"); + } + } + + if (dsGetSurroundModeFunc != 0) { + int dsSurroundMode = 0; + dsError_t eError = dsGetSurroundModeFunc(handle, &dsSurroundMode); + if (eError == dsERR_NONE) { + surroundMode = static_cast(dsSurroundMode); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetVideoPortDisplaySurroundMode: SUCCESS - surroundMode=%d", static_cast(surroundMode)); + } else { + LOGERR("GetVideoPortDisplaySurroundMode: dsGetSurroundMode failed with error: %d", eError); + } + } else { + LOGERR("GetVideoPortDisplaySurroundMode: dsGetSurroundMode function not available"); + surroundMode = VideoPortSurroundMode::DS_VIDEO_PORT_SURROUNDMODE_NONE; + } + + return retCode; + } + + uint32_t GetCurrentOutputSettings(const int32_t handle, DSOutputSettings& outputSettings) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetCurrentOutputSettings: handle=%d", handle); + + typedef dsError_t (*dsGetCurrentOutputSettings_t)(intptr_t handle, dsHDRStandard_t* video_eotf, dsDisplayMatrixCoefficients_t* matrix_coefficients, dsDisplayColorSpace_t* color_space, unsigned int* color_depth, dsDisplayQuantizationRange_t* quantization_range); + static dsGetCurrentOutputSettings_t dsGetCurrentOutputSettingsFunc = 0; + + if (dsGetCurrentOutputSettingsFunc == 0) { + dsGetCurrentOutputSettingsFunc = (dsGetCurrentOutputSettings_t)resolve(RDK_DSHAL_NAME, "dsGetCurrentOutputSettings"); + if(dsGetCurrentOutputSettingsFunc == 0) { + LOGERR("dsGetCurrentOutputSettings is not defined"); + } + else { + LOGINFO("dsGetCurrentOutputSettings loaded"); + } + } + + if (dsGetCurrentOutputSettingsFunc != 0) { + dsHDRStandard_t dsVideoEotf; + dsDisplayMatrixCoefficients_t dsMatrixCoefficients; + dsDisplayColorSpace_t dsColorSpace; + unsigned int dsColorDepth; + dsDisplayQuantizationRange_t dsQuantizationRange; + + dsError_t eError = dsGetCurrentOutputSettingsFunc(handle, &dsVideoEotf, &dsMatrixCoefficients, &dsColorSpace, &dsColorDepth, &dsQuantizationRange); + if (eError == dsERR_NONE) { + outputSettings.videoEotf = static_cast(dsVideoEotf); + outputSettings.matrixCoefficients = static_cast(dsMatrixCoefficients); + outputSettings.colorDepth = static_cast(dsColorDepth); + outputSettings.colorSpace = static_cast(dsColorSpace); + outputSettings.quantizationRange = static_cast(dsQuantizationRange); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetCurrentOutputSettings: SUCCESS - eotf=%d, matrix=%d, colorDepth=%u, colorSpace=%d, quantization=%d", + static_cast(outputSettings.videoEotf), static_cast(outputSettings.matrixCoefficients), + outputSettings.colorDepth, static_cast(outputSettings.colorSpace), static_cast(outputSettings.quantizationRange)); + } else { + LOGERR("GetCurrentOutputSettings: dsGetCurrentOutputSettings failed with error: %d", eError); + } + } else { + LOGERR("GetCurrentOutputSettings: dsGetCurrentOutputSettings function not available"); + // Set default values + outputSettings.videoEotf = static_cast(dsHDRSTANDARD_NONE); + outputSettings.matrixCoefficients = static_cast(dsDISPLAY_MATRIXCOEFFICIENT_UNKNOWN); + outputSettings.colorDepth = 0; + outputSettings.colorSpace = static_cast(dsDISPLAY_COLORSPACE_UNKNOWN); + outputSettings.quantizationRange = static_cast(dsDISPLAY_QUANTIZATIONRANGE_UNKNOWN); + } + + return retCode; + } + + uint32_t GetPreferredColorDepth(const int32_t handle, DisplayColorDepth& colorDepth, const bool persist) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetPreferredColorDepth: handle=%d, persist=%s", handle, persist ? "true" : "false"); + + if (persist) { + // Use persistent color depth - following dsVideoPort.c pattern + DisplayColorDepth persistentColorDepth = getPersistentColorDepth(); + colorDepth = persistentColorDepth; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetPreferredColorDepth: SUCCESS (from persistence) - colorDepth=%d", static_cast(colorDepth)); + } else { + // Get from HAL + typedef dsError_t (*dsGetPreferredColorDepth_t)(intptr_t handle, dsDisplayColorDepth_t *colorDepth); + static dsGetPreferredColorDepth_t dsGetPreferredColorDepthFunc = 0; + + if (dsGetPreferredColorDepthFunc == 0) { + dsGetPreferredColorDepthFunc = (dsGetPreferredColorDepth_t)resolve(RDK_DSHAL_NAME, "dsGetPreferredColorDepth"); + if(dsGetPreferredColorDepthFunc == 0) { + LOGERR("dsGetPreferredColorDepth is not defined"); + } + else { + LOGINFO("dsGetPreferredColorDepth loaded"); + } + } + + if (dsGetPreferredColorDepthFunc != 0) { + dsDisplayColorDepth_t dsColorDepth; + dsError_t eError = dsGetPreferredColorDepthFunc(handle, &dsColorDepth); + if (eError == dsERR_NONE) { + colorDepth = static_cast(dsColorDepth); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetPreferredColorDepth: SUCCESS (from HAL) - colorDepth=%d", static_cast(colorDepth)); + } else { + LOGERR("GetPreferredColorDepth: dsGetPreferredColorDepth failed with error: %d", eError); + } + } else { + LOGERR("GetPreferredColorDepth: dsGetPreferredColorDepth function not available"); + colorDepth = static_cast(dsDISPLAY_COLORDEPTH_UNKNOWN); + } + } + + return retCode; + } + + uint32_t SetPreferredColorDepth(const int32_t handle, const DisplayColorDepth colorDepth, const bool persist) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetPreferredColorDepth: handle=%d, colorDepth=%d, persist=%s", handle, static_cast(colorDepth), persist ? "true" : "false"); + + typedef dsError_t (*dsSetPreferredColorDepth_t)(intptr_t handle, dsDisplayColorDepth_t colorDepth); + static dsSetPreferredColorDepth_t dsSetPreferredColorDepthFunc = 0; + + if (dsSetPreferredColorDepthFunc == 0) { + dsSetPreferredColorDepthFunc = (dsSetPreferredColorDepth_t)resolve(RDK_DSHAL_NAME, "dsSetPreferredColorDepth"); + if(dsSetPreferredColorDepthFunc == 0) { + LOGERR("dsSetPreferredColorDepth is not defined"); + } + else { + LOGINFO("dsSetPreferredColorDepth loaded"); + } + } + + if (dsSetPreferredColorDepthFunc != 0) { + dsDisplayColorDepth_t dsColorDepth = static_cast(colorDepth); + dsError_t eError = dsSetPreferredColorDepthFunc(handle, dsColorDepth); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetPreferredColorDepth: SUCCESS"); + + // Persist color depth setting if requested - following dsVideoPort.c pattern + if (persist) { + try { + std::string colorDepthStr = std::to_string(static_cast(colorDepth)); + device::HostPersistence::getInstance().persistHostProperty("HDMI0.colorDepth", colorDepthStr); + LOGINFO("Color depth persisted: %s", colorDepthStr.c_str()); + } catch(...) { + LOGERR("Failed to persist color depth setting"); + } + } + } else { + LOGERR("SetPreferredColorDepth: dsSetPreferredColorDepth failed with error: %d", eError); + } + } else { + LOGERR("SetPreferredColorDepth: dsSetPreferredColorDepth function not available"); + } + + return retCode; + } + + uint32_t SetVideoPortResolution(const int32_t handle, const VideoPortResolution resolution, const bool persist, const bool forceCompatibility) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetVideoPortResolution: handle=%d, persist=%s, forceCompatibility=%s", handle, persist ? "true" : "false", forceCompatibility ? "true" : "false"); + + dsVideoPortResolution_t dsResolution = convertVideoPortResolution(resolution); + + // Trigger resolution pre-change callback + VideoPortPreResolutionChange(&dsResolution); + + dsError_t eError = dsSetResolution(handle, &dsResolution); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetVideoPortResolution: SUCCESS"); + + // Persist resolution setting if requested - following dsVideoPort.c pattern + if (persist) { + persistVideoPortResolution(handle, dsResolution, forceCompatibility); + } + + // Trigger resolution post-change callback on successful resolution change + VideoPortPostResolutionChange(&dsResolution); + } else { + LOGERR("SetVideoPortResolution: dsSetResolution failed with error: %d", eError); + } + + return retCode; + } + + uint32_t EnableHDCPOnVideoPort(const int32_t handle, const bool hdcpEnable, const uint8_t* hdcpKey, const uint16_t hdcpKeySize) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("EnableHDCPOnVideoPort: handle=%d, hdcpEnable=%s, hdcpKeySize=%u", handle, hdcpEnable ? "true" : "false", hdcpKeySize); + + dsError_t eError = dsEnableHDCP(handle, hdcpEnable, (char*)hdcpKey, static_cast(hdcpKeySize)); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("EnableHDCPOnVideoPort: SUCCESS"); + } else { + LOGERR("EnableHDCPOnVideoPort: dsEnableHDCP failed with error: %d", eError); + } + + return retCode; + } + + uint32_t IsHDCPEnabledOnVideoPort(const int32_t handle, bool& hdcpEnabled) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsHDCPEnabledOnVideoPort: handle=%d", handle); + + bool dsHdcpEnabled = false; + dsError_t eError = dsIsHDCPEnabled(handle, &dsHdcpEnabled); + if (eError == dsERR_NONE) { + hdcpEnabled = dsHdcpEnabled; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsHDCPEnabledOnVideoPort: SUCCESS - hdcpEnabled=%s", hdcpEnabled ? "true" : "false"); + } else { + LOGERR("IsHDCPEnabledOnVideoPort: dsIsHDCPEnabled failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetTVHDRCapabilities(const int32_t handle, int32_t& capabilities) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetTVHDRCapabilities: handle=%d", handle); + + typedef dsError_t (*dsGetTVHDRCapabilitiesFunc_t)(intptr_t handle, int* capabilities); + static dsGetTVHDRCapabilitiesFunc_t dsGetTVHDRCapabilitiesFunc = 0; + + if (dsGetTVHDRCapabilitiesFunc == 0) { + dsGetTVHDRCapabilitiesFunc = (dsGetTVHDRCapabilitiesFunc_t)resolve(RDK_DSHAL_NAME, "dsGetTVHDRCapabilities"); + if(dsGetTVHDRCapabilitiesFunc == 0) { + LOGERR("dsGetTVHDRCapabilities is not defined"); + } + else { + LOGINFO("dsGetTVHDRCapabilities loaded"); + } + } + + if (dsGetTVHDRCapabilitiesFunc != 0) { + int dsCapabilities = 0; + dsError_t eError = dsGetTVHDRCapabilitiesFunc(handle, &dsCapabilities); + if (eError == dsERR_NONE) { + capabilities = static_cast(dsCapabilities); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetTVHDRCapabilities: SUCCESS - capabilities=0x%x", capabilities); + } else { + LOGERR("GetTVHDRCapabilities: dsGetTVHDRCapabilities failed with error: %d", eError); + } + } else { + LOGERR("GetTVHDRCapabilities: dsGetTVHDRCapabilities function not available"); + capabilities = 0; // Default value + } + + return retCode; + } + + uint32_t GetTVSupportedResolutions(const int32_t handle, int32_t& resolutions) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetTVSupportedResolutions: handle=%d", handle); + + typedef dsError_t (*dsSupportedTvResolutionsFunc_t)(intptr_t handle, int* resolutions); + static dsSupportedTvResolutionsFunc_t dsSupportedTvResolutionsFunc = 0; + + if (dsSupportedTvResolutionsFunc == 0) { + dsSupportedTvResolutionsFunc = (dsSupportedTvResolutionsFunc_t)resolve(RDK_DSHAL_NAME, "dsSupportedTvResolutions"); + if(dsSupportedTvResolutionsFunc == 0) { + LOGERR("dsSupportedTvResolutions is not defined"); + } + else { + LOGINFO("dsSupportedTvResolutions loaded"); + } + } + + if (dsSupportedTvResolutionsFunc != 0) { + int dsResolutions = 0; + dsError_t eError = dsSupportedTvResolutionsFunc(handle, &dsResolutions); + if (eError == dsERR_NONE) { + resolutions = static_cast(dsResolutions); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetTVSupportedResolutions: SUCCESS - resolutions=0x%x", resolutions); + } else { + LOGERR("GetTVSupportedResolutions: dsSupportedTvResolutions failed with error: %d", eError); + } + } else { + LOGERR("GetTVSupportedResolutions: dsSupportedTvResolutions function not available"); + resolutions = 0; // Default value + } + + return retCode; + } + + uint32_t SetForceDisable4K(const int32_t handle, const bool disable) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetForceDisable4K: handle=%d, disable=%s", handle, disable ? "true" : "false"); + + dsError_t eError = dsSetForceDisable4KSupport(handle, disable); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetForceDisable4K: SUCCESS"); + /* Persist 4K disable state — matches dsVideoPort.c _dsSetForceDisable4K() */ + try { + device::HostPersistence::getInstance().persistHostProperty( + "VideoDevice.force4KDisabled", disable ? "true" : "false"); + LOGINFO("SetForceDisable4K: persisted VideoDevice.force4KDisabled=%s", + disable ? "true" : "false"); + } catch (...) { + LOGERR("SetForceDisable4K: failed to persist force4KDisabled"); + } + } else { + LOGERR("SetForceDisable4K: dsSetForceDisable4KSupport failed with error: %d", eError); + } + + return retCode; + } + + uint32_t GetForceDisable4K(const int32_t handle, bool& disabled) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetForceDisable4K: handle=%d", handle); + + // Use correct DS HAL function: dsGetForceDisable4KSupport + bool dsDisabled = false; + dsError_t eError = dsGetForceDisable4KSupport(handle, &dsDisabled); + if (eError == dsERR_NONE) { + disabled = dsDisabled; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetForceDisable4K: SUCCESS - disabled=%s", disabled ? "true" : "false"); + } else { + LOGERR("GetForceDisable4K: dsGetForceDisable4KSupport failed with error: %d", eError); + disabled = false; // Default value on error + } + + return retCode; + } + + uint32_t IsVideoPortOutputHDR(const int32_t handle, bool& isHDR) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("IsVideoPortOutputHDR: handle=%d", handle); + + typedef dsError_t (*dsIsOutputHDR_t)(intptr_t handle, bool* isHDR); + static dsIsOutputHDR_t dsIsOutputHDRFunc = 0; + + if (dsIsOutputHDRFunc == 0) { + dsIsOutputHDRFunc = (dsIsOutputHDR_t)resolve(RDK_DSHAL_NAME, "dsIsOutputHDR"); + if(dsIsOutputHDRFunc == 0) { + LOGERR("dsIsOutputHDR is not defined"); + } + else { + LOGINFO("dsIsOutputHDR loaded"); + } + } + + if (dsIsOutputHDRFunc != 0) { + bool dsIsHDR = false; + dsError_t eError = dsIsOutputHDRFunc(handle, &dsIsHDR); + if (eError == dsERR_NONE) { + isHDR = dsIsHDR; + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("IsVideoPortOutputHDR: SUCCESS - isHDR=%s", isHDR ? "true" : "false"); + } else { + LOGERR("IsVideoPortOutputHDR: dsIsOutputHDR failed with error: %d", eError); + } + } else { + LOGERR("IsVideoPortOutputHDR: dsIsOutputHDR function not available"); + isHDR = false; // Default value + } + + return retCode; + } + + uint32_t ResetVideoPortOutputToSDR() override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("ResetVideoPortOutputToSDR"); + + typedef dsError_t (*dsResetOutputToSDR_t)(void); + static dsResetOutputToSDR_t dsResetOutputToSDRFunc = 0; + + if (dsResetOutputToSDRFunc == 0) { + dsResetOutputToSDRFunc = (dsResetOutputToSDR_t)resolve(RDK_DSHAL_NAME, "dsResetOutputToSDR"); + if(dsResetOutputToSDRFunc == 0) { + LOGERR("dsResetOutputToSDR is not defined"); + } + else { + LOGINFO("dsResetOutputToSDR loaded"); + } + } + + if (dsResetOutputToSDRFunc != 0) { + dsError_t eError = dsResetOutputToSDRFunc(); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("ResetVideoPortOutputToSDR: SUCCESS"); + } else { + LOGERR("ResetVideoPortOutputToSDR: dsResetOutputToSDR failed with error: %d", eError); + } + } else { + LOGERR("ResetVideoPortOutputToSDR: dsResetOutputToSDR function not available"); + } + + return retCode; + } + + uint32_t GetHDMIPreference(const int32_t handle, VideoPortHdcpProtocolVersion& hdcpVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetHDMIPreference: handle=%d", handle); + + typedef dsError_t (*dsGetHdmiPreference_t)(intptr_t handle, dsHdcpProtocolVersion_t* hdcpVersion); + static dsGetHdmiPreference_t dsGetHdmiPreferenceFunc = 0; + + if (dsGetHdmiPreferenceFunc == 0) { + dsGetHdmiPreferenceFunc = (dsGetHdmiPreference_t)resolve(RDK_DSHAL_NAME, "dsGetHdmiPreference"); + if(dsGetHdmiPreferenceFunc == 0) { + LOGERR("dsGetHdmiPreference is not defined"); + } + else { + LOGINFO("dsGetHdmiPreference loaded"); + } + } + + if (dsGetHdmiPreferenceFunc != 0) { + dsHdcpProtocolVersion_t dsHdcpVersion; + dsError_t eError = dsGetHdmiPreferenceFunc(handle, &dsHdcpVersion); + if (eError == dsERR_NONE) { + hdcpVersion = convertHdcpProtocolVersion(dsHdcpVersion); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetHDMIPreference: SUCCESS - hdcpVersion=%d", static_cast(hdcpVersion)); + } else { + LOGERR("GetHDMIPreference: dsGetHdmiPreference failed with error: %d", eError); + } + } else { + LOGERR("GetHDMIPreference: dsGetHdmiPreference function not available"); + } + + return retCode; + } + + uint32_t SetHDMIPreference(const int32_t handle, const VideoPortHdcpProtocolVersion hdcpVersion) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetHDMIPreference: handle=%d, hdcpVersion=%d", handle, static_cast(hdcpVersion)); + + typedef dsError_t (*dsSetHdmiPreference_t)(intptr_t handle, dsHdcpProtocolVersion_t* hdcpVersion); + static dsSetHdmiPreference_t dsSetHdmiPreferenceFunc = 0; + + if (dsSetHdmiPreferenceFunc == 0) { + dsSetHdmiPreferenceFunc = (dsSetHdmiPreference_t)resolve(RDK_DSHAL_NAME, "dsSetHdmiPreference"); + if(dsSetHdmiPreferenceFunc == 0) { + LOGERR("dsSetHdmiPreference is not defined"); + } + else { + LOGINFO("dsSetHdmiPreference loaded"); + } + } + + if (dsSetHdmiPreferenceFunc != 0) { + dsHdcpProtocolVersion_t dsHdcpVersion = convertHdcpProtocolVersionToDSHal(hdcpVersion); + dsError_t eError = dsSetHdmiPreferenceFunc(handle, &dsHdcpVersion); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetHDMIPreference: SUCCESS"); + } else { + LOGERR("SetHDMIPreference: dsSetHdmiPreference failed with error: %d", eError); + } + } else { + LOGERR("SetHDMIPreference: dsSetHdmiPreference function not available"); + } + + return retCode; + } + + uint32_t SetBackgroundColor(const int32_t handle, const VideoBackgroundColor backgroundColor) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetBackgroundColor: handle=%d, backgroundColor=%d", handle, static_cast(backgroundColor)); + + dsVideoBackgroundColor_t dsBackgroundColor = static_cast(backgroundColor); + dsError_t eError = dsSetBackgroundColor(handle, dsBackgroundColor); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetBackgroundColor: SUCCESS"); + } else { + LOGERR("SetBackgroundColor: dsSetBackgroundColor failed with error: %d", eError); + } + + return retCode; + } + + uint32_t SetForceHDRMode(const int32_t handle, const HDRStandard hdrMode) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("SetForceHDRMode: handle=%d, hdrMode=%d", handle, static_cast(hdrMode)); + + typedef dsError_t (*dsSetForceHDRMode_t)(intptr_t handle, dsHDRStandard_t hdrMode); + static dsSetForceHDRMode_t dsSetForceHDRModeFunc = 0; + + if (dsSetForceHDRModeFunc == 0) { + dsSetForceHDRModeFunc = (dsSetForceHDRMode_t)resolve(RDK_DSHAL_NAME, "dsSetForceHDRMode"); + if(dsSetForceHDRModeFunc == 0) { + LOGERR("dsSetForceHDRMode is not defined"); + } + else { + LOGINFO("dsSetForceHDRMode loaded"); + } + } + + if (dsSetForceHDRModeFunc != 0) { + dsHDRStandard_t dsHdrMode = static_cast(hdrMode); + dsError_t eError = dsSetForceHDRModeFunc(handle, dsHdrMode); + if (eError == dsERR_NONE) { + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("SetForceHDRMode: SUCCESS"); + } else if (eError == dsERR_OPERATION_NOT_SUPPORTED) { + LOGWARN("SetForceHDRMode: not supported on this platform"); + } else { + LOGERR("SetForceHDRMode: dsSetForceHDRMode failed with error: %d", eError); + } + } else { + LOGERR("SetForceHDRMode: dsSetForceHDRMode function not available"); + } + + return retCode; + } + + uint32_t GetColorDepthCapabilities(const int32_t handle, uint32_t& colorDepthCapabilities) override + { + uint32_t retCode = WPEFramework::Core::ERROR_GENERAL; + LOGINFO("GetColorDepthCapabilities: handle=%d", handle); + + typedef dsError_t (*dsColorDepthCapabilities_t)(intptr_t handle, unsigned int* colorDepthCapability); + static dsColorDepthCapabilities_t dsColorDepthCapabilitiesFunc = 0; + + if (dsColorDepthCapabilitiesFunc == 0) { + dsColorDepthCapabilitiesFunc = (dsColorDepthCapabilities_t)resolve(RDK_DSHAL_NAME, "dsColorDepthCapabilities"); + if (dsColorDepthCapabilitiesFunc == 0) { + LOGERR("GetColorDepthCapabilities: dsColorDepthCapabilities(intptr_t handle, unsigned int *colorDepthCapability ) is not defined"); + } + else { + LOGINFO("GetColorDepthCapabilities: dsColorDepthCapabilities(intptr_t handle, unsigned int *colorDepthCapability ) is defined and loaded"); + } + } + + if (dsColorDepthCapabilitiesFunc != 0) { + unsigned int dsColorDepthCapabilities = 0; + dsError_t eError = dsColorDepthCapabilitiesFunc(handle, &dsColorDepthCapabilities); + if (eError == dsERR_NONE) { + LOGINFO("GetColorDepthCapabilities: dsColorDepthCapabilities returned:%d colorDepthCapability: 0x%x", + eError, dsColorDepthCapabilities); + + // Add auto by default - consistent with _dsColorDepthCapabilities in dsVideoPort.c + dsColorDepthCapabilities = (dsColorDepthCapabilities | dsDISPLAY_COLORDEPTH_AUTO); + + colorDepthCapabilities = static_cast(dsColorDepthCapabilities); + retCode = WPEFramework::Core::ERROR_NONE; + LOGINFO("GetColorDepthCapabilities: SUCCESS - final colorDepthCapabilities=0x%x", colorDepthCapabilities); + } else { + LOGERR("GetColorDepthCapabilities: dsColorDepthCapabilities failed with error: %d", eError); + colorDepthCapabilities = 0; // Default value on error + } + } else { + LOGERR("GetColorDepthCapabilities: not able to load function dsColorDepthCapabilitiesFunc:%p", dsColorDepthCapabilitiesFunc); + colorDepthCapabilities = 0; // Default value + } + + return retCode; + } + + // VideoPort Event Handling Infrastructure - following HdmiIn singleton pattern + void setAllCallbacks(const CallbackBundle& bundle) override + { + ENTRY_LOG; + LOGINFO("VideoPort::setAllCallbacks - Registering event callbacks with DS HAL"); + + // Debug logging to diagnose condition failure + LOGINFO("VideoPort callback registration check: videoPort_isInitialized=%d, videoPort_isPlatInitialized=%d", + videoPort_isInitialized, videoPort_isPlatInitialized); + + if (videoPort_isPlatInitialized && !videoPort_isInitialized) { + LOGINFO("VideoPort platform callback Initialization"); + + // Register Resolution Pre/Post Change callbacks + if (bundle.OnResolutionPreChange) { + LOGINFO("VideoPort Resolution PreChange Event Callback Registered"); + g_VideoPortResolutionPreChangeCallback = bundle.OnResolutionPreChange; + // Resolution callbacks are handled manually during resolution setting + } + + if (bundle.OnResolutionPostChange) { + LOGINFO("VideoPort Resolution PostChange Event Callback Registered"); + g_VideoPortResolutionPostChangeCallback = bundle.OnResolutionPostChange; + // Resolution callbacks are handled manually during resolution setting + } + + // Register HDCP Status Callback with DS HAL + if (bundle.OnHDCPStatusChange) { + LOGINFO("VideoPort HDCP Status Change Event Callback Registered"); + g_VideoPortHDCPStatusChangeCallback = bundle.OnHDCPStatusChange; + + intptr_t handle = 0; + dsError_t eReturn = dsGetVideoPort(dsVIDEOPORT_TYPE_HDMI, 0, &handle); + if (dsERR_NONE != eReturn) { + eReturn = dsGetVideoPort(dsVIDEOPORT_TYPE_INTERNAL, 0, &handle); + } + + if (dsERR_NONE == eReturn && handle != 0) { + LOGINFO("Registering HDCP status callback with handle: %p", (void*)handle); + dsRegisterHdcpStatusCallback(handle, VideoPortHDCPStatusCallback); + } else { + LOGERR("Failed to get video port handle for HDCP callback registration"); + } + } + + // Register Video Format Update Callback with DS HAL + if (bundle.OnVideoFormatUpdate) { + LOGINFO("VideoPort Video Format Update Event Callback Registered"); + g_VideoPortVideoFormatUpdateCallback = bundle.OnVideoFormatUpdate; + + dsError_t eRet = VideoPortRegisterVideoFormatUpdateCB(VideoPortVideoFormatUpdateCallback); + if (dsERR_NONE != eRet) { + LOGERR("VideoPortRegisterVideoFormatUpdateCB failed with error: %d", eRet); + } else { + LOGINFO("Video format update callback registered successfully"); + } + } + + videoPort_isInitialized = 1; + LOGINFO("VideoPort platform callback Initialization done"); + } else { + if (!videoPort_isPlatInitialized) { + LOGERR("VideoPort callback registration FAILED: Platform not initialized (videoPort_isPlatInitialized=%d)", + videoPort_isPlatInitialized); + } + if (videoPort_isInitialized) { + LOGWARN("VideoPort callback registration SKIPPED: Callbacks already initialized (videoPort_isInitialized=%d)", + videoPort_isInitialized); + } + } + + EXIT_LOG; + } + + void getPersistenceValue() + { + ENTRY_LOG; + LOGINFO("VideoPort::getPersistenceValue - Loading persistence settings"); + + try { + // Match dsVideoPort.c pattern: TV profile (profileType=1) defaults to 2160p, STB to 1080p + std::string defaultResolution = (profileType == 1) ? DS_VP_DEFAULT_RESOLUTION_2160P : DS_VP_DEFAULT_RESOLUTION_1080P; + + _dsHDMIResolution = device::HostPersistence::getInstance().getProperty("HDMI0.resolution", defaultResolution); + LOGINFO("Persistent HDMI resolution read: %s", _dsHDMIResolution.c_str()); + + #ifdef HAS_ONLY_COMPOSITE + _dsCompResolution = device::HostPersistence::getInstance().getProperty("Baseband0.resolution", defaultResolution); + #else + _dsCompResolution = device::HostPersistence::getInstance().getProperty("COMPONENT0.resolution", defaultResolution); + #endif + LOGINFO("Persistent Component/Composite resolution read: %s", _dsCompResolution.c_str()); + + _dsRFResolution = device::HostPersistence::getInstance().getProperty("RF0.resolution", defaultResolution); + LOGINFO("Persistent RF resolution read: %s", _dsRFResolution.c_str()); + + _dsBBResolution = device::HostPersistence::getInstance().getProperty("Baseband0.resolution", defaultResolution); + LOGINFO("Persistent BB resolution read: %s", _dsBBResolution.c_str()); + + // Read 4K disable setting and apply to HAL — matches dsVideoPort.c getPersistenceValue() + std::string force4KDisabled = "false"; + force4KDisabled = device::HostPersistence::getInstance().getProperty("VideoDevice.force4KDisabled", force4KDisabled); + if (force4KDisabled.compare("true") == 0) { + LOGINFO("4K support is force disabled via persistence — applying to HAL"); + intptr_t hdmiHandle = 0; + if (dsGetVideoPort(dsVIDEOPORT_TYPE_HDMI, 0, &hdmiHandle) == dsERR_NONE) { + dsSetForceDisable4KSupport(hdmiHandle, true); + } + } + + } catch(...) { + LOGERR("Error reading persistence values for VideoPort"); + } + + EXIT_LOG; + } + + // Static callback functions for DS HAL integration - following HdmiIn pattern + static void VideoPortHDCPStatusCallback(intptr_t handle, dsHdcpStatus_t status) + { + LOGINFO("VideoPortHDCPStatusCallback: handle=%p, status=%d", (void*)handle, status); + + // Convert DS HAL HDCP status to VideoPortHdcpStatus + VideoPortHdcpStatus hdcpStatus; + switch (status) { + case dsHDCP_STATUS_UNPOWERED: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_UNPOWERED; + break; + case dsHDCP_STATUS_UNAUTHENTICATED: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_UNAUTHENTICATED; + break; + case dsHDCP_STATUS_AUTHENTICATED: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_AUTHENTICATED; + break; + case dsHDCP_STATUS_AUTHENTICATIONFAILURE: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_AUTHENTICATIONFAILURE; + break; + case dsHDCP_STATUS_INPROGRESS: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_INPROGRESS; + break; + case dsHDCP_STATUS_PORTDISABLED: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_PORTDISABLED; + break; + default: + hdcpStatus = VideoPortHdcpStatus::DS_HDCP_STATUS_UNAUTHENTICATED; + LOGWARN("VideoPortHDCPStatusCallback: unknown HDCP status %d, defaulting to unauthenticated", status); + break; + } + + // Call the stored global callback if available + if (g_VideoPortHDCPStatusChangeCallback) { + g_VideoPortHDCPStatusChangeCallback(hdcpStatus); + } + } + + static void VideoPortVideoFormatUpdateCallback(dsHDRStandard_t videoFormat) + { + LOGINFO("VideoPortVideoFormatUpdateCallback: videoFormat=%d", videoFormat); + + // Convert DS HAL HDR standard to HDRStandard + HDRStandard hdrStandard; + switch (videoFormat) { + case dsHDRSTANDARD_NONE: // 0 = no HDR signal / SDR + case dsHDRSTANDARD_SDR: + hdrStandard = HDRStandard::DS_HDRSTANDARD_SDR; + break; + case dsHDRSTANDARD_HDR10: + hdrStandard = HDRStandard::DS_HDRSTANDARD_HDR10; + break; + case dsHDRSTANDARD_HDR10PLUS: + hdrStandard = HDRStandard::DS_HDRSTANDARD_HDR10PLUS; + break; + case dsHDRSTANDARD_DolbyVision: + hdrStandard = HDRStandard::DS_HDRSTANDARD_DOLBYVISION; + break; + default: + hdrStandard = HDRStandard::DS_HDRSTANDARD_SDR; + LOGWARN("Unrecognised HDR standard %d, treating as SDR", videoFormat); + break; + } + + // Call the stored global callback if available + if (g_VideoPortVideoFormatUpdateCallback) { + g_VideoPortVideoFormatUpdateCallback(hdrStandard); + } + } + + // DS HAL Video Format Update Callback Registration + static dsError_t VideoPortRegisterVideoFormatUpdateCB(dsVideoFormatUpdateCB_t cbFun) + { + dsError_t eRet = dsERR_GENERAL; + LOGINFO("VideoPortRegisterVideoFormatUpdateCB: Registering video format callback"); + + typedef dsError_t (*dsVideoFormatUpdateRegisterCB_t)(dsVideoFormatUpdateCB_t cbFunArg); + static dsVideoFormatUpdateRegisterCB_t dsVideoFormatUpdateRegisterCBFunc = 0; + + if (dsVideoFormatUpdateRegisterCBFunc == 0) { + void* dllib = dlopen(RDK_DSHAL_NAME, RTLD_LAZY); + if (dllib) { + dsVideoFormatUpdateRegisterCBFunc = (dsVideoFormatUpdateRegisterCB_t) dlsym(dllib, "dsVideoFormatUpdateRegisterCB"); + if (dsVideoFormatUpdateRegisterCBFunc == 0) { + LOGERR("dsVideoFormatUpdateRegisterCB is not defined: %s", dlerror()); + eRet = dsERR_GENERAL; + } else { + LOGINFO("dsVideoFormatUpdateRegisterCB loaded successfully"); + } + dlclose(dllib); + } else { + LOGERR("Failed to open RDK_DSHAL_NAME [%s]: %s", RDK_DSHAL_NAME, dlerror()); + eRet = dsERR_GENERAL; + } + } + + if (dsVideoFormatUpdateRegisterCBFunc != 0) { + eRet = dsVideoFormatUpdateRegisterCBFunc(cbFun); + if (dsERR_NONE == eRet) { + LOGINFO("Video format update callback registered successfully"); + } else { + LOGERR("Failed to register video format callback: %d", eRet); + } + } + + return eRet; + } + + // Resolution Change Helper Functions - Following dsVideoPort.c RPC server pattern + static void VideoPortPreResolutionChange(dsVideoPortResolution_t* resolution) + { + if (!resolution) { + LOGERR("VideoPortPreResolutionChange: Invalid resolution parameter"); + return; + } + + LOGINFO("VideoPortPreResolutionChange: pixelResolution=%d", resolution->pixelResolution); + + // Convert dsVideoPortResolution_t to ResolutionChange structure - based on dsVideoPort.c + ResolutionChange resolutionChange; + switch(resolution->pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + resolutionChange.width = 720; + resolutionChange.height = 480; + break; + case dsVIDEO_PIXELRES_720x576: + resolutionChange.width = 720; + resolutionChange.height = 576; + break; + case dsVIDEO_PIXELRES_1280x720: + resolutionChange.width = 1280; + resolutionChange.height = 720; + break; + case dsVIDEO_PIXELRES_1366x768: + resolutionChange.width = 1366; + resolutionChange.height = 768; + break; + case dsVIDEO_PIXELRES_1920x1080: + resolutionChange.width = 1920; + resolutionChange.height = 1080; + break; + case dsVIDEO_PIXELRES_3840x2160: + resolutionChange.width = 3840; + resolutionChange.height = 2160; + break; + case dsVIDEO_PIXELRES_4096x2160: + resolutionChange.width = 4096; + resolutionChange.height = 2160; + break; + default: + resolutionChange.width = 1280; + resolutionChange.height = 720; + LOGERR("Unknown pixel resolution: %d, defaulting to 720p", resolution->pixelResolution); + break; + } + + // Call the stored global callback if available + if (g_VideoPortResolutionPreChangeCallback) { + g_VideoPortResolutionPreChangeCallback(resolutionChange); + } + } + + static void VideoPortPostResolutionChange(dsVideoPortResolution_t* resolution) + { + if (!resolution) { + LOGERR("VideoPortPostResolutionChange: Invalid resolution parameter"); + return; + } + + LOGINFO("VideoPortPostResolutionChange: pixelResolution=%d", resolution->pixelResolution); + + ResolutionChange resolutionChange; + switch(resolution->pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + resolutionChange.width = 720; + resolutionChange.height = 480; + break; + case dsVIDEO_PIXELRES_720x576: + resolutionChange.width = 720; + resolutionChange.height = 576; + break; + case dsVIDEO_PIXELRES_1280x720: + resolutionChange.width = 1280; + resolutionChange.height = 720; + break; + case dsVIDEO_PIXELRES_1366x768: + resolutionChange.width = 1366; + resolutionChange.height = 768; + break; + case dsVIDEO_PIXELRES_1920x1080: + resolutionChange.width = 1920; + resolutionChange.height = 1080; + break; + case dsVIDEO_PIXELRES_3840x2160: + resolutionChange.width = 3840; + resolutionChange.height = 2160; + break; + case dsVIDEO_PIXELRES_4096x2160: + resolutionChange.width = 4096; + resolutionChange.height = 2160; + break; + default: + resolutionChange.width = 1280; + resolutionChange.height = 720; + LOGERR("Unknown pixel resolution: %d, defaulting to 720p", resolution->pixelResolution); + break; + } + + // Call the stored global callback if available + if (g_VideoPortResolutionPostChangeCallback) { + g_VideoPortResolutionPostChangeCallback(resolutionChange); + } + } + + static void convertDSResolutionToResolutionChange(dsVideoPortResolution_t* dsResolution, ResolutionChange& resolutionChange) + { + // Convert pixel resolution to width/height based on dsVideoPort.c pattern + switch (dsResolution->pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + resolutionChange.width = 720; + resolutionChange.height = 480; + break; + case dsVIDEO_PIXELRES_720x576: + resolutionChange.width = 720; + resolutionChange.height = 576; + break; + case dsVIDEO_PIXELRES_1280x720: + resolutionChange.width = 1280; + resolutionChange.height = 720; + break; + case dsVIDEO_PIXELRES_1920x1080: + resolutionChange.width = 1920; + resolutionChange.height = 1080; + break; + case dsVIDEO_PIXELRES_3840x2160: + resolutionChange.width = 3840; + resolutionChange.height = 2160; + break; + case dsVIDEO_PIXELRES_4096x2160: + resolutionChange.width = 4096; + resolutionChange.height = 2160; + break; + default: + resolutionChange.width = 1920; + resolutionChange.height = 1080; + LOGERR("Unknown pixel resolution: %d, defaulting to 1920x1080", dsResolution->pixelResolution); + break; + } + + // Note: ResolutionChange only has width/height members + // Additional information like pixelResolution, frameRate, interlaced are not part of the interface + } + +private: + + + // Helper methods for DS VideoPort HAL conversion + dsVideoPortType_t convertVideoPortType(const VideoPortType videoPort) + { + switch (videoPort) { + case VideoPortType::DS_VIDEO_PORT_TYPE_HDMI: + return dsVIDEOPORT_TYPE_HDMI; + case VideoPortType::DS_VIDEO_PORT_TYPE_COMPONENT: + return dsVIDEOPORT_TYPE_COMPONENT; + case VideoPortType::DS_VIDEO_PORT_TYPE_SVIDEO: + return dsVIDEOPORT_TYPE_SVIDEO; + case VideoPortType::DS_VIDEO_PORT_TYPE_1394: + return dsVIDEOPORT_TYPE_1394; + case VideoPortType::DS_VIDEO_PORT_TYPE_DVI: + return dsVIDEOPORT_TYPE_DVI; + case VideoPortType::DS_VIDEO_PORT_TYPE_INTERNAL: + return dsVIDEOPORT_TYPE_INTERNAL; + default: + return dsVIDEOPORT_TYPE_HDMI; + } + } + + VideoPortType convertVideoPortType(const dsVideoPortType_t dsVideoPort) + { + switch (dsVideoPort) { + case dsVIDEOPORT_TYPE_HDMI: + return VideoPortType::DS_VIDEO_PORT_TYPE_HDMI; + case dsVIDEOPORT_TYPE_COMPONENT: + return VideoPortType::DS_VIDEO_PORT_TYPE_COMPONENT; + case dsVIDEOPORT_TYPE_SVIDEO: + return VideoPortType::DS_VIDEO_PORT_TYPE_SVIDEO; + case dsVIDEOPORT_TYPE_1394: + return VideoPortType::DS_VIDEO_PORT_TYPE_1394; + case dsVIDEOPORT_TYPE_DVI: + return VideoPortType::DS_VIDEO_PORT_TYPE_DVI; + case dsVIDEOPORT_TYPE_INTERNAL: + return VideoPortType::DS_VIDEO_PORT_TYPE_INTERNAL; + default: + return VideoPortType::DS_VIDEO_PORT_TYPE_HDMI; + } + } + + VideoPortResolution convertVideoPortResolution(const dsVideoPortResolution_t& dsResolution) + { + VideoPortResolution resolution; + + /* Use the name filled in by dsGetResolution() — this is exactly what + * device::VideoOutputPort::getResolution().getName() returns in the + * DS_IARM path (e.g. "1080i", "1080p", "720p", "2160p30"). + * Fall back to deriving the name from pixelResolution + interlaced only + * when the HAL left the name field empty. */ + if (dsResolution.name[0] != '\0') { + resolution.name = std::string(dsResolution.name); + } else { + switch (dsResolution.pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + resolution.name = dsResolution.interlaced ? "480i" : "480p"; + break; + case dsVIDEO_PIXELRES_720x576: + resolution.name = dsResolution.interlaced ? "576i50" : "576p50"; + break; + case dsVIDEO_PIXELRES_1280x720: + resolution.name = "720p"; + break; + case dsVIDEO_PIXELRES_1366x768: + resolution.name = "768p60"; + break; + case dsVIDEO_PIXELRES_1920x1080: + resolution.name = dsResolution.interlaced ? "1080i" : "1080p"; + break; + case dsVIDEO_PIXELRES_3840x2160: + resolution.name = "2160p60"; + break; + case dsVIDEO_PIXELRES_4096x2160: + resolution.name = "4096x2160"; + break; + default: + resolution.name = "1080p"; + break; + } + } + + // Map DS pixel resolution to interface VideoResolution enum + switch (dsResolution.pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_720X480; + break; + case dsVIDEO_PIXELRES_720x576: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_720X576; + break; + case dsVIDEO_PIXELRES_1280x720: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_1280X720; + break; + case dsVIDEO_PIXELRES_1920x1080: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_1920X1080; + break; + case dsVIDEO_PIXELRES_3840x2160: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_3840X2160; + break; + default: + resolution.pixelResolution = VideoResolution::DS_VIDEO_PIXELRES_1920X1080; + break; + } + + // aspectRatio, stereoScopicMode, frameRate enums align between DS HAL and WPE interface + resolution.aspectRatio = static_cast(dsResolution.aspectRatio); + resolution.stereoScopicMode = static_cast(dsResolution.stereoScopicMode); + // DS HAL may have extra frameRate values (59fps=15, 23fps=16) beyond WPE MAX=15; clamp to UNKNOWN + resolution.frameRate = (dsResolution.frameRate < dsVIDEO_FRAMERATE_MAX && + static_cast(dsResolution.frameRate) < static_cast(VideoFrameRate::DS_VIDEO_FRAMERATE_MAX)) + ? static_cast(dsResolution.frameRate) + : VideoFrameRate::DS_VIDEO_FRAMERATE_UNKNOWN; + resolution.interlaced = dsResolution.interlaced; + + LOGINFO("convertVideoPortResolution: name='%s', pixelRes=%d, frameRate=%d, interlaced=%d", + resolution.name.c_str(), static_cast(resolution.pixelResolution), + static_cast(resolution.frameRate), resolution.interlaced); + return resolution; + } + + dsVideoPortResolution_t convertVideoPortResolution(const VideoPortResolution& resolution) + { + dsVideoPortResolution_t dsResolution = {}; + + strncpy(dsResolution.name, resolution.name.c_str(), sizeof(dsResolution.name) - 1); + + switch (resolution.pixelResolution) { + case VideoResolution::DS_VIDEO_PIXELRES_720X480: dsResolution.pixelResolution = dsVIDEO_PIXELRES_720x480; break; + case VideoResolution::DS_VIDEO_PIXELRES_720X576: dsResolution.pixelResolution = dsVIDEO_PIXELRES_720x576; break; + case VideoResolution::DS_VIDEO_PIXELRES_1280X720: dsResolution.pixelResolution = dsVIDEO_PIXELRES_1280x720; break; + case VideoResolution::DS_VIDEO_PIXELRES_1920X1080: dsResolution.pixelResolution = dsVIDEO_PIXELRES_1920x1080; break; + case VideoResolution::DS_VIDEO_PIXELRES_3840X2160: dsResolution.pixelResolution = dsVIDEO_PIXELRES_3840x2160; break; + default: dsResolution.pixelResolution = dsVIDEO_PIXELRES_1920x1080; break; + } + + // enum ordinals match between interface and DS HAL for these types + dsResolution.aspectRatio = static_cast(resolution.aspectRatio); + dsResolution.stereoScopicMode = static_cast(resolution.stereoScopicMode); + dsResolution.frameRate = static_cast(resolution.frameRate); + dsResolution.interlaced = resolution.interlaced; + + return dsResolution; + } + + // Convert DS HAL HDCP version to interface HDCP version + VideoPortHdcpProtocolVersion convertHdcpProtocolVersion(const dsHdcpProtocolVersion_t dsHdcpVersion) + { + switch (dsHdcpVersion) { + case dsHDCP_VERSION_1X: + return VideoPortHdcpProtocolVersion::DS_HDCP_VERSION_1X; + case dsHDCP_VERSION_2X: + return VideoPortHdcpProtocolVersion::DS_HDCP_VERSION_2X; + default: + return VideoPortHdcpProtocolVersion::DS_HDCP_VERSION_1X; + } + } + + // Convert interface HDCP version to DS HAL HDCP version + dsHdcpProtocolVersion_t convertHdcpProtocolVersionToDSHal(const VideoPortHdcpProtocolVersion hdcpVersion) + { + switch (hdcpVersion) { + case VideoPortHdcpProtocolVersion::DS_HDCP_VERSION_1X: + return dsHDCP_VERSION_1X; + case VideoPortHdcpProtocolVersion::DS_HDCP_VERSION_2X: + return dsHDCP_VERSION_2X; + default: + return dsHDCP_VERSION_1X; + } + } + + dsDisplayColorSpace_t convertColorSpace(const VideoPortColorSpace colorSpace) + { + switch (colorSpace) { + case VideoPortColorSpace::DS_DISPLAY_COLORSPACE_RGB: + return dsDISPLAY_COLORSPACE_RGB; + case VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR422: + return dsDISPLAY_COLORSPACE_YCbCr422; + case VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR444: + return dsDISPLAY_COLORSPACE_YCbCr444; + case VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR420: + return dsDISPLAY_COLORSPACE_YCbCr420; + default: + return dsDISPLAY_COLORSPACE_RGB; + } + } + + VideoPortColorSpace convertColorSpace(const dsDisplayColorSpace_t dsColorSpace) + { + switch (dsColorSpace) { + case dsDISPLAY_COLORSPACE_RGB: + return VideoPortColorSpace::DS_DISPLAY_COLORSPACE_RGB; + case dsDISPLAY_COLORSPACE_YCbCr422: + return VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR422; + case dsDISPLAY_COLORSPACE_YCbCr444: + return VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR444; + case dsDISPLAY_COLORSPACE_YCbCr420: + return VideoPortColorSpace::DS_DISPLAY_COLORSPACE_YCBCR420; + default: + return VideoPortColorSpace::DS_DISPLAY_COLORSPACE_RGB; + } + } + + dsDisplayQuantizationRange_t convertQuantizationRange(const VideoPortQuantizationRange quantizationRange) + { + switch (quantizationRange) { + case VideoPortQuantizationRange::DS_DISPLAY_QUANTIZATIONRANGE_LIMITED: + return dsDISPLAY_QUANTIZATIONRANGE_LIMITED; + case VideoPortQuantizationRange::DS_DISPLAY_QUANTIZATIONRANGE_FULL: + return dsDISPLAY_QUANTIZATIONRANGE_FULL; + default: + return dsDISPLAY_QUANTIZATIONRANGE_LIMITED; + } + } + + VideoPortQuantizationRange convertQuantizationRange(const dsDisplayQuantizationRange_t dsQuantizationRange) + { + switch (dsQuantizationRange) { + case dsDISPLAY_QUANTIZATIONRANGE_LIMITED: + return VideoPortQuantizationRange::DS_DISPLAY_QUANTIZATIONRANGE_LIMITED; + case dsDISPLAY_QUANTIZATIONRANGE_FULL: + return VideoPortQuantizationRange::DS_DISPLAY_QUANTIZATIONRANGE_FULL; + default: + return VideoPortQuantizationRange::DS_DISPLAY_QUANTIZATIONRANGE_LIMITED; + } + } + + VideoPortHdcpStatus convertHdcpStatus(const dsHdcpStatus_t& dsHdcpStatus) + { + switch (dsHdcpStatus) { + case dsHDCP_STATUS_UNPOWERED: + return VideoPortHdcpStatus::DS_HDCP_STATUS_UNPOWERED; + case dsHDCP_STATUS_UNAUTHENTICATED: + return VideoPortHdcpStatus::DS_HDCP_STATUS_UNAUTHENTICATED; + case dsHDCP_STATUS_AUTHENTICATED: + return VideoPortHdcpStatus::DS_HDCP_STATUS_AUTHENTICATED; + case dsHDCP_STATUS_AUTHENTICATIONFAILURE: + return VideoPortHdcpStatus::DS_HDCP_STATUS_AUTHENTICATIONFAILURE; + default: + return VideoPortHdcpStatus::DS_HDCP_STATUS_UNPOWERED; + } + } + + + void persistVideoPortResolution(const int32_t handle, const dsVideoPortResolution_t& resolution, const bool forceCompatible) + { + LOGINFO("persistVideoPortResolution: handle=%d, forceCompatible=%s", handle, forceCompatible ? "true" : "false"); + + try { + std::string resolutionName(resolution.name); + + dsVideoPortType_t portType = dsVIDEOPORT_TYPE_HDMI; + intptr_t test_handle = 0; + if (dsGetVideoPort(dsVIDEOPORT_TYPE_HDMI, 0, &test_handle) == dsERR_NONE && test_handle == handle) { + portType = dsVIDEOPORT_TYPE_HDMI; + } else if (dsGetVideoPort(dsVIDEOPORT_TYPE_COMPONENT, 0, &test_handle) == dsERR_NONE && test_handle == handle) { + portType = dsVIDEOPORT_TYPE_COMPONENT; + } else if (dsGetVideoPort(dsVIDEOPORT_TYPE_INTERNAL, 0, &test_handle) == dsERR_NONE && test_handle == handle) { + portType = dsVIDEOPORT_TYPE_INTERNAL; + } else if (dsGetVideoPort(dsVIDEOPORT_TYPE_BB, 0, &test_handle) == dsERR_NONE && test_handle == handle) { + portType = dsVIDEOPORT_TYPE_BB; + } else if (dsGetVideoPort(dsVIDEOPORT_TYPE_RF, 0, &test_handle) == dsERR_NONE && test_handle == handle) { + portType = dsVIDEOPORT_TYPE_RF; + } + + if (portType == dsVIDEOPORT_TYPE_HDMI || portType == dsVIDEOPORT_TYPE_INTERNAL) { + device::HostPersistence::getInstance().persistHostProperty("HDMI0.resolution", resolutionName); + LOGINFO("Persisted HDMI resolution: %s", resolutionName.c_str()); + _dsHDMIResolution = resolutionName; + + if (forceCompatible) { + std::string compatibleResolution = getCompatibleAnalogResolution(resolution); + if (!compatibleResolution.empty() && compatibleResolution != _dsCompResolution) { + #ifdef HAS_ONLY_COMPOSITE + device::HostPersistence::getInstance().persistHostProperty("Baseband0.resolution", compatibleResolution); + #else + device::HostPersistence::getInstance().persistHostProperty("COMPONENT0.resolution", compatibleResolution); + #endif + _dsCompResolution = compatibleResolution; + LOGINFO("Force compatible: Updated analog resolution to %s", compatibleResolution.c_str()); + } + } + } else if (portType == dsVIDEOPORT_TYPE_COMPONENT) { + #ifdef HAS_ONLY_COMPOSITE + device::HostPersistence::getInstance().persistHostProperty("Baseband0.resolution", resolutionName); + #else + device::HostPersistence::getInstance().persistHostProperty("COMPONENT0.resolution", resolutionName); + #endif + LOGINFO("Persisted Component resolution: %s", resolutionName.c_str()); + _dsCompResolution = resolutionName; + + if (forceCompatible) { + std::string compatibleResolution = getCompatibleHDMIResolution(resolution); + if (!compatibleResolution.empty() && compatibleResolution != _dsHDMIResolution) { + device::HostPersistence::getInstance().persistHostProperty("HDMI0.resolution", compatibleResolution); + _dsHDMIResolution = compatibleResolution; + LOGINFO("Force compatible: Updated HDMI resolution to %s", compatibleResolution.c_str()); + } + } + } else if (portType == dsVIDEOPORT_TYPE_BB) { + /* dsVideoPort.c: _dsSetResolution BB case persists Baseband0.resolution */ + device::HostPersistence::getInstance().persistHostProperty("Baseband0.resolution", resolutionName); + LOGINFO("Persisted Baseband resolution: %s", resolutionName.c_str()); + _dsBBResolution = resolutionName; + } else if (portType == dsVIDEOPORT_TYPE_RF) { + /* dsVideoPort.c: _dsSetResolution RF case persists RF0.resolution */ + device::HostPersistence::getInstance().persistHostProperty("RF0.resolution", resolutionName); + LOGINFO("Persisted RF resolution: %s", resolutionName.c_str()); + _dsRFResolution = resolutionName; + } + + } catch(...) { + LOGERR("Exception in persistVideoPortResolution"); + } + } + + // Helper function to get compatible analog resolution - simplified from dsVideoPort.c + std::string getCompatibleAnalogResolution(const dsVideoPortResolution_t& hdmiResolution) + { + // Simplified compatibility mapping based on dsVideoPort.c patterns + switch(hdmiResolution.pixelResolution) { + case dsVIDEO_PIXELRES_3840x2160: + case dsVIDEO_PIXELRES_4096x2160: + return "1080p"; // 4K -> 1080p for analog + case dsVIDEO_PIXELRES_1920x1080: + return "1080p"; + case dsVIDEO_PIXELRES_1280x720: + return "720p"; + case dsVIDEO_PIXELRES_720x480: + return "480p"; + case dsVIDEO_PIXELRES_720x576: + return "576p"; + default: + return "1080p"; // Default fallback + } + } + + // Helper function to get compatible HDMI resolution - simplified from dsVideoPort.c + std::string getCompatibleHDMIResolution(const dsVideoPortResolution_t& analogResolution) + { + // For analog to HDMI, generally same resolution or upgrade + switch(analogResolution.pixelResolution) { + case dsVIDEO_PIXELRES_720x480: + return "480p"; // Note: dsVideoPort.c converts 480i to 480p + case dsVIDEO_PIXELRES_720x576: + return "576p"; + case dsVIDEO_PIXELRES_1280x720: + return "720p"; + case dsVIDEO_PIXELRES_1920x1080: + return "1080p"; + default: + return "1080p"; // Default fallback + } + } + + // Get persistent color depth - following dsVideoPort.c getPersistentColorDepth() pattern + DisplayColorDepth getPersistentColorDepth() + { + DisplayColorDepth defaultColorDepth = static_cast(DEFAULT_COLOR_DEPTH); + std::string colorDepthStr = std::to_string(static_cast(defaultColorDepth)); + + try { + colorDepthStr = device::HostPersistence::getInstance().getProperty("HDMI0.colorDepth", colorDepthStr); + int colorDepthValue = std::stoi(colorDepthStr); + DisplayColorDepth persistentColorDepth = static_cast(colorDepthValue); + LOGINFO("Reading HDMI persistent color depth: %d", colorDepthValue); + return persistentColorDepth; + } catch(...) { + LOGERR("Reading HDMI persistent color depth %s conversion failed", colorDepthStr.c_str()); + return defaultColorDepth; + } + } +}; diff --git a/services.cmake b/services.cmake new file mode 100644 index 0000000..0c3d961 --- /dev/null +++ b/services.cmake @@ -0,0 +1,18 @@ +# If not stated otherwise in this file or this component's license file the +# following copyright and licenses apply: +# +# Copyright 2024 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. + +option(PLUGIN_DEVICESETTINGS "PLUGIN_DEVICESETTINGS" ON) \ No newline at end of file