summaryrefslogtreecommitdiff
path: root/hdmi_cec.cpp (plain)
blob: 2173b1e416b68fb696b0172771efe5469510deed
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16/*
17 * Amlogic HDMITX CEC HAL
18 * Copyright (C) 2014
19 *
20 * This implements a hdmi cec hardware library for the Android emulator.
21 * the following code should be built as a shared library that will be
22 * placed into /system/lib/hw/hdmi_cec.so
23 *
24 * It will be loaded by the code in hardware/libhardware/hardware.c
25 * which is itself called from
26 * frameworks/base/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp
27 */
28
29#include <cutils/log.h>
30#include <stdint.h>
31#include <stdlib.h>
32#include <string.h>
33#include <unistd.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <pthread.h>
37#include <sys/ioctl.h>
38#include <sys/types.h>
39#include <hardware/hdmi_cec.h>
40#include <hardware/hardware.h>
41#include <cutils/properties.h>
42#include "hdmi_cec.h"
43#include <jni.h>
44#include <JNIHelp.h>
45
46#include <HdmiCecClient.h>
47#include <HdmiCecHidlClient.h>
48
49#ifdef LOG_TAG
50#undef LOG_TAG
51#define LOG_TAG "CEC"
52#else
53#define LOG_TAG "CEC"
54#endif
55
56/* Set to 1 to enable debug messages to the log */
57#define DEBUG 1
58#if DEBUG
59# define D(format, args...) ALOGD("[%s]" format, __FUNCTION__, ##args)
60#else
61# define D(...) do{}while(0)
62#endif
63
64#define E(format, args...) ALOGE("[%s]" format, __FUNCTION__, ##args)
65
66using namespace android;
67
68typedef struct aml_cec_hal {
69 hdmi_cec_device_t device;
70 sp<HdmiCecClient> client;
71 HdmiCecHidlClient *hidlClient;
72 void *cb_data;
73 event_callback_t cb;
74 int fd;
75} aml_cec_hal_t;
76
77struct aml_cec_hal *hal_info = NULL;
78
79class HdmiCecCallback : public HdmiCecEventListener {
80public:
81 HdmiCecCallback(){}
82 ~HdmiCecCallback(){}
83 virtual void onEventUpdate(const hdmi_cec_event_t* event);
84};
85
86void HdmiCecCallback::onEventUpdate(const hdmi_cec_event_t* cecEvent)
87{
88 if (cecEvent == NULL)
89 return;
90
91 int type = cecEvent->eventType;
92
93 if ((cecEvent->eventType & HDMI_EVENT_CEC_MESSAGE) != 0 && hal_info->cb) {
94 D("send cec message, event type = %d", type);
95 hdmi_event_t event;
96 event.type = HDMI_EVENT_CEC_MESSAGE;
97 event.dev = &hal_info->device;
98 event.cec.initiator = cecEvent->cec.initiator;
99 event.cec.destination = cecEvent->cec.destination;
100 event.cec.length = cecEvent->cec.length;
101 memcpy(event.cec.body, cecEvent->cec.body, event.cec.length);
102 hal_info->cb(&event, hal_info->cb_data);
103 } else if ((cecEvent->eventType & HDMI_EVENT_HOT_PLUG) != 0 && hal_info->cb) {
104 D("cec hot plug, event type = %d", type);
105 hdmi_event_t event;
106 event.type = HDMI_EVENT_HOT_PLUG;
107 event.dev = &hal_info->device;
108 event.hotplug.connected = cecEvent->hotplug.connected;
109 event.hotplug.port_id = cecEvent->hotplug.port_id;
110 hal_info->cb(&event, hal_info->cb_data);
111 }
112}
113
114/*
115 * (*add_logical_address)() passes the logical address that will be used
116 * in this system.
117 *
118 * HAL may use it to configure the hardware so that the CEC commands addressed
119 * the given logical address can be filtered in. This method can be called
120 * as many times as necessary in order to support multiple logical devices.
121 * addr should be in the range of valid logical addresses for the call
122 * to succeed.
123 *
124 * Returns 0 on success or -errno on error.
125 */
126static int cec_add_logical_address(const struct hdmi_cec_device* dev, cec_logical_address_t addr)
127{
128 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
129 //return priv->client->addLogicalAddress(addr);
130 return priv->hidlClient->addLogicalAddress(addr);
131}
132
133/*
134 * (*clear_logical_address)() tells HAL to reset all the logical addresses.
135 *
136 * It is used when the system doesn't need to process CEC command any more,
137 * hence to tell HAL to stop receiving commands from the CEC bus, and change
138 * the state back to the beginning.
139 */
140static void cec_clear_logical_address(const struct hdmi_cec_device* dev)
141{
142 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
143 //priv->client->clearLogicaladdress();
144 priv->hidlClient->clearLogicaladdress();
145}
146
147/*
148 * (*get_physical_address)() returns the CEC physical address. The
149 * address is written to addr.
150 *
151 * The physical address depends on the topology of the network formed
152 * by connected HDMI devices. It is therefore likely to change if the cable
153 * is plugged off and on again. It is advised to call get_physical_address
154 * to get the updated address when hot plug event takes place.
155 *
156 * Returns 0 on success or -errno on error.
157 */
158static int cec_get_physical_address(const struct hdmi_cec_device* dev, uint16_t* addr)
159{
160 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
161 //return priv->client->getPhysicalAddress(addr);
162 return priv->hidlClient->getPhysicalAddress(addr);
163}
164
165/*
166 * (*send_message)() transmits HDMI-CEC message to other HDMI device.
167 *
168 * The method should be designed to return in a certain amount of time not
169 * hanging forever, which can happen if CEC signal line is pulled low for
170 * some reason. HAL implementation should take the situation into account
171 * so as not to wait forever for the message to get sent out.
172 *
173 * It should try retransmission at least once as specified in the standard.
174 *
175 * Returns error code. See HDMI_RESULT_SUCCESS, HDMI_RESULT_NACK, and
176 * HDMI_RESULT_BUSY.
177 */
178static int cec_send_message(const struct hdmi_cec_device* dev, const cec_message_t* msg)
179{
180 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
181 //return priv->client->sendMessage(msg);
182 return priv->hidlClient->sendMessage(msg);
183}
184
185/*
186 * (*register_event_callback)() registers a callback that HDMI-CEC HAL
187 * can later use for incoming CEC messages or internal HDMI events.
188 * When calling from C++, use the argument arg to pass the calling object.
189 * It will be passed back when the callback is invoked so that the context
190 * can be retrieved.
191 */
192static void cec_register_event_callback(const struct hdmi_cec_device* dev __unused,
193 event_callback_t callback, void* arg)
194{
195 if (!hal_info || hal_info->fd < 0)
196 return;
197
198 hal_info->cb = callback;
199 hal_info->cb_data = arg;
200}
201
202/*
203 * (*get_version)() returns the CEC version supported by underlying hardware.
204 */
205static void cec_get_version(const struct hdmi_cec_device* dev, int* version)
206{
207 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
208 //priv->client->getVersion(version);
209 priv->hidlClient->getVersion(version);
210}
211
212/*
213 * (*get_vendor_id)() returns the identifier of the vendor. It is
214 * the 24-bit unique company ID obtained from the IEEE Registration
215 * Authority Committee (RAC).
216 */
217static void cec_get_vendor_id(const struct hdmi_cec_device* dev, uint32_t* vendor_id)
218{
219 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
220 //priv->client->getVendorId(vendor_id);
221 priv->hidlClient->getVendorId(vendor_id);
222}
223
224/*
225 * (*get_port_info)() returns the hdmi port information of underlying hardware.
226 * info is the list of HDMI port information, and 'total' is the number of
227 * HDMI ports in the system.
228 */
229static void cec_get_port_info(const struct hdmi_cec_device* dev,
230 struct hdmi_port_info* list[], int* total)
231{
232 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
233 //priv->client->getPortInfos(list, total);
234 priv->hidlClient->getPortInfos(list, total);
235}
236
237/*
238 * (*set_option)() passes flags controlling the way HDMI-CEC service works down
239 * to HAL implementation. Those flags will be used in case the feature needs
240 * update in HAL itself, firmware or microcontroller.
241 */
242static void cec_set_option(const struct hdmi_cec_device* dev, int flag, int value)
243{
244 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
245 //priv->client->setOption(flag, value);
246 priv->hidlClient->setOption(flag, value);
247}
248
249
250/*
251 * (*set_audio_return_channel)() configures ARC circuit in the hardware logic
252 * to start or stop the feature. Flag can be either 1 to start the feature
253 * or 0 to stop it.
254 *
255 * Returns 0 on success or -errno on error.
256 */
257static void cec_set_audio_return_channel(const struct hdmi_cec_device* dev, int port_id, int flag)
258{
259 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
260 //priv->client->setAudioReturnChannel(port_id, flag);
261 priv->hidlClient->setAudioReturnChannel(port_id, flag);
262}
263
264/*
265 * (*is_connected)() returns the connection status of the specified port.
266 * Returns HDMI_CONNECTED if a device is connected, otherwise HDMI_NOT_CONNECTED.
267 * The HAL should watch for +5V power signal to determine the status.
268 */
269static int cec_is_connected(const struct hdmi_cec_device* dev, int port_id)
270{
271 aml_cec_hal_t *priv = (aml_cec_hal_t *)dev;
272 //return priv->client->isConnected(port_id);
273 return priv->hidlClient->isConnected(port_id);
274}
275
276/** Close the hdmi cec device */
277static int cec_close(struct hw_device_t *dev)
278{
279 if (hal_info != NULL) {
280 //return hal_info->client->closeCecDevice();
281 return hal_info->hidlClient->closeCecDevice();
282 }
283 free(dev);
284 return -1;
285}
286
287/**
288 * module methods
289 */
290static int open_cec( const struct hw_module_t* module, char const *name,
291 struct hw_device_t **device )
292{
293
294 if (strcmp(name, HDMI_CEC_HARDWARE_INTERFACE) != 0) {
295 D("cec strcmp fail !!!");
296 return -EINVAL;
297 }
298
299 if (device == NULL) {
300 D("NULL cec device on open");
301 return -EINVAL;
302 }
303
304 aml_cec_hal_t *dev = (aml_cec_hal_t*)malloc(sizeof(*dev));
305 memset(dev, 0, sizeof(*dev));
306 //dev->client = HdmiCecClient::connect();
307 //dev->client->setEventObserver(new HdmiCecCallback());
308 //dev->fd = dev->client->openCecDevice();
309 dev->hidlClient = HdmiCecHidlClient::connect(CONNECT_TYPE_HAL);
310 dev->hidlClient->setEventObserver(new HdmiCecCallback());
311 dev->fd = dev->hidlClient->openCecDevice();
312
313 dev->device.common.tag = HARDWARE_DEVICE_TAG;
314 dev->device.common.version = 0;
315 dev->device.common.module = (struct hw_module_t*) module;
316 dev->device.common.close = cec_close;
317
318 dev->device.add_logical_address = cec_add_logical_address;
319 dev->device.clear_logical_address = cec_clear_logical_address;
320 dev->device.get_physical_address = cec_get_physical_address;
321 dev->device.send_message = cec_send_message;
322 dev->device.register_event_callback = cec_register_event_callback;
323 dev->device.get_version = cec_get_version;
324 dev->device.get_vendor_id = cec_get_vendor_id;
325 dev->device.get_port_info = cec_get_port_info;
326 dev->device.set_option = cec_set_option;
327 dev->device.set_audio_return_channel = cec_set_audio_return_channel;
328 dev->device.is_connected = cec_is_connected;
329
330 *device = &dev->device.common;
331
332 hal_info = dev;
333 return 0;
334}
335
336static struct hw_module_methods_t hdmi_cec_module_methods = {
337 .open = open_cec,
338};
339
340/*
341 * The hdmi cec Module
342 */
343struct hdmi_cec_module HAL_MODULE_INFO_SYM = {
344 .common = {
345 .tag = HARDWARE_MODULE_TAG,
346 .module_api_version = HDMI_CEC_MODULE_API_VERSION_1_0,
347 .hal_api_version = HARDWARE_HAL_API_VERSION,
348 .id = HDMI_CEC_HARDWARE_MODULE_ID,
349 .name = "Amlogic hdmi cec Module",
350 .author = "Amlogic Corp.",
351 .methods = &hdmi_cec_module_methods,
352 },
353};
354
355