HDMI CEC (Consumer Electronics Control) is a middleware library component in RDK that enables devices to communicate and control each other over HDMI connections through standardized control messages defined in the HDMI specification.

The library serves as an abstraction layer between application components and the hardware abstraction layer. It manages the complexities of CEC communication including address management, message routing, and protocol timing. The component provides both synchronous and asynchronous APIs for transmitting CEC messages and receiving messages from connected devices through a callback-based mechanism.

Operating within the RDK core middleware layer, the HDMI CEC library sits between Thunder plugins and the vendor HAL implementation. It provides a unified programming interface that handles CEC bus management, multi-threaded message processing, and frame-level protocol operations. The library enables features such as device power control coordination, input source switching, system audio control, and device capability discovery across the HDMI ecosystem.

```mermaid
flowchart LR

%% Styles
classDef Apps stroke:#00B9F1,fill:#E6F7FD,stroke-width:2px;
classDef RDKMW stroke:#75D701,fill:#F1FFE6,stroke-width:2px;
classDef VL stroke:#808080,fill:#F2F2F2,stroke-width:2px;

%% Apps Layer
    subgraph Apps["Apps & Runtimes"]
        RDKUI["UI"]
        FBApps["Firebolt Apps"]
        WPE_RT["WPE Runtime"]
    end

%% Middleware
    subgraph RDKMW["RDK Core Middleware"]
        Thunder["WPE Framework (Thunder)"]
        CEC["HDMI CEC Library"]
        IARM["IARM Bus"]
    end

%% Vendor Layer
    subgraph VL["Vendor Layer"]
        HAL["HDMI CEC HAL"]
        DRV["SoC CEC Driver"]
    end

    %% External connections
    Apps -->|Firebolt APIs| Thunder
    Thunder -->|Library APIs| CEC
    Thunder -->|Events| IARM
    CEC -->|HAL APIs| HAL
    HAL -->|Driver Calls| DRV
```

Key Features & Responsibilities:


Design

The HDMI CEC component implements a layered architecture that separates bus management, protocol handling, and hardware abstraction. At the core is the Bus singleton which manages the CEC communication channel through dedicated reader and writer threads. The reader thread continuously polls the driver for incoming frames and dispatches them to registered listeners, while the writer thread processes an event queue of outgoing frames. This design ensures non-blocking operations and meets CEC protocol timing requirements.

Applications interact with the CEC bus through Connection objects that represent logical taps into the bus. Each Connection is associated with a source logical address and can register FrameListener instances to receive incoming messages. The Bus dispatches received frames to all registered listeners, with each Connection applying its own filtering based on destination address. This design allows multiple concurrent connections to coexist, each handling messages for specific logical addresses.

Message handling uses a visitor pattern through the MessageProcessor class. Raw CEC frames are decoded into strongly-typed message objects by MessageDecoder, which then dispatches to appropriate process() methods. The MessageEncoder performs the inverse operation, converting message objects into byte-level frames for transmission. This separation allows applications to work with high-level message abstractions while the library handles protocol-level encoding details.

The southbound interface to hardware is provided through the Driver abstraction. DriverImpl wraps the HDMI CEC HAL functions, managing handle allocation, callback registration, and error translation. The HAL provides the actual hardware access through vendor-specific implementations. All HAL interactions are synchronized through mutexes to ensure thread safety.

The library does not implement direct inter-process communication mechanisms. Applications using this library typically integrate with IARM Bus for system-wide event distribution, but this integration happens at the application layer rather than within the library itself. The library focuses solely on CEC bus management and message transport.

Data persistence is not implemented within the library. Address discovery occurs at runtime during initialization, and no configuration state is stored. Applications are responsible for persisting any settings or preferences that need to survive across reboots.

```mermaid
graph TD
    subgraph HDMICEC["HDMI CEC Library"]
        subgraph Application["Application Layer"]
            CONN[Connection]
            LIBCEC[LibCCEC]
            noteAPP["Purpose: Connection management<br/>and message transmission APIs"]
        end

        subgraph Processing["Message Processing Layer"]
            BUS[Bus Singleton]
            READER[Reader Thread]
            WRITER[Writer Thread]
            DECODER[MessageDecoder]
            ENCODER[MessageEncoder]
            noteProc["Purpose: Frame routing, thread management,<br/>message encoding/decoding"]
        end

        subgraph OSAL["OS Abstraction Layer"]
            THREAD[Thread]
            MUTEX[Mutex]
            CONDVAR[ConditionVariable]
            QUEUE[EventQueue]
            noteOSAL["Purpose: Platform-independent threading<br/>and synchronization primitives"]
        end

        subgraph Driver["Driver Abstraction"]
            DRVR[Driver Interface]
            IMPL[DriverImpl]
            noteDRVR["Purpose: HAL access wrapper<br/>and callback management"]
        end
    end

    subgraph HAL["HDMI CEC HAL"]
        HALAPI[HAL Functions]
        noteHAL["HdmiCecOpen, HdmiCecTx,<br/>HdmiCecSetRxCallback, etc."]
    end

    subgraph SOC["Vendor Implementation"]
        SOCDRV[SoC CEC Driver]
    end

    CONN -->|Uses| BUS
    LIBCEC -->|Manages| DRVR
    BUS -->|Contains| READER
    BUS -->|Contains| WRITER
    READER -->|Reads from| DRVR
    WRITER -->|Writes to| DRVR
    DECODER -->|Parses frames| READER
    ENCODER -->|Creates frames| WRITER
    READER -->|Uses| THREAD
    WRITER -->|Uses| THREAD
    BUS -->|Uses| MUTEX
    WRITER -->|Uses| QUEUE
    DRVR -->|Implemented by| IMPL
    IMPL -->|Calls| HALAPI
    HALAPI -->|Invokes| SOCDRV
```

Prerequisites & Dependencies

The HDMI CEC library is designed as a standalone middleware library with minimal external dependencies. It requires a vendor-specific HAL implementation to interface with hardware. The build system checks for glib-2.0 via pkg-config, but the library does not currently call GLib APIs directly. The library does not directly depend on IARM, Device Settings, or Thunder framework components - these are used by applications that consume the library, not by the library itself. Build-time dependencies are limited to essential libraries that are actually invoked in the library source code.

Threading Model

The HDMI CEC component implements a multi-threaded architecture with explicit separation between message reception and transmission.

RDK-V Platform and Integration Requirements


Component State Flow

Initialization to Active State

Initialization typically begins when an application obtains the singleton via LibCCEC::getInstance() and then calls LibCCEC::init(). The first call to Bus::getInstance() (performed inside LibCCEC::init()) constructs the Bus and starts the reader/writer threads. LibCCEC::init() also opens the driver and starts the Bus; consuming applications normally do not need to call Bus::start() directly.

```mermaid
sequenceDiagram
    participant App as Application
    participant LibCEC as LibCCEC Singleton
    participant Bus as Bus Singleton
    participant Reader as Reader Thread
    participant Writer as Writer Thread
    participant Driver as DriverImpl
    participant HAL as HDMI CEC HAL

    App->>LibCEC: getInstance()
    LibCEC->>LibCEC: Create singleton
    LibCEC-->>App: libCCEC reference

    App->>LibCEC: init(name)
    LibCEC->>LibCEC: Set initialized flag / set log prefix / read /tmp/cec_log_enabled
    LibCEC->>Bus: getInstance()
    Bus->>Bus: Constructor
    Bus->>Reader: Create and start thread
    Bus->>Writer: Create and start thread
    Note over Reader,Writer: Threads running, waiting for driver

    LibCEC->>Bus: start()
    Bus->>Driver: open()
    Driver->>HAL: HdmiCecOpen(&handle)
    Note over HAL: Initialize hardware<br/>Discover physical address<br/>Sources: discover logical address
    HAL-->>Driver: handle, SUCCESS
    Driver->>HAL: HdmiCecSetRxCallback(handle, callback)
    HAL-->>Driver: SUCCESS
    Driver->>HAL: HdmiCecSetTxCallback(handle, callback)
    HAL-->>Driver: SUCCESS
    Driver-->>Bus: Opened
    Bus-->>App: Started

    App->>App: new Connection(source)
    App->>Bus: addFrameListener(listener)
    Bus-->>App: Listener registered
    Note over App: State: Active - can send/receive

    loop Runtime
        Note over Reader: Poll driver for frames
        Note over Writer: Process transmission queue
    end

    App->>Bus: stop()
    Bus->>Reader: stop()
    Bus->>Writer: stop()
    Bus->>Driver: close()
    Driver->>HAL: HdmiCecClose(handle)
    HAL-->>Driver: SUCCESS
    Note over Bus: State: Stopped
```

Runtime State Changes

During active operation, the component maintains stable state with continuous frame processing. State changes occur primarily in response to errors or explicit shutdown requests.

State Change Triggers:

Context Switching Scenarios:


Call Flows

Initialization Call Flow

```mermaid
sequenceDiagram
    participant App as Application
    participant LibCEC as LibCCEC
    participant Bus as Bus Singleton
    participant Driver as DriverImpl
    participant HAL as HDMI CEC HAL

    App->>LibCEC: getInstance()
    Note over LibCEC: First call creates singleton
    LibCEC-->>App: libCCEC reference

    App->>LibCEC: init("component")
    LibCEC->>LibCEC: Check initialized flag
    alt Not yet initialized
        LibCEC->>LibCEC: Set initialized = true
        LibCEC->>LibCEC: Set log prefix
    end
    LibCEC->>Bus: getInstance()
    Note over Bus: Constructor starts threads
    LibCEC->>Bus: start()
    Bus->>Driver: getInstance()
    Driver->>Driver: Create singleton
    Bus->>Driver: open()
    Driver->>Driver: Lock mutex
    alt Status == CLOSED
        Driver->>HAL: HdmiCecOpen(&nativeHandle)
        HAL-->>Driver: handle, status
        Driver->>HAL: HdmiCecSetRxCallback(handle, DriverReceiveCallback)
        Driver->>HAL: HdmiCecSetTxCallback(handle, DriverTransmitCallback)
        Driver->>Driver: status = OPENED
    end
    Driver-->>Bus: Opened
    Bus->>Bus: started = true
    Bus-->>App: Success

    App->>Driver: getLogicalAddress(devType)
    Driver->>HAL: HdmiCecGetLogicalAddress(handle, &address)
    HAL-->>Driver: address
    Driver-->>App: logicalAddress

    App->>Driver: getPhysicalAddress(&physAddr)
    Driver->>HAL: HdmiCecGetPhysicalAddress(handle, &physAddr)
    HAL-->>Driver: physicalAddress
    Driver-->>App: physicalAddress
```

Message Transmission Call Flow

```mermaid
sequenceDiagram
    participant App as Application
    participant Conn as Connection
    participant Bus as Bus
    participant Writer as Writer Thread
    participant Driver as DriverImpl
    participant HAL as HDMI CEC HAL

    alt Synchronous Transmission
        App->>Conn: send(CECFrame, timeout)
        Conn->>Bus: send(frame)
        Bus->>Driver: write(frame)
        Driver->>Driver: Lock mutex, check status
        Driver->>Driver: Extract buffer from frame
        Driver->>HAL: HdmiCecTx(handle, buf, len, &result)
        Note over HAL: Transmit on bus<br/>Wait for ACK/NACK
        HAL-->>Driver: status, result
        Driver->>Driver: Map result to exception
        alt result == SENT_BUT_NOT_ACKD
            Driver->>Driver: Throw CECNoAckException
        else result == SENT_FAILED
            Driver->>Driver: Throw IOException
        end
        Driver-->>Bus: Success or Exception
        Bus-->>Conn: Success or Exception
        Conn-->>App: Success or Exception
    else Asynchronous Transmission
        App->>Conn: sendAsync(CECFrame)
        Conn->>Bus: sendAsync(frame)
        Bus->>Bus: Create heap frame copy
        Bus->>Writer: Enqueue to EventQueue
        Note over Writer: Condition variable signaled
        Bus-->>Conn: Queued
        Conn-->>App: Return immediately

        Note over Writer: Writer thread wakes
        Writer->>Writer: Dequeue frame
        Writer->>Driver: write(frame)
        Driver->>Driver: Lock mutex, check status
        Driver->>HAL: HdmiCecTx(handle, buf, len, &result)
        HAL-->>Driver: status, result
        Driver-->>Writer: Success or Exception
        Writer->>Writer: Delete frame
    end
```

Message Reception Call Flow

```mermaid
sequenceDiagram
    participant HAL as HDMI CEC HAL
    participant Driver as DriverImpl
    participant Reader as Reader Thread
    participant Bus as Bus
    participant Listener as FrameListener
    participant App as Application

    Note over HAL: Frame received on CEC bus

    HAL->>Driver: DriverReceiveCallback(handle, data, buf, len)
    Driver->>Driver: Create new CECFrame
    Driver->>Driver: Append bytes to frame
    Driver->>Driver: Offer frame to rQueue
    Note over Driver: Frame queued, callback returns

    Note over Reader: Reader thread polling
    Reader->>Driver: read(frame)
    Driver->>Driver: Poll rQueue
    alt Frame available
        Driver->>Driver: Copy frame from queue
        Driver->>Driver: Delete queued frame
        Driver-->>Reader: frame
    else Queue empty and status != OPENED
        Driver->>Driver: Throw InvalidStateException
    end

    Reader->>Reader: Frame received
    Reader->>Bus: Process frame
    Bus->>Bus: Lock rMutex

    loop For each registered listener
        Bus->>Listener: notify(frame)
        Listener->>Listener: Apply frame filter
        alt Frame matches filter criteria
            Listener->>App: Dispatch to application callback
            Note over App: Process frame<br/>Decode message
            App-->>Listener: Return
        end
        Listener-->>Bus: Return
    end

    Bus->>Bus: Unlock rMutex
    Note over Reader: Continue polling for next frame
```

Internal Modules

The HDMI CEC library is structured into functional modules that separate concerns across abstraction layers.

Module / ClassDescriptionKey Files
ConnectionApplication interface for CEC bus access. Represents a logical tap into the bus with a specific source address. Manages FrameListener registration and filtering.Connection.cpp, Connection.hpp
LibCCECLibrary singleton managing initialization state and providing logical address allocation interface. Entry point for library setup.LibCCEC.cpp, LibCCEC.hpp
BusCentral message routing hub. Manages reader and writer threads. Dispatches incoming frames to all registered listeners and queues outgoing frames for transmission.Bus.cpp, Bus.hpp
DriverAbstract interface defining CEC driver operations. DriverImpl provides concrete implementation wrapping HAL function calls. Manages HAL handle and callback registration.Driver.cpp, Driver.hpp, DriverImpl.cpp, DriverImpl.hpp
CECFrameRepresents a raw CEC frame as a byte sequence. Provides methods for appending bytes, extracting buffer, and frame manipulation.CECFrame.cpp, CECFrame.hpp
MessageEncoderConverts strongly-typed CEC message objects into raw CECFrame byte sequences with proper header, opcode, and operand encoding.MessageEncoder.hpp
MessageDecoderParses incoming CECFrame byte sequences into strongly-typed message objects. Receives external data from the CEC bus via HAL callbacks and decodes it into application-level messages.MessageDecoder.cpp, MessageDecoder.hpp
MessageProcessorBase class defining virtual process() methods for each supported CEC message type. Applications extend this class to implement custom message handling logic.MessageProcessor.hpp
MessagesDefines strongly-typed classes for CEC messages including ActiveSource, Standby, ReportPhysicalAddress, UserControlPressed, SetSystemAudioMode, and others.Messages.hpp
OperandsDefines operand classes for CEC message parameters including PhysicalAddress, Version, PowerStatus, and device type enumerations.Operands.hpp
OpCodeEnumerates all CEC operation codes and provides opcode-related utilities.OpCode.cpp, OpCode.hpp
HeaderRepresents the CEC header block containing source and destination logical addresses.Header.hpp
FrameListenerAbstract callback interface for receiving frame notifications. Implementations receive external CEC frames from connected devices via the Bus dispatcher.FrameListener.hpp
ThreadOS abstraction for pthread management. Wraps Runnable instances and provides start/stop lifecycle control.Thread.cpp, Thread.hpp
MutexOS abstraction for pthread mutex. Provides AutoLock RAII wrapper for exception-safe locking.Mutex.cpp, Mutex.hpp
ConditionVariableOS abstraction for pthread condition variables. Used by EventQueue for thread signaling.ConditionVariable.cpp, ConditionVariable.hpp
EventQueueTemplate-based thread-safe queue with condition variable signaling. Used by writer thread to queue outgoing frames.EventQueue.hpp

Component Interactions

The HDMI CEC library interacts primarily with the vendor HAL layer and is consumed by Thunder plugins or other middleware components. The library itself does not directly participate in inter-process communication.

Interaction Matrix

Target Component / LayerInteraction PurposeKey APIs / Topics
RDK-E Plugins

Thunder HdmiCec PluginExposes CEC functionality via JSON-RPC APIs to applicationsConnection::send(), Connection::sendAsync(), FrameListener::notify()
Device Services / HAL

HDMI CEC HALHardware abstraction for CEC transmission, reception, and address managementHdmiCecOpen(), HdmiCecClose(), HdmiCecTx(), HdmiCecTxAsync(), HdmiCecSetRxCallback(), HdmiCecSetTxCallback(), HdmiCecAddLogicalAddress(), HdmiCecRemoveLogicalAddress(), HdmiCecGetLogicalAddress(), HdmiCecGetPhysicalAddress()
TelemetryLogging of error events and diagnosticst2_event_s() for exception telemetry markers
External Systems

Connected CEC DevicesBi-directional CEC protocol messaging over HDMI physical layerCEC protocol messages per HDMI Specification 1.4b

Events Published

The library itself does not publish events. Applications using this library publish events through their own mechanisms:

Event NameIARM / JSON-RPC TopicTrigger ConditionSubscriber Components
cecAddressesChangedThunder JSON-RPCLogical address added or removed (published by Thunder plugin)UI applications, management services
onMessageThunder JSON-RPCCEC frame received matching application filter (published by Thunder plugin)UI applications requiring message visibility

IPC Flow Patterns

The library provides direct function call APIs and does not implement IPC itself. Applications using the library may implement IPC:

Primary Request / Response Flow:

```mermaid
sequenceDiagram
    participant Client as Thunder Client
    participant Plugin as HdmiCec Plugin
    participant Lib as HDMI CEC Library
    participant HAL as HDMI CEC HAL

    Client->>Plugin: JSON-RPC: sendMessage(params)
    Plugin->>Plugin: Validate parameters
    Plugin->>Lib: Connection::send(CECFrame)
    Lib->>Lib: Bus routing
    Lib->>HAL: HdmiCecTx(handle, buf, len, &result)
    HAL->>HAL: Hardware transmission
    HAL-->>Lib: Success/failure status
    Lib-->>Plugin: Success or exception
    Plugin-->>Client: JSON-RPC Response
```

Event Notification Flow:

```mermaid
sequenceDiagram
    participant Device as CEC Device
    participant HAL as HDMI CEC HAL
    participant Lib as HDMI CEC Library
    participant Plugin as HdmiCec Plugin
    participant Client as Thunder Client

    Device->>HAL: CEC frame on bus
    HAL->>Lib: DriverReceiveCallback(buf, len)
    Lib->>Lib: Queue frame, Reader thread polls
    Lib->>Lib: Dispatch to listeners
    Lib->>Plugin: FrameListener::notify(frame)
    Plugin->>Plugin: Decode and filter message
    Plugin->>Client: JSON-RPC notification
```

Implementation Details

Major HAL APIs Integration

The library integrates with all HDMI CEC HAL functions defined in rdk-halif-hdmi_cec.

HAL / DS APIPurposeImplementation File
HdmiCecOpen()Initializes the HAL and returns a handle. For source devices, performs logical address discovery.DriverImpl.cpp in open() method
HdmiCecClose()Closes the HAL instance and releases resources associated with the handle.DriverImpl.cpp in close() method
HdmiCecTx()Synchronously transmits a CEC message and waits for acknowledgment. Returns transmission result.DriverImpl.cpp in write() method
HdmiCecTxAsync()Asynchronously transmits a CEC message without blocking. Result delivered via callback.DriverImpl.cpp in writeAsync() method
HdmiCecSetRxCallback()Registers DriverReceiveCallback to receive incoming CEC messages from hardware.DriverImpl.cpp in open() method
HdmiCecSetTxCallback()Registers DriverTransmitCallback to receive asynchronous transmission status.DriverImpl.cpp in open() method
HdmiCecAddLogicalAddress()Adds a logical address for sink devices. Only applicable to sink devices.DriverImpl.cpp in addLogicalAddress()
HdmiCecRemoveLogicalAddress()Removes a previously added logical address for sink devices.DriverImpl.cpp in removeLogicalAddress()
HdmiCecGetLogicalAddress()Queries the current logical address assigned to the device.DriverImpl.cpp in getLogicalAddress()
HdmiCecGetPhysicalAddress()Retrieves the physical address based on HDMI connection topology.DriverImpl.cpp in getPhysicalAddress()

Key Implementation Logic


Configuration

Key Configuration Files

Configuration FilePurposeOverride Mechanism
/tmp/cec_log_enabledControls runtime log verbosity levelCreate file with single line containing desired level: FATAL, ERROR, WARN, EXP, NOTICE, INFO, DEBUG, or TRACE

Key Configuration Parameters

No persistent configuration parameters. Runtime behavior controlled through:

ParameterTypeDefaultDescription
Log LevelstringINFORuntime logging verbosity configured via /tmp/cec_log_enabled file
Logical Addressint (0x0-0xF)Discovered at runtimeCEC logical address for message routing
Physical Addressuint (0x0000-0xFFFF)Discovered at runtimeFour-nibble physical address from HDMI topology
Timeoutint (milliseconds)0Optional timeout parameter for synchronous send operations

Runtime Configuration

Log level can be changed at runtime by writing to /tmp/cec_log_enabled file. The check_cec_log_status() function reads this file but there is no active file monitoring - level changes take effect based on when the check function is called.

Address changes require API calls: - For sink devices: Call HdmiCecRemoveLogicalAddress() then HdmiCecAddLogicalAddress() through Driver interface - For source devices: Close and reopen the driver to trigger new address discovery

Configuration Persistence

Configuration changes are not persisted across reboots. Logical and physical addresses are rediscovered during each initialization. Log level configuration via /tmp/cec_log_enabled persists only as long as the tmpfs filesystem retains the file.