요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
CEC Kernel Support
==================
The CEC framework provides a unified kernel interface for use with HDMI CEC
hardware. It is designed to handle a multiple types of hardware (receivers,
transmitters, USB dongles). The framework also gives the option to decide
what to do in the kernel driver and what should be handled by userspace
applications. In addition it integrates the remote control passthrough
feature into the kernel's remote control framework.
The CEC Protocol
----------------
The CEC protocol enables consumer electronic devices to communicate with each
other through the HDMI connection. The protocol uses logical addresses in the
communication. The logical address is strictly connected with the functionality
provided by the device. The TV acting as the communication hub is always
assigned address 0. The physical address is determined by the physical
connection between devices.
The CEC framework described here is up to date with the CEC 2.0 specification.
It is documented in the HDMI 1.4 specification with the new 2.0 bits documented
in the HDMI 2.0 specification. But for most of the features the freely available
HDMI 1.3a specification is sufficient:
https://www.hdmi.org/spec/index
CEC Adapter Interface
---------------------
The struct cec_adapter represents the CEC adapter hardware. It is created by
calling cec_allocate_adapter() and deleted by calling cec_delete_adapter():
.. c:function::
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, \
void *priv, const char *name, \
u32 caps, u8 available_las);
.. c:function::
void cec_delete_adapter(struct cec_adapter *adap);
To create an adapter you need to pass the following information:
ops:
adapter operations which are called by the CEC framework and that you
have to implement.
priv:
will be stored in adap->priv and can be used by the adapter ops.
Use cec_get_drvdata(adap) to get the priv pointer.
name:
the name of the CEC adapter. Note: this name will be copied.
caps:
capabilities of the CEC adapter. These capabilities determine the
capabilities of the hardware and which parts are to be handled
by userspace and which parts are handled by kernelspace. The
capabilities are returned by CEC_ADAP_G_CAPS.
available_las:
the number of simultaneous logical addresses that this
adapter can handle. Must be 1 <= available_las <= CEC_MAX_LOG_ADDRS.
To obtain the priv pointer use this helper function:
.. c:function::
void *cec_get_drvdata(const struct cec_adapter *adap);
To register the /dev/cecX device node and the remote control device (if
CEC_CAP_RC is set) you call:
.. c:function::
int cec_register_adapter(struct cec_adapter *adap, \
struct device *parent);
where parent is the parent device.
To unregister the devices call:
.. c:function::
void cec_unregister_adapter(struct cec_adapter *adap);
Note: if cec_register_adapter() fails, then call cec_delete_adapter() to
clean up. But if cec_register_adapter() succeeded, then only call
cec_unregister_adapter() to clean up, never cec_delete_adapter(). The
unregister function will delete the adapter automatically once the last user
of that /dev/cecX device has closed its file handle.
Implementing the Low-Level CEC Adapter
--------------------------------------
The following low-level adapter operations have to be implemented in
your driver:
.. c:struct:: cec_adap_ops
.. code-block:: none
struct cec_adap_ops
{
/* Low-level callbacks */
int (*adap_enable)(struct cec_adapter *adap, bool enable);
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable);
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
void (*adap_unconfigured)(struct cec_adapter *adap);
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg);
void (*adap_nb_transmit_canceled)(struct cec_adapter *adap,
const struct cec_msg *msg);
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
void (*adap_free)(struct cec_adapter *adap);
/* Error injection callbacks */
...
/* High-level callback */
...
};
These low-level ops deal with various aspects of controlling the CEC adapter
hardware. They are all called with the mutex adap->lock held.
To enable/disable the hardware::
int (*adap_enable)(struct cec_adapter *adap, bool enable);
This callback enables or disables the CEC hardware. Enabling the CEC hardware
means powering it up in a state where no logical addresses are claimed. The
physical address will always be valid if CEC_CAP_NEEDS_HPD is set. If that
capability is not set, then the physical address can change while the CEC
hardware is enabled. CEC drivers should not set CEC_CAP_NEEDS_HPD unless
the hardware design requires that as this will make it impossible to wake
up displays that pull the HPD low when in standby mode. The initial
state of the CEC adapter after calling cec_allocate_adapter() is disabled.
Note that adap_enable must return 0 if enable is false.
To enable/disable the 'monitor all' mode::
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
If enabled, then the adapter should be put in a mode to also monitor messages
that are not for us. Not all hardware supports this and this function is only
called if the CEC_CAP_MONITOR_ALL capability is set. This callback is optional
(some hardware may always be in 'monitor all' mode).
Note that adap_monitor_all_enable must return 0 if enable is false.
To enable/disable the 'monitor pin' mode::
int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable);
If enabled, then the adapter should be put in a mode to also monitor CEC pin
changes. Not all hardware supports this and this function is only called if
the CEC_CAP_MONITOR_PIN capability is set. This callback is optional
(some hardware may always be in 'monitor pin' mode).
Note that adap_monitor_pin_enable must return 0 if enable is false.
To program a new logical address::
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
If logical_addr == CEC_LOG_ADDR_INVALID then all programmed logical addresses
are to be erased. Otherwise the given logical address should be programmed.
If the maximum number of available logical addresses is exceeded, then it
should return -ENXIO. Once a logical address is programmed the CEC hardware
can receive directed messages to that address.
Note that adap_log_addr must return 0 if logical_addr is CEC_LOG_ADDR_INVALID.
Called when the adapter is unconfigured::
void (*adap_unconfigured)(struct cec_adapter *adap);
The adapter is unconfigured. If the driver has to take specific actions after
unconfiguration, then that can be done through this optional callback.
To transmit a new message::
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg);
This transmits a new message. The attempts argument is the suggested number of
attempts for the transmit.
The signal_free_time is the number of data bit periods that the adapter should
wait when the line is free before attempting to send a message. This value
depends on whether this transmit is a retry, a message from a new initiator or
a new message for the same initiator. Most hardware will handle this
automatically, but in some cases this information is needed.
The CEC_FREE_TIME_TO_USEC macro can be used to convert signal_free_time to
microseconds (one data bit period is 2.4 ms).
To pass on the result of a canceled non-blocking transmit::
void (*adap_nb_transmit_canceled)(struct cec_adapter *adap,
const struct cec_msg *msg);
This optional callback can be used to obtain the result of a canceled
non-blocking transmit with sequence number msg->sequence. This is
called if the transmit was aborted, the transmit timed out (i.e. the
hardware never signaled that the transmit finished), or the transmit
was successful, but the wait for the expected reply was either aborted
or it timed out.
To log the current CEC hardware status::
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
This optional callback can be used to show the status of the CEC hardware.
The status is available through debugfs: cat /sys/kernel/debug/cec/cecX/status
To free any resources when the adapter is deleted::
void (*adap_free)(struct cec_adapter *adap);
This optional callback can be used to free any resources that might have been
allocated by the driver. It's called from cec_delete_adapter.
Your adapter driver will also have to react to events (typically interrupt
driven) by calling into the framework in the following situations:
When a transmit finished (successfully or otherwise)::
void cec_transmit_done(struct cec_adapter *adap, u8 status,
u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt,
u8 error_cnt);
or::
void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status);
The status can be one of:
CEC_TX_STATUS_OK:
the transmit was successful.
CEC_TX_STATUS_ARB_LOST:
arbitration was lost: another CEC initiator
took control of the CEC line and you lost the arbitration.
CEC_TX_STATUS_NACK:
the message was nacked (for a directed message) or
acked (for a broadcast message). A retransmission is needed.
CEC_TX_STATUS_LOW_DRIVE:
low drive was detected on the CEC bus. This indicates that
a follower detected an error on the bus and requested a
retransmission.
CEC_TX_STATUS_ERROR:
some unspecified error occurred: this can be one of ARB_LOST
or LOW_DRIVE if the hardware cannot differentiate or something
else entirely. Some hardware only supports OK and FAIL as the
result of a transmit, i.e. there is no way to differentiate
between the different possible errors. In that case map FAIL
to CEC_TX_STATUS_NACK and not to CEC_TX_STATUS_ERROR.
CEC_TX_STATUS_MAX_RETRIES:
could not transmit the message after trying multiple times.
Should only be set by the driver if it has hardware support for
retrying messages. If set, then the framework assumes that it
doesn't have to make another attempt to transmit the message
since the hardware did that already.
The hardware must be able to differentiate between OK, NACK and 'something
else'.
The \*_cnt arguments are the number of error conditions that were seen.
This may be 0 if no information is available. Drivers that do not support
hardware retry can just set the counter corresponding to the transmit error
to 1, if the hardware does support retry then either set these counters to
0 if the hardware provides no feedback of which errors occurred and how many
times, or fill in the correct values as reported by the hardware.
Be aware that calling these functions can immediately start a new transmit
if there is one pending in the queue. So make sure that the hardware is in
a state where new transmits can be started *before* calling these functions.
The cec_transmit_attempt_done() function is a helper for cases where the
hardware never retries, so the transmit is always for just a single
attempt. It will call cec_transmit_done() in turn, filling in 1 for the
count argument corresponding to the status. Or all 0 if the status was OK.
When a CEC message was received:
.. c:function::
void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
Speaks for itself.
Implementing the interrupt handler
----------------------------------
Typically the CEC hardware provides interrupts that signal when a transmit
finished and whether it was successful or not, and it provides and interrupt
when a CEC message was received.
The CEC driver should always process the transmit interrupts first before
handling the receive interrupt. The framework expects to see the cec_transmit_done
call before the cec_received_msg call, otherwise it can get confused if the
received message was in reply to the transmitted message.
Optional: Implementing Error Injection Support
----------------------------------------------
If the CEC adapter supports Error Injection functionality, then that can
be exposed through the Error Injection callbacks:
.. code-block:: none
struct cec_adap_ops {
/* Low-level callbacks */
...
/* Error injection callbacks */
int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
/* High-level CEC message callback */
...
};
If both callbacks are set, then an ``error-inj`` file will appear in debugfs.
The basic syntax is as follows:
Leading spaces/tabs are ignored. If the next character is a ``#`` or the end of the
line was reached, then the whole line is ignored. Otherwise a command is expected.
This basic parsing is done in the CEC Framework. It is up to the driver to decide
what commands to implement. The only requirement is that the command ``clear`` without
any arguments must be implemented and that it will remove all current error injection
commands.
This ensures that you can always do ``echo clear >error-inj`` to clear any error
injections without having to know the details of the driver-specific commands.
Note that the output of ``error-inj`` shall be valid as input to ``error-inj``.
So this must work:
.. code-block:: none
$ cat error-inj >einj.txt
$ cat einj.txt >error-inj
The first callback is called when this file is read and it should show the
current error injection state::
int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
It is recommended that it starts with a comment block with basic usage
information. It returns 0 for success and an error otherwise.
The second callback will parse commands written to the ``error-inj`` file::
bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
The ``line`` argument points to the start of the command. Any leading
spaces or tabs have already been skipped. It is a single line only (so there
are no embedded newlines) and it is 0-terminated. The callback is free to
modify the contents of the buffer. It is only called for lines containing a
command, so this callback is never called for empty lines or comment lines.
Return true if the command was valid or false if there were syntax errors.
Implementing the High-Level CEC Adapter
---------------------------------------
The low-level operations drive the hardware, the high-level operations are
CEC protocol driven. The high-level callbacks are called without the adap->lock
mutex being held. The following high-level callbacks are available:
.. code-block:: none
struct cec_adap_ops {
/* Low-level callbacks */
...
/* Error injection callbacks */
...
/* High-level CEC message callback */
void (*configured)(struct cec_adapter *adap);
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
};
Called when the adapter is configured::
void (*configured)(struct cec_adapter *adap);
The adapter is fully configured, i.e. all logical addresses have been
successfully claimed. If the driver has to take specific actions after
configuration, then that can be done through this optional callback.
The received() callback allows the driver to optionally handle a newly
received CEC message::
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
If the driver wants to process a CEC message, then it can implement this
callback. If it doesn't want to handle this message, then it should return
-ENOMSG, otherwise the CEC framework assumes it processed this message and
it will not do anything with it.
CEC framework functions
-----------------------
CEC Adapter drivers can call the following CEC framework functions:
.. c:function::
int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, \
bool block);
Transmit a CEC message. If block is true, then wait until the message has been
transmitted, otherwise just queue it and return.
.. c:function::
void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
Change the physical address. This function will set adap->phys_addr and
send an event if it has changed. If cec_s_log_addrs() has been called and
the physical address has become valid, then the CEC framework will start
claiming the logical addresses. If block is true, then this function won't
return until this process has finished.
When the physical address is set to a valid value the CEC adapter will
be enabled (see the adap_enable op). When it is set to CEC_PHYS_ADDR_INVALID,
then the CEC adapter will be disabled. If you change a valid physical address
to another valid physical address, then this function will first set the
address to CEC_PHYS_ADDR_INVALID before enabling the new physical address.
.. c:function::
void cec_s_phys_addr_from_edid(struct cec_adapter *adap, \
const struct edid *edid);
A helper function that extracts the physical address from the edid struct
and calls cec_s_phys_addr() with that address, or CEC_PHYS_ADDR_INVALID
if the EDID did not contain a physical address or edid was a NULL pointer.
.. c:function::
int cec_s_log_addrs(struct cec_adapter *adap, \
struct cec_log_addrs *log_addrs, bool block);
Claim the CEC logical addresses. Should never be called if CEC_CAP_LOG_ADDRS
is set. If block is true, then wait until the logical addresses have been
claimed, otherwise just queue it and return. To unconfigure all logical
addresses call this function with log_addrs set to NULL or with
log_addrs->num_log_addrs set to 0. The block argument is ignored when
unconfiguring. This function will just return if the physical address is
invalid. Once the physical address becomes valid, then the framework will
attempt to claim these logical addresses.
CEC Pin framework
-----------------
Most CEC hardware operates on full CEC messages where the software provides
the message and the hardware handles the low-level CEC protocol. But some
hardware only drives the CEC pin and software has to handle the low-level
CEC protocol. The CEC pin framework was created to handle such devices.
Note that due to the close-to-realtime requirements it can never be guaranteed
to work 100%. This framework uses highres timers internally, but if a
timer goes off too late by more than 300 microseconds wrong results can
occur. In reality it appears to be fairly reliable.
One advantage of this low-level implementation is that it can be used as
a cheap CEC analyser, especially if interrupts can be used to detect
CEC pin transitions from low to high or vice versa.
.. kernel-doc:: include/media/cec-pin.h
CEC Notifier framework
----------------------
Most drm HDMI implementations have an integrated CEC implementation and no
notifier support is needed. But some have independent CEC implementations
that have their own driver. This could be an IP block for an SoC or a
completely separate chip that deals with the CEC pin. For those cases a
drm driver can install a notifier and use the notifier to inform the
CEC driver about changes in the physical address.
.. kernel-doc:: include/media/cec-notifier.h
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
CEC Kernel Support 개요
1-13이 문서는 GPL-2.0 SPDX license를 사용합니다. CEC framework는 HDMI CEC hardware를 위한 통합 kernel interface를 제공합니다.
Receiver, transmitter, USB dongle 등 여러 hardware type을 다루며, 어떤 처리를 kernel driver에 두고 무엇을 userspace application에 맡길지 선택할 수 있습니다. Remote-control passthrough도 kernel remote-control framework와 통합합니다.
다양한 CEC hardware를 공통 kernel API와 userspace policy에 연결합니다.
CEC protocol과 address
14-31CEC protocol은 HDMI connection을 통해 consumer electronic device들이 서로 통신하게 합니다. 통신에는 logical address를 사용하며, 이 address는 device가 제공하는 기능과 엄격히 연결됩니다. Communication hub 역할을 하는 TV는 항상 address 0입니다.
Physical address는 device 사이의 물리적 연결로 결정됩니다. 이 CEC framework는 CEC 2.0 specification에 맞춰져 있으며, HDMI 1.4 specification과 HDMI 2.0의 새 2.0 bit에 문서화되어 있습니다. 대부분의 기능에는 무료 HDMI 1.3a specification으로 충분하며 문서는 `https://www.hdmi.org/spec/index`를 안내합니다.
Logical address와 physical address가 나타내는 바가 다릅니다.
CEC adapter allocate와 parameter
32-68`struct cec_adapter`는 CEC adapter hardware를 나타냅니다. `cec_allocate_adapter()`로 만들고 `cec_delete_adapter()`로 삭제합니다.
.. c:function::
struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops, \
void *priv, const char *name, \
u32 caps, u8 available_las);
.. c:function::
void cec_delete_adapter(struct cec_adapter *adap);
`ops`는 CEC framework가 호출하며 driver가 구현해야 하는 adapter operation입니다. `priv`는 `adap->priv`에 저장되어 adapter op에서 사용할 수 있고 `cec_get_drvdata(adap)`로 얻습니다.
`name`은 CEC adapter 이름이며 framework가 복사합니다. `caps`는 hardware capability와 userspace·kernelspace 처리 경계를 결정하며 `CEC_ADAP_G_CAPS`로 반환됩니다.
`available_las`는 adapter가 동시에 처리할 수 있는 logical address 수이며 `1 <= available_las <= CEC_MAX_LOG_ADDRS`여야 합니다.
Adapter 생성에 필요한 정보입니다.
Driver data와 device registration lifecycle
69-94Private pointer를 얻는 helper는 다음과 같습니다.
.. c:function::
void *cec_get_drvdata(const struct cec_adapter *adap);
`/dev/cecX` device node와 `CEC_CAP_RC`가 설정된 경우 remote-control device를 등록할 때 `cec_register_adapter()`를 호출하며 `parent`는 parent device입니다.
.. c:function::
int cec_register_adapter(struct cec_adapter *adap, \
struct device *parent);
Device를 unregister할 때는 `cec_unregister_adapter()`를 호출합니다.
.. c:function::
void cec_unregister_adapter(struct cec_adapter *adap);
`cec_register_adapter()`가 실패하면 `cec_delete_adapter()`로 정리합니다. 등록에 성공했다면 `cec_delete_adapter()`를 절대 직접 호출하지 말고 `cec_unregister_adapter()`만 호출해야 합니다. 마지막 `/dev/cecX` user가 file handle을 닫으면 unregister function이 adapter를 자동 삭제합니다.
Registration 성공 여부에 따라 cleanup API가 달라집니다.
Double deletion을 피하기 위한 엄격한 분기입니다.
Low-level cec_adap_ops
95-130Driver는 다음 low-level adapter operation을 구현해야 합니다.
.. c:struct:: cec_adap_ops
.. code-block:: none
struct cec_adap_ops
{
/* Low-level callbacks */
int (*adap_enable)(struct cec_adapter *adap, bool enable);
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable);
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
void (*adap_unconfigured)(struct cec_adapter *adap);
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg);
void (*adap_nb_transmit_canceled)(struct cec_adapter *adap,
const struct cec_msg *msg);
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
void (*adap_free)(struct cec_adapter *adap);
/* Error injection callbacks */
...
/* High-level callback */
...
};
이 low-level op들은 CEC adapter hardware 제어의 여러 측면을 담당하며 모두 `adap->lock` mutex를 잡은 상태에서 호출됩니다.
Hardware control callback과 선택 callback을 분류했습니다.
adap_enable과 HPD capability
131-146Hardware enable·disable callback signature는 다음과 같습니다.
int (*adap_enable)(struct cec_adapter *adap, bool enable);
Enable은 logical address를 하나도 claim하지 않은 상태로 CEC hardware의 power를 올리는 것을 뜻합니다. `CEC_CAP_NEEDS_HPD`가 있으면 physical address가 항상 valid하고, 없으면 hardware가 enable된 동안에도 physical address가 바뀔 수 있습니다.
Hardware 설계가 요구하지 않는 한 `CEC_CAP_NEEDS_HPD`를 설정하지 않아야 합니다. 이 flag는 standby에서 HPD를 low로 끌어내리는 display를 깨울 수 없게 만들기 때문입니다. `cec_allocate_adapter()` 직후 adapter 초기 상태는 disabled입니다.
`enable`이 false일 때 `adap_enable`은 반드시 0을 반환해야 합니다.
Physical address와 HPD requirement가 enable semantics에 영향을 줍니다.
Monitor-all과 monitor-pin
147-170`monitor all` mode callback은 다음과 같습니다.
int (*adap_monitor_all_enable)(struct cec_adapter *adap, bool enable);
Enable하면 adapter는 자신에게 온 message뿐 아니라 다른 destination의 message도 monitor해야 합니다. `CEC_CAP_MONITOR_ALL`이 있을 때만 호출되며, hardware가 항상 monitor-all mode라면 callback은 optional입니다. Disable 때는 반드시 0을 반환합니다.
`monitor pin` mode callback은 다음과 같습니다.
int (*adap_monitor_pin_enable)(struct cec_adapter *adap, bool enable);
Enable하면 CEC pin 변화도 monitor합니다. `CEC_CAP_MONITOR_PIN`이 있을 때만 호출되며 hardware가 항상 이 mode라면 optional입니다. Disable 때는 반드시 0을 반환합니다.
Capability gate와 관찰 대상입니다.
Logical address와 unconfigured callback
171-191새 logical address를 program하는 callback은 다음과 같습니다.
int (*adap_log_addr)(struct cec_adapter *adap, u8 logical_addr);
`logical_addr == CEC_LOG_ADDR_INVALID`이면 program된 모든 logical address를 지웁니다. 그 외에는 지정 address를 program합니다. 최대 available logical address 수를 넘으면 `-ENXIO`를 반환해야 합니다. Address가 program되면 hardware가 그 destination으로 온 directed message를 받을 수 있습니다.
`CEC_LOG_ADDR_INVALID`를 전달했을 때 `adap_log_addr`는 반드시 0을 반환해야 합니다.
Adapter unconfiguration callback은 다음과 같습니다.
void (*adap_unconfigured)(struct cec_adapter *adap);
Unconfigure 뒤 driver가 특별한 작업을 해야 할 때 사용하는 optional callback입니다.
Invalid sentinel은 모든 address를 지우는 명령입니다.
Message transmit와 canceled non-blocking 결과
192-221새 message transmit callback은 다음과 같습니다.
int (*adap_transmit)(struct cec_adapter *adap, u8 attempts,
u32 signal_free_time, struct cec_msg *msg);
`attempts`는 권장 transmit 시도 횟수입니다. `signal_free_time`은 line이 free가 된 뒤 송신하기 전에 기다릴 data bit period 수입니다. Retry인지, 새 initiator의 message인지, 같은 initiator의 새 message인지에 따라 값이 달라집니다.
대부분 hardware가 이를 자동 처리하지만 일부는 이 정보가 필요합니다. `CEC_FREE_TIME_TO_USEC` macro로 microsecond로 바꿀 수 있고 data bit period 하나는 2.4 ms입니다.
Canceled non-blocking transmit 결과 callback은 다음과 같습니다.
void (*adap_nb_transmit_canceled)(struct cec_adapter *adap,
const struct cec_msg *msg);
이 optional callback은 `msg->sequence`인 non-blocking transmit이 abort되었거나 timeout되었거나, transmit은 성공했지만 expected reply wait가 abort 또는 timeout된 경우 결과를 얻는 데 사용합니다.
Hardware가 송신 timing과 retry를 수행하는 데 필요한 값입니다.
Transmit 또는 reply wait가 끝나지 못한 경로입니다.
Debug status와 adapter resource free
222-237현재 CEC hardware status를 출력하는 optional callback은 다음과 같습니다.
void (*adap_status)(struct cec_adapter *adap, struct seq_file *file);
Status는 debugfs의 `cat /sys/kernel/debug/cec/cecX/status`로 확인할 수 있습니다.
Adapter 삭제 때 resource를 free하는 optional callback은 다음과 같습니다.
void (*adap_free)(struct cec_adapter *adap);
Driver가 allocate한 resource를 해제하는 데 사용하며 `cec_delete_adapter()`에서 호출됩니다.
두 optional callback의 호출 지점입니다.
Transmit completion, status와 receive event
238-309Adapter driver는 보통 interrupt event에 반응해 framework function을 호출해야 합니다. Transmit이 성공 또는 실패로 끝났을 때는 다음 둘 중 하나를 사용합니다.
void cec_transmit_done(struct cec_adapter *adap, u8 status,
u8 arb_lost_cnt, u8 nack_cnt, u8 low_drive_cnt,
u8 error_cnt);
or::
void cec_transmit_attempt_done(struct cec_adapter *adap, u8 status);
`CEC_TX_STATUS_OK`는 성공, `CEC_TX_STATUS_ARB_LOST`는 다른 initiator가 line control을 가져가 arbitration에서 진 상태입니다. `CEC_TX_STATUS_NACK`은 directed message가 NACK되었거나 broadcast message가 ACK되어 retransmission이 필요함을 뜻합니다.
`CEC_TX_STATUS_LOW_DRIVE`는 follower가 bus error를 감지해 retransmission을 요청했음을 뜻합니다. `CEC_TX_STATUS_ERROR`는 hardware가 구분하지 못하는 ARB_LOST·LOW_DRIVE 또는 다른 unspecified error입니다. Hardware가 OK와 FAIL만 제공하면 FAIL은 `CEC_TX_STATUS_ERROR`가 아니라 `CEC_TX_STATUS_NACK`으로 mapping해야 합니다.
`CEC_TX_STATUS_MAX_RETRIES`는 여러 번 시도했지만 전송하지 못한 상태입니다. Hardware retry support가 있을 때만 driver가 설정하며 framework는 hardware가 이미 retry했다고 보고 추가 시도를 하지 않습니다. Hardware는 최소한 OK, NACK, 그 밖의 상태를 구분할 수 있어야 합니다.
`*_cnt` 인자는 관찰한 각 error condition 횟수입니다. 정보가 없으면 0일 수 있습니다. Hardware retry가 없으면 해당 error counter를 1로 설정할 수 있습니다. Retry가 있으면 feedback이 없을 때 모두 0, feedback이 있으면 hardware 보고값을 채웁니다.
Completion function 호출은 queue에 pending transmit이 있으면 즉시 다음 transmit을 시작할 수 있습니다. 따라서 호출 전에 hardware가 새 transmit을 시작할 수 있는 상태여야 합니다.
`cec_transmit_attempt_done()`은 hardware가 절대 retry하지 않아 항상 single attempt인 경우의 helper입니다. 내부에서 `cec_transmit_done()`을 호출하고 해당 status counter를 1로, OK라면 모든 counter를 0으로 채웁니다.
CEC message를 받았을 때 호출하는 API는 다음과 같습니다.
.. c:function::
void cec_received_msg(struct cec_adapter *adap, struct cec_msg *msg);
Framework에 보고할 status와 의미입니다.
Callback이 다음 queued transmit을 즉시 시작할 수 있습니다.
Interrupt handler ordering
310-321일반적인 CEC hardware는 transmit 완료와 성공 여부, CEC message 수신을 interrupt로 알립니다.
CEC driver는 receive interrupt보다 transmit interrupt를 항상 먼저 처리해야 합니다. Framework는 `cec_received_msg()`보다 `cec_transmit_done()`을 먼저 볼 것으로 기대합니다. 순서가 바뀌면 받은 message가 방금 송신한 message의 reply인 경우 framework가 혼동할 수 있습니다.
동시에 TX와 RX가 pending일 때 TX completion을 먼저 보고합니다.
Error injection interface와 syntax
322-363CEC adapter가 Error Injection 기능을 지원하면 다음 callback으로 노출할 수 있습니다.
.. code-block:: none
struct cec_adap_ops {
/* Low-level callbacks */
...
/* Error injection callbacks */
int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
/* High-level CEC message callback */
...
};
두 callback이 모두 설정되면 debugfs에 `error-inj` file이 생깁니다. Leading space와 tab은 무시하며 다음 character가 `#`이거나 line 끝이면 전체 line을 무시하고, 그렇지 않으면 command를 기대합니다. 이 기본 parsing은 CEC framework가 수행하고 driver가 구현할 command를 정합니다.
인자 없는 `clear` command는 반드시 구현하여 현재 error injection command를 모두 제거해야 합니다. 따라서 driver-specific syntax를 몰라도 `echo clear >error-inj`로 항상 정리할 수 있습니다.
`error-inj` output은 그대로 input으로 유효해야 하므로 다음 round trip이 동작해야 합니다.
.. code-block:: none
$ cat error-inj >einj.txt
$ cat einj.txt >error-inj
Framework의 공통 syntax 처리 뒤 driver parser가 command를 검증합니다.
Error injection callback 동작
364-383File read 때 현재 error injection state를 출력하는 callback은 다음과 같습니다.
int (*error_inj_show)(struct cec_adapter *adap, struct seq_file *sf);
기본 사용법을 담은 comment block으로 시작하는 것이 권장되며 성공 시 0, 실패 시 error를 반환합니다.
`error-inj`에 쓴 command를 parse하는 callback은 다음과 같습니다.
bool (*error_inj_parse_line)(struct cec_adapter *adap, char *line);
`line`은 leading space·tab을 이미 건너뛴 command 시작을 가리킵니다. Embedded newline이 없는 single line이고 NUL-terminated이며 callback이 buffer 내용을 수정해도 됩니다. Empty line이나 comment line에는 호출되지 않습니다. Command가 valid하면 true, syntax error면 false를 반환합니다.
High-level CEC adapter callback
384-424Low-level operation은 hardware를 구동하고 high-level operation은 CEC protocol에 따라 동작합니다. High-level callback은 `adap->lock` mutex를 잡지 않은 상태에서 호출됩니다.
.. code-block:: none
struct cec_adap_ops {
/* Low-level callbacks */
...
/* Error injection callbacks */
...
/* High-level CEC message callback */
void (*configured)(struct cec_adapter *adap);
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
};
Adapter가 완전히 configured되어 모든 logical address를 성공적으로 claim했을 때 호출하는 optional callback은 다음과 같습니다.
void (*configured)(struct cec_adapter *adap);
Configuration 뒤 driver-specific action이 필요하면 `configured()`에서 수행합니다.
새로 받은 CEC message를 driver가 선택적으로 처리하는 callback은 다음과 같습니다.
int (*received)(struct cec_adapter *adap, struct cec_msg *msg);
Driver가 message를 처리하지 않으려면 `-ENOMSG`를 반환해야 합니다. 그 외 return이면 framework는 driver가 처리했다고 가정하고 추가 동작을 하지 않습니다.
Lock context와 책임이 다릅니다.
Framework transmit과 physical address
425-451Adapter driver가 CEC message를 보내는 framework function은 다음과 같습니다.
.. c:function::
int cec_transmit_msg(struct cec_adapter *adap, struct cec_msg *msg, \
bool block);
`block`이 true면 transmit 완료까지 기다리고, false면 queue에 넣고 즉시 반환합니다.
Physical address 변경 API는 다음과 같습니다.
.. c:function::
void cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block);
이 function은 `adap->phys_addr`를 설정하고 값이 바뀌면 event를 보냅니다. `cec_s_log_addrs()`가 이미 호출되었고 physical address가 valid가 되면 framework가 logical address claim을 시작합니다. `block`이 true면 이 과정이 끝날 때까지 반환하지 않습니다.
Physical address를 valid value로 설정하면 `adap_enable`로 adapter를 enable하고 `CEC_PHYS_ADDR_INVALID`로 설정하면 disable합니다. Valid address에서 다른 valid address로 바꿀 때는 먼저 invalid로 설정한 뒤 새 address로 enable합니다.
Valid address 전환은 항상 invalid 중간 상태를 거칩니다.
EDID helper와 logical address claim
452-472EDID에서 physical address를 추출하는 helper는 다음과 같습니다.
.. c:function::
void cec_s_phys_addr_from_edid(struct cec_adapter *adap, \
const struct edid *edid);
EDID에 physical address가 없거나 `edid`가 NULL이면 `CEC_PHYS_ADDR_INVALID`, 있으면 추출한 address로 `cec_s_phys_addr()`를 호출합니다.
CEC logical address를 claim하는 API는 다음과 같습니다.
.. c:function::
int cec_s_log_addrs(struct cec_adapter *adap, \
struct cec_log_addrs *log_addrs, bool block);
`CEC_CAP_LOG_ADDRS`가 설정된 경우에는 절대 호출하면 안 됩니다. `block`이 true면 claim 완료까지 기다리고 false면 queue 후 반환합니다.
모든 logical address를 unconfigure하려면 `log_addrs`를 NULL로 전달하거나 `log_addrs->num_log_addrs`를 0으로 설정합니다. Unconfigure 때는 `block`을 무시합니다. Physical address가 invalid면 function은 바로 반환하지만, 나중에 valid가 되면 framework가 이 logical address들을 claim합니다.
Physical address 유효성이 claim 실행 시점을 결정합니다.
CEC Pin framework
473-491대부분 CEC hardware는 full CEC message를 받고 low-level protocol을 hardware가 처리합니다. 일부 hardware는 CEC pin만 구동해 software가 low-level CEC protocol을 처리해야 하며, 이를 위해 CEC pin framework가 만들어졌습니다.
Close-to-realtime requirement 때문에 100% 동작을 보장할 수 없습니다. 내부적으로 high-resolution timer를 사용하지만 timer가 300 microseconds보다 더 늦게 실행되면 잘못된 결과가 생길 수 있습니다. 실제로는 상당히 신뢰할 만한 것으로 보입니다.
이 low-level 구현은 값싼 CEC analyser로 사용할 수 있다는 장점이 있습니다. 특히 interrupt로 CEC pin의 low-to-high 또는 high-to-low transition을 감지할 수 있으면 유용합니다. API는 다음 kernel-doc에서 가져옵니다.
.. kernel-doc:: include/media/cec-pin.h
Software protocol 처리의 장점과 timing 위험입니다.
CEC Notifier framework
492-502대부분 DRM HDMI 구현은 CEC가 통합되어 notifier가 필요 없습니다. 하지만 SoC IP block이나 CEC pin 전용 별도 chip처럼 독립 CEC 구현과 자체 driver를 가진 경우가 있습니다.
이 경우 DRM driver는 notifier를 설치해 physical address 변경을 CEC driver에 알릴 수 있습니다. API는 다음 kernel-doc에서 가져옵니다.
.. kernel-doc:: include/media/cec-notifier.h
DRM HDMI와 별도 CEC driver 사이에 physical address를 전달합니다.
요약과 해설
cec-core.rst:1-502CEC core는 다양한 HDMI CEC hardware를 `cec_adapter` lifecycle과 `cec_adap_ops`로 통합합니다. Low-level callback은 `adap->lock` 아래 hardware를 제어하고, high-level callback은 lock 없이 protocol policy를 처리합니다.
Driver는 TX completion을 RX보다 먼저 보고하고 hardware를 다음 송신 가능 상태로 만든 뒤 completion API를 호출해야 합니다. Physical address validity가 adapter enable과 logical address claim을 제어하며, error injection·pin·notifier framework가 진단과 별도 hardware 구성을 지원합니다.