OMAP3 ISP: VIDIOC_STREAMON and VIDIOC_QBUF calls fail

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

in order to familiarize myself with Media Controller and V4L2 I am creating a small example program for capturing some frames through the OMAP3 ISP. The hardware used is a TAO-3530 on a Tsunami daughterboard from Technexion. My video source is a standard DVD player connected to the daughterboards S-VIDEO port. That port itself is wired to a TVP5146 decoder chip from TI. A precompiled Android image with a demo app proofs, that the hardware is working fine.

My example program is mostly based on the following wiki page and the capture example in the V4L2 documentation.
http://processors.wiki.ti.com/index.php/Writing_V4L2_Media_Controller_Applications_on_Dm36x_Video_Capture

My code sets up the ISP pipeline, configures the format on all the subdevices pads and the actual video device. Works fine so far. Then I passed user pointers (aquired with malloc) to the device driver for the capture buffers. Before issuing VIDIOC_STREAMON, I enqueue my buffers with VIDIOC_QBUF, which fails with errno = EIO. I don't know, why this is happening or where to got from here.

When using memory-mapped buffers instead, mapping the addresses to userspace works fine as well as VIDIOC_QBUF calls. But then VIDIOC_STREAMON fails with EINVAL. According to V4L documentation, EINVAL means
a) buffertype (V4L2_BUF_TYPE_VIDEO_CAPTURE in this case) not supported
b) no buffers have been allocated (memory mapping)
c) or enqueued yet

Because I tested V4L2_CAP_VIDEO_CAPTURE capability, I guess option a) does not apply. Buffers have been enqueud, so c) doesn't apply either. What about b) ? As I chose memory-mapped buffers here, the device drivers manages the buffers. How can I make sure, that buffers were actually allocated?

And am I missing something else?

I attached my example code. If you need more information, I will provide it.

Note: I have to use the Technexion 2.6.37 kernel, which is based on the TI kernel. It's the only kernel that comes with the ISP driver and Media Controller API onboard and I guess, TI or TN included this stuff somehow. Normally, this shouldn't be available until 2.6.39. Sadly, I cannot use another kernel, because Technexion doesn't push board support anywhere.


Best regards,
Andreas



/*
 * capture.c
 *
 *  Created on: 29.10.2012
 *      Author: andreas
 */

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/media.h>
#include <linux/v4l2-subdev.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>


#include "capture.h"

#define CLEAR(x) memset(&x, 0, sizeof(x))

int main(void) {

	/*
	 * Open media device.
	 */
	int media_fd = open(DEVNODE_ISP, O_RDWR);
	if (media_fd < 0) {
		puts("Can't open media device.");
		return -1;
	}

	/*
	 * Get the required entities.
	 */
	entities_t entities;
	CLEAR(entities);

	if (get_entities(media_fd, &entities) != 0) {
		puts("Required entity IDs could not be determined.");
		return -1;
	}

	/*
	 * Connect tvp --> ccdc
	 */
	struct media_link_desc link;
	CLEAR(link);

	link.flags |= MEDIA_LINK_FLAG_ENABLED;
	link.source.entity = entities.tvp514x;
	link.source.index = PAD_TVP514X;
	link.source.flags = MEDIA_PAD_FLAG_OUTPUT;
	link.sink.entity = entities.ccdc;
	link.sink.index = PAD_CCDC_SINK;
	link.sink.flags = MEDIA_PAD_FLAG_INPUT;

	if (ioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) == 0) {
		puts("tvp --> ccdc: enabled");
	}
	else {
		printf("Error setting up link [tvp --> ccdc]. Error %d, %s.\n", errno, strerror(errno));
		return -1;
	}

	/*
	 * Connect ccdc --> ccdc_output
	 */
	CLEAR(link);

	link.flags |= MEDIA_LINK_FLAG_ENABLED;
	link.source.entity = entities.ccdc;
	link.source.index = PAD_CCDC_SOURCE;
	link.source.flags = MEDIA_PAD_FLAG_OUTPUT;
	link.sink.entity = entities.ccdc_out;
	link.sink.index = PAD_CCDC_OUTPUT;
	link.sink.flags = MEDIA_PAD_FLAG_INPUT;

	if (ioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) == 0) {
		puts("ccdc --> ccdc-out: enabled");
	}
	else {
		printf(	"Error setting up link [ccdc --> ccdc-out]. Error %d, %s.\n",
				errno,
				strerror(errno));
		return -1;
	}

	/*
	 * Open capture device
	 */
	int video_fd = open(DEVNODE_CCDC_OUT, O_RDWR | O_NONBLOCK, 0);
	if (video_fd < 0) {
		puts("Can't open capture device.");
		return -1;
	}

	/*
	 * Check some capabilities.
	 */
	struct v4l2_capability cap;
	printf("Checking device capabilites...");
	if (ioctl(video_fd, VIDIOC_QUERYCAP, &cap) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
	}
	else {
		puts("ok.");
		printf(	"Device %s is a video capture device... %s\n",
				DEVNODE_CCDC_OUT,
				cap.capabilities & V4L2_CAP_VIDEO_CAPTURE ? "yes" : "no");

		printf(	"Device %s supports streaming... %s\n",
				DEVNODE_CCDC_OUT,
				cap.capabilities & V4L2_CAP_STREAMING ? "yes" : "no");
	}

	/*
	 * Setting camera as input
	 */
	struct v4l2_input input;
	CLEAR(input);
	input.type = V4L2_INPUT_TYPE_CAMERA;
	input.index = 70;		// required for the tvp514x decoder chip

	printf("Setting camera input... ");
	if (-1 == ioctl(video_fd, VIDIOC_S_INPUT, &input.index)) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		puts("ok.");
	}

	/*
	 * Set format on TVP output pad.
	 */
	int tvp_fd = open(DEVNODE_TVP514X, O_RDWR);
	if (tvp_fd < 0) {
		puts("Can't open tvp device.");
		return -1;
	}

	struct v4l2_subdev_format fmt;
	CLEAR(fmt);

	fmt.pad = PAD_TVP514X;
	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
	fmt.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
	fmt.format.width = WIDTH;
	fmt.format.height = HEIGHT;
	fmt.format.field = V4L2_FIELD_INTERLACED;

	printf("Setting format on TVP subdev... ");
	if (ioctl(tvp_fd, VIDIOC_SUBDEV_S_FMT, &fmt) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		puts("ok.");
	}

	/*
	 * Set format on CCDC input pad.
	 */
	int ccdc_fd = open(DEVNODE_CCDC, O_RDWR);
	if (ccdc_fd < 0) {
		puts("Can't open CCDC subdev.");
		return -1;
	}

	CLEAR(fmt);
	fmt.pad = PAD_CCDC_SINK;
	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
	fmt.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
	fmt.format.width = WIDTH;
	fmt.format.height = HEIGHT;
	fmt.format.field = V4L2_FIELD_INTERLACED;

	printf("Setting format on CCDC sink pad... ");
	if (ioctl(ccdc_fd, VIDIOC_SUBDEV_S_FMT, &fmt) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		puts("ok.");
	}

	/*
	 * Set format on CCDC output pad.
	 */
	CLEAR(fmt);
	fmt.pad = PAD_CCDC_SOURCE;
	fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
	fmt.format.code = V4L2_MBUS_FMT_UYVY8_2X8;
	fmt.format.width = WIDTH;
	fmt.format.height = HEIGHT;
	fmt.format.colorspace = V4L2_COLORSPACE_SMPTE170M; // <-- ITU BT.601
	fmt.format.field = V4L2_FIELD_INTERLACED;

	printf("Setting format on CCDC source pad... ");
	if (ioctl(ccdc_fd, VIDIOC_SUBDEV_S_FMT, &fmt) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		puts("ok.");
	}

	/*
	 * Set format on video node
	 */
	struct v4l2_format fmtx;
	CLEAR(fmtx);
	fmtx.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmtx.fmt.pix.width = WIDTH;
	fmtx.fmt.pix.height = HEIGHT;
	fmtx.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
	fmtx.fmt.pix.field = V4L2_FIELD_INTERLACED;

	printf("Setting format on video node... ");
	if (ioctl(video_fd, VIDIOC_S_FMT, &fmtx) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		puts("ok.");
	}

	/*
	 * Get pitch (bytes per line).
	 */
	printf("Getting pitch... ");
	if (ioctl(video_fd, VIDIOC_G_FMT, &fmtx) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		printf("%d Bytes.\n", fmtx.fmt.pix.bytesperline);
	}

	/*
	 * Request buffers.
	 */
	enum v4l2_memory io_method = V4L2_MEMORY_MMAP;

	struct v4l2_requestbuffers req;
	CLEAR(req);
	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	req.count = NUM_BUFS;
	req.memory = io_method;

	printf("Request buffers (%s)... ", io_method == V4L2_MEMORY_MMAP ? "memory-mapped" : "userptr");
	if (ioctl(video_fd, VIDIOC_REQBUFS, &req) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
		return -1;
	}
	else {
		printf("ok. Got %d buffers.\n", req.count);
	}

	/*
	 * Query buffers (because of memory mapping).
	 */
	void *capture_buffers[NUM_BUFS];

	int i;
	for (i = 0; i < NUM_BUFS; i++) {
		struct v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = io_method;
		buf.index = i;

		printf("Querying buffer %d... ", i);
		if (ioctl(video_fd, VIDIOC_QUERYBUF, &buf) != 0) {
			printf("failed. Error %d (%s).\n", errno, strerror(errno));
			break;
		}
		else {
			puts("ok.");
		}

		capture_buffers[i] = mmap(	NULL,
									buf.length,
									PROT_READ | PROT_WRITE,
									MAP_SHARED,
									video_fd,
									buf.m.offset);

		printf("Mapping of buffer %d... ", i);
		if (MAP_FAILED == capture_buffers[i]) {
			puts("failed.");
			return -1;
		}
		else {
			puts("ok.");
		}
	}

	/*
	 * Queue buffers
	 */
	for (i = 0; i < NUM_BUFS; i++) {

		struct v4l2_buffer buf;
		CLEAR(buf);

		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = io_method;
		buf.index = i;

		printf("Enqueuing buffer %d... ", i);
		if (ioctl(video_fd, VIDIOC_QBUF, &buf) != 0) {
			printf("failed. Error %d (%s).\n", errno, strerror(errno));
			return -1;
		}
		else {
			puts("ok.");
		}
	}

	/*
	 * Start streaming
	 */
	enum v4l2_buf_type type;
	CLEAR(type);

	type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	printf("Start streaming... ");
	if (ioctl(video_fd, VIDIOC_STREAMON, &type) != 0) {
		printf("failed. Error %d (%s).\n", errno, strerror(errno));
	}
	else {
		puts("ok.");
	}

	return 0;
}

//int allocate_cmem_buffers(buf_info_t *bufs) {
//	void *pool;
//
//	CMEM_AllocParams cParams;
//	CMEM_init();
//
//	cParams.type = CMEM_POOL;
//	cParams.flags = CMEM_NONCACHED;
//	cParams.alignment = 32;
//	pool = CMEM_allocPool(0, &cParams);
//	if (pool == NULL ) {
//		puts("Failed to allocate CMEM pool.");
//		return -1;
//	}
//
//	int i;
//	for (i = 0; i < NUM_BUFS; i++) {
////		bufs[i]->user_addr = CMEM_alloc(BUFSIZ, &cParams);
////		if (!bufs[i]->user_addr) {
////			puts("Error allocating cmem buffer.");
////			return -1;
////		}
////		bufs[i]->phy_addr = CMEM_getPhys(bufs[i].user_addr);
////		if (bufs[i]->phy_addr == 0){
////			puts("Error getting physical address.");
////			return -1;
////		}
//		printf("Buffer %d allocated.\n", i);
//
//	}
//
//	return 0;
//}

int get_entities(int media_fd, entities_t *e) {
	/*
	 * This function will determine the entity ids of the needed subdevices.
	 * If all devices are found, it returns 0. In any other case non-zero.
	 */
	int count = 20;

	/*
	 * Set success count to number of integers in struct.
	 * If all entities are found, this count will be 0, indiciating success.
	 */
	int success = sizeof(entities_t) / sizeof(int);

	struct media_entity_desc entity[count];
	int ret;
	int index;
	for (index = 0; index < count; index++) {
		memset(&entity[index], 0, sizeof(struct media_entity_desc));
		entity[index].id = index | MEDIA_ENTITY_ID_FLAG_NEXT;

		ret = ioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity[index]);
		if (ret < 0) {
			if (errno == EINVAL)
				break;
		}
		else {
			printf("[%2d] %s\t\t", entity[index].id, entity[index].name);

			if (!strcmp(entity[index].name, ENTITY_CCDC_OUT_NAME)) {
				e->ccdc_out = entity[index].id;
				success--;
				puts("(selected)");
			}
			else if (!strcmp(entity[index].name, ENTITY_TVP514X_NAME)) {
				e->tvp514x = entity[index].id;
				success--;
				puts("(selected)");
			}
			else if (!strcmp(entity[index].name, ENTITY_CCDC_NAME)) {
				e->ccdc = entity[index].id;
				success--;
				puts("(selected)");
			}
			else
				puts("");
		}

		/* Break loop, if all devices are found. */
		if (success == 0)
			break;

	}

	return success;
}
/*
 * capture.h
 *
 *  Created on: 30.10.2012
 *      Author: andreas
 */

#ifndef CAPTURE_H_
#define CAPTURE_H_

#define ENTITY_CCDC_NAME "OMAP3 ISP CCDC"
#define ENTITY_CCDC_OUT_NAME "OMAP3 ISP CCDC output"
#define ENTITY_TVP514X_NAME "tvp514x 2-005d"

#define DEVNODE_CCDC "/dev/v4l-subdev2"
#define DEVNODE_CCDC_OUT "/dev/video2"
#define DEVNODE_TVP514X "/dev/v4l-subdev8"
#define DEVNODE_ISP "/dev/media0"

#define PAD_TVP514X 0
#define PAD_CCDC_SINK 0
#define PAD_CCDC_SOURCE 1
#define PAD_CCDC_OUTPUT 0

#define WIDTH 720
#define HEIGHT 576

#define NUM_BUFS 4
#define BUFSIZE WIDTH*HEIGHT*2



/*
 * Holds the entity ids of the specified devices.
 * Only integers here!!
 */
typedef struct entities {
	int tvp514x;
	int ccdc;
	int ccdc_out;
} entities_t;

typedef struct buf_info {
	void *user_addr;
	unsigned long phy_addr;
} buf_info_t;



//int allocate_cmem_buffers(buf_info_t *bufs);
int get_entities(int media_fd, entities_t *e);

#endif /* CAPTURE_H_ */

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux