Commit | Line | Data |
---|---|---|
5763fb39 T |
1 | /* |
2 | * Copyright@ Samsung Electronics Co. LTD | |
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 | #include <stdio.h> | |
18 | #include <sys/types.h> | |
19 | #include <sys/stat.h> | |
20 | #include <fcntl.h> | |
21 | #include <sys/ioctl.h> | |
22 | #include <unistd.h> | |
23 | #include <stdlib.h> | |
24 | #include <cutils/log.h> | |
25 | ||
26 | /* drv. header */ | |
27 | #include "cec.h" | |
28 | ||
29 | #include "libcec.h" | |
30 | ||
400322e1 | 31 | //#define CEC_DEBUG 0 |
5763fb39 T |
32 | |
33 | /** | |
34 | * @def CEC_DEVICE_NAME | |
35 | * Defines simbolic name of the CEC device. | |
36 | */ | |
37 | #define CEC_DEVICE_NAME "/dev/CEC" | |
38 | ||
39 | static struct { | |
40 | enum CECDeviceType devtype; | |
41 | unsigned char laddr; | |
42 | } laddresses[] = { | |
43 | { CEC_DEVICE_RECODER, 1 }, | |
44 | { CEC_DEVICE_RECODER, 2 }, | |
45 | { CEC_DEVICE_TUNER, 3 }, | |
46 | { CEC_DEVICE_PLAYER, 4 }, | |
47 | { CEC_DEVICE_AUDIO, 5 }, | |
48 | { CEC_DEVICE_TUNER, 6 }, | |
49 | { CEC_DEVICE_TUNER, 7 }, | |
50 | { CEC_DEVICE_PLAYER, 8 }, | |
51 | { CEC_DEVICE_RECODER, 9 }, | |
52 | { CEC_DEVICE_TUNER, 10 }, | |
53 | { CEC_DEVICE_PLAYER, 11 }, | |
54 | }; | |
55 | ||
56 | static int CECSetLogicalAddr(unsigned int laddr); | |
57 | ||
58 | #ifdef CEC_DEBUG | |
59 | inline static void CECPrintFrame(unsigned char *buffer, unsigned int size); | |
60 | #endif | |
61 | ||
62 | static int fd = -1; | |
63 | ||
64 | /** | |
65 | * Open device driver and assign CEC file descriptor. | |
66 | * | |
67 | * @return If success to assign CEC file descriptor, return fd; otherwise, return -1. | |
68 | */ | |
69 | int CECOpen() | |
70 | { | |
71 | if (fd != -1) | |
72 | CECClose(); | |
73 | ||
74 | if ((fd = open(CEC_DEVICE_NAME, O_RDWR)) < 0) { | |
75 | ALOGE("Can't open %s!\n", CEC_DEVICE_NAME); | |
76 | return -1; | |
77 | } | |
78 | ||
79 | return fd; | |
80 | } | |
81 | ||
82 | /** | |
83 | * Close CEC file descriptor. | |
84 | * | |
85 | * @return If success to close CEC file descriptor, return 1; otherwise, return 0. | |
86 | */ | |
87 | int CECClose() | |
88 | { | |
89 | int res = 1; | |
90 | ||
91 | if (fd != -1) { | |
92 | if (close(fd) != 0) { | |
93 | ALOGE("close() failed!\n"); | |
94 | res = 0; | |
95 | } | |
96 | fd = -1; | |
97 | } | |
98 | ||
99 | return res; | |
100 | } | |
101 | ||
102 | /** | |
103 | * Allocate logical address. | |
104 | * | |
105 | * @param paddr [in] CEC device physical address. | |
106 | * @param devtype [in] CEC device type. | |
107 | * | |
108 | * @return new logical address, or 0 if an error occured. | |
109 | */ | |
110 | int CECAllocLogicalAddress(int paddr, enum CECDeviceType devtype) | |
111 | { | |
112 | unsigned char laddr = CEC_LADDR_UNREGISTERED; | |
113 | size_t i = 0; | |
114 | ||
115 | if (fd == -1) { | |
116 | ALOGE("open device first!\n"); | |
117 | return 0; | |
118 | } | |
119 | ||
120 | if (CECSetLogicalAddr(laddr) < 0) { | |
121 | ALOGE("CECSetLogicalAddr() failed!\n"); | |
122 | return 0; | |
123 | } | |
124 | ||
125 | if (paddr == CEC_NOT_VALID_PHYSICAL_ADDRESS) | |
126 | return CEC_LADDR_UNREGISTERED; | |
127 | ||
128 | /* send "Polling Message" */ | |
129 | while (i < sizeof(laddresses) / sizeof(laddresses[0])) { | |
130 | if (laddresses[i].devtype == devtype) { | |
131 | unsigned char _laddr = laddresses[i].laddr; | |
132 | unsigned char message = ((_laddr << 4) | _laddr); | |
133 | if (CECSendMessage(&message, 1) != 1) { | |
134 | laddr = _laddr; | |
135 | break; | |
136 | } | |
137 | } | |
138 | i++; | |
139 | } | |
140 | ||
141 | if (laddr == CEC_LADDR_UNREGISTERED) { | |
142 | ALOGE("All LA addresses in use!!!\n"); | |
143 | return CEC_LADDR_UNREGISTERED; | |
144 | } | |
145 | ||
146 | if (CECSetLogicalAddr(laddr) < 0) { | |
147 | ALOGE("CECSetLogicalAddr() failed!\n"); | |
148 | return 0; | |
149 | } | |
150 | ||
151 | /* broadcast "Report Physical Address" */ | |
152 | unsigned char buffer[5]; | |
153 | buffer[0] = (laddr << 4) | CEC_MSG_BROADCAST; | |
154 | buffer[1] = CEC_OPCODE_REPORT_PHYSICAL_ADDRESS; | |
155 | buffer[2] = (paddr >> 8) & 0xFF; | |
156 | buffer[3] = paddr & 0xFF; | |
157 | buffer[4] = devtype; | |
158 | ||
159 | if (CECSendMessage(buffer, 5) != 5) { | |
160 | ALOGE("CECSendMessage() failed!\n"); | |
161 | return 0; | |
162 | } | |
163 | ||
164 | return laddr; | |
165 | } | |
166 | ||
167 | /** | |
168 | * Send CEC message. | |
169 | * | |
170 | * @param *buffer [in] pointer to buffer address where message located. | |
171 | * @param size [in] message size. | |
172 | * | |
173 | * @return number of bytes written, or 0 if an error occured. | |
174 | */ | |
175 | int CECSendMessage(unsigned char *buffer, int size) | |
176 | { | |
177 | if (fd == -1) { | |
178 | ALOGE("open device first!\n"); | |
179 | return 0; | |
180 | } | |
181 | ||
182 | if (size > CEC_MAX_FRAME_SIZE) { | |
183 | ALOGE("size should not exceed %d\n", CEC_MAX_FRAME_SIZE); | |
184 | return 0; | |
185 | } | |
186 | ||
187 | #if CEC_DEBUG | |
188 | ALOGI("CECSendMessage() : "); | |
189 | CECPrintFrame(buffer, size); | |
190 | #endif | |
191 | ||
192 | return write(fd, buffer, size); | |
193 | } | |
194 | ||
195 | /** | |
196 | * Receive CEC message. | |
197 | * | |
198 | * @param *buffer [in] pointer to buffer address where message will be stored. | |
199 | * @param size [in] buffer size. | |
200 | * @param timeout [in] timeout in microseconds. | |
201 | * | |
202 | * @return number of bytes received, or 0 if an error occured. | |
203 | */ | |
204 | int CECReceiveMessage(unsigned char *buffer, int size, long timeout) | |
205 | { | |
206 | int bytes = 0; | |
207 | fd_set rfds; | |
208 | struct timeval tv; | |
209 | int retval; | |
210 | ||
211 | if (fd == -1) { | |
212 | ALOGE("open device first!\n"); | |
213 | return 0; | |
214 | } | |
215 | ||
216 | tv.tv_sec = 0; | |
217 | tv.tv_usec = timeout; | |
218 | ||
219 | FD_ZERO(&rfds); | |
220 | FD_SET(fd, &rfds); | |
221 | ||
222 | retval = select(fd + 1, &rfds, NULL, NULL, &tv); | |
223 | ||
224 | if (retval == -1) { | |
225 | return 0; | |
226 | } else if (retval) { | |
227 | bytes = read(fd, buffer, size); | |
228 | #if CEC_DEBUG | |
229 | ALOGI("CECReceiveMessage() : size(%d)", bytes); | |
230 | if(bytes > 0) | |
231 | CECPrintFrame(buffer, bytes); | |
232 | #endif | |
233 | } | |
234 | ||
235 | return bytes; | |
236 | } | |
237 | ||
238 | /** | |
239 | * Set CEC logical address. | |
240 | * | |
241 | * @return 1 if success, otherwise, return 0. | |
242 | */ | |
243 | int CECSetLogicalAddr(unsigned int laddr) | |
244 | { | |
245 | if (ioctl(fd, CEC_IOC_SETLADDR, &laddr)) { | |
246 | ALOGE("ioctl(CEC_IOC_SETLA) failed!\n"); | |
247 | return 0; | |
248 | } | |
249 | ||
250 | return 1; | |
251 | } | |
252 | ||
253 | #if CEC_DEBUG | |
254 | /** | |
255 | * Print CEC frame. | |
256 | */ | |
257 | void CECPrintFrame(unsigned char *buffer, unsigned int size) | |
258 | { | |
259 | if (size > 0) { | |
260 | size_t i; | |
261 | ALOGI("fsize: %d ", size); | |
262 | ALOGI("frame: "); | |
263 | for (i = 0; i < size; i++) | |
264 | ALOGI("0x%02x ", buffer[i]); | |
265 | ||
266 | ALOGI("\n"); | |
267 | } | |
268 | } | |
269 | #endif | |
270 | ||
271 | /** | |
272 | * Check CEC message. | |
273 | * | |
274 | * @param opcode [in] pointer to buffer address where message will be stored. | |
275 | * @param lsrc [in] buffer size. | |
276 | * | |
277 | * @return 1 if message should be ignored, otherwise, return 0. | |
278 | */ | |
279 | //TODO: not finished | |
280 | int CECIgnoreMessage(unsigned char opcode, unsigned char lsrc) | |
281 | { | |
282 | int retval = 0; | |
283 | ||
284 | /* if a message coming from address 15 (unregistered) */ | |
285 | if (lsrc == CEC_LADDR_UNREGISTERED) { | |
286 | switch (opcode) { | |
287 | case CEC_OPCODE_DECK_CONTROL: | |
288 | case CEC_OPCODE_PLAY: | |
289 | retval = 1; | |
290 | default: | |
291 | break; | |
292 | } | |
293 | } | |
294 | ||
295 | return retval; | |
296 | } | |
297 | ||
298 | /** | |
299 | * Check CEC message. | |
300 | * | |
301 | * @param opcode [in] pointer to buffer address where message will be stored. | |
302 | * @param size [in] message size. | |
303 | * | |
304 | * @return 0 if message should be ignored, otherwise, return 1. | |
305 | */ | |
306 | //TODO: not finished | |
307 | int CECCheckMessageSize(unsigned char opcode, int size) | |
308 | { | |
309 | int retval = 1; | |
310 | ||
311 | switch (opcode) { | |
312 | case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: | |
313 | if (size != 1) | |
314 | retval = 0; | |
315 | break; | |
316 | case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE: | |
317 | if (size != 2) | |
318 | retval = 0; | |
319 | break; | |
320 | case CEC_OPCODE_PLAY: | |
321 | case CEC_OPCODE_DECK_CONTROL: | |
322 | case CEC_OPCODE_SET_MENU_LANGUAGE: | |
323 | case CEC_OPCODE_ACTIVE_SOURCE: | |
324 | case CEC_OPCODE_ROUTING_INFORMATION: | |
325 | case CEC_OPCODE_SET_STREAM_PATH: | |
326 | if (size != 3) | |
327 | retval = 0; | |
328 | break; | |
329 | case CEC_OPCODE_FEATURE_ABORT: | |
330 | case CEC_OPCODE_DEVICE_VENDOR_ID: | |
331 | case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS: | |
332 | if (size != 4) | |
333 | retval = 0; | |
334 | break; | |
335 | case CEC_OPCODE_ROUTING_CHANGE: | |
336 | if (size != 5) | |
337 | retval = 0; | |
338 | break; | |
339 | /* CDC - 1.4 */ | |
340 | case 0xf8: | |
341 | if (!(size > 5 && size <= 16)) | |
342 | retval = 0; | |
343 | break; | |
344 | default: | |
345 | break; | |
346 | } | |
347 | ||
348 | return retval; | |
349 | } | |
350 | ||
351 | /** | |
352 | * Check CEC message. | |
353 | * | |
354 | * @param opcode [in] pointer to buffer address where message will be stored. | |
355 | * @param broadcast [in] broadcast/direct message. | |
356 | * | |
357 | * @return 0 if message should be ignored, otherwise, return 1. | |
358 | */ | |
359 | //TODO: not finished | |
360 | int CECCheckMessageMode(unsigned char opcode, int broadcast) | |
361 | { | |
362 | int retval = 1; | |
363 | ||
364 | switch (opcode) { | |
365 | case CEC_OPCODE_REQUEST_ACTIVE_SOURCE: | |
366 | case CEC_OPCODE_SET_MENU_LANGUAGE: | |
367 | case CEC_OPCODE_ACTIVE_SOURCE: | |
368 | if (!broadcast) | |
369 | retval = 0; | |
370 | break; | |
371 | case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS: | |
372 | case CEC_OPCODE_DECK_CONTROL: | |
373 | case CEC_OPCODE_PLAY: | |
374 | case CEC_OPCODE_FEATURE_ABORT: | |
375 | case CEC_OPCODE_ABORT: | |
376 | if (broadcast) | |
377 | retval = 0; | |
378 | break; | |
379 | default: | |
380 | break; | |
381 | } | |
382 | ||
383 | return retval; | |
384 | } |