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