1 /*
2  * Copyright (C) 2011 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 /*!
18  * \file      exynos_mc.c
19  * \brief     source file for libexynosv4l2
20  * \author    Jinsung Yang (jsgood.yang@samsung.com)
21  * \author    Sangwoo Park (sw5771.park@samsung.com)
22  * \date      2012/01/17
23  *
24  * <b>Revision History: </b>
25  * - 2012/01/17: Jinsung Yang (jsgood.yang@samsung.com) \n
26  *   Initial version
27  *
28  */
29 
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <ctype.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/ioctl.h>
39 #include <sys/stat.h>
40 #include <media.h>
41 #include <linux/kdev_t.h>
42 #include <linux/types.h>
43 #include <unistd.h>
44 
45 #include "exynos_v4l2.h"
46 
47 //#define LOG_NDEBUG 0
48 #define LOG_TAG "libexynosv4l2-mc"
49 #include <log/log.h>
50 
__media_entity_type(struct media_entity * entity)51 static inline unsigned int __media_entity_type(struct media_entity *entity)
52 {
53     return entity->info.type & MEDIA_ENT_TYPE_MASK;
54 }
55 
__media_debug_default(void * ptr,...)56 static void __media_debug_default(void *ptr, ...)
57 {
58     va_list argptr;
59     va_start(argptr, ptr);
60     vprintf((const char*)ptr, argptr);
61     va_end(argptr);
62 }
63 
__media_debug_set_handler(struct media_device * media,void (* debug_handler)(void *,...),void * debug_priv)64 static void __media_debug_set_handler(
65                 struct media_device *media,
66                 void (*debug_handler)(void *, ...),
67                 void *debug_priv)
68 {
69     if (debug_handler) {
70         media->debug_handler = debug_handler;
71         media->debug_priv = debug_priv;
72     } else {
73         media->debug_handler = __media_debug_default;
74         media->debug_priv = NULL;
75     }
76 }
77 
__media_entity_add_link(struct media_entity * entity)78 static struct media_link *__media_entity_add_link(struct media_entity *entity)
79 {
80     return NULL;
81 }
82 
83 
__media_enum_links(struct media_device * media)84 static int __media_enum_links(struct media_device *media)
85 {
86     return 0;
87 }
88 
__media_get_devname_sysfs(struct media_entity * entity)89 static int __media_get_devname_sysfs(struct media_entity *entity)
90 {
91     //struct stat devstat;
92     char devname[32];
93     char sysname[32];
94     char target[1024];
95     char *p;
96     int ret;
97 
98     snprintf(sysname, sizeof(sysname), "/sys/dev/char/%u:%u", entity->info.v4l.major,
99         entity->info.v4l.minor);
100 
101     ret = readlink(sysname, target, sizeof(target));
102     if (ret < 0 || ret >= (int)sizeof(target))
103         return -errno;
104 
105     target[ret] = '\0';
106     p = strrchr(target, '/');
107     if (p == NULL)
108         return -EINVAL;
109 
110     snprintf(devname, sizeof(devname), "/tmp/%s", p + 1);
111 
112     ret = mknod(devname, 0666 | S_IFCHR, MKDEV(81, entity->info.v4l.minor));
113     strncpy(entity->devname, devname, sizeof(devname) - 1);
114 
115     return 0;
116 }
117 
__media_get_media_fd(const char * filename,struct media_device * media)118 static int __media_get_media_fd(const char *filename, struct media_device *media)
119 {
120     ssize_t num;
121     int media_node;
122     char *ptr;
123 
124     ALOGD("%s: %s", __func__, filename);
125 
126     media->fd = open(filename, O_RDWR, 0);
127     if (media->fd < 0) {
128         ALOGE("Open sysfs media device failed, media->fd: %d", media->fd);
129         return -1;
130     }
131 
132     ALOGD("%s: media->fd: %d", __func__, media->fd);
133 
134     return media->fd;
135 
136 }
137 
__media_enum_entities(struct media_device * media)138 static int __media_enum_entities(struct media_device *media)
139 {
140     return 0;
141 }
142 
__media_open_debug(const char * filename,void (* debug_handler)(void *,...),void * debug_priv)143 static struct media_device *__media_open_debug(
144         const char *filename,
145         void (*debug_handler)(void *, ...),
146         void *debug_priv)
147 {
148     struct media_device *media;
149     int ret;
150 
151     media = (struct media_device *)calloc(1, sizeof(struct media_device));
152     if (media == NULL) {
153         ALOGE("media: %p", media);
154         return NULL;
155     }
156 
157     __media_debug_set_handler(media, debug_handler, debug_priv);
158 
159     ALOGD("%s: Opening media device %s", __func__, filename);
160     ALOGD("%s: media: %p", __func__, media);
161 
162     media->fd = __media_get_media_fd(filename, media);
163     if (media->fd < 0) {
164         exynos_media_close(media);
165         ALOGE("failed __media_get_media_fd %s", filename);
166         return NULL;
167     }
168 
169     ALOGD("%s: media->fd: %d", __func__, media->fd);
170     ret = __media_enum_entities(media);
171 
172     if (ret < 0) {
173         ALOGE("Unable to enumerate entities for device %s (%s)", filename, strerror(-ret));
174         exynos_media_close(media);
175         return NULL;
176     }
177 
178     ALOGD("%s: Found %u entities", __func__, media->entities_count);
179     ALOGD("%s: Enumerating pads and links", __func__);
180 
181     ret = __media_enum_links(media);
182     if (ret < 0) {
183         ALOGE("Unable to enumerate pads and links for device %s", filename);
184         exynos_media_close(media);
185         return NULL;
186     }
187 
188     return media;
189 }
190 
191 /**
192  * @brief Open a media device.
193  * @param filename - name (including path) of the device node.
194  *
195  * Open the media device referenced by @a filename and enumerate entities, pads and
196  * links.
197  *
198  * @return A pointer to a newly allocated media_device structure instance on
199  * success and NULL on failure. The returned pointer must be freed with
200  * exynos_media_close when the device isn't needed anymore.
201  */
exynos_media_open(const char * filename)202 struct media_device *exynos_media_open(const char *filename)
203 {
204     return __media_open_debug(filename, (void (*)(void *, ...))fprintf, stdout);
205 }
206 
207 /**
208  * @brief Close a media device.
209  * @param media - device instance.
210  *
211  * Close the @a media device instance and free allocated resources. Access to the
212  * device instance is forbidden after this function returns.
213  */
exynos_media_close(struct media_device * media)214 void exynos_media_close(struct media_device *media)
215 {
216     unsigned int i;
217 
218     if (media->fd != -1)
219         close(media->fd);
220 
221     for (i = 0; i < media->entities_count; ++i) {
222         struct media_entity *entity = &media->entities[i];
223 
224         free(entity->pads);
225         free(entity->links);
226         if (entity->fd != -1)
227             close(entity->fd);
228     }
229 
230     free(media->entities);
231     free(media);
232 }
233 
234 /**
235  * @brief Locate the pad at the other end of a link.
236  * @param pad - sink pad at one end of the link.
237  *
238  * Locate the source pad connected to @a pad through an enabled link. As only one
239  * link connected to a sink pad can be enabled at a time, the connected source
240  * pad is guaranteed to be unique.
241  *
242  * @return A pointer to the connected source pad, or NULL if all links connected
243  * to @a pad are disabled. Return NULL also if @a pad is not a sink pad.
244  */
exynos_media_entity_remote_source(struct media_pad * pad)245 struct media_pad *exynos_media_entity_remote_source(struct media_pad *pad)
246 {
247     return NULL;
248 }
249 
250 /**
251  * @brief Find an entity by its name.
252  * @param media - media device.
253  * @param name - entity name.
254  * @param length - size of @a name.
255  *
256  * Search for an entity with a name equal to @a name.
257  *
258  * @return A pointer to the entity if found, or NULL otherwise.
259  */
exynos_media_get_entity_by_name(struct media_device * media,const char * name,size_t length)260 struct media_entity *exynos_media_get_entity_by_name(struct media_device *media,
261                           const char *name, size_t length)
262 {
263     unsigned int i;
264     struct media_entity *entity;
265 
266     for (i = 0; i < media->entities_count; ++i) {
267         entity = &media->entities[i];
268 
269         if (strncmp(entity->info.name, name, length) == 0)
270             return entity;
271     }
272 
273     return NULL;
274 }
275 
276 /**
277  * @brief Find an entity by its ID.
278  * @param media - media device.
279  * @param id - entity ID.
280  *
281  * Search for an entity with an ID equal to @a id.
282  *
283  * @return A pointer to the entity if found, or NULL otherwise.
284  */
exynos_media_get_entity_by_id(struct media_device * media,__u32 id)285 struct media_entity *exynos_media_get_entity_by_id(struct media_device *media,
286                         __u32 id)
287 {
288     unsigned int i;
289 
290     for (i = 0; i < media->entities_count; ++i) {
291         struct media_entity *entity = &media->entities[i];
292 
293         if (entity->info.id == id)
294             return entity;
295     }
296 
297     return NULL;
298 }
299 
300 /**
301  * @brief Configure a link.
302  * @param media - media device.
303  * @param source - source pad at the link origin.
304  * @param sink - sink pad at the link target.
305  * @param flags - configuration flags.
306  *
307  * Locate the link between @a source and @a sink, and configure it by applying
308  * the new @a flags.
309  *
310  * Only the MEDIA_LINK_FLAG_ENABLED flag is writable.
311  *
312  * @return 0 on success, -1 on failure:
313  *       -ENOENT: link not found
314  *       - other error codes returned by MEDIA_IOC_SETUP_LINK
315  */
exynos_media_setup_link(struct media_device * media,struct media_pad * source,struct media_pad * sink,__u32 flags)316 int exynos_media_setup_link(struct media_device *media,
317              struct media_pad *source,
318              struct media_pad *sink,
319              __u32 flags)
320 {
321     return 0;
322 }
323 
324 /**
325  * @brief Reset all links to the disabled state.
326  * @param media - media device.
327  *
328  * Disable all links in the media device. This function is usually used after
329  * opening a media device to reset all links to a known state.
330  *
331  * @return 0 on success, or a negative error code on failure.
332  */
exynos_media_reset_links(struct media_device * media)333 int exynos_media_reset_links(struct media_device *media)
334 {
335     return 0;
336 }
337 
338 #ifdef HAVE_LIBUDEV
339 
340 #include <libudev.h>
341 
__media_udev_open(struct udev ** udev)342 static inline int __media_udev_open(struct udev **udev)
343 {
344     *udev = udev_new();
345     if (*udev == NULL)
346         return -ENOMEM;
347     return 0;
348 }
349 
__media_udev_close(struct udev * udev)350 static inline void __media_udev_close(struct udev *udev)
351 {
352     if (udev != NULL)
353         udev_unref(udev);
354 }
355 
__media_get_devname_udev(struct udev * udev,struct media_entity * entity)356 static int __media_get_devname_udev(struct udev *udev,
357         struct media_entity *entity)
358 {
359     struct udev_device *device;
360     dev_t devnum;
361     const char *p;
362     int ret = -ENODEV;
363 
364     if (udev == NULL)
365         return -EINVAL;
366 
367     devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor);
368     ALOGE("looking up device: %u:%u",
369           major(devnum), minor(devnum));
370     device = udev_device_new_from_devnum(udev, 'c', devnum);
371     if (device) {
372         p = udev_device_get_devnode(device);
373         if (p) {
374             strncpy(entity->devname, p, sizeof(entity->devname));
375             entity->devname[sizeof(entity->devname) - 1] = '\0';
376         }
377         ret = 0;
378     }
379 
380     udev_device_unref(device);
381 
382     return ret;
383 }
384 
385 #else    /* HAVE_LIBUDEV */
386 
387 struct udev;
388 
__media_udev_open(struct udev ** udev)389 static inline int __media_udev_open(struct udev **udev) { return 0; }
390 
__media_udev_close(struct udev * udev)391 static inline void __media_udev_close(struct udev *udev) { }
392 
__media_get_devname_udev(struct udev * udev,struct media_entity * entity)393 static inline int __media_get_devname_udev(struct udev *udev,
394         struct media_entity *entity)
395 {
396     return -ENOTSUP;
397 }
398 
399 #endif    /* HAVE_LIBUDEV */
400 
401 /**
402  * @brief Parse string to a pad on the media device.
403  * @param media - media device.
404  * @param p - input string
405  * @param endp - pointer to string where parsing ended
406  *
407  * Parse NULL terminated string describing a pad and return its struct
408  * media_pad instance.
409  *
410  * @return Pointer to struct media_pad on success, NULL on failure.
411  */
exynos_media_parse_pad(struct media_device * media,const char * p,char ** endp)412 struct media_pad *exynos_media_parse_pad(struct media_device *media,
413                   const char *p, char **endp)
414 {
415     return NULL;
416 }
417 
418 /**
419  * @brief Parse string to a link on the media device.
420  * @param media - media device.
421  * @param p - input string
422  * @param endp - pointer to p where parsing ended
423  *
424  * Parse NULL terminated string p describing a link and return its struct
425  * media_link instance.
426  *
427  * @return Pointer to struct media_link on success, NULL on failure.
428  */
exynos_media_parse_link(struct media_device * media,const char * p,char ** endp)429 struct media_link *exynos_media_parse_link(
430         struct media_device *media,
431         const char *p,
432         char **endp)
433 {
434     struct media_link *link;
435     struct media_pad *source;
436     struct media_pad *sink;
437     unsigned int i;
438     char *end;
439 
440     source = exynos_media_parse_pad(media, p, &end);
441     if (source == NULL)
442         return NULL;
443 
444     if (end[0] != '-' || end[1] != '>')
445         return NULL;
446     p = end + 2;
447 
448     sink = exynos_media_parse_pad(media, p, &end);
449     if (sink == NULL)
450         return NULL;
451 
452     *endp = end;
453 
454     for (i = 0; i < source->entity->num_links; i++) {
455         link = &source->entity->links[i];
456 
457         if (link->source == source && link->sink == sink)
458             return link;
459     }
460 
461     return NULL;
462 }
463 
464 /**
465  * @brief Parse string to a link on the media device and set it up.
466  * @param media - media device.
467  * @param p - input string
468  *
469  * Parse NULL terminated string p describing a link and its configuration
470  * and configure the link.
471  *
472  * @return 0 on success, or a negative error code on failure.
473  */
exynos_media_parse_setup_link(struct media_device * media,const char * p,char ** endp)474 int exynos_media_parse_setup_link(
475         struct media_device *media,
476         const char *p,
477         char **endp)
478 {
479     return 0;
480 }
481 
482 /**
483  * @brief Parse string to link(s) on the media device and set it up.
484  * @param media - media device.
485  * @param p - input string
486  *
487  * Parse NULL terminated string p describing link(s) separated by
488  * commas (,) and configure the link(s).
489  *
490  * @return 0 on success, or a negative error code on failure.
491  */
exynos_media_parse_setup_links(struct media_device * media,const char * p)492 int exynos_media_parse_setup_links(struct media_device *media, const char *p)
493 {
494     return 0;
495 }
496