static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval) { int ret; if (interval->numerator == 0) return 0; media_dbg(entity->media, "Setting up frame interval %u/%u on entity %s\n", interval->numerator, interval->denominator, entity->info.name); ret = v4l2_subdev_set_frame_interval(entity, interval); if (ret < 0) { media_dbg(entity->media, "Unable to set frame interval: %s (%d)", strerror(-ret), ret); return ret; } media_dbg(entity->media, "Frame interval set: %u/%u\n", interval->numerator, interval->denominator); return 0; }
static int set_format(struct media_pad *pad, struct v4l2_mbus_framefmt *format) { int ret; if (format->width == 0 || format->height == 0) return 0; media_dbg(pad->entity->media, "Setting up format %s %ux%u on pad %s/%u\n", v4l2_subdev_pixelcode_to_string(format->code), format->width, format->height, pad->entity->info.name, pad->index); ret = v4l2_subdev_set_format(pad->entity, format, pad->index, V4L2_SUBDEV_FORMAT_ACTIVE); if (ret < 0) { media_dbg(pad->entity->media, "Unable to set format: %s (%d)\n", strerror(-ret), ret); return ret; } media_dbg(pad->entity->media, "Format set: %s %ux%u\n", v4l2_subdev_pixelcode_to_string(format->code), format->width, format->height); return 0; }
static int set_selection(struct media_pad *pad, unsigned int target, struct v4l2_rect *rect) { int ret; if (rect->left == -1 || rect->top == -1) return 0; media_dbg(pad->entity->media, "Setting up selection target %u rectangle (%u,%u)/%ux%u on pad %s/%u\n", target, rect->left, rect->top, rect->width, rect->height, pad->entity->info.name, pad->index); ret = v4l2_subdev_set_selection(pad->entity, rect, pad->index, target, V4L2_SUBDEV_FORMAT_ACTIVE); if (ret < 0) { media_dbg(pad->entity->media, "Unable to set selection rectangle: %s (%d)\n", strerror(-ret), ret); return ret; } media_dbg(pad->entity->media, "Selection rectangle set: (%u,%u)/%ux%u\n", rect->left, rect->top, rect->width, rect->height); return 0; }
struct media_device *media_open_debug( const char *name, void (*debug_handler)(void *, ...), void *debug_priv) { struct media_device *media; int ret; media = calloc(1, sizeof(*media)); if (media == NULL) return NULL; media_debug_set_handler(media, debug_handler, debug_priv); media_dbg(media, "Opening media device %s\n", name); media->fd = open(name, O_RDWR); if (media->fd < 0) { media_close(media); media_dbg(media, "%s: Can't open media device %s\n", __func__, name); return NULL; } ret = ioctl(media->fd, MEDIA_IOC_DEVICE_INFO, &media->info); if (ret < 0) { media_dbg(media, "%s: Unable to retrieve media device " "information for device %s (%s)\n", __func__, name, strerror(errno)); media_close(media); return NULL; } media_dbg(media, "Enumerating entities\n"); ret = media_enum_entities(media); if (ret < 0) { media_dbg(media, "%s: Unable to enumerate entities for device %s (%s)\n", __func__, name, strerror(-ret)); media_close(media); return NULL; } media_dbg(media, "Found %u entities\n", media->entities_count); media_dbg(media, "Enumerating pads and links\n"); ret = media_enum_links(media); if (ret < 0) { media_dbg(media, "%s: Unable to enumerate pads and linksfor device %s\n", __func__, name); media_close(media); return NULL; } return media; }
static int media_get_devname_udev(struct udev *udev, struct media_entity *entity) { struct udev_device *device; dev_t devnum; const char *p; int ret = -ENODEV; if (udev == NULL) 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)); device = udev_device_new_from_devnum(udev, 'c', devnum); if (device) { p = udev_device_get_devnode(device); if (p) { strncpy(entity->devname, p, sizeof(entity->devname)); entity->devname[sizeof(entity->devname) - 1] = '\0'; } ret = 0; } udev_device_unref(device); return ret; }
static int media_enum_entities(struct media_device *media) { struct media_entity *entity; struct udev *udev; unsigned int size; __u32 id; int ret; ret = media_udev_open(&udev); if (ret < 0) media_dbg(media, "Can't get udev context\n"); for (id = 0, ret = 0; ; id = entity->info.id) { size = (media->entities_count + 1) * sizeof(*media->entities); media->entities = realloc(media->entities, size); entity = &media->entities[media->entities_count]; memset(entity, 0, sizeof(*entity)); entity->fd = -1; entity->info.id = id | MEDIA_ENT_ID_FLAG_NEXT; entity->media = media; ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); if (ret < 0) { ret = errno != EINVAL ? -errno : 0; break; } /* Number of links (for outbound links) plus number of pads (for * inbound links) is a good safe initial estimate of the total * number of links. */ entity->max_links = entity->info.pads + entity->info.links; entity->pads = malloc(entity->info.pads * sizeof(*entity->pads)); entity->links = malloc(entity->max_links * sizeof(*entity->links)); if (entity->pads == NULL || entity->links == NULL) { ret = -ENOMEM; break; } media->entities_count++; /* Find the corresponding device name. */ if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE && media_entity_type(entity) != MEDIA_ENT_T_V4L2_SUBDEV) continue; /* Try to get the device name via udev */ if (!media_get_devname_udev(udev, entity)) continue; /* Fall back to get the device name via sysfs */ media_get_devname_sysfs(entity); } media_udev_close(udev); return ret; }
static int v4l2_subdev_parse_rectangle(struct media_device *media, struct v4l2_rect *r, const char *p, char **endp) { char *end; if (*p++ != '(') { media_dbg(media, "Expected '('\n"); *endp = (char *)p - 1; return -EINVAL; } r->left = strtoul(p, &end, 10); if (*end != ',') { media_dbg(media, "Expected ','\n"); *endp = end; return -EINVAL; } p = end + 1; r->top = strtoul(p, &end, 10); if (*end++ != ')') { media_dbg(media, "Expected ')'\n"); *endp = end - 1; return -EINVAL; } if (*end != '/') { media_dbg(media, "Expected '/'\n"); *endp = end; return -EINVAL; } p = end + 1; r->width = strtoul(p, &end, 10); if (*end != 'x') { media_dbg(media, "Expected 'x'\n"); *endp = end; return -EINVAL; } p = end + 1; r->height = strtoul(p, &end, 10); *endp = end; return 0; }
static int v4l2_subdev_parse_format(struct media_device *media, struct v4l2_mbus_framefmt *format, const char *p, char **endp) { enum v4l2_mbus_pixelcode code; unsigned int width, height; char *end; /* * Compatibility with the old syntax: consider space as valid * separator between the media bus pixel code and the size. */ for (; isspace(*p); ++p); for (end = (char *)p; *end != '/' && *end != ' ' && *end != '\0'; ++end); code = v4l2_subdev_string_to_pixelcode(p, end - p); if (code == (enum v4l2_mbus_pixelcode)-1) { media_dbg(media, "Invalid pixel code '%.*s'\n", end - p, p); return -EINVAL; } p = end + 1; width = strtoul(p, &end, 10); if (*end != 'x') { media_dbg(media, "Expected 'x'\n"); return -EINVAL; } p = end + 1; height = strtoul(p, &end, 10); *endp = end; memset(format, 0, sizeof(*format)); format->width = width; format->height = height; format->code = code; return 0; }
int v4l2_subdev_open(struct media_entity *entity) { if (entity->fd != -1) return 0; entity->fd = open(entity->devname, O_RDWR); if (entity->fd == -1) { media_dbg(entity->media, "%s: Failed to open subdev device node %s\n", __func__, entity->devname); return -errno; } return 0; }
static int v4l2_subdev_parse_frame_interval(struct media_device *media, struct v4l2_fract *interval, const char *p, char **endp) { char *end; for (; isspace(*p); ++p); interval->numerator = strtoul(p, &end, 10); for (p = end; isspace(*p); ++p); if (*p++ != '/') { media_dbg(media, "Expected '/'\n"); *endp = (char *)p - 1; return -EINVAL; } for (; isspace(*p); ++p); interval->denominator = strtoul(p, &end, 10); *endp = end; return 0; }
static int v4l2_subdev_parse_setup_format(struct media_device *media, const char *p, char **endp) { struct v4l2_mbus_framefmt format = { 0, 0, 0 , 0, 0, {0}}; struct media_pad *pad; struct v4l2_rect crop = { -1, -1, -1, -1 }; struct v4l2_rect compose = crop; struct v4l2_fract interval = { 0, 0 }; unsigned int i; char *end; int ret; pad = v4l2_subdev_parse_pad_format(media, &format, &crop, &compose, &interval, p, &end); if (pad == NULL) { media_print_streampos(media, p, end); media_dbg(media, "Unable to parse format\n"); return -EINVAL; } if (pad->flags & MEDIA_PAD_FL_SINK) { ret = set_format(pad, &format); if (ret < 0) return ret; } ret = set_selection(pad, V4L2_SEL_TGT_CROP, &crop); if (ret < 0) return ret; ret = set_selection(pad, V4L2_SEL_TGT_COMPOSE, &compose); if (ret < 0) return ret; if (pad->flags & MEDIA_PAD_FL_SOURCE) { ret = set_format(pad, &format); if (ret < 0) return ret; } ret = set_frame_interval(pad->entity, &interval); if (ret < 0) return ret; /* If the pad is an output pad, automatically set the same format on * the remote subdev input pads, if any. */ if (pad->flags & MEDIA_PAD_FL_SOURCE) { for (i = 0; i < pad->entity->num_links; ++i) { struct media_link *link = &pad->entity->links[i]; struct v4l2_mbus_framefmt remote_format; if (!(link->flags & MEDIA_LNK_FL_ENABLED)) continue; if (link->source == pad && link->sink->entity->info.type == MEDIA_ENT_T_V4L2_SUBDEV) { remote_format = format; set_format(link->sink, &remote_format); } } } *endp = end; return 0; }
static struct media_pad *v4l2_subdev_parse_pad_format( struct media_device *media, struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop, struct v4l2_rect *compose, struct v4l2_fract *interval, const char *p, char **endp) { struct media_pad *pad; bool first; char *end; int ret; for (; isspace(*p); ++p); pad = media_parse_pad(media, p, &end); if (pad == NULL) { *endp = end; return NULL; } for (p = end; isspace(*p); ++p); if (*p++ != '[') { media_dbg(media, "Expected '['\n"); *endp = (char *)p - 1; return NULL; } for (first = true; ; first = false) { for (; isspace(*p); p++); /* * Backward compatibility: if the first property starts with an * uppercase later, process it as a format description. */ if (strhazit("fmt:", &p) || (first && isupper(*p))) { ret = v4l2_subdev_parse_format(media, format, p, &end); if (ret < 0) { *endp = end; return NULL; } p = end; continue; } /* * Backward compatibility: crop rectangles can be specified * implicitly without the 'crop:' property name. */ if (strhazit("crop:", &p) || *p == '(') { ret = v4l2_subdev_parse_rectangle(media, crop, p, &end); if (ret < 0) { *endp = end; return NULL; } p = end; continue; } if (strhazit("compose:", &p)) { ret = v4l2_subdev_parse_rectangle(media, compose, p, &end); if (ret < 0) { *endp = end; return NULL; } for (p = end; isspace(*p); p++); continue; } if (*p == '@') { ret = v4l2_subdev_parse_frame_interval(media, interval, ++p, &end); if (ret < 0) { *endp = end; return NULL; } p = end; continue; } break; } if (*p != ']') { media_dbg(media, "Expected ']'\n"); *endp = (char *)p; return NULL; } *endp = (char *)p + 1; return pad; }
static int media_enum_links(struct media_device *media) { __u32 id; int ret = 0; for (id = 1; id <= media->entities_count; id++) { struct media_entity *entity = &media->entities[id - 1]; struct media_links_enum links; unsigned int i; links.entity = entity->info.id; links.pads = malloc(entity->info.pads * sizeof(struct media_pad_desc)); links.links = malloc(entity->info.links * sizeof(struct media_link_desc)); if (ioctl(media->fd, MEDIA_IOC_ENUM_LINKS, &links) < 0) { media_dbg(media, "%s: Unable to enumerate pads and links (%s).\n", __func__, strerror(errno)); free(links.pads); free(links.links); return -errno; } for (i = 0; i < entity->info.pads; ++i) { entity->pads[i].entity = entity; entity->pads[i].index = links.pads[i].index; entity->pads[i].flags = links.pads[i].flags; } for (i = 0; i < entity->info.links; ++i) { struct media_link_desc *link = &links.links[i]; struct media_link *fwdlink; struct media_link *backlink; struct media_entity *source; struct media_entity *sink; source = media_get_entity_by_id(media, link->source.entity); sink = media_get_entity_by_id(media, link->sink.entity); if (source == NULL || sink == NULL) { media_dbg(media, "WARNING entity %u link %u from %u/%u to %u/%u is invalid!\n", id, i, link->source.entity, link->source.index, link->sink.entity, link->sink.index); ret = -EINVAL; } else { fwdlink = media_entity_add_link(source); fwdlink->source = &source->pads[link->source.index]; fwdlink->sink = &sink->pads[link->sink.index]; fwdlink->flags = link->flags; backlink = media_entity_add_link(sink); backlink->source = &source->pads[link->source.index]; backlink->sink = &sink->pads[link->sink.index]; backlink->flags = link->flags; fwdlink->twin = backlink; backlink->twin = fwdlink; } } free(links.pads); free(links.links); } return ret; }