Это справочное руководство Rockchip HDMI-CEC по китайскому PDF V1.1.0 (39 страниц); английский оригинал не предоставлен. Английская и русская статьи полностью переводят технический текст; во всех трёх языках используются рисунки китайского оригинала с локализованными описаниями. Оригинал охватывает Android 5.x и новее, Linux kernel 4.4/4.19 и RK322X/RK3328/RK3368/RK3399/RK3288. Это не подтверждает поддержку всех современных BSP или плат ArmSoM. Проверьте SoC, разводку HDMI CEC, HAL, драйвер CEC, службы Android и прошивку ожидания. Испытания оборудования ArmSoM и совместимости телевизоров не проводились; журналы и действия — примеры оригинала.
Редакционные замечания · ArmSoM documentation · Rockchip
1. Введение в CEC
1.1 Определение CEC
CEC означает Consumer Electronics Control. Протокол обеспечивает расширенное управление домашними аудиовизуальными устройствами, соединёнными HDMI, с одного пульта.

Как показано на рисунке, и пульт телевизора, и пульт приставки могут управлять обоими устройствами.
1.2 Обзор протокола CEC
CEC предполагает, что все источники аудио/видео прямо или косвенно подключены к корневому дисплею. HDMI образует дерево сверху вниз: дисплей — корень, коммутаторы — ветви, источники — листья. Для адресации и управления каждому устройству нужен ровно один физический адрес. ПО назначает физические адреса сети CEC с помощью EDID.

Каждое устройство шины CEC должно получить логический адрес, определяющий его тип. Каждый адрес принадлежит только одному устройству, кроме адреса 15. Большинство устройств использует один логический адрес, некоторые — до двух.

| Адрес | Устройство |
|---|---|
| 0 | Телевизор |
| 1 | Устройство записи 1 |
| 2 | Устройство записи 2 |
| 3 | Тюнер 1 |
| 4 | Устройство воспроизведения 1 |
| 5 | Аудиосистема |
| 6 | Тюнер 2 |
| 7 | Тюнер 3 |
| 8 | Устройство воспроизведения 2 |
| 9 | Устройство записи 3 |
| 10 | Тюнер 4 |
| 11 | Устройство воспроизведения 3 |
| 12 | Резерв |
| 13 | Резерв |
| 14 | Специальное применение |
| 15 | Незарегистрированный (инициатор) / широковещательный (получатель) |
Назначение логического адреса показано ниже. POLL MSG с одинаковыми SRC и DST проверяет, занят ли адрес другим устройством.

Устройства CEC взаимодействуют посредством сообщений CEC.

Сообщение CEC содержит один или несколько блоков. В каждом блоке 8 бит Header/Data, бит EOM и бит ACK. EOM=1 означает последний блок без дальнейших данных. Передатчик устанавливает ACK в 1, а получатель при успешном приёме устанавливает 0 для подтверждения.

Header Block — первый блок сообщения. Старшие четыре информационных бита содержат логический адрес отправителя, младшие четыре — получателя. Информационные биты Data Block содержат opcode или параметры.
2. Реализация CEC
2.1 Инфраструктура CEC Android
2.1.1 Обзор инфраструктуры
Многие производители реализуют HDMI-CEC, что делает возможной совместную работу устройств разных марок. Но различия реализации и функций мешают полной совместимости: два изделия с заявленной поддержкой CEC не обязательно взаимодействуют. Вместе с Android TIF (TV Input Framework) HDMI-CEC обеспечивает обмен между подключёнными устройствами с уменьшением проблем совместимости. Для этого Android ввёл HdmiControlService. Как часть экосистемы Android, он предоставляет производителям стандартную реализацию HDMI-CEC и сокращает несовместимость. Раньше производители разрабатывали собственные реализации либо использовали сторонние. Оригинал описывает HdmiControlService как службу, проверенную с распространёнными устройствами HDMI-CEC, учитывающую исследования совместимости и предложения опытных отраслевых партнёров. Она сочетает стандарт с изменениями, необходимыми для уже используемых изделий. Это утверждения источника, а не испытания совместимости для статьи.
2.1.1.1 HdmiControlService
HdmiControlService реализует CEC совместно с TIF, аудиослужбами, управлением питанием и другими компонентами системы. Схема показывает переход от специального контроллера CEC к более простому аппаратному абстрактному слою HDMI-CEC и реализацию службы HDMI. Основные компоненты:
HdmiControlManager предоставляет API приложениям с соответствующими разрешениями. Системные службы, например TV Input Manager и аудиослужбы, могут использовать класс непосредственно.
Путь исходного кода:
frameworks/base/core/java/android/hardware/hdmi
Служба рассчитана на несколько типов логических устройств.
Путь исходного кода:
frameworks/base/services/core/java/com/android/server/hdmi
HDMI-CEC обращается к аппаратуре через HAL, скрывающий различия протокола и сигнализации устройств. Производители могут реализовать собственный HAL по существующему интерфейсу.


2.1.1.2 HDMI-CEC HAL
Для работы службы CEC реализуйте HDMI-CEC HAL по определениям Android. Он скрывает аппаратные различия и предоставляет API базовых операций: выделения, чтения и записи. Производитель должен поддерживать следующие API. Передача/приём/события:
send_message
register_event_callback
Информация:
get_physical_address
get_version
get_vendor_id
get_port_info
Логические адреса:
add_logical_address
clear_logical_address
Состояние:
is_connected; set_option
set_audio_return_channel
Фрагмент определения API HDMI-CEC HAL:
#ifndef ANDROID_INCLUDE_HARDWARE_HDMI_CEC_H
#define ANDROID_INCLUDE_HARDWARE_HDMI_CEC_H
...
/*
* HDMI-CEC HAL interface definition.
*/
typedef struct hdmi_cec_device {
/**
* Common methods of the HDMI-CEC device. This *must* be the first member of
* hdmi_cec_device as users of this structure will cast a hw_device_t to
hdmi_cec_device
* pointer in contexts where it's known the hw_device_t references a
hdmi_cec_device.
*/
struct hw_device_t common;
/*
* (*add_logical_address) passes the logical address that will be used
* in this system.
*
* HAL may use it to configure the hardware so that the CEC commands
addressed
* the given logical address can be filtered in. This method can be called
* as many times as necessary in order to support multiple logical devices.
* addr should be in the range of valid logical addresses for the call
* to succeed.
*
* Returns 0 on success or -errno on error.
*/
int (*add_logical_address)(const struct hdmi_cec_device* dev,
cec_logical_address_t addr);
/*
* (*clear_logical_address) tells HAL to reset all the logical addresses.
*
* It is used when the system doesn't need to process CEC command any more,
* hence to tell HAL to stop receiving commands from the CEC bus, and change
* the state back to the beginning.
*/
void (*clear_logical_address)(const struct hdmi_cec_device* dev);
/*
* (*get_physical_address) returns the CEC physical address. The
* address is written to addr.
*
* The physical address depends on the topology of the network formed
* by connected HDMI devices. It is therefore likely to change if the cable
* is plugged off and on again. It is advised to call get_physical_address
* to get the updated address when hot plug event takes place.
*
* Returns 0 on success or -errno on error.
*/
int (*get_physical_address)(const struct hdmi_cec_device* dev, uint16_t*
addr);
/*
* (*send_message) transmits HDMI-CEC message to other HDMI device.
*
* The method should be designed to return in a certain amount of time not
* hanging forever, which can happen if CEC signal line is pulled low for
* some reason. HAL implementation should take the situation into account
* so as not to wait forever for the message to get sent out.
*
* It should try retransmission at least once as specified in the standard.
*
* Returns error code. See HDMI_RESULT_SUCCESS, HDMI_RESULT_NACK, and
* HDMI_RESULT_BUSY.
*/
int (*send_message)(const struct hdmi_cec_device* dev, const cec_message_t*);
/*
* (*register_event_callback) registers a callback that HDMI-CEC HAL
* can later use for incoming CEC messages or internal HDMI events.
* When calling from C++, use the argument arg to pass the calling object.
* It will be passed back when the callback is invoked so that the context
* can be retrieved.
*/
void (*register_event_callback)(const struct hdmi_cec_device* dev,
event_callback_t callback, void* arg);
/*
* (*get_version) returns the CEC version supported by underlying hardware.
*/
void (*get_version)(const struct hdmi_cec_device* dev, int* version);
/*
* (*get_vendor_id) returns the identifier of the vendor. It is
* the 24-bit unique company ID obtained from the IEEE Registration
* Authority Committee (RAC).
*/
void (*get_vendor_id)(const struct hdmi_cec_device* dev, uint32_t*
vendor_id);
/*
* (*get_port_info) returns the hdmi port information of underlying hardware.
* info is the list of HDMI port information, and 'total' is the number of
* HDMI ports in the system.
*/
void (*get_port_info)(const struct hdmi_cec_device* dev,
struct hdmi_port_info* list[], int* total);
/*
* (*set_option) passes flags controlling the way HDMI-CEC service works down
* to HAL implementation. Those flags will be used in case the feature needs
* update in HAL itself, firmware or microcontroller.
*/
void (*set_option)(const struct hdmi_cec_device* dev, int flag, int value);
/*
* (*set_audio_return_channel) configures ARC circuit in the hardware logic
* to start or stop the feature. Flag can be either 1 to start the feature
* or 0 to stop it.
*
* Returns 0 on success or -errno on error.
*/
void (*set_audio_return_channel)(const struct hdmi_cec_device* dev, int
flag);
/*
* (*is_connected) returns the connection status of the specified port.
* Returns HDMI_CONNECTED if a device is connected, otherwise
HDMI_NOT_CONNECTED.
* The HAL should watch for +5V power signal to determine the status.
*/
int (*is_connected)(const struct hdmi_cec_device* dev, int port);
/* Reserved for future use to maximum 16 functions. Must be NULL. */
void* reserved[16 - 11];
} hdmi_cec_device_t;
#endif /* ANDROID_INCLUDE_HARDWARE_HDMI_CEC_H */
Через эти API служба CEC передаёт и принимает команды HDMI-CEC, выполняет настройку и при необходимости взаимодействует с низкоуровневым микропроцессором, управляющим CEC во время ожидания Android.
2.1.2 Пути исходного кода
Пути кода CEC и их назначение:
| Путь | Описание |
|---|---|
| kernel/drivers/media/cec/cec-adap.c | Ядро драйвера CEC: назначение физических/логических адресов и управление событиями и передачей/приёмом сообщений. |
| kernel/drivers/media/cec/cec-api.c | Предоставляет IOCTL пользовательскому пространству. |
| kernel/drivers/media/cec/cec-core.c | Регистрирует устройства CEC. |
| kernel/drivers/media/cec/cec-notifier.c | Уведомляет драйвер CEC об изменениях физического адреса. |
| kernel/drivers/media/cec/cec-edid.c | Вспомогательная обработка EDID, прежде всего получение физического адреса. |
| kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | Драйвер DW-HDMI CEC, главным образом операции с регистрами DW-HDMI CEC. |
Пути исходного кода Android:
| Путь | Описание |
|---|---|
| hardware/rockchip/hdmicec/hdmi_cec.cpp | Реализует стандартный интерфейс CEC HAL. |
| hardware/rockchip/hdmicec/hdmicec_event.cpp | Поток отслеживает сообщения/события CEC ядра и передаёт их вверх. |
| frameworks/base/services/core/java/com/android/server/hdmi/HdmiControlService.java | Служба отправки и обработки управляющих сообщений HDMI и команд CEC. |
| frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecController.java | Управляет командами и операциями CEC. Преобразует команды пользователя в CEC для HAL; разбирает входящие сообщения и распределяет по модулям. |
| frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java | Моделирует логическое устройство CEC, инициализирует его и вызывает специализированные обработчики входящих сообщений. |
| frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java | Абстрагирует устройство PLAYBACK и предоставляет его интерфейсы. |
2.2 Приложения HDMI CEC в Linux
В Linux утилита cec-ctl из v4l-utils управляет устройствами CEC из командной строки.
2.2.1 Установка v4l-utils
В Debian установите v4l-utils командой:
sudo apt-get install v4l-utils
В Buildroot включите следующие параметры сборки:
BR2_PACKAGE_LIBV4L=y
BR2_PACKAGE_LIBV4L_UTILS=y
2.2.2 Команды
Команда Playback:
[root@rk3288:/]#cec-ctl --playback -o Rockchip -V 0xaabbcc -M -T
Часть вывода:
CEC_ADAP_G_CAPS returned 0 (Success)
CEC_ADAP_G_PHYS_ADDR returned 0 (Success)
CEC_ADAP_S_LOG_ADDRS returned 0 (Success)
CEC_ADAP_S_LOG_ADDRS returned 0 (Success)
CEC_ADAP_G_LOG_ADDRS returned 0 (Success)
Driver Info:
Driver Name : dwhdmi-rockchip
Adapter Name : dw_hdmi
Capabilities : 0x0000000e
Logical Addresses
Transmit
Passthrough
Driver version : 4.4.167
Available Logical Addresses: 4
Physical Address : 1.0.0.0
Logical Address Mask : 0x0010
CEC Version : 2.0
Vendor ID : 0xaabbcc
Logical Address : 4 (Playback Device 1)
Primary Device Type : Playback
Logical Address Type : Playback
All Device Types : Playback
RC TV Profile : None
Device Features : None
Monitor All mode is not supported, falling back to regular monitoring
CEC_S_MODE returned 0 (Success)
CEC_DQEVENT returned 0 (Success)
При инициализации CEC передаются основные сведения: vendor ID, имя OSD и версия CEC:
Received from TV to Playback Device 1 (0 to 4): CEC_MSG_GIVE_DEVICE_VENDOR_ID
(0x8c)
CEC_RECEIVE returned 0 (Success)
Transmitted by Playback Device 1 to all (4 to 15): CEC_MSG_DEVICE_VENDOR_ID
(0x87):
vendor-id: 11189196 (0x00aabbcc)
CEC_RECEIVE returned 0 (Success)
Received from TV to Playback Device 1 (0 to 4): CEC_MSG_GIVE_OSD_NAME (0x46)
CEC_RECEIVE returned 0 (Success)
Transmitted by Playback Device 1 to TV (4 to 0): CEC_MSG_SET_OSD_NAME (0x47):
name: Rockchip
CEC_RECEIVE returned 0 (Success)
При переходе телевизора в ожидание он отправляет SoC сообщение standby:
Received from TV to all (0 to 15): CEC_MSG_STANDBY (0x36)
CEC_RECEIVE returned 0 (Success)
При смене источника телевизор отправляет следующие сообщения:
Received from TV to all (0 to 15): CEC_MSG_SET_STREAM_PATH (0x86):
phys-addr: 2.0.0.0
CEC_RECEIVE returned 0 (Success)
Received from TV to all (0 to 15): CEC_MSG_ROUTING_CHANGE (0x80):
orig-phys-addr: 0.0.0.0
new-phys-addr: 2.0.0.0
CEC_RECEIVE returned 0 (Success)
Команда One-touch-play:
[root@rk3288:/]# cec-ctl --help-one-touch-play
One Touch Play Feature:
--active-source=phys-addr=<val> Send ACTIVE_SOURCE message (0x82)
--image-view-on Send IMAGE_VIEW_ON message (0x04)
--text-view-on Send TEXT_VIEW_ON message (0x0d)
Разбудить телевизор:
[root@rk3288:/]#cec-ctl --image-view-on -to 0
Команда Standby:
[root@rk3288:/]# cec-ctl --standby --to 0
Дополнительные команды пользовательского пространства приведены в cec-ctl –help.
Примечания
Описанная реализация пока не поддерживает переход SoC в ожидание и пробуждение по событиям телевизора.
Для ожидания используйте пример Playback из §2.2.2: после CEC_MSG_STANDBY вызовите echo mem > /sys/power/state для синхронного перехода SoC в ожидание. Пробуждение требует обработки trust, cec-clk и HDMI PHY во время ожидания и недостаточно поддерживается описанной реализацией.
Не каждое HDMI-устройство поддерживает CEC. Сначала проверьте поддержку CEC и конкретной команды телевизором или HDMI-дисплеем.
2.3 Программные процессы CEC
2.3.1 Инициализация CEC
2.3.1.1 Регистрация драйвера CEC

Схема выше показывает регистрацию драйвера CEC.
dw_hdmi_cec_probe:
Инициализирует драйвер CEC и связывает абстрактное устройство системы с реальной аппаратурой. Также настраивает регистры CEC и регистрирует прерывание передачи/приёма сообщений (cec->irq).
cec_allocate_adapter:
(1) Создаёт и настраивает адаптер.
Структура адаптера:
struct cec_adapter {
struct module *owner;
char name[32];
struct cec_devnode devnode;
struct mutex lock;
struct rc_dev *rc;
struct list_head transmit_queue;
unsigned int transmit_queue_sz;
struct list_head wait_queue;
struct cec_data *transmitting;
struct task_struct *kthread_config;
struct completion config_completion;
struct task_struct *kthread;
wait_queue_head_t kthread_waitq;
wait_queue_head_t waitq;
const struct cec_adap_ops *ops;
void *priv;
u32 capabilities;
u8 available_log_addrs;
u32 monitor_all_cnt;
u32 monitor_pin_cnt;
u32 follower_cnt;
struct cec_fh *cec_follower;
struct cec_fh *cec_initiator;
bool passthrough;
struct cec_log_addrs log_addrs;
u32 tx_timeouts;
#ifdef CONFIG_CEC_NOTIFIER
struct cec_notifier *notifier;
#endif
#ifdef CONFIG_CEC_PIN
struct cec_pin *pin;
#endif
struct dentry *cec_dir;
struct dentry *status_file;
u16 phys_addrs[15];
u32 sequence;
char input_name[32];
char input_phys[32];
char input_drv[32];
};
Основные поля структуры описаны ниже:
| Имя | Описание |
|---|---|
| transmit_queue | Очередь ожидающих передачи сообщений CEC. |
| transmit_queue_sz | Длина очереди передачи. |
| wait_queue | Хранит сообщения, ожидающие ответа, если включено ожидание; в этой версии не используется. |
| transmitting | Текущее передаваемое сообщение CEC. |
| kthread_config | Поток начальной настройки CEC. |
| config_completion | Объект completion для ожидания инициализации CEC (в оригинале назван семафором). |
| kthread | Дескриптор cec_thread_func, управляющего очередью передачи CEC. |
| kthread_waitq | Очередь ожидания cec_thread_func. |
| ops | Обратные вызовы адаптера; см. kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c. |
| capabilities | Настроенные возможности адаптера. |
| available_log_addrs | Максимальное число назначаемых логических адресов. |
| phys_addr | Текущий физический адрес. |
| is_configuring | Выполняется начальная настройка. |
| is_configured | Начальная настройка завершена. |
| follower_cnt | Число follower; в описанном решении 1. |
| cec_follower | Текущий cec_follower; в этой версии fh. |
| cec_initiator | Текущий cec_initiator; в этой версии fh. |
| passthrough | Использует ли текущий cec_follower passthrough. |
| log_addrs | Один или несколько назначенных логических адресов. |
| tx_timeouts | Число тайм-аутов передачи CEC; обычно редки. |
| notifier | Уведомитель CEC. |
| phys_addrs | Физические адреса для случая нескольких логических адресов. |
| sequence | Порядковый номер передачи адаптера для отслеживания сообщений, ожидающих ответа. |
(2) Запускает cec_thread_func для управления очередью передачи сообщений CEC.
(3) Настраивает возможности адаптера, описанные ниже:
| Имя | Описание |
|---|---|
| CEC_CAP_PHYS_ADDR | Пользовательское пространство должно задать физический адрес. |
| CEC_CAP_LOG_ADDRS | Пользовательское пространство должно задать логические адреса. |
| CEC_CAP_TRANSMIT | Разрешает отправку сообщений CEC из пользовательского пространства. |
| CEC_CAP_PASSTHROUGH | Драйвер передаёт входящие сообщения пользовательскому пространству без обработки. |
| CEC_CAP_RC | Поддерживает управление CEC через драйвер дистанционного управления. |
| CEC_CAP_MONITOR_ALL | Принимает все сообщения CEC, включая адресованные другим устройствам, обычно для отладки. |
| CEC_CAP_NEEDS_HPD | Включает CEC только при высоком уровне HDMI HPD. |
| CEC_CAP_MONITOR_PIN | Отслеживает изменения вывода CEC. |
cec_register_adapter:
(1) Регистрирует узел устройства CEC.
(2) Регистрирует отладочные узлы CEC.
cec_register_cec_notifier:
Регистрирует CEC notifier и привязывает его к адаптеру.
2.3.1.2 Начальная настройка CEC
Для работы CEC нужна правильная инициализация: включение функций, назначение и сохранение логического адреса и завершение настройки драйвера. Процессы FRAMEWORK, JNI и HAL показаны ниже:

Основные этапы:
initializeCec:
initializeCec — основной этап инициализации. Он выполняется при загрузке, после пробуждения и при включении CEC в Settings. Он инициализирует локальные устройства, назначает логические адреса и включает CEC. В описанном Android поддерживаются только TV и PLAYBACK; Rockchip главным образом использует PLAYBACK.
initializeLocalDevices:
(1) Создаёт и инициализирует локальное устройство CEC — HdmiCecLocalDevicePlayback. (2) localDevice.init вызывает getPreferredAddress для чтения persist.sys.hdmi.addr.playback. Сначала пробуется ранее назначенный адрес; иначе поиск начинается с первого адреса типа устройства — 0x4 для PLAYBACK. (3) localDevices.add добавляет устройство в список localDevices.
hdmi_cec_set_option:
При инициализации HAL включает следующие параметры CEC:
| Имя | Описание |
|---|---|
| HDMI_OPTION_WAKEUP | При false сообщения Image View On/Text View On и другие команды пробуждения не будят систему. В описанной версии переключатель фактически не действует, поскольку пробуждение системы по CEC не реализовано. |
| HDMI_OPTION_ENABLE_CEC | Главный переключатель CEC. При false HAL не отправляет и не принимает сообщения. Управляется общим переключателем CEC в Settings. |
| HDMI_OPTION_SYSTEM_CEC_CONTROL | Устанавливается в false при ожидании и true при пробуждении. False означает, что Android перестаёт обрабатывать сообщения CEC и управление передаётся нижнему уровню. Сейчас практически не действует; может использоваться при будущей поддержке пробуждения CEC. |
allocateLogicalAddress:
Главным образом назначает логический адрес.
(1) Вызывает hdmi_cec_send_message HAL для POLL. Если все попытки дают NACK, выбирается этот адрес. Число попыток задаётся HdmiConfig.ADDRESS_ALLOCATION_RETRY.
(2) Вызывает hdmi_cec_add_logical_address HAL для передачи выбранного адреса драйверу.
(3) После назначения onAddressAllocated отправляет несколько сообщений CEC, включая ReportPhysicalAddress.
Здесь возникает несогласованность процессов. Назначение логического адреса CEC требует POLL: NACK означает свободный адрес, который можно выбрать.
Android FRAMEWORK непосредственно отправляет несколько POLL через hdmi_cec_send_message HAL. Если подтверждения нет, он выбирает адрес и лишь затем передаёт его драйверу. Драйвер CEC ядра сначала требует инициализацию/configure, выполняет назначение через IOCTL CEC_ADAP_S_PHYS_ADDR согласно этому фрагменту оригинала и сообщает адрес вверх. До этого прямая отправка POLL через send_message запрещена. Далее подробный процесс оригинала использует CEC_ADAP_S_LOG_ADDRS; различие сохранено для проверки.
HAL согласует эти процессы. Когда FRAMEWORK запрашивает POLL через hdmi_cec_send_message, HAL читает адрес ядра через CEC_ADAP_G_LOG_ADDRS. При отсутствии адреса (CEC_LOG_ADDR_INVALID) назначает его через CEC_ADAP_S_LOG_ADDRS. Если адрес ядра совпадает с адресом POLL, HAL возвращает NACK; иначе success. FRAMEWORK проверяет следующий адрес, пока его выбор не совпадёт с выбором ядра.
Процесс инициализации CEC в ядре:

HAL вызывает cec_adap_s_log_addrs через IOCTL CEC_ADAP_S_LOG_ADDRS.
cec_claim_log_addrs запускает cec_config_thread_func и ожидает завершения через wait_for_completion(&adap->config_completion).
static void cec_claim_log_addrs(struct cec_adapter *adap, bool block)
{
if (WARN_ON(adap->is_configuring || adap->is_configured))
return;
init_completion(&adap->config_completion);
/* Ready to kick off the thread */
adap->is_configuring = true;
adap->kthread_config = kthread_run(cec_config_thread_func, adap,
"ceccfg-%s", adap->name);
if (IS_ERR(adap->kthread_config)) {
adap->kthread_config = NULL;
} else if (block) {
mutex_unlock(&adap->lock);
wait_for_completion(&adap->config_completion);
mutex_lock(&adap->lock);
}
}
cec_config_thread_func помещает POLL в очередь и отправляет через cec_transmit_msg_fh. При отсутствии подтверждения назначается этот адрес.
Обратный вызов adap->ops->adap_log_addr вызывает dw_hdmi_cec_log_addr для записи адреса в регистры CEC.
Инициализация завершена (adap->is_configured=true). cec_post_state_event сообщает об изменении состояния верхнему уровню.
cec_msg_report_physical_addr создаёт сообщение report physical addr, помещает его в очередь и отправляет.
complete(&adap->config_completion) пробуждает поток, ожидающий завершения настройки.
2.3.2 Передача сообщений CEC
Передача с верхнего уровня проста: вызовите sendCecCommand, как показано на схеме.

Параметр HdmiCecMessage функции sendCecCommand описан в:
frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
Процесс в ядре:

В итоге HAL вызывает cec_transmit драйвера через IOCTL CEC_TRANSMIT.
static long cec_transmit(struct cec_adapter *adap, struct cec_fh *fh,
bool block, struct cec_msg __user *parg)
{
struct cec_msg msg = {};
long err = 0;
if (!(adap->capabilities & CEC_CAP_TRANSMIT))
return -ENOTTY;
if (copy_from_user(&msg, parg, sizeof(msg)))
return -EFAULT;
/* A CDC-Only device can only send CDC messages */
if ((adap->log_addrs.flags & CEC_LOG_ADDRS_FL_CDC_ONLY) &&
(msg.len == 1 || msg.msg[1] != CEC_MSG_CDC_MESSAGE))
return -EINVAL;
mutex_lock(&adap->lock);
if (adap->log_addrs.num_log_addrs == 0)
err = -EPERM;
else if (adap->is_configuring)
err = -ENONET;
else if (!adap->is_configured &&
(adap->needs_hpd || msg.msg[0] != 0xf0))
err = -ENONET;
else if (cec_is_busy(adap, fh))
err = -EBUSY;
else
err = cec_transmit_msg_fh(adap, &msg, fh, block);
mutex_unlock(&adap->lock);
if (err)
return err;
if (copy_to_user(parg, &msg, sizeof(msg)))
return -EFAULT;
return 0;
}
Сначала проверки определяют возможность передачи. Затем cec_transmit_msg_fh добавляет сообщение в очередь отправки. Структура сообщения:
struct cec_msg {
__u64 tx_ts;
__u64 rx_ts;
__u32 len;
__u32 timeout;
__u32 sequence;
__u32 flags;
__u8 msg[CEC_MAX_MSG_SIZE];
__u8 reply;
__u8 rx_status;
__u8 tx_status;
__u8 tx_arb_lost_cnt;
__u8 tx_nack_cnt;
__u8 tx_low_drive_cnt;
__u8 tx_error_cnt;
};
Поля структуры:
| Имя | Описание |
|---|---|
| tx_ts | Метка времени в наносекундах после завершения передачи. |
| rx_ts | Метка времени в наносекундах после завершения приёма. |
| len | Длина сообщения. |
| sequence | Номер передачи, назначаемый инфраструктурой для отслеживания ответов на предыдущие сообщения. |
| msg | Хранит полезную нагрузку сообщения CEC. |
| reply | Только передача: ненулевое значение включает ожидание ответа, например give device power status ожидает report power status. Описанная реализация всегда задаёт 0 и не ждёт ответа внутри драйвера. |
| rx_status | Состояние после приёма: CEC_RX_STATUS_OK, CEC_RX_STATUS_TIMEOUT, CEC_RX_STATUS_FEATURE_ABORT. |
| tx_status | Состояние после передачи: CEC_TX_STATUS_OK, CEC_TX_STATUS_ARB_LOST, CEC_TX_STATUS_NACK, CEC_TX_STATUS_LOW_DRIVE, CEC_TX_STATUS_ERROR, CEC_TX_STATUS_MAX_RETRIES. |
| tx_arb_lost_cnt | Число ошибок Arbitration Lost после передачи. |
| tx_nack_cnt | Число ошибок Not Acknowledged после передачи. |
| tx_low_drive_cnt | Число ошибок Low Drive Detected после передачи. |
| tx_error_cnt | Число Error после передачи. |
cec_thread_func управляет очередью передачи. Если сообщение не передаётся, поток ожидает новые сообщения.
wait_event_interruptible(adap->kthread_waitq,
kthread_should_stop ||
(!adap->transmitting &&
!list_empty(&adap->transmit_queue)));
После добавления сообщения cec_transmit_msg_fh пробуждает cec_thread_func, если передачи нет. Иначе сообщение ждёт в очереди завершения текущей передачи.
if (fh)
list_add_tail(&data->xfer_list, &fh->xfer_list);
list_add_tail(&data->list, &adap->transmit_queue);
adap->transmit_queue_sz++;
if (!adap->transmitting)
wake_up_interruptible(&adap->kthread_waitq);
/* All done if we don't need to block waiting for completion */
if (!block)
return 0;
/*
* If we don't get a completion before this time something is really
* wrong and we time out.
*/
timeout = CEC_XFER_TIMEOUT_MS;
/* Add the requested timeout if we have to wait for a reply as well */
if (msg->timeout)
timeout += msg->timeout;
/*
* Release the lock and wait, retake the lock afterwards.
*/
mutex_unlock(&adap->lock);
res = wait_for_completion_killable_timeout(&data->c,
msecs_to_jiffies(timeout));
mutex_lock(&adap->lock);
if (data->completed) {
/* The transmit completed (possibly with an error) */
*msg = data->msg;
kfree(data);
return 0;
}
Обратный вызов adap->ops->adap_transmit вызывает dw_hdmi_cec_transmit для настройки регистров CEC и начала передачи.
После передачи возникает прерывание CEC. dw_hdmi_cec_hardirq читает регистры и записывает успех либо тип ошибки в tx_status.
cec_transmit_done_ts собирает ошибки и при необходимости повторяет передачу согласно attempts. Без ошибок cec_data_completed завершает передачу и пробуждает cec_thread_func для следующего сообщения.
2.3.3 Приём сообщений CEC

Входящее сообщение CEC вызывает прерывание. dw_hdmi_cec_hardirq получает его из регистров CEC RX DATA.
cec_received_msg_ts проверяет пригодность принятого сообщения.
Без passthrough cec_receive_notify автоматически отвечает по протоколу CEC. Описанная реализация всегда использует passthrough: cec_queue_msg_fh помещает сообщение в очередь, а poll_wait пробуждается для уведомления пользовательского пространства.
*/
static void cec_queue_msg_fh(struct cec_fh *fh, const struct cec_msg *msg)
{
static const struct cec_event ev_lost_msgs = {
.event = CEC_EVENT_LOST_MSGS,
.lost_msgs.lost_msgs = 1,
};
struct cec_msg_entry *entry;
mutex_lock(&fh->lock);
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
if (entry) {
entry->msg = *msg;
/* Add new msg at the end of the queue */
list_add_tail(&entry->list, &fh->msgs);
if (fh->queued_msgs < CEC_MAX_MSG_RX_QUEUE_SZ) {
/* All is fine if there is enough room */
fh->queued_msgs++;
mutex_unlock(&fh->lock);
wake_up_interruptible(&fh->wait);
return;
}
/*
* if the message queue is full, then drop the oldest one and
* send a lost message event.
*/
entry = list_first_entry(&fh->msgs, struct cec_msg_entry, list);
list_del(&entry->list);
kfree(entry);
}
mutex_unlock(&fh->lock);
/*
* We lost a message, either because kmalloc failed or the queue
* was full.
*/
cec_queue_event_fh(fh, &ev_lost_msgs, ktime_get_ns);
}
Когда поток мониторинга событий HAL uevent_loop получает уведомление, он извлекает сообщения из очереди через IOCTL CEC_RECEIVE.

FRAMEWORK передаёт сообщения вверх по уровням; затем onMessage направляет каждый opcode соответствующему обработчику, как показано на схеме.
2.3.4 Обработка событий CEC
Драйвер сообщает об изменении состояния следующими событиями:
| Имя | Описание |
|---|---|
| CEC_EVENT_STATE_CHANGE | Состояние адаптера изменилось, например configured на unconfigured. |
| CEC_EVENT_LOST_MSGS | Сообщения потеряны из-за несвоевременного чтения очереди приёма. |
| CEC_EVENT_PIN_CEC_LOW | Вывод CEC переходит в низкий уровень; в этой версии не используется. |
| CEC_EVENT_PIN_CEC_HIGH | Вывод CEC переходит в высокий уровень; в этой версии не используется. |
| CEC_EVENT_PIN_HPD_LOW | Оригинал указывает высокий HPD, обычно подключение HDMI. Это противоречит имени LOW; сохранено для проверки. |
| CEC_EVENT_PIN_HPD_HIGH | Оригинал указывает низкий HPD, обычно отключение HDMI. Это противоречит имени HIGH; сохранено для проверки. |
Пример горячего подключения HDMI:

Подключение или отключение HDMI вызывает прерывание HDMI; cec_queue_pin_hpd_event создаёт событие HPD CEC.
cec_queue_event_fh ставит событие в очередь и пробуждает poll_wait для уведомления.
HAL вызывает cec_dqevent через IOCTL CEC_DQEVENT для получения события и завершения уведомления.

HAL отдельно обрабатывает типы событий; HPD передаётся дальше вверх.
Окончательная обработка выполняется в onHotplug. При подключении вызывается allocateLogicalAddress для повторного назначения адреса, практически по §2.3.1.2 (оригинал ошибочно ссылается на §2.2.1.2).
2.4 Интерфейсы добавления функций CEC
Исходный Android уже поддерживает многие функции CEC. Для добавления остальных используйте следующие интерфейсы:
Инициирование сообщения CEC:
frameworks/base/services/core/java/com/android/server/hdmi/HdmiControlService.java
/**
* Transmit a CEC command to CEC bus.
*
* @param command CEC command to send out
* @param callback interface used to the result of send command
*/
@ServiceThreadOnly
void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback
callback) {
assertRunOnServiceThread;
if (mMessageValidator.isValid(command) == HdmiCecMessageValidator.OK) {
mCecController.sendCommand(command, callback);
} else {
HdmiLogger.error("Invalid message type:" + command);
if (callback != null) {
callback.onSendCompleted(SendMessageResult.FAIL);
}
}
}
@ServiceThreadOnly
void sendCecCommand(HdmiCecMessage command) {
assertRunOnServiceThread;
sendCecCommand(command, null);
}
Создайте параметр HdmiCecMessage command соответствующим интерфейсом по пути:
frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@Override
@ServiceThreadOnly
protected void onStandby(boolean initiatedByCec, int standbyAction) {
assertRunOnServiceThread;
if (!mService.isControlEnabled || initiatedByCec || !mAutoTvOff) {
return;
}
switch (standbyAction) {
case HdmiControlService.STANDBY_SCREEN_OFF:
Slog.e(TAG, "send standby eee");
/* cec cts specification requires that standby message must be
broadcast */
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress,
Constants.ADDR_BROADCAST));
break;
case HdmiControlService.STANDBY_SHUTDOWN:
// ACTION_SHUTDOWN is taken as a signal to power off all the
devices.
mService.sendCecCommand(
HdmiCecMessageBuilder.buildStandby(mAddress,
Constants.ADDR_BROADCAST));
break;
}
}
Чтобы приставка перевела телевизор в ожидание, нужен CEC standby. Пример вызывает sendCecCommand с сообщением, созданным HdmiCecMessageBuilder.buildStandby.
Добавление обработки сообщений CEC:
Система уже принимает и передаёт сообщения вверх. Если обработчика принятого сообщения нет, добавьте его. Сообщения поступают в onMessage, где message.getOpcode получает opcode, а функции handlexxxxx обрабатывают соответствующие сообщения.
frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@ServiceThreadOnly
protected final boolean onMessage(HdmiCecMessage message) {
assertRunOnServiceThread;
if (dispatchMessageToAction(message)) {
return true;
}
switch (message.getOpcode) {
case Constants.MESSAGE_ACTIVE_SOURCE:
return handleActiveSource(message);
case Constants.MESSAGE_INACTIVE_SOURCE:
return handleInactiveSource(message);
case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
return handleRequestActiveSource(message);
case Constants.MESSAGE_GET_MENU_LANGUAGE:
return handleGetMenuLanguage(message);
case Constants.MESSAGE_SET_MENU_LANGUAGE:
return handleSetMenuLanguage(message);
case Constants.MESSAGE_GIVE_PHYSICAL_ADDRESS:
return handleGivePhysicalAddress;
case Constants.MESSAGE_GIVE_OSD_NAME:
return handleGiveOsdName(message);
case Constants.MESSAGE_GIVE_DEVICE_VENDOR_ID:
return handleGiveDeviceVendorId;
case Constants.MESSAGE_GET_CEC_VERSION:
return handleGetCecVersion(message);
case Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS:
return handleReportPhysicalAddress(message);
case Constants.MESSAGE_ROUTING_CHANGE:
return handleRoutingChange(message);
case Constants.MESSAGE_ROUTING_INFORMATION:
return handleRoutingInformation(message);
case Constants.MESSAGE_INITIATE_ARC:
return handleInitiateArc(message);
case Constants.MESSAGE_TERMINATE_ARC:
return handleTerminateArc(message);
case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
return handleSetSystemAudioMode(message);
case Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
return handleSystemAudioModeStatus(message);
case Constants.MESSAGE_REPORT_AUDIO_STATUS:
return handleReportAudioStatus(message);
case Constants.MESSAGE_STANDBY:
return handleStandby(message);
case Constants.MESSAGE_TEXT_VIEW_ON:
return handleTextViewOn(message);
case Constants.MESSAGE_IMAGE_VIEW_ON:
return handleImageViewOn(message);
case Constants.MESSAGE_USER_CONTROL_PRESSED:
return handleUserControlPressed(message);
case Constants.MESSAGE_USER_CONTROL_RELEASED:
return handleUserControlReleased;
case Constants.MESSAGE_SET_STREAM_PATH:
return handleSetStreamPath(message);
case Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS:
return handleGiveDevicePowerStatus(message);
case Constants.MESSAGE_MENU_REQUEST:
return handleMenuRequest(message);
case Constants.MESSAGE_MENU_STATUS:
return handleMenuStatus(message);
case Constants.MESSAGE_VENDOR_COMMAND:
return handleVendorCommand(message);
case Constants.MESSAGE_VENDOR_COMMAND_WITH_ID:
return handleVendorCommandWithId(message);
case Constants.MESSAGE_SET_OSD_NAME:
return handleSetOsdName(message);
case Constants.MESSAGE_RECORD_TV_SCREEN:
return handleRecordTvScreen(message);
case Constants.MESSAGE_TIMER_CLEARED_STATUS:
return handleTimerClearedStatus(message);
case Constants.MESSAGE_REPORT_POWER_STATUS:
return handleReportPowerStatus(message);
case Constants.MESSAGE_TIMER_STATUS:
return handleTimerStatus(message);
case Constants.MESSAGE_RECORD_STATUS:
return handleRecordStatus(message);
default:
return false;
}
}
Таким образом, для нового сообщения CEC добавьте соответствующий обработчик в switch.
3. Основные методы отладки
При неправильной работе CEC выполняйте отладку в следующем порядке:
Запустите сценарий для сбора состояния отладочных узлов и журналов.
По состоянию узлов проверьте правильность инициализации и работы.
По журналу ядра проверьте успешность передачи и приёма CEC.
По журналам Android проверьте обработку функций.
3.1 Сценарий отладки
Сценарий отладки CEC:
#!/system/bin/sh
log ************start cec debug script**************
echo 2 > sys/module/cec/parameters/debug
if [ -f "/data/cec_status.txt" ]
then
rm /data/cec_status.log
fi
if [ -f "/data/cec_log.txt" ]
then
rm /data/cec_log.txt
fi
cat /sys/kernel/debug/cec/cec0/status >> /data/cec_status.txt
cat /sys/kernel/debug/dw-hdmi/ctrl >> /data/cec_status.txt
logcat -b all -f /data/cec_log.txt
Использование:
Скопируйте cec_debug.sh в /system/bin.
Запустите ./system/bin/cec_debug.sh до воспроизведения ошибки; относительный путь сохранён по оригиналу.
Метка **start cec debug script** обозначает запуск сценария.
Выполните действия для воспроизведения ошибки.
Состояние узлов CEC сохраняется в /data/cec_status.txt.
Синхронные журналы ядра и logcat сохраняются в /data/cec_log.txt.
3.1.1 Содержимое журналов
Сценарий собирает состояние узлов и журналы. Узлы описаны в §3.1.2. В журналах важны следующие три категории:
Сообщения с cec-dw_hdmi относятся к ядру:
04-11 11:44:50.969 0 0 I cec-dw_hdmi: cec_transmit_msg_fh: 44
Для ручного сбора выполните:
dmesg | grep cec-dw_hdmi
Сообщения с hdmicec относятся к HAL:
04-11 12:39:59.015 281 281 I hdmicec : hdmi_cec_set_option: system_control:
value: 0
Для ручного сбора выполните:
logcat | grep hdmicec
Сообщения с Hdmi относятся к framework:
04-11 11:44:50.565 391 494 W HdmiCecController: Failed to send <Give Device
Power Status> src: 4, dst: 0 with errorCode=-1
Для ручного сбора выполните:
logcat | grep Hdmi
Примеры анализа журналов приведены в §3.2.
3.1.2 Отладочные узлы CEC
Путь отладочного узла состояния CEC:
/sys/kernel/debug/cec/cec0/status
Чтение через cat выводит:

configured: 0
configuring: 0
phys_addr: f.f.f.f
number of LAs: 1
LA mask: 0x0000
has CEC follower (in passthrough mode)
pending transmits: 0
Описание параметров:
| Имя | Описание |
|---|---|
| configured | 1: правильно настроен. 0: не настроен правильно. |
| configuring | 1: настройка выполняется. 0: не начиналась или завершена. |
| phys_addr | Физический адрес из EDID телевизора. f.f.f.f означает отсутствие инициализации или ошибку получения адреса. |
| LA mask | Маска логического адреса: 0x0000 до инициализации. Сдвиг до 0x1 определяет адрес; например 0x0010 » 4 = 0x1 означает адрес 0x4. |
| has CEC follower (in passthrough mode) | Драйвер CEC не обрабатывает сообщения непосредственно. |
| pending transmits | Количество сообщений CEC, ожидающих передачи. |
Узел регистров контроллера HDMI:
/d/dw-hdmi/ctrl
Команда cat для узла выводит регистры контроллера HDMI. Проверьте связанные с CEC регистры; определения приведены в документации контроллера HDMI.
3.1.3 Включение журналов CEC ядра
echo 2 > /sys/module/cec/parameters/debug
Включить журналы передачи/приёма сообщений CEC.
echo 1 > /sys/module/cec/parameters/debug
Включить остальные журналы CEC.
3.2 Диагностика типичных неисправностей
3.2.1 Не работают все функции CEC
Выполните эти проверки и при отказе только отдельных функций CEC.
Проверьте включение CEC в Settings/Input.
Проверьте наличие hdmi_cec_device_open open error! в журнале и выполните ls -l /dev/cec0.
130|rk3328_box:/ # ls -l /dev/cec0
crw-rw---- 1 system system 250, 0 2016-01-21 08:50 /dev/cec0
Проверьте, что права cec0 не ниже показанных. Если их недостаточно, проверьте наличие изменения:
diff --git a/ueventd.rockchip.rc b/ueventd.rockchip.rc
index a7d1356..9eb824d 100755
--- a/ueventd.rockchip.rc
+++ b/ueventd.rockchip.rc
@@ -96,6 +96,7 @@
#for hdmi cec
/dev/cec 0666 system system
+/dev/cec0 0660 system system
#sofia
/dev/dcc 0666 system system
/dev/pmem_gfx 0666 system system
Проверьте нормальное состояние /sys/kernel/debug/cec/cec0/status (§3.1.2; оригинал ссылается на §3.1.1 и разрывает слово status). Типичные ошибки:
(1) phys_addr=f.f.f.f: проверьте чтение EDID и поддержку CEC телевизором. (2) LA mask=0x0000: ошибка назначения логического адреса. Сначала найдите:
04-11 08:56:51.764 296 296 E hdmicec : can't make kernel addr done
При наличии проверьте правильность чтения EDID. Иначе найдите:
04-11 08:56:51.764 296 296 D hdmicec : kernel logic addr:ff, preferred logic
addr:04
04-11 08:56:51.764 296 296 I hdmicec : kernel logaddr is not existing
04-11 08:56:51.764 296 296 D hdmicec : set_kernel_logical_address, logic
addr:04
logic addr не обязательно 04, но должен быть 04, 08 или 0b. Если записи нет, проследите код по пути:
Hardware/rockchip/hdmicec/hdmi_cec.cpp
При наличии записи выше проверьте:
04-11 08:35:37.297 0 0 I cec-dw_hdmi: new physical address 3.0.0.0
04-11 08:35:37.297 0 0 I cec-dw_hdmi: physical address: 3.0.0.0, claim 1
logical addresses
Если отсутствует, проследите cec_adap_s_log_addrs по пути:
Kernel/drivers/media/cec/cec-api.c
Затем проверьте записи ниже. 44 в первой строке — адрес POLL; возможны также 88 и bb, вплоть до записей для всех трёх. Сопоставьте адреса с количеством устройств CEC телевизора; полный процесс описан в разделе CEC спецификации HDMI. Важен status второй строки. Если адрес точно свободен, но status не 04 (NACK), анализируйте регистры HDMI вместе с осциллограммой CEC.
04-11 08:35:37.297 0 0 I cec-dw_hdmi: cec_transmit_msg_fh: 44
04-11 08:35:37.326 0 0 I cec-dw_hdmi: cec_transmit_done_ts: status 04
3.2.2 Телевизор переходит в ожидание, приставка — нет
Android по умолчанию отключает переход приставки в ожидание по CEC. Задайте persist.sys.hdmi.keep_awake=false либо измените значение по умолчанию на false:
diff --git
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -210,7 +210,7 @@
private ActiveWakeLock getWakeLock {
assertRunOnServiceThread;
if (mWakeLock == null) {
- if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE,
true)) {
+ if (SystemProperties.getBoolean(Constants.PROPERTY_KEEP_AWAKE,
false)) {
mWakeLock = new SystemWakeLock;
} else {
// Create a dummy lock object that doesn't do anything about
wake lock,
Проверьте, получила ли приставка standby (0x36), по журналу:
[ 3465.670484] cec-dw_hdmi: cec_received_msg_ts: 0f 36
[ 3465.670617] cec-dw_hdmi: cec_receive_notify: 0f 36
01-21 09:47:47.675 0 0 I cec-dw_hdmi: cec_received_msg_ts: 0f 36
04-11 09:16:32.963 299 342 D hdmicec : poll revent:41
01-21 09:47:47.675 0 0 I cec-dw_hdmi: cec_receive_notify: 0f 36
04-11 09:16:32.963 299 342 D hdmicec : poll receive msg
04-11 09:16:32.963 299 342 D hdmicec : poll receive msg[0]:0f
04-11 09:16:32.963 299 342 D hdmicec : poll receive msg[1]:36
Если нет, осциллографом проверьте, действительно ли телевизор передал standby по шине CEC.
3.2.3 Приставка переходит в ожидание, телевизор — нет
Если это происходит сразу после включения телевизора, подождите 15 секунд и повторите. Некоторые телевизоры некоторое время после включения игнорируют standby.
Проверьте процесс ожидания по журналам:
04-11 08:37:13.823 394 394 V HdmiControlService: On standby-action cleared:4
04-11 08:37:13.823 394 394 V HdmiControlService: onStandbyCompleted
04-11 08:37:13.927 296 296 I hdmicec : send msg LEN:2,opcode:36,addr:4f
Если записей нет или присутствуют только первые две, проследите код по пути:
frameworks/base/services/core/java/com/android/server/hdmi/HdmiControlService.java
Затем проверьте успешную отправку standby по журналам:
04-11 08:37:13.928 0 0 I cec-dw_hdmi: cec_transmit_msg_fh: 4f 36
04-11 08:37:13.991 0 0 I cec-dw_hdmi: cec_transmit_done_ts: status 01
Если записей нет, проверьте правильность вызова hdmi_cec_send_message по следующему пути и успешный вызов IOCTL CEC_TRANSMIT:
Hardware/rockchip/hdmicec/hdmi_cec.cpp
Если обе записи есть, но status cec_transmit_done_ts не 01, при передаче произошла ошибка шины CEC. Анализируйте регистры контроллера HDMI совместно с сигналами CEC.
3.2.4 Приставка пробуждается, телевизор — нет
Проверьте нормальное пробуждение приставки и инициализацию CEC по §3.2.1.
Проверьте успешную передачу сообщения пробуждения CEC:
04-11 09:27:37.540 0 0 I cec-dw_hdmi: cec_transmit_msg_fh: 40 0d
04-11 09:27:37.593 0 0 I cec-dw_hdmi: cec_transmit_done_ts: status 01
04-11 09:27:37.594 0 0 I cec-dw_hdmi: cec_transmit_msg_fh: 4f 82 30 00
04-11 09:27:37.707 0 0 I cec-dw_hdmi: cec_transmit_done_ts: status 01
Если обе записи есть, но передача неуспешна (status не 01), анализируйте регистры HDMI и сигналы CEC. Если записей нет, проследите передачу ActiveSource и TextViewOn начиная с onAddressAllocated в:
Frameworks/base/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
3.2.5 Пульт телевизора не управляет UI приставки
Марки и модели телевизоров сильно различаются, поэтому универсального решения нет. Следующие рекомендации из опыта подходят не каждому телевизору.
Проверьте наличие отдельного входа CEC. Некоторые телевизоры Samsung требуют переключения на Anynet для этой функции.
После стабилизации работы телевизора отправьте ActiveSource, например через системный интерфейс onetouchplay:
Frameworks/base/services/core/java/com/android/server/hdmi/OneTouchPlayAction.java
4. Справочные документы
HDMISpecification1_4b.pdf
RockchipAndroid5.x_HDMI_CEC_软件说明.doc (имя исходного файла)
