Add helper functions that allow for easy instantiation of media_device object basing on whether the media device contains v4l2 subdev with given file descriptor. Signed-off-by: Jacek Anaszewski <j.anaszewski@xxxxxxxxxxx> Acked-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- utils/media-ctl/libmediactl.c | 131 +++++++++++++++++++++++++++++++++++++++++- utils/media-ctl/mediactl.h | 27 +++++++++ 2 files changed, 156 insertions(+), 2 deletions(-) diff --git a/utils/media-ctl/libmediactl.c b/utils/media-ctl/libmediactl.c index 155b65f..d347a40 100644 --- a/utils/media-ctl/libmediactl.c +++ b/utils/media-ctl/libmediactl.c @@ -27,6 +27,7 @@ #include <sys/sysmacros.h> #include <ctype.h> +#include <dirent.h> #include <errno.h> #include <fcntl.h> #include <stdbool.h> @@ -440,8 +441,9 @@ static int media_get_devname_udev(struct udev *udev, return -EINVAL; devnum = makedev(entity->info.v4l.major, entity->info.v4l.minor); - media_dbg(entity->media, "looking up device: %u:%u\n", - major(devnum), minor(devnum)); + if (entity->media) + media_dbg(entity->media, "looking up device: %u:%u\n", + major(devnum), minor(devnum)); device = udev_device_new_from_devnum(udev, 'c', devnum); if (device) { p = udev_device_get_devnode(device); @@ -523,6 +525,7 @@ static int media_get_devname_sysfs(struct media_entity *entity) return 0; } + static int media_enum_entities(struct media_device *media) { struct media_entity *entity; @@ -707,6 +710,92 @@ struct media_device *media_device_new(const char *devnode) return media; } +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity) +{ + char video_devname[32], device_dir_path[256], media_dev_path[256], media_major_minor[10]; + struct media_device *media = NULL; + struct dirent *entry; + struct media_entity tmp_entity; + DIR *device_dir; + struct udev *udev; + char *p; + int ret, i; + + if (fd_entity == NULL) + return NULL; + + ret = media_get_devname_by_fd(fd, video_devname); + if (ret < 0) + return NULL; + + p = strrchr(video_devname, '/'); + if (p == NULL) + return NULL; + + ret = media_udev_open(&udev); + if (ret < 0) + return NULL; + + sprintf(device_dir_path, "/sys/class/video4linux/%s/device/", p + 1); + + device_dir = opendir(device_dir_path); + if (device_dir == NULL) + return NULL; + + while ((entry = readdir(device_dir))) { + if (strncmp(entry->d_name, "media", 4)) + continue; + + sprintf(media_dev_path, "%s%s/dev", device_dir_path, entry->d_name); + + fd = open(media_dev_path, O_RDONLY); + if (fd < 0) + continue; + + ret = read(fd, media_major_minor, sizeof(media_major_minor)); + if (ret < 0) + continue; + + sscanf(media_major_minor, "%d:%d", &tmp_entity.info.dev.major, &tmp_entity.info.dev.minor); + + /* Try to get the device name via udev */ + if (media_get_devname_udev(udev, &tmp_entity)) { + /* Fall back to get the device name via sysfs */ + if (media_get_devname_sysfs(&tmp_entity)) + continue; + } + + media = media_device_new(tmp_entity.devname); + if (media == NULL) + continue; + + ret = media_device_enumerate(media); + if (ret < 0) { + media_dbg(media, "Failed to enumerate %s (%d)\n", + tmp_entity.devname, ret); + media_device_unref(media); + media = NULL; + continue; + } + + /* Get the entity associated with given fd */ + for (i = 0; i < media->entities_count; i++) { + struct media_entity *entity = &media->entities[i]; + + if (!strcmp(entity->devname, video_devname)) { + *fd_entity = &media->entities[i]; + break; + } + } + + break; + } + + media_udev_close(udev); + + return media; +} + struct media_device *media_device_new_emulated(struct media_device_info *info) { struct media_device *media; @@ -748,6 +837,44 @@ void media_device_unref(struct media_device *media) free(media); } +int media_get_devname_by_fd(int fd, char *node_name) +{ + struct udev *udev; + struct media_entity tmp_entity; + struct stat stat; + int ret, ret_udev; + + if (node_name == NULL) + return -EINVAL; + + ret = fstat(fd, &stat); + if (ret < 0) + return -errno; + + tmp_entity.info.v4l.major = MAJOR(stat.st_rdev); + tmp_entity.info.v4l.minor = MINOR(stat.st_rdev); + + ret_udev = media_udev_open(&udev); + if (ret_udev < 0) + printf("Can't get udev context\n"); + + /* Try to get the device name via udev */ + ret = media_get_devname_udev(udev, &tmp_entity); + if (!ret) + goto out; + + ret = media_get_devname_sysfs(&tmp_entity); + if (ret < 0) + goto err_get_devname; + +out: + strncpy(node_name, tmp_entity.devname, sizeof(tmp_entity.devname)); +err_get_devname: + if (!ret_udev) + media_udev_close(udev); + return ret; +} + int media_device_add_entity(struct media_device *media, const struct media_entity_desc *desc, const char *devnode) diff --git a/utils/media-ctl/mediactl.h b/utils/media-ctl/mediactl.h index b1f33cd..580a25a 100644 --- a/utils/media-ctl/mediactl.h +++ b/utils/media-ctl/mediactl.h @@ -76,6 +76,21 @@ struct media_device *media_device_new(const char *devnode); struct media_device *media_device_new_emulated(struct media_device_info *info); /** + * @brief Create a new media device contatning entity associated with v4l2 subdev fd. + * @param fd - file descriptor of a v4l2 subdev. + * @param fd_entity - media entity associated with the v4l2 subdev. + * + * Create a representation of the media device referenced by the v4l2-subdev. + * The media device instance is initialized with enumerated entities and links. + * + * Media devices are reference-counted, see media_device_ref() and + * media_device_unref() for more information. + * + * @return A pointer to the new media device or NULL if error occurred. + */ +struct media_device *media_device_new_by_subdev_fd(int fd, struct media_entity **fd_entity); + +/** * @brief Take a reference to the device. * @param media - device instance. * @@ -231,6 +246,18 @@ const struct media_link *media_entity_get_link(struct media_entity *entity, const char *media_entity_get_devname(struct media_entity *entity); /** + * @brief Get the device node name by its file descriptor + * @param fd - file descriptor of a device. + * @param node_name - output device node name string. + * + * This function returns the full path and name to the device node corresponding + * to the given file descriptor. + * + * @return 0 on success, or a negative error code on failure. + */ +int media_get_devname_by_fd(int fd, char *node_name); + +/** * @brief Get the type of an entity. * @param entity - the entity. * -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html