summaryrefslogtreecommitdiffstats
path: root/exynos4/hal/libhdmi/libsForhdmi/libcec/libcec.c
blob: e088346ef589130a31d3d38e54ea91e11ff16cae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/*
* Copyright@ Samsung Electronics Co. LTD
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <cutils/log.h>

/* drv. header */
#include "cec.h"

#include "libcec.h"

#define CEC_DEBUG 0

/**
 * @def CEC_DEVICE_NAME
 * Defines simbolic name of the CEC device.
 */
#define CEC_DEVICE_NAME         "/dev/CEC"

static struct {
    enum CECDeviceType devtype;
    unsigned char laddr;
} laddresses[] = {
    { CEC_DEVICE_RECODER, 1  },
    { CEC_DEVICE_RECODER, 2  },
    { CEC_DEVICE_TUNER,   3  },
    { CEC_DEVICE_PLAYER,  4  },
    { CEC_DEVICE_AUDIO,   5  },
    { CEC_DEVICE_TUNER,   6  },
    { CEC_DEVICE_TUNER,   7  },
    { CEC_DEVICE_PLAYER,  8  },
    { CEC_DEVICE_RECODER, 9  },
    { CEC_DEVICE_TUNER,   10 },
    { CEC_DEVICE_PLAYER,  11 },
};

static int CECSetLogicalAddr(unsigned int laddr);

#ifdef CEC_DEBUG
inline static void CECPrintFrame(unsigned char *buffer, unsigned int size);
#endif

static int fd = -1;

/**
 * Open device driver and assign CEC file descriptor.
 *
 * @return  If success to assign CEC file descriptor, return 1; otherwise, return 0.
 */
int CECOpen()
{
    int res = 1;

    if (fd != -1)
        CECClose();

    if ((fd = open(CEC_DEVICE_NAME, O_RDWR)) < 0) {
        ALOGE("Can't open %s!\n", CEC_DEVICE_NAME);
        res = 0;
    }

    return res;
}

/**
 * Close CEC file descriptor.
 *
 * @return  If success to close CEC file descriptor, return 1; otherwise, return 0.
 */
int CECClose()
{
    int res = 1;

    if (fd != -1) {
        if (close(fd) != 0) {
            ALOGE("close() failed!\n");
            res = 0;
        }
        fd = -1;
    }

    return res;
}

/**
 * Allocate logical address.
 *
 * @param paddr   [in] CEC device physical address.
 * @param devtype [in] CEC device type.
 *
 * @return new logical address, or 0 if an arror occured.
 */
int CECAllocLogicalAddress(int paddr, enum CECDeviceType devtype)
{
    unsigned char laddr = CEC_LADDR_UNREGISTERED;
    int i = 0;

    if (fd == -1) {
        ALOGE("open device first!\n");
        return 0;
    }

    if (CECSetLogicalAddr(laddr) < 0) {
        ALOGE("CECSetLogicalAddr() failed!\n");
        return 0;
    }

    if (paddr == CEC_NOT_VALID_PHYSICAL_ADDRESS)
        return CEC_LADDR_UNREGISTERED;

    /* send "Polling Message" */
    while (i < sizeof(laddresses)/sizeof(laddresses[0])) {
        if (laddresses[i].devtype == devtype) {
            unsigned char _laddr = laddresses[i].laddr;
            unsigned char message = ((_laddr << 4) | _laddr);
            if (CECSendMessage(&message, 1) != 1) {
                laddr = _laddr;
                break;
            }
        }
        i++;
    }

    if (laddr == CEC_LADDR_UNREGISTERED) {
        ALOGE("All LA addresses in use!!!\n");
        return CEC_LADDR_UNREGISTERED;
    }

    if (CECSetLogicalAddr(laddr) < 0) {
        ALOGE("CECSetLogicalAddr() failed!\n");
        return 0;
    }

    /* broadcast "Report Physical Address" */
    unsigned char buffer[5];
    buffer[0] = (laddr << 4) | CEC_MSG_BROADCAST;
    buffer[1] = CEC_OPCODE_REPORT_PHYSICAL_ADDRESS;
    buffer[2] = (paddr >> 8) & 0xFF;
    buffer[3] = paddr & 0xFF;
    buffer[4] = devtype;

    if (CECSendMessage(buffer, 5) != 5) {
        ALOGE("CECSendMessage() failed!\n");
        return 0;
    }

    return laddr;
}

/**
 * Send CEC message.
 *
 * @param *buffer   [in] pointer to buffer address where message located.
 * @param size      [in] message size.
 *
 * @return number of bytes written, or 0 if an arror occured.
 */
int CECSendMessage(unsigned char *buffer, int size)
{
    if (fd == -1) {
        ALOGE("open device first!\n");
        return 0;
    }

    if (size > CEC_MAX_FRAME_SIZE) {
        ALOGE("size should not exceed %d\n", CEC_MAX_FRAME_SIZE);
        return 0;
    }

#if CEC_DEBUG
    ALOGI("CECSendMessage() : ");
    CECPrintFrame(buffer, size);
#endif

    return write(fd, buffer, size);
}

/**
 * Receive CEC message.
 *
 * @param *buffer   [in] pointer to buffer address where message will be stored.
 * @param size      [in] buffer size.
 * @param timeout   [in] timeout in microseconds.
 *
 * @return number of bytes received, or 0 if an arror occured.
 */
int CECReceiveMessage(unsigned char *buffer, int size, long timeout)
{
    int bytes = 0;
    fd_set rfds;
    struct timeval tv;
    int retval;

    if (fd == -1) {
        ALOGE("open device first!\n");
        return 0;
    }

    tv.tv_sec = 0;
    tv.tv_usec = timeout;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    retval = select(fd + 1, &rfds, NULL, NULL, &tv);

    if (retval == -1) {
        return 0;
    } else if (retval) {
        bytes = read(fd, buffer, size);
#if CEC_DEBUG
        ALOGI("CECReceiveMessage() : size(%d)", bytes);
        if(bytes > 0)
            CECPrintFrame(buffer, bytes);
#endif
    }

    return bytes;
}

/**
 * Set CEC logical address.
 *
 * @return 1 if success, otherwise, return 0.
 */
int CECSetLogicalAddr(unsigned int laddr)
{
    if (ioctl(fd, CEC_IOC_SETLADDR, &laddr)) {
        ALOGE("ioctl(CEC_IOC_SETLA) failed!\n");
        return 0;
    }

    return 1;
}

#if CEC_DEBUG
/**
 * Print CEC frame.
 */
void CECPrintFrame(unsigned char *buffer, unsigned int size)
{
    if (size > 0) {
        int i;
        ALOGI("fsize: %d ", size);
        ALOGI("frame: ");
        for (i = 0; i < size; i++)
            ALOGI("0x%02x ", buffer[i]);

        ALOGI("\n");
    }
}
#endif

/**
 * Check CEC message.
 *
 * @param opcode   [in] pointer to buffer address where message will be stored.
 * @param lsrc     [in] buffer size.
 *
 * @return 1 if message should be ignored, otherwise, return 0.
 */
//TODO: not finished
int CECIgnoreMessage(unsigned char opcode, unsigned char lsrc)
{
    int retval = 0;

    /* if a message coming from address 15 (unregistered) */
    if (lsrc == CEC_LADDR_UNREGISTERED) {
        switch (opcode) {
        case CEC_OPCODE_DECK_CONTROL:
        case CEC_OPCODE_PLAY:
            retval = 1;
        default:
            break;
        }
    }

    return retval;
}

/**
 * Check CEC message.
 *
 * @param opcode   [in] pointer to buffer address where message will be stored.
 * @param size     [in] message size.
 *
 * @return 0 if message should be ignored, otherwise, return 1.
 */
//TODO: not finished
int CECCheckMessageSize(unsigned char opcode, int size)
{
    int retval = 1;

    switch (opcode) {
    case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
        if (size != 1)
            retval = 0;
        break;
    case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
        if (size != 2)
            retval = 0;
        break;
    case CEC_OPCODE_PLAY:
    case CEC_OPCODE_DECK_CONTROL:
    case CEC_OPCODE_SET_MENU_LANGUAGE:
    case CEC_OPCODE_ACTIVE_SOURCE:
    case CEC_OPCODE_ROUTING_INFORMATION:
    case CEC_OPCODE_SET_STREAM_PATH:
        if (size != 3)
            retval = 0;
        break;
    case CEC_OPCODE_FEATURE_ABORT:
    case CEC_OPCODE_DEVICE_VENDOR_ID:
    case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
        if (size != 4)
            retval = 0;
        break;
    case CEC_OPCODE_ROUTING_CHANGE:
        if (size != 5)
            retval = 0;
        break;
    /* CDC - 1.4 */
    case 0xf8:
        if (!(size > 5 && size <= 16))
            retval = 0;
        break;
    default:
        break;
    }

    return retval;
}

/**
 * Check CEC message.
 *
 * @param opcode    [in] pointer to buffer address where message will be stored.
 * @param broadcast [in] broadcast/direct message.
 *
 * @return 0 if message should be ignored, otherwise, return 1.
 */
//TODO: not finished
int CECCheckMessageMode(unsigned char opcode, int broadcast)
{
    int retval = 1;

    switch (opcode) {
    case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
    case CEC_OPCODE_SET_MENU_LANGUAGE:
    case CEC_OPCODE_ACTIVE_SOURCE:
        if (!broadcast)
            retval = 0;
        break;
    case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
    case CEC_OPCODE_DECK_CONTROL:
    case CEC_OPCODE_PLAY:
    case CEC_OPCODE_FEATURE_ABORT:
    case CEC_OPCODE_ABORT:
        if (broadcast)
            retval = 0;
        break;
    default:
        break;
    }

    return retval;
}