diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 3 | ||||
| -rw-r--r-- | src/main.c | 809 | ||||
| -rw-r--r-- | src/media.c | 388 | ||||
| -rw-r--r-- | src/media.h | 161 | ||||
| -rw-r--r-- | src/options.c | 155 | ||||
| -rw-r--r-- | src/options.h | 42 | ||||
| -rw-r--r-- | src/subdev.c | 187 | ||||
| -rw-r--r-- | src/subdev.h | 162 | ||||
| -rw-r--r-- | src/tools.h | 27 | 
9 files changed, 1934 insertions, 0 deletions
| diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..b112ba6 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,3 @@ +bin_PROGRAMS = media-ctl +media_ctl_SOURCES = main.c media.c media.h options.c options.h subdev.c subdev.h tools.h + diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..aa4ced0 --- /dev/null +++ b/src/main.c @@ -0,0 +1,809 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include <sys/mman.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <linux/types.h> +#include <linux/media.h> +#include <linux/v4l2-mediabus.h> +#include <linux/v4l2-subdev.h> +#include <linux/videodev2.h> + +#include "media.h" +#include "options.h" +#include "subdev.h" +#include "tools.h" + +/* ----------------------------------------------------------------------------- + * Printing + */ + +static struct { +	const char *name; +	enum v4l2_mbus_pixelcode code; +} mbus_formats[] = { +	{ "Y8", V4L2_MBUS_FMT_Y8_1X8}, +	{ "YUYV", V4L2_MBUS_FMT_YUYV8_1X16 }, +	{ "UYVY", V4L2_MBUS_FMT_UYVY8_1X16 }, +	{ "SGRBG10", V4L2_MBUS_FMT_SGRBG10_1X10 }, +	{ "SGRBG10_DPCM8", V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8 }, +}; + +static const char *pixelcode_to_string(enum v4l2_mbus_pixelcode code) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { +		if (mbus_formats[i].code == code) +			return mbus_formats[i].name; +	} + +	return "unknown"; +} + +static enum v4l2_mbus_pixelcode string_to_pixelcode(const char *string, +					     unsigned int length) +{ +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(mbus_formats); ++i) { +		if (strncmp(mbus_formats[i].name, string, length) == 0) +			break; +	} + +	if (i == ARRAY_SIZE(mbus_formats)) +		return (enum v4l2_mbus_pixelcode)-1; + +	return mbus_formats[i].code; +} + +static void v4l2_subdev_print_format(struct media_entity *entity, +	unsigned int pad, enum v4l2_subdev_format_whence which) +{ +	struct v4l2_mbus_framefmt format; +	struct v4l2_rect rect; +	int ret; + +	ret = v4l2_subdev_get_format(entity, &format, pad, which); +	if (ret != 0) +		return; + +	printf("[%s %ux%u", pixelcode_to_string(format.code), +	       format.width, format.height); + +	ret = v4l2_subdev_get_crop(entity, &rect, pad, which); +	if (ret == 0) +		printf(" (%u,%u)/%ux%u", rect.left, rect.top, +		       rect.width, rect.height); +	printf("]"); +} + +static const char *media_entity_type_to_string(unsigned type) +{ +	static const struct { +		__u32 type; +		const char *name; +	} types[] = { +		{ MEDIA_ENTITY_TYPE_NODE, "Node" }, +		{ MEDIA_ENTITY_TYPE_SUBDEV, "V4L2 subdev" }, +	}; + +	unsigned int i; + +	type &= MEDIA_ENTITY_TYPE_MASK; + +	for (i = 0; i < ARRAY_SIZE(types); i++) { +		if (types[i].type == type) +			return types[i].name; +	} + +	return "Unknown"; +} + +static const char *media_entity_subtype_to_string(unsigned type) +{ +	static const char *node_types[] = { +		"Unknown", +		"V4L", +		"FB", +		"ALSA", +		"DVB", +	}; +	static const char *subdev_types[] = { +		"Unknown", +		"Sensor", +		"Flash", +		"Lens", +	}; + +	unsigned int subtype = type & MEDIA_ENTITY_SUBTYPE_MASK; + +	switch (type & MEDIA_ENTITY_TYPE_MASK) { +	case MEDIA_ENTITY_TYPE_NODE: +		if (subtype >= ARRAY_SIZE(node_types)) +			subtype = 0; +		return node_types[subtype]; + +	case MEDIA_ENTITY_TYPE_SUBDEV: +		if (subtype >= ARRAY_SIZE(subdev_types)) +			subtype = 0; +		return subdev_types[subtype]; +	default: +		return node_types[0]; +	} +} + +static const char *media_pad_type_to_string(unsigned flag) +{ +	static const struct { +		__u32 flag; +		const char *name; +	} flags[] = { +		{ MEDIA_PAD_FLAG_INPUT, "Input" }, +		{ MEDIA_PAD_FLAG_OUTPUT, "Output" }, +	}; + +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(flags); i++) { +		if (flags[i].flag & flag) +			return flags[i].name; +	} + +	return "Unknown"; +} + +static void media_print_topology_dot(struct media_device *media) +{ +	unsigned int i, j; + +	printf("digraph board {\n"); +	printf("\trankdir=TB\n"); + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; +		unsigned int npads; + +		switch (media_entity_type(entity)) { +		case MEDIA_ENTITY_TYPE_NODE: +			printf("\tn%08x [label=\"%s\\n%s\", shape=box, style=filled, " +			       "fillcolor=yellow]\n", +			       entity->info.id, entity->info.name, entity->devname); +			break; + +		case MEDIA_ENTITY_TYPE_SUBDEV: +			printf("\tn%08x [label=\"{{", entity->info.id); + +			for (j = 0, npads = 0; j < entity->info.pads; ++j) { +				if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_INPUT)) +					continue; + +				printf("%s<port%u> %u", npads ? " | " : "", j, j); +				npads++; +			} + +			printf("} | %s", entity->info.name); +			if (entity->devname) +				printf("\\n%s", entity->devname); +			printf(" | {"); + +			for (j = 0, npads = 0; j < entity->info.pads; ++j) { +				if (!(entity->pads[j].flags & MEDIA_PAD_FLAG_OUTPUT)) +					continue; + +				printf("%s<port%u> %u", npads ? " | " : "", j, j); +				npads++; +			} + +			printf("}}\", shape=Mrecord, style=filled, fillcolor=green]\n"); +			break; + +		default: +			continue; +		} + +		for (j = 0; j < entity->num_links; j++) { +			struct media_link *link = &entity->links[j]; + +			if (link->source->entity != entity) +				continue; + +			printf("\tn%08x", link->source->entity->info.id); +			if (media_entity_type(link->source->entity) == MEDIA_ENTITY_TYPE_SUBDEV) +				printf(":port%u", link->source->index); +			printf(" -> "); +			printf("n%08x", link->sink->entity->info.id); +			if (media_entity_type(link->sink->entity) == MEDIA_ENTITY_TYPE_SUBDEV) +				printf(":port%u", link->sink->index); + +			if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) +				printf(" [style=bold]"); +			else if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) +				printf(" [style=dashed]"); +			printf("\n"); +		} +	} + +	printf("}\n"); +} + +static void media_print_topology_text(struct media_device *media) +{ +	unsigned int i, j, k; +	unsigned int padding; + +	printf("Device topology\n"); + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; + +		padding = printf("- entity %u: ", entity->info.id); +		printf("%s (%u pad%s, %u link%s)\n", entity->info.name, +			entity->info.pads, entity->info.pads > 1 ? "s" : "", +			entity->info.links, entity->info.links > 1 ? "s" : ""); +		printf("%*ctype %s subtype %s\n", padding, ' ', +			media_entity_type_to_string(entity->info.type), +			media_entity_subtype_to_string(entity->info.type)); +		if (entity->devname[0]) +			printf("%*cdevice node name %s\n", padding, ' ', entity->devname); + +		for (j = 0; j < entity->info.pads; j++) { +			struct media_pad *pad = &entity->pads[j]; + +			printf("\tpad%u: %s ", j, media_pad_type_to_string(pad->flags)); + +			if (media_entity_type(entity) == MEDIA_ENTITY_TYPE_SUBDEV) +				v4l2_subdev_print_format(entity, j, V4L2_SUBDEV_FORMAT_ACTIVE); + +			printf("\n"); + +			for (k = 0; k < entity->num_links; k++) { +				struct media_link *link = &entity->links[k]; + +				if (link->source->entity != entity || +				    link->source->index != j) +					continue; + +				printf("\t\t-> '%s':pad%u [", +					link->sink->entity->info.name, link->sink->index); + +				if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE) +					printf("IMMUTABLE,"); +				if (link->flags & MEDIA_LINK_FLAG_ACTIVE) +					printf("ACTIVE"); + +				printf("]\n"); +			} +		} +		printf("\n"); +	} +} + +void media_print_topology(struct media_device *media, int dot) +{ +	if (dot) +		media_print_topology_dot(media); +	else +		media_print_topology_text(media); +} + +/* ----------------------------------------------------------------------------- + * Links setup + */ + +static struct media_pad *parse_pad(struct media_device *media, const char *p, char **endp) +{ +	unsigned int entity_id, pad; +	struct media_entity *entity; +	char *end; + +	for (; isspace(*p); ++p); + +	if (*p == '"') { +		for (end = (char *)p + 1; *end && *end != '"'; ++end); +		if (*end != '"') +			return NULL; + +		entity = media_get_entity_by_name(media, p + 1, end - p - 1); +		if (entity == NULL) +			return NULL; + +		++end; +	} else { +		entity_id = strtoul(p, &end, 10); +		entity = media_get_entity_by_id(media, entity_id); +		if (entity == NULL) +			return NULL; +	} +	for (; isspace(*end); ++end); + +	if (*end != ':') +		return NULL; +	for (p = end + 1; isspace(*p); ++p); + +	pad = strtoul(p, &end, 10); +	for (p = end; isspace(*p); ++p); + +	if (pad >= entity->info.pads) +		return NULL; + +	for (p = end; isspace(*p); ++p); +	if (endp) +		*endp = (char *)p; + +	return &entity->pads[pad]; +} + +static struct media_link *parse_link(struct media_device *media, const char *p, char **endp) +{ +	struct media_link *link; +	struct media_pad *source; +	struct media_pad *sink; +	unsigned int i; +	char *end; + +	source = parse_pad(media, p, &end); +	if (source == NULL) +		return NULL; + +	if (end[0] != '-' || end[1] != '>') +		return NULL; +	p = end + 2; + +	sink = parse_pad(media, p, &end); +	if (sink == NULL) +		return NULL; + +	*endp = end; + +	for (i = 0; i < source->entity->num_links; i++) { +		link = &source->entity->links[i]; + +		if (link->source == source && link->sink == sink) +			return link; +	} + +	return NULL; +} + +static int setup_link(struct media_device *media, const char *p, char **endp) +{ +	struct media_link *link; +	__u32 flags; +	char *end; + +	link = parse_link(media, p, &end); +	if (link == NULL) { +		printf("Unable to parse link\n"); +		return -EINVAL; +	} + +	p = end; +	if (*p++ != '[') { +		printf("Unable to parse link flags\n"); +		return -EINVAL; +	} + +	flags = strtoul(p, &end, 10); +	for (p = end; isspace(*p); p++); +	if (*p++ != ']') { +		printf("Unable to parse link flags\n"); +		return -EINVAL; +	} + +	for (; isspace(*p); p++); +	*endp = (char *)p; + +	printf("Setting up link %u:%u -> %u:%u [%u]\n", +		link->source->entity->info.id, link->source->index, +		link->sink->entity->info.id, link->sink->index, +		flags); + +	return media_setup_link(media, link->source, link->sink, flags); +} + +static int setup_links(struct media_device *media, const char *p) +{ +	char *end; +	int ret; + +	do { +		ret = setup_link(media, p, &end); +		if (ret < 0) +			return ret; + +		p = end + 1; +	} while (*end == ','); + +	return *end ? -EINVAL : 0; +} + +/* ----------------------------------------------------------------------------- + * Formats setup + */ + +static int parse_format(struct v4l2_mbus_framefmt *format, const char *p, char **endp) +{ +	enum v4l2_mbus_pixelcode code; +	unsigned int width, height; +	char *end; + +	for (; isspace(*p); ++p); +	for (end = (char *)p; !isspace(*end) && *end != '\0'; ++end); + +	code = string_to_pixelcode(p, end - p); +	if (code == (enum v4l2_mbus_pixelcode)-1) +		return -EINVAL; + +	for (p = end; isspace(*p); ++p); +	width = strtoul(p, &end, 10); +	if (*end != 'x') +		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; +} + +static int parse_crop(struct v4l2_rect *crop, const char *p, char **endp) +{ +	char *end; + +	if (*p++ != '(') +		return -EINVAL; + +	crop->left = strtoul(p, &end, 10); +	if (*end != ',') +		return -EINVAL; + +	p = end + 1; +	crop->top = strtoul(p, &end, 10); +	if (*end++ != ')') +		return -EINVAL; +	if (*end != '/') +		return -EINVAL; + +	p = end + 1; +	crop->width = strtoul(p, &end, 10); +	if (*end != 'x') +		return -EINVAL; + +	p = end + 1; +	crop->height = strtoul(p, &end, 10); +	*endp = end; + +	return 0; +} + +static int parse_frame_interval(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++ != '/') +		return -EINVAL; + +	for (; isspace(*p); ++p); +	interval->denominator = strtoul(p, &end, 10); + +	*endp = end; +	return 0; +} + +static struct media_pad *parse_pad_format(struct media_device *media, +	struct v4l2_mbus_framefmt *format, struct v4l2_rect *crop, +	struct v4l2_fract *interval, const char *p, char **endp) +{ +	struct media_pad *pad; +	char *end; +	int ret; + +	for (; isspace(*p); ++p); + +	pad = parse_pad(media, p, &end); +	if (pad == NULL) +		return NULL; + +	for (p = end; isspace(*p); ++p); +	if (*p++ != '[') +		return NULL; + +	for (; isspace(*p); ++p); + +	if (isalnum(*p)) { +		ret = parse_format(format, p, &end); +		if (ret < 0) +			return NULL; + +		for (p = end; isspace(*p); p++); +	} + +	if (*p == '(') { +		ret = parse_crop(crop, p, &end); +		if (ret < 0) +			return NULL; + +		for (p = end; isspace(*p); p++); +	} + +	if (*p == '@') { +		ret = parse_frame_interval(interval, ++p, &end); +		if (ret < 0) +			return NULL; + +		for (p = end; isspace(*p); p++); +	} + +	if (*p != ']') +		return NULL; + +	*endp = (char *)p + 1; +	return pad; +} + +static int set_format(struct media_pad *pad, struct v4l2_mbus_framefmt *format) +{ +	int ret; + +	if (format->width == 0 || format->height == 0) +		return 0; + +	printf("Setting up format %s %ux%u on pad %s/%u\n", +		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) { +		printf("Unable to set format: %s (%d)\n", strerror(-ret), ret); +		return ret; +	} + +	printf("Format set: %s %ux%u\n", +		pixelcode_to_string(format->code), format->width, format->height); + +	return 0; +} + +static int set_crop(struct media_pad *pad, struct v4l2_rect *crop) +{ +	int ret; + +	if (crop->left == -1 || crop->top == -1) +		return 0; + +	printf("Setting up crop rectangle (%u,%u)/%ux%u on pad %s/%u\n", +		crop->left, crop->top, crop->width, crop->height, +		pad->entity->info.name, pad->index); + +	ret = v4l2_subdev_set_crop(pad->entity, crop, pad->index, +				   V4L2_SUBDEV_FORMAT_ACTIVE); +	if (ret < 0) { +		printf("Unable to set crop rectangle: %s (%d)\n", strerror(-ret), ret); +		return ret; +	} + +	printf("Crop rectangle set: (%u,%u)/%ux%u\n", +		crop->left, crop->top, crop->width, crop->height); + +	return 0; +} + +static int set_frame_interval(struct media_entity *entity, struct v4l2_fract *interval) +{ +	int ret; + +	if (interval->numerator == 0) +		return 0; + +	printf("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) { +		printf("Unable to set frame interval: %s (%d)", strerror(-ret), ret); +		return ret; +	} + +	printf("Frame interval set: %u/%u\n", +		interval->numerator, interval->denominator); + +	return 0; +} + + +static int setup_format(struct media_device *media, const char *p, char **endp) +{ +	struct v4l2_mbus_framefmt format = { 0, 0, 0 }; +	struct media_pad *pad; +	struct v4l2_rect crop = { -1, -1, -1, -1 }; +	struct v4l2_fract interval = { 0, 0 }; +	unsigned int i; +	char *end; +	int ret; + +	pad = parse_pad_format(media, &format, &crop, &interval, p, &end); +	if (pad == NULL) { +		printf("Unable to parse format\n"); +		return -EINVAL; +	} + +	if (pad->flags & MEDIA_PAD_FLAG_OUTPUT) { +		ret = set_crop(pad, &crop); +		if (ret < 0) +			return ret; +	} + +	ret = set_format(pad, &format); +	if (ret < 0) +		return ret; + +	if (pad->flags & MEDIA_PAD_FLAG_INPUT) { +		ret = set_crop(pad, &crop); +		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_FLAG_OUTPUT) { +		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_LINK_FLAG_ACTIVE)) +				continue; + +			if (link->source == pad && +			    link->sink->entity->info.type == MEDIA_ENTITY_TYPE_SUBDEV) { +				remote_format = format; +				set_format(link->sink, &remote_format); +			} +		} +	} + +	*endp = end; +	return 0; +} + +static int setup_formats(struct media_device *media, const char *p) +{ +	char *end; +	int ret; + +	do { +		ret = setup_format(media, p, &end); +		if (ret < 0) +			return ret; + +		p = end + 1; +	} while (*end == ','); + +	return *end ? -EINVAL : 0; +} + +int main(int argc, char **argv) +{ +	struct media_device *media; +	int ret = -1; + +	if (parse_cmdline(argc, argv)) +		return EXIT_FAILURE; + +	/* Open the media device and enumerate entities, pads and links. */ +	media = media_open(media_opts.devname, media_opts.verbose); +	if (media == NULL) +		goto out; + +	if (media_opts.entity) { +		struct media_entity *entity; + +		entity = media_get_entity_by_name(media, media_opts.entity, +						  strlen(media_opts.entity)); +		if (entity == NULL) { +			printf("Entity '%s' not found\n", media_opts.entity); +			goto out; +		} + +		printf("%s\n", entity->devname); +	} + +	if (media_opts.pad) { +		struct media_pad *pad; + +		pad = parse_pad(media, media_opts.pad, NULL); +		if (pad == NULL) { +			printf("Pad '%s' not found\n", media_opts.pad); +			goto out; +		} + +		v4l2_subdev_print_format(pad->entity, pad->index, +					 V4L2_SUBDEV_FORMAT_ACTIVE); +		printf("\n"); +	} + +	if (media_opts.print || media_opts.print_dot) { +		media_print_topology(media, media_opts.print_dot); +		printf("\n"); +	} + +	if (media_opts.reset) { +		printf("Resetting all links to inactive\n"); +		media_reset_links(media); +	} + +	if (media_opts.links) +		setup_links(media, media_opts.links); + +	if (media_opts.formats) +		setup_formats(media, media_opts.formats); + +	if (media_opts.interactive) { +		while (1) { +			char buffer[32]; +			char *end; + +			printf("Enter a link to modify or enter to stop\n"); +			if (fgets(buffer, sizeof buffer, stdin) == NULL) +				break; + +			if (buffer[0] == '\n') +				break; + +			setup_link(media, buffer, &end); +		} +	} + +	ret = 0; + +out: +	if (media) +		media_close(media); + +	return ret ? EXIT_FAILURE : EXIT_SUCCESS; +} + diff --git a/src/media.c b/src/media.c new file mode 100644 index 0000000..456b882 --- /dev/null +++ b/src/media.c @@ -0,0 +1,388 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +#include <linux/videodev2.h> +#include <linux/media.h> + +#include "media.h" +#include "tools.h" + +struct media_pad *media_entity_remote_source(struct media_pad *pad) +{ +	unsigned int i; + +	if (!(pad->flags & MEDIA_PAD_FLAG_INPUT)) +		return NULL; + +	for (i = 0; i < pad->entity->num_links; ++i) { +		struct media_link *link = &pad->entity->links[i]; + +		if (!(link->flags & MEDIA_LINK_FLAG_ACTIVE)) +			continue; + +		if (link->sink == pad) +			return link->source; +	} + +	return NULL; +} + +struct media_entity *media_get_entity_by_name(struct media_device *media, +					      const char *name, size_t length) +{ +	unsigned int i; + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; + +		if (strncmp(entity->info.name, name, length) == 0) +			return entity; +	} + +	return NULL; +} + +struct media_entity *media_get_entity_by_id(struct media_device *media, +					    __u32 id) +{ +	unsigned int i; + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; + +		if (entity->info.id == id) +			return entity; +	} + +	return NULL; +} + +int media_setup_link(struct media_device *media, +		     struct media_pad *source, +		     struct media_pad *sink, +		     __u32 flags) +{ +	struct media_link *link; +	struct media_link_desc ulink; +	unsigned int i; +	int ret; + +	for (i = 0; i < source->entity->num_links; i++) { +		link = &source->entity->links[i]; + +		if (link->source->entity == source->entity && +		    link->source->index == source->index && +		    link->sink->entity == sink->entity && +		    link->sink->index == sink->index) +			break; +	} + +	if (i == source->entity->num_links) { +		printf("%s: Link not found\n", __func__); +		return -EINVAL; +	} + +	/* source pad */ +	ulink.source.entity = source->entity->info.id; +	ulink.source.index = source->index; +	ulink.source.flags = MEDIA_PAD_FLAG_OUTPUT; + +	/* sink pad */ +	ulink.sink.entity = sink->entity->info.id; +	ulink.sink.index = sink->index; +	ulink.sink.flags = MEDIA_PAD_FLAG_INPUT; + +	ulink.flags = flags | (link->flags & MEDIA_LINK_FLAG_IMMUTABLE); + +	ret = ioctl(media->fd, MEDIA_IOC_SETUP_LINK, &ulink); +	if (ret < 0) { +		printf("%s: Unable to setup link (%s)\n", __func__, +			strerror(errno)); +		return ret; +	} + +	link->flags = flags; +	return 0; +} + +int media_reset_links(struct media_device *media) +{ +	unsigned int i, j; +	int ret; + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; + +		for (j = 0; j < entity->num_links; j++) { +			struct media_link *link = &entity->links[j]; + +			if (link->flags & MEDIA_LINK_FLAG_IMMUTABLE || +			    link->source->entity != entity) +				continue; + +			ret = media_setup_link(media, link->source, link->sink, +					       link->flags & ~MEDIA_LINK_FLAG_ACTIVE); +			if (ret < 0) +				return ret; +		} +	} + +	return 0; +} + +static struct media_link *media_entity_add_link(struct media_entity *entity) +{ +	if (entity->num_links >= entity->max_links) { +		struct media_link *links = entity->links; +		unsigned int max_links = entity->max_links * 2; +		unsigned int i; + +		links = realloc(links, max_links * sizeof *links); +		if (links == NULL) +			return NULL; + +		for (i = 0; i < entity->num_links; ++i) +			links[i].twin->twin = &links[i]; + +		entity->max_links = max_links; +		entity->links = links; +	} + +	return &entity->links[entity->num_links++]; +} + +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) { +			printf("%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) { +				printf("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 = links.links[i].flags; + +				backlink = media_entity_add_link(sink); +				backlink->source = &source->pads[link->source.index]; +				backlink->sink = &sink->pads[link->sink.index]; +				backlink->flags = links.links[i].flags; + +				fwdlink->twin = backlink; +				backlink->twin = fwdlink; +			} +		} + +		free(links.pads); +		free(links.links); +	} + +	return ret; +} + +static int media_enum_entities(struct media_device *media) +{ +	struct media_entity *entity; +	struct stat devstat; +	unsigned int size; +	char devname[32]; +	char sysname[32]; +	char target[1024]; +	char *p; +	__u32 id; +	int ret; + +	for (id = 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_ENTITY_ID_FLAG_NEXT; + +		ret = ioctl(media->fd, MEDIA_IOC_ENUM_ENTITIES, &entity->info); +		if (ret < 0) { +			if (errno == EINVAL) +				break; +			return -errno; +		} + +		/* 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) +			return -ENOMEM; + +		media->entities_count++; + +		/* Find the corresponding device name. */ +		if (media_entity_type(entity) != MEDIA_ENTITY_TYPE_NODE && +		    media_entity_type(entity) != MEDIA_ENTITY_TYPE_SUBDEV) +			continue; + +		sprintf(sysname, "/sys/dev/char/%u:%u", entity->info.v4l.major, +			entity->info.v4l.minor); +		ret = readlink(sysname, target, sizeof(target)); +		if (ret < 0) +			continue; + +		target[ret] = '\0'; +		p = strrchr(target, '/'); +		if (p == NULL) +			continue; + +		sprintf(devname, "/dev/%s", p + 1); +		ret = stat(devname, &devstat); +		if (ret < 0) +			continue; + +		/* Sanity check: udev might have reordered the device nodes. +		 * Make sure the major/minor match. We should really use +		 * libudev. +		 */ +		if (major(devstat.st_rdev) == entity->info.v4l.major && +		    minor(devstat.st_rdev) == entity->info.v4l.minor) +			strcpy(entity->devname, devname); +	} + +	return 0; +} + +struct media_device *media_open(const char *name, int verbose) +{ +	struct media_device *media; +	int ret; + +	media = malloc(sizeof(*media)); +	if (media == NULL) { +		printf("%s: unable to allocate memory\n", __func__); +		return NULL; +	} +	memset(media, 0, sizeof(*media)); + +	if (verbose) +		printf("Opening media device %s\n", name); +	media->fd = open(name, O_RDWR); +	if (media->fd < 0) { +		media_close(media); +		printf("%s: Can't open media device %s\n", __func__, name); +		return NULL; +	} + +	if (verbose) +		printf("Enumerating entities\n"); + +	ret = media_enum_entities(media); +	if (ret < 0) { +		printf("%s: Unable to enumerate entities for device %s (%s)\n", +			__func__, name, strerror(-ret)); +		media_close(media); +		return NULL; +	} + +	if (verbose) { +		printf("Found %u entities\n", media->entities_count); +		printf("Enumerating pads and links\n"); +	} + +	ret = media_enum_links(media); +	if (ret < 0) { +		printf("%s: Unable to enumerate pads and linksfor device %s\n", +			__func__, name); +		media_close(media); +		return NULL; +	} + +	return media; +} + +void media_close(struct media_device *media) +{ +	unsigned int i; + +	if (media->fd != -1) +		close(media->fd); + +	for (i = 0; i < media->entities_count; ++i) { +		struct media_entity *entity = &media->entities[i]; + +		free(entity->pads); +		free(entity->links); +		if (entity->fd != -1) +			close(entity->fd); +	} + +	free(media->entities); +	free(media); +} + diff --git a/src/media.h b/src/media.h new file mode 100644 index 0000000..80cf976 --- /dev/null +++ b/src/media.h @@ -0,0 +1,161 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __MEDIA_H__ +#define __MEDIA_H__ + +#include <linux/media.h> + +struct media_link { +	struct media_pad *source; +	struct media_pad *sink; +	struct media_link *twin; +	__u32 flags; +	__u32 padding[3]; +}; + +struct media_pad { +	struct media_entity *entity; +	__u32 index; +	__u32 flags; +	__u32 padding[3]; +}; + +struct media_entity { +	struct media_entity_desc info; +	struct media_pad *pads; +	struct media_link *links; +	unsigned int max_links; +	unsigned int num_links; + +	char devname[32]; +	int fd; +	__u32 padding[6]; +}; + +struct media_device { +	int fd; +	struct media_entity *entities; +	unsigned int entities_count; +	__u32 padding[6]; +}; + +/** + * @brief Open a media device. + * @param name - name (including path) of the device node. + * @param verbose - whether to print verbose information on the standard output. + * + * Open the media device referenced by @a name and enumerate entities, pads and + * links. + * + * @return A pointer to a newly allocated media_device structure instance on + * success and NULL on failure. The returned pointer must be freed with + * media_close when the device isn't needed anymore. + */ +struct media_device *media_open(const char *name, int verbose); + +/** + * @brief Close a media device. + * @param media - device instance. + * + * Close the @a media device instance and free allocated resources. Access to the + * device instance is forbidden after this function returns. + */ +void media_close(struct media_device *media); + +/** + * @brief Locate the pad at the other end of a link. + * @param pad - sink pad at one end of the link. + * + * Locate the source pad connected to @a pad through an enabled link. As only one + * link connected to a sink pad can be enabled at a time, the connected source + * pad is guaranteed to be unique. + * + * @return A pointer to the connected source pad, or NULL if all links connected + * to @a pad are disabled. Return NULL also if @a pad is not a sink pad. + */ +struct media_pad *media_entity_remote_source(struct media_pad *pad); + +/** + * @brief Get the type of an entity. + * @param entity - the entity. + * + * @return The type of @a entity. + */ +static inline unsigned int media_entity_type(struct media_entity *entity) +{ +	return entity->info.type & MEDIA_ENTITY_TYPE_MASK; +} + +/** + * @brief Find an entity by its name. + * @param media - media device. + * @param name - entity name. + * @param length - size of @a name. + * + * Search for an entity with a name equal to @a name. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *media_get_entity_by_name(struct media_device *media, +	const char *name, size_t length); + +/** + * @brief Find an entity by its ID. + * @param media - media device. + * @param id - entity ID. + * + * Search for an entity with an ID equal to @a id. + * + * @return A pointer to the entity if found, or NULL otherwise. + */ +struct media_entity *media_get_entity_by_id(struct media_device *media, +	__u32 id); + +/** + * @brief Configure a link. + * @param media - media device. + * @param source - source pad at the link origin. + * @param sink - sink pad at the link target. + * @param flags - configuration flags. + * + * Locate the link between @a source and @a sink, and configure it by applying + * the new @a flags. + * + * Only the MEDIA_LINK_FLAG_ENABLED flag is writable. + * + * @return 0 on success, or a negative error code on failure. + */ +int media_setup_link(struct media_device *media, +	struct media_pad *source, struct media_pad *sink, +	__u32 flags); + +/** + * @brief Reset all links to the disabled state. + * @param media - media device. + * + * Disable all links in the media device. This function is usually used after + * opening a media device to reset all links to a known state. + * + * @return 0 on success, or a negative error code on failure. + */ +int media_reset_links(struct media_device *media); + +#endif + diff --git a/src/options.c b/src/options.c new file mode 100644 index 0000000..186425e --- /dev/null +++ b/src/options.c @@ -0,0 +1,155 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include <stdlib.h> +#include <getopt.h> +#include <stdio.h> +#include <unistd.h> + +#include "options.h" + +#define MEDIA_DEVNAME_DEFAULT		"/dev/media0" + +struct media_options media_opts = { +	.devname = MEDIA_DEVNAME_DEFAULT, +}; + +static void usage(const char *argv0, int verbose) +{ +	printf("%s [options] device\n", argv0); +	printf("-d, --device dev	Media device name (default: %s)\n", MEDIA_DEVNAME_DEFAULT); +	printf("-e, --entity name	Print the device name associated with the given entity\n"); +	printf("-f, --set-format	Comma-separated list of formats to setup\n"); +	printf("    --get-format pad	Print the active format on a given pad\n"); +	printf("-h, --help		Show verbose help and exit\n"); +	printf("-i, --interactive	Modify links interactively\n"); +	printf("-l, --links		Comma-separated list of links descriptors to setup\n"); +	printf("-p, --print-topology	Print the device topology (implies -v)\n"); +	printf("    --print-dot		Print the device topology as a dot graph (implies -v)\n"); +	printf("-r, --reset		Reset all links to inactive\n"); +	printf("-v, --verbose		Be verbose\n"); + +	if (!verbose) +		return; + +	printf("\n"); +	printf("Links and formats are defined as\n"); +	printf("\tlink            = pad, '->', pad, '[', flags, ']' ;\n"); +	printf("\tformat          = pad, '[', fcc, ' ', size, [ ' ', crop ], [ ' ', '@', frame interval ], ']' ;\n"); +	printf("\tpad             = entity, ':', pad number ;\n"); +	printf("\tentity          = entity number | ( '\"', entity name, '\"' ) ;\n"); +	printf("\tsize            = width, 'x', height ;\n"); +	printf("\tcrop            = '(', left, ',', top, ')', '/', size ;\n"); +	printf("\tframe interval  = numerator, '/', denominator ;\n"); +	printf("where the fields are\n"); +	printf("\tentity number   Entity numeric identifier\n"); +	printf("\tentity name     Entity name (string) \n"); +	printf("\tpad number      Pad numeric identifier\n"); +	printf("\tflags           Link flags (0: inactive, 1: active)\n"); +	printf("\tfcc             Format FourCC\n"); +	printf("\twidth           Image width in pixels\n"); +	printf("\theight          Image height in pixels\n"); +	printf("\tnumerator       Frame interval numerator\n"); +	printf("\tdenominator     Frame interval denominator\n"); +} + +#define OPT_PRINT_DOT		256 +#define OPT_GET_FORMAT		257 + +static struct option opts[] = { +	{"device", 1, 0, 'd'}, +	{"entity", 1, 0, 'e'}, +	{"set-format", 1, 0, 'f'}, +	{"get-format", 1, 0, OPT_GET_FORMAT}, +	{"help", 0, 0, 'h'}, +	{"interactive", 0, 0, 'i'}, +	{"links", 1, 0, 'l'}, +	{"print-dot", 0, 0, OPT_PRINT_DOT}, +	{"print-topology", 0, 0, 'p'}, +	{"reset", 0, 0, 'r'}, +	{"verbose", 0, 0, 'v'}, +}; + +int parse_cmdline(int argc, char **argv) +{ +	int opt; + +	if (argc == 1) { +		usage(argv[0], 0); +		return 1; +	} + +	/* parse options */ +	while ((opt = getopt_long(argc, argv, "d:e:f:hil:prv", opts, NULL)) != -1) { +		switch (opt) { +		case 'd': +			media_opts.devname = optarg; +			break; + +		case 'e': +			media_opts.entity = optarg; +			break; + +		case 'f': +			media_opts.formats = optarg; +			break; + +		case 'h': +			usage(argv[0], 1); +			exit(0); + +		case 'i': +			media_opts.interactive = 1; +			break; + +		case 'l': +			media_opts.links = optarg; +			break; + +		case 'p': +			media_opts.print = 1; +			media_opts.verbose = 1; +			break; + +		case 'r': +			media_opts.reset = 1; +			break; + +		case 'v': +			media_opts.verbose = 1; +			break; + +		case OPT_PRINT_DOT: +			media_opts.print_dot = 1; +			break; + +		case OPT_GET_FORMAT: +			media_opts.pad = optarg; +			break; + +		default: +			printf("Invalid option -%c\n", opt); +			printf("Run %s -h for help.\n", argv[0]); +			return 1; +		} +	} + +	return 0; +} + diff --git a/src/options.h b/src/options.h new file mode 100644 index 0000000..2467fbe --- /dev/null +++ b/src/options.h @@ -0,0 +1,42 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __OPTIONS_H +#define __OPTIONS_H + +struct media_options +{ +	const char *devname; +	unsigned int interactive:1, +		     print:1, +		     print_dot:1, +		     reset:1, +		     verbose:1; +	const char *entity; +	const char *formats; +	const char *links; +	const char *pad; +}; + +extern struct media_options media_opts; + +extern int parse_cmdline(int argc, char **argv); + +#endif /* __OPTIONS_H */ + diff --git a/src/subdev.c b/src/subdev.c new file mode 100644 index 0000000..3960d05 --- /dev/null +++ b/src/subdev.c @@ -0,0 +1,187 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <linux/v4l2-subdev.h> + +#include "media.h" +#include "subdev.h" +#include "tools.h" + +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) { +		printf("%s: Failed to open subdev device node %s\n", __func__, +			entity->devname); +		return -errno; +	} + +	return 0; +} + +void v4l2_subdev_close(struct media_entity *entity) +{ +	close(entity->fd); +} + +int v4l2_subdev_get_format(struct media_entity *entity, +	struct v4l2_mbus_framefmt *format, unsigned int pad, +	enum v4l2_subdev_format_whence which) +{ +	struct v4l2_subdev_format fmt; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&fmt, 0, sizeof(fmt)); +	fmt.pad = pad; +	fmt.which = which; + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FMT, &fmt); +	if (ret < 0) +		return -errno; + +	*format = fmt.format; +	return 0; +} + +int v4l2_subdev_set_format(struct media_entity *entity, +	struct v4l2_mbus_framefmt *format, unsigned int pad, +	enum v4l2_subdev_format_whence which) +{ +	struct v4l2_subdev_format fmt; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&fmt, 0, sizeof(fmt)); +	fmt.pad = pad; +	fmt.which = which; +	fmt.format = *format; + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FMT, &fmt); +	if (ret < 0) +		return -errno; + +	*format = fmt.format; +	return 0; +} + +int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, +			 unsigned int pad, enum v4l2_subdev_format_whence which) +{ +	struct v4l2_subdev_crop crop; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&crop, 0, sizeof(crop)); +	crop.pad = pad; +	crop.which = which; + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_CROP, &crop); +	if (ret < 0) +		return -errno; + +	*rect = crop.rect; +	return 0; +} + +int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, +			 unsigned int pad, enum v4l2_subdev_format_whence which) +{ +	struct v4l2_subdev_crop crop; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&crop, 0, sizeof(crop)); +	crop.pad = pad; +	crop.which = which; +	crop.rect = *rect; + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_CROP, &crop); +	if (ret < 0) +		return -errno; + +	*rect = crop.rect; +	return 0; +} + +int v4l2_subdev_get_frame_interval(struct media_entity *entity, +				   struct v4l2_fract *interval) +{ +	struct v4l2_subdev_frame_interval ival; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&ival, 0, sizeof(ival)); + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_G_FRAME_INTERVAL, &ival); +	if (ret < 0) +		return -errno; + +	*interval = ival.interval; +	return 0; +} + +int v4l2_subdev_set_frame_interval(struct media_entity *entity, +				   struct v4l2_fract *interval) +{ +	struct v4l2_subdev_frame_interval ival; +	int ret; + +	ret = v4l2_subdev_open(entity); +	if (ret < 0) +		return ret; + +	memset(&ival, 0, sizeof(ival)); +	ival.interval = *interval; + +	ret = ioctl(entity->fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &ival); +	if (ret < 0) +		return -errno; + +	*interval = ival.interval; +	return 0; +} diff --git a/src/subdev.h b/src/subdev.h new file mode 100644 index 0000000..b5772e0 --- /dev/null +++ b/src/subdev.h @@ -0,0 +1,162 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + */ + +#ifndef __SUBDEV_H__ +#define __SUBDEV_H__ + +#include <linux/v4l2-subdev.h> + +struct media_entity; + +/** + * @brief Open a sub-device. + * @param entity - sub-device media entity. + * + * Open the V4L2 subdev device node associated with @a entity. The file + * descriptor is stored in the media_entity structure. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_open(struct media_entity *entity); + +/** + * @brief Close a sub-device. + * @param entity - sub-device media entity. + * + * Close the V4L2 subdev device node associated with the @a entity and opened by + * a previous call to v4l2_subdev_open() (either explicit or implicit). + */ +void v4l2_subdev_close(struct media_entity *entity); + +/** + * @brief Retrieve the format on a pad. + * @param entity - subdev-device media entity. + * @param format - format to be filled. + * @param pad - pad number. + * @param which - identifier of the format to get. + * + * Retrieve the current format on the @a entity @a pad and store it in the + * @a format structure. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try format stored + * in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the current + * active format. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_get_format(struct media_entity *entity, +	struct v4l2_mbus_framefmt *format, unsigned int pad, +	enum v4l2_subdev_format_whence which); + +/** + * @brief Set the format on a pad. + * @param entity - subdev-device media entity. + * @param format - format. + * @param pad - pad number. + * @param which - identifier of the format to set. + * + * Set the format on the @a entity @a pad to @a format. The driver is allowed to + * modify the requested format, in which case @a format is updated with the + * modifications. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try format stored in the + * file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the device with an + * active format. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_format(struct media_entity *entity, +	struct v4l2_mbus_framefmt *format, unsigned int pad, +	enum v4l2_subdev_format_whence which); + +/** + * @brief Retrieve the crop rectangle on a pad. + * @param entity - subdev-device media entity. + * @param rect - crop rectangle to be filled. + * @param pad - pad number. + * @param which - identifier of the format to get. + * + * Retrieve the current crop rectangleon the @a entity @a pad and store it in + * the @a rect structure. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to retrieve the try crop rectangle + * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to retrieve the + * current active crop rectangle. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_get_crop(struct media_entity *entity, struct v4l2_rect *rect, +	unsigned int pad, enum v4l2_subdev_format_whence which); + +/** + * @brief Set the crop rectangle on a pad. + * @param entity - subdev-device media entity. + * @param rect - crop rectangle. + * @param pad - pad number. + * @param which - identifier of the format to set. + * + * Set the crop rectangle on the @a entity @a pad to @a rect. The driver is + * allowed to modify the requested rectangle, in which case @a rect is updated + * with the modifications. + * + * @a which is set to V4L2_SUBDEV_FORMAT_TRY to set the try crop rectangle + * stored in the file handle, of V4L2_SUBDEV_FORMAT_ACTIVE to configure the + * device with an active crop rectangle. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_crop(struct media_entity *entity, struct v4l2_rect *rect, +	unsigned int pad, enum v4l2_subdev_format_whence which); + +/** + * @brief Retrieve the frame interval on a sub-device. + * @param entity - subdev-device media entity. + * @param interval - frame interval to be filled. + * + * Retrieve the current frame interval on subdev @a entity and store it in the + * @a interval structure. + * + * Frame interval retrieving is usually supported only on devices at the + * beginning of video pipelines, such as sensors. + * + * @return 0 on success, or a negative error code on failure. + */ + +int v4l2_subdev_get_frame_interval(struct media_entity *entity, +	struct v4l2_fract *interval); + +/** + * @brief Set the frame interval on a sub-device. + * @param entity - subdev-device media entity. + * @param interval - frame interval. + * + * Set the frame interval on subdev @a entity to @a interval. The driver is + * allowed to modify the requested frame interval, in which case @a interval is + * updated with the modifications. + * + * Frame interval setting is usually supported only on devices at the beginning + * of video pipelines, such as sensors. + * + * @return 0 on success, or a negative error code on failure. + */ +int v4l2_subdev_set_frame_interval(struct media_entity *entity, +	struct v4l2_fract *interval); + +#endif + diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..0a0a948 --- /dev/null +++ b/src/tools.h @@ -0,0 +1,27 @@ +/* + * Media controller test application + * + * Copyright (C) 2010 Ideas on board SPRL <laurent.pinchart@ideasonboard.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __TOOLS_H__ +#define __TOOLS_H__ + +#define ARRAY_SIZE(array)	(sizeof(array) / sizeof((array)[0])) + +#endif /* __TOOLS_H__ */ + | 
