[Test Application PATCH 1/2] UVC gadget: Add support for integration with a video-capture device and other fixes

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

 



This patch adds the following support to the UVC gadget test application:

- To emulate a real UVC webcam gadget (capturing live video from a video
  capture source and sending it over USB bus), it is possible to
  integrate a V4L2 based video-capture device driver with the UVC gadget
  test application acting as an interface between the UVC based
  video-output device and V4L2 based video-capture device.

- If the underlying video capture device supports changing the Brightness
  attribute of the image being acquired (like the Virtual Video, VIVI
  driver does), this patch adds the support to route this UVC request to
  the respective video capture device.

  Incase, there is no actual video capture device associated with the UVC
  gadget and we wish to use this application as the final destination of
  the UVC specific requests then we should return pre-cooked (static)
  responses to GET_CUR(BRIGHTNESS) and SET_CUR(BRIGHTNESS) commands to
  keep command verifier test tools like UVC class specific test suite of
  USBCV, happy.

This patch is tested in the following use-case scenarios:

- UVC webcam standalone (not integrated with any video capture source),
  supporting IO_METHOD_MMAP method to operate on videobuffers:

  This implies that the videobuffers are allocated by the UVC webcam gadget
  itself and they are filled with dummy test pattern by UVC gadget test
  application.

- UVC webcam integrated with V4L2 video-capture driver, with VIVI
  video-capture driver supporting IO_METHOD_MMAP method and UVC webcam
  supporting IO_METHOD_USERPTR:

  This implies that the VIVI is available as a video capture device and is
  integrated with the UVC webcam gadget via UVC gadget test application, with
  V4L2 video-capture driver supporting IO_METHOD_MMAP method and UVC webcam
  supporting IO_METHOD_USERPTR.

Signed-off-by: Bhupesh Sharma <bhupesh.sharma@xxxxxx>
---
 uvc-gadget.c | 2156 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 1950 insertions(+), 206 deletions(-)

diff --git a/uvc-gadget.c b/uvc-gadget.c
index 9ef315c..add0127 100644
--- a/uvc-gadget.c
+++ b/uvc-gadget.c
@@ -38,6 +38,13 @@
 
 #include "../drivers/usb/gadget/uvc.h"
 
+/* Enable debug prints. */
+#undef ENABLE_BUFFER_DEBUG
+#undef ENABLE_USB_REQUEST_DEBUG
+
+#define CLEAR(x)	memset (&(x), 0, sizeof (x))
+#define max(a, b)	(((a) > (b)) ? (a) : (b))
+
 #define clamp(val, min, max) ({                 \
         typeof(val) __val = (val);              \
         typeof(min) __min = (min);              \
@@ -48,79 +55,844 @@
         __val > __max ? __max: __val; })
 
 #define ARRAY_SIZE(a)	((sizeof(a) / sizeof(a[0])))
+#define pixfmtstr(x) 	(x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \
+			((x) >> 24) & 0xff
 
-struct uvc_device
-{
-	int fd;
+/*
+ * The UVC webcam gadget kernel driver (g_webcam.ko) supports changing
+ * the Brightness attribute of the Processing Unit (PU). by default. If
+ * the underlying video capture device supports changing the Brightness
+ * attribute of the image being acquired (like the Virtual Video, VIVI
+ * driver), then we should route this UVC request to the respective
+ * video capture device.
+ *
+ * Incase, there is no actual video capture device associated with the
+ * UVC gadget and we wish to use this application as the final
+ * destination of the UVC specific requests then we should return
+ * pre-cooked (static) responses to GET_CUR(BRIGHTNESS) and
+ * SET_CUR(BRIGHTNESS) commands to keep command verifier test tools like
+ * UVC class specific test suite of USBCV, happy.
+ *
+ * Note that the values taken below are in sync with the VIVI driver and
+ * must be changed for your specific video capture device. These values
+ * also work well in case there in no actual video capture device.
+ */
+#define PU_BRIGHTNESS_MIN_VAL		0
+#define PU_BRIGHTNESS_MAX_VAL		255
+#define PU_BRIGHTNESS_STEP_SIZE		1
+#define PU_BRIGHTNESS_DEFAULT_VAL	127
+
+/* ---------------------------------------------------------------------------
+ * Generic stuff
+ */
+
+/* IO methods supported */
+enum io_method {
+	IO_METHOD_MMAP,
+	IO_METHOD_USERPTR,
+};
+
+/* Buffer representing one video frame */
+struct buffer {
+	struct v4l2_buffer buf;
+	void *start;
+	size_t length;
+};
+
+/* ---------------------------------------------------------------------------
+ * UVC specific stuff
+ */
+
+struct uvc_frame_info {
+	unsigned int width;
+	unsigned int height;
+	unsigned int intervals[8];
+};
+
+struct uvc_format_info {
+	unsigned int fcc;
+	const struct uvc_frame_info *frames;
+};
+
+static const struct uvc_frame_info uvc_frames_yuyv[] = {
+	{  640, 360, { 666666, 10000000, 50000000, 0 }, },
+	{ 1280, 720, { 50000000, 0 }, },
+	{ 0, 0, { 0, }, },
+};
+
+static const struct uvc_frame_info uvc_frames_mjpeg[] = {
+	{  640, 360, { 666666, 10000000, 50000000, 0 }, },
+	{ 1280, 720, { 50000000, 0 }, },
+	{ 0, 0, { 0, }, },
+};
+
+static const struct uvc_format_info uvc_formats[] = {
+	{ V4L2_PIX_FMT_YUYV, uvc_frames_yuyv },
+	{ V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg },
+};
+
+/* ---------------------------------------------------------------------------
+ * V4L2 and UVC device instances
+ */
 
+/* Represents a V4L2 based video capture device */
+struct v4l2_device {
+	/* v4l2 device specific */
+	int v4l2_fd;
+	int is_streaming;
+	char *v4l2_devname;
+
+	/* v4l2 buffer specific */
+	enum io_method io;
+	struct buffer *mem;
+	unsigned int nbufs;
+
+	/* v4l2 buffer queue and dequeue counters */
+	unsigned long long int qbuf_count;
+	unsigned long long int dqbuf_count;
+
+	/* uvc device hook */
+	struct uvc_device *udev;
+};
+
+/* Represents a UVC based video output device */
+struct uvc_device {
+	/* uvc device specific */
+	int uvc_fd;
+	int is_streaming;
+	int run_standalone;
+	char *uvc_devname;
+
+	/* uvc control request specific */
 	struct uvc_streaming_control probe;
 	struct uvc_streaming_control commit;
-
 	int control;
+	struct uvc_request_data request_error_code;
+	unsigned int brightness_val;
 
+	/* uvc buffer specific */
+	enum io_method io;
+	struct buffer *mem;
+	struct buffer *dummy_buf;
+	unsigned int nbufs;
 	unsigned int fcc;
 	unsigned int width;
 	unsigned int height;
 
-	void **mem;
-	unsigned int nbufs;
-	unsigned int bufsize;
-
 	unsigned int bulk;
 	uint8_t color;
 	unsigned int imgsize;
 	void *imgdata;
+
+	/* USB speed specific */
+	int mult;
+	int burst;
+	int maxpkt;
+	enum usb_device_speed speed;
+
+	/* uvc specific flags */
+	int first_buffer_queued;
+	int uvc_shutdown_requested;
+
+	/* uvc buffer queue and dequeue counters */
+	unsigned long long int qbuf_count;
+	unsigned long long int dqbuf_count;
+
+	/* v4l2 device hook */
+	struct v4l2_device *vdev;
 };
 
-static struct uvc_device *
-uvc_open(const char *devname)
+/* forward declarations */
+static int uvc_video_stream(struct uvc_device *dev, int enable);
+
+/* ---------------------------------------------------------------------------
+ * V4L2 streaming related
+ */
+
+static int
+v4l2_uninit_device(struct v4l2_device *dev)
 {
-	struct uvc_device *dev;
+	unsigned int i;
+	int ret;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		for (i = 0; i < dev->nbufs; ++i) {
+			ret = munmap (dev->mem[i].start, dev->mem[i].length);
+			if (ret < 0) {
+				printf("V4L2: munmap failed\n");
+				return ret;
+			}
+		}
+
+		free(dev->mem);
+		break;
+
+	case IO_METHOD_USERPTR:
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int
+v4l2_reqbufs_mmap(struct v4l2_device *dev, int nbufs)
+{
+	struct v4l2_requestbuffers req;
+	unsigned int i = 0;
+	int ret;
+
+	CLEAR(req);
+
+	req.count = nbufs;
+	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	req.memory = V4L2_MEMORY_MMAP;
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_REQBUFS, &req);
+	if (ret < 0) {
+		if (ret == -EINVAL)
+			printf("V4L2: does not support memory mapping\n");
+		else
+			printf("V4L2: VIDIOC_REQBUFS error %s (%d).\n",
+					strerror(errno), errno);
+		goto err;
+	}
+
+	if (!req.count)
+		return 0;
+
+	if (req.count < 2) {
+		printf("V4L2: Insufficient buffer memory.\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* Map the buffers. */
+	dev->mem = calloc(req.count, sizeof dev->mem[0]);
+	if (!dev->mem) {
+		printf("V4L2: Out of memory\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < req.count; ++i) {
+		memset(&dev->mem[i].buf, 0, sizeof(dev->mem[i].buf));
+
+		dev->mem[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		dev->mem[i].buf.memory = V4L2_MEMORY_MMAP;
+		dev->mem[i].buf.index = i;
+
+		ret = ioctl(dev->v4l2_fd, VIDIOC_QUERYBUF, &(dev->mem[i].buf));
+		if (ret < 0) {
+			printf("V4L2: VIDIOC_QUERYBUF failed for buf %d: "
+				"%s (%d).\n", i, strerror(errno), errno);
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+		dev->mem[i].start = mmap (NULL /* start anywhere */,
+					dev->mem[i].buf.length,
+					PROT_READ | PROT_WRITE /* required */,
+					MAP_SHARED /* recommended */,
+					dev->v4l2_fd, dev->mem[i].buf.m.offset);
+
+		if (MAP_FAILED == dev->mem[i].start) {
+			printf("V4L2: Unable to map buffer %u: %s (%d).\n", i,
+				strerror(errno), errno);
+			dev->mem[i].length = 0;
+			ret = -EINVAL;
+			goto err_free;
+		}
+
+		dev->mem[i].length = dev->mem[i].buf.length;
+		printf("V4L2: Buffer %u mapped at address %p.\n", i,
+				dev->mem[i].start);
+	}
+
+	dev->nbufs = req.count;
+	printf("V4L2: %u buffers allocated.\n", req.count);
+
+	return 0;
+
+err_free:
+	free(dev->mem);
+err:
+	return ret;
+}
+
+static int
+v4l2_reqbufs_userptr(struct v4l2_device *dev, int nbufs)
+{
+	struct v4l2_requestbuffers req;
+	int ret;
+
+	CLEAR(req);
+
+	req.count = nbufs;
+	req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	req.memory = V4L2_MEMORY_USERPTR;
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_REQBUFS, &req);
+	if (ret < 0) {
+		if (ret == -EINVAL)
+			printf("V4L2: does not support user pointer i/o\n");
+		else
+			printf("V4L2: VIDIOC_REQBUFS error %s (%d).\n",
+					strerror(errno), errno);
+		return ret;
+	}
+
+	dev->nbufs = req.count;
+	printf("V4L2: %u buffers allocated.\n", req.count);
+
+	return 0;
+}
+
+static int
+v4l2_reqbufs(struct v4l2_device *dev, int nbufs)
+{
+	int ret = 0;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		ret = v4l2_reqbufs_mmap(dev, nbufs);
+		break;
+
+	case IO_METHOD_USERPTR:
+		ret = v4l2_reqbufs_userptr(dev, nbufs);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+v4l2_qbuf_mmap(struct v4l2_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	for (i = 0; i < dev->nbufs; ++i) {
+		memset(&dev->mem[i].buf, 0, sizeof(dev->mem[i].buf));
+
+		dev->mem[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		dev->mem[i].buf.memory = V4L2_MEMORY_MMAP;
+		dev->mem[i].buf.index = i;
+
+		ret = ioctl(dev->v4l2_fd, VIDIOC_QBUF, &(dev->mem[i].buf));
+		if (ret < 0) {
+			printf("V4L2: VIDIOC_QBUF failed : %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		dev->qbuf_count++;
+	}
+
+	return 0;
+}
+
+static int
+v4l2_qbuf(struct v4l2_device *dev)
+{
+	int ret = 0;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		ret = v4l2_qbuf_mmap(dev);
+		break;
+
+	case IO_METHOD_USERPTR:
+		/* Empty. */
+		ret = 0;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+v4l2_process_data(struct v4l2_device *dev)
+{
+	int ret;
+	struct v4l2_buffer vbuf;
+	struct v4l2_buffer ubuf;
+
+	/* Return immediately if V4l2 streaming has not yet started. */
+	if (!dev->is_streaming)
+		return 0;
+
+	if (dev->udev->first_buffer_queued)
+		if ((dev->dqbuf_count + 1) >= dev->qbuf_count)
+			return 0;
+
+	/* Dequeue spent buffer rom V4L2 domain. */
+	CLEAR(vbuf);
+
+	vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	switch (dev->io) {
+	case IO_METHOD_USERPTR:
+		vbuf.memory = V4L2_MEMORY_USERPTR;
+		break;
+
+	case IO_METHOD_MMAP:
+	default:
+		vbuf.memory = V4L2_MEMORY_MMAP;
+		break;
+	}
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_DQBUF, &vbuf);
+	if (ret < 0) {
+		printf("V4L2: Unable to dequeue buffer: %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	dev->dqbuf_count++;
+
+#ifdef ENABLE_BUFFER_DEBUG
+	printf("Dequeueing buffer at V4L2 side = %d\n", vbuf.index);
+#endif
+
+	/* Queue video buffer to UVC domain. */
+	CLEAR(ubuf);
+
+	ubuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	switch (dev->udev->io) {
+	case IO_METHOD_MMAP:
+		ubuf.memory = V4L2_MEMORY_MMAP;
+		ubuf.length = vbuf.length;
+		ubuf.index = vbuf.index;
+		ubuf.bytesused = vbuf.bytesused;
+		break;
+
+	case IO_METHOD_USERPTR:
+	default:
+		ubuf.memory = V4L2_MEMORY_USERPTR;
+		ubuf.m.userptr = (unsigned long) dev->mem[vbuf.index].start;
+		ubuf.length = dev->mem[vbuf.index].length;
+		ubuf.index = vbuf.index;
+		ubuf.bytesused = vbuf.bytesused;
+		break;
+	}
+
+	ret = ioctl(dev->udev->uvc_fd, VIDIOC_QBUF, &ubuf);
+	if (ret < 0) {
+		printf("UVC: Unable to queue buffer %d: %s (%d).\n",
+			ubuf.index, strerror(errno), errno);
+		/* Check for a USB disconnect/shutdown event. */
+		if (errno == ENODEV) {
+			dev->udev->uvc_shutdown_requested = 1;
+			printf("UVC: Possible USB shutdown requested from "
+				"Host, seen during VIDIOC_QBUF\n");
+			return 0;
+		} else {
+			return ret;
+		}
+	}
+
+	dev->udev->qbuf_count++;
+
+#ifdef ENABLE_BUFFER_DEBUG
+	printf("Queueing buffer at UVC side = %d\n", ubuf.index);
+#endif
+
+	if (!dev->udev->first_buffer_queued && !dev->udev->run_standalone) {
+		uvc_video_stream(dev->udev, 1);
+		dev->udev->first_buffer_queued = 1;
+		dev->udev->is_streaming = 1;
+	}
+
+	return 0;
+}
+
+/* ---------------------------------------------------------------------------
+ * V4L2 generic stuff
+ */
+
+static int
+v4l2_get_format(struct v4l2_device *dev)
+{
+	struct v4l2_format fmt;
+	int ret;
+
+	CLEAR(fmt);
+	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_G_FMT, &fmt);
+	if (ret < 0) {
+		printf("V4L2: Unable to get format: %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	printf("V4L2: Getting current format: %c%c%c%c %ux%u\n",
+		pixfmtstr(fmt.fmt.pix.pixelformat),
+		fmt.fmt.pix.width, fmt.fmt.pix.height);
+
+	return 0;
+}
+
+static int
+v4l2_set_format(struct v4l2_device *dev, struct v4l2_format *fmt)
+{
+	int ret;
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_S_FMT, fmt);
+	if (ret < 0) {
+		printf("V4L2: Unable to set format %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	printf("V4L2: Setting format to: %c%c%c%c %ux%u\n",
+		pixfmtstr(fmt->fmt.pix.pixelformat),
+		fmt->fmt.pix.width, fmt->fmt.pix.height);
+
+	return 0;
+}
+
+static int
+v4l2_set_ctrl(struct v4l2_device *dev, int new_val, int ctrl)
+{
+	struct v4l2_queryctrl queryctrl;
+	struct v4l2_control control;
+	int ret;
+
+	CLEAR(queryctrl);
+
+	switch (ctrl) {
+	case V4L2_CID_BRIGHTNESS:
+		queryctrl.id = V4L2_CID_BRIGHTNESS;
+		ret = ioctl(dev->v4l2_fd, VIDIOC_QUERYCTRL, &queryctrl);
+		if (-1 == ret) {
+			if (errno != EINVAL)
+				printf("V4L2: VIDIOC_QUERYCTRL"
+					" failed: %s (%d).\n",
+					strerror(errno), errno);
+			else
+				printf ("V4L2_CID_BRIGHTNESS is not"
+					" supported: %s (%d).\n",
+					strerror(errno), errno);
+
+			return ret;
+		} else if (queryctrl.flags & V4L2_CTRL_FLAG_DISABLED) {
+			printf ("V4L2_CID_BRIGHTNESS is not supported.\n");
+			ret = -EINVAL;
+			return ret;
+		} else {
+			CLEAR(control);
+			control.id = V4L2_CID_BRIGHTNESS;
+			control.value = new_val;
+
+			ret = ioctl(dev->v4l2_fd, VIDIOC_S_CTRL, &control);
+			if (-1 == ret) {
+				printf("V4L2: VIDIOC_S_CTRL failed: %s (%d).\n",
+					strerror(errno), errno);
+				return ret;
+			}
+		}
+		printf ("V4L2: Brightness control changed to value = 0x%x\n",
+				new_val);
+		break;
+
+	default:
+		/* TODO: We don't support any other controls. */
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int
+v4l2_start_capturing(struct v4l2_device *dev)
+{
+	int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	int ret;
+
+	ret = ioctl(dev->v4l2_fd, VIDIOC_STREAMON, &type);
+	if (ret < 0) {
+		printf("V4L2: Unable to start streaming: %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	printf("V4L2: Starting video stream.\n");
+
+	return 0;
+}
+
+static int
+v4l2_stop_capturing(struct v4l2_device *dev)
+{
+	enum v4l2_buf_type type;
+	int ret;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+		ret = ioctl(dev->v4l2_fd, VIDIOC_STREAMOFF, &type);
+		if (ret < 0) {
+			printf("V4L2: VIDIOC_STREAMOFF failed: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		break;
+	default:
+		/* Nothing to do. */
+		break;
+	}
+
+	return 0;
+}
+
+static int
+v4l2_open(struct v4l2_device **v4l2, char *devname, struct v4l2_format *s_fmt)
+{
+	struct v4l2_device *dev;
 	struct v4l2_capability cap;
+	int fd;
+	int ret = -EINVAL;
+
+	fd = open(devname, O_RDWR | O_NONBLOCK, 0);
+	if (fd == -1) {
+		printf("V4L2: device open failed: %s (%d).\n",
+				strerror(errno), errno);
+		return ret;
+	}
+
+	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
+	if (ret < 0) {
+		printf ("V4L2: VIDIOC_QUERYCAP failed: %s (%d).\n",
+				strerror(errno), errno);
+		goto err;
+	}
+
+	if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+		printf("V4L2: %s is no video capture device\n", devname);
+		goto err;
+	}
+
+	if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
+		printf("V4L2: %s does not support streaming i/o\n",
+					devname);
+		goto err;
+	}
+
+	dev = calloc(1, sizeof *dev);
+	if (dev == NULL) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	printf("V4L2 device is %s on bus %s\n", cap.card, cap.bus_info);
+
+	dev->v4l2_fd = fd;
+
+	/* Get the default image format supported. */
+	ret = v4l2_get_format(dev);
+	if (ret < 0)
+		goto err_free;
+
+	/*
+	 * Set the desired image format.
+	 * Note: VIDIOC_S_FMT may change width and height.
+	 */
+	ret = v4l2_set_format(dev, s_fmt);
+	if (ret < 0)
+		goto err_free;
+
+	/* Get the changed image format. */
+	ret = v4l2_get_format(dev);
+	if (ret < 0)
+		goto err_free;
+
+	printf("v4l2 open succeeded, file descriptor = %d\n", fd);
+
+	*v4l2 = dev;
+
+	return 0;
+
+err_free:
+	free(dev);
+err:
+	close (fd);
+
+	return ret;
+}
+
+static void
+v4l2_close(struct v4l2_device *dev)
+{
+	close(dev->v4l2_fd);
+	free(dev);
+}
+
+/* ---------------------------------------------------------------------------
+ * UVC generic stuff
+ */
+
+static int
+uvc_video_set_format(struct uvc_device *dev)
+{
+	struct v4l2_format fmt;
+	int ret;
+
+	CLEAR(fmt);
+
+	fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	fmt.fmt.pix.width = dev->width;
+	fmt.fmt.pix.height = dev->height;
+	fmt.fmt.pix.pixelformat = dev->fcc;
+	fmt.fmt.pix.field = V4L2_FIELD_NONE;
+	if (dev->fcc == V4L2_PIX_FMT_MJPEG)
+		fmt.fmt.pix.sizeimage = dev->imgsize * 1.5;
+
+	ret = ioctl(dev->uvc_fd, VIDIOC_S_FMT, &fmt);
+	if (ret < 0) {
+		printf("UVC: Unable to set format %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	printf("UVC: Setting format to: %c%c%c%c %ux%u\n",
+		pixfmtstr(dev->fcc), dev->width, dev->height);
+
+	return 0;
+}
+
+static int
+uvc_video_stream(struct uvc_device *dev, int enable)
+{
+	int type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 	int ret;
+
+	if (!enable) {
+		ret = ioctl(dev->uvc_fd, VIDIOC_STREAMOFF, &type);
+		if (ret < 0) {
+			printf("UVC: VIDIOC_STREAMOFF failed: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		printf("UVC: Stopping video stream.\n");
+
+		return 0;
+	}
+
+	ret = ioctl(dev->uvc_fd, VIDIOC_STREAMON, &type);
+	if (ret < 0) {
+		printf("UVC: Unable to start streaming %s (%d).\n",
+			strerror(errno), errno);
+		return ret;
+	}
+
+	printf("UVC: Starting video stream.\n");
+
+	dev->uvc_shutdown_requested = 0;
+
+	return 0;
+}
+
+static int
+uvc_uninit_device(struct uvc_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		for (i = 0; i < dev->nbufs; ++i) {
+			ret = munmap(dev->mem[i].start, dev->mem[i].length);
+			if (ret < 0) {
+				printf("UVC: munmap failed\n");
+				return ret;
+			}
+		}
+
+		free(dev->mem);
+		break;
+
+	case IO_METHOD_USERPTR:
+	default:
+		if (dev->run_standalone) {
+			for (i = 0; i < dev->nbufs; ++i)
+				free(dev->dummy_buf[i].start);
+
+			free(dev->dummy_buf);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int
+uvc_open(struct uvc_device **uvc, char *devname)
+{
+	struct uvc_device *dev;
+	struct v4l2_capability cap;
 	int fd;
+	int ret = -EINVAL;
 
 	fd = open(devname, O_RDWR | O_NONBLOCK);
 	if (fd == -1) {
-		printf("v4l2 open failed: %s (%d)\n", strerror(errno), errno);
-		return NULL;
+		printf("UVC: device open failed: %s (%d).\n",
+				strerror(errno), errno);
+		return ret;
 	}
 
-	printf("open succeeded, file descriptor = %d\n", fd);
-
 	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
 	if (ret < 0) {
-		printf("unable to query device: %s (%d)\n", strerror(errno),
-			errno);
-		close(fd);
-		return NULL;
-        }
+		printf("UVC: unable to query uvc device: %s (%d)\n",
+				strerror(errno), errno);
+		goto err;
+	}
 
-	printf("device is %s on bus %s\n", cap.card, cap.bus_info);
+	if (!(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT)) {
+		printf("UVC: %s is no video output device\n", devname);
+		goto err;
+	}
 
-	dev = malloc(sizeof *dev);
+	dev = calloc(1, sizeof *dev);
 	if (dev == NULL) {
-		close(fd);
-		return NULL;
+		ret = -ENOMEM;
+		goto err;
 	}
 
-	memset(dev, 0, sizeof *dev);
-	dev->fd = fd;
+	printf("uvc device is %s on bus %s\n", cap.card, cap.bus_info);
+	printf("uvc open succeeded, file descriptor = %d\n", fd);
+
+	dev->uvc_fd = fd;
+	*uvc = dev;
 
-	return dev;
+	return 0;
+
+err:
+	return ret;
 }
 
 static void
 uvc_close(struct uvc_device *dev)
 {
-	close(dev->fd);
+	close(dev->uvc_fd);
 	free(dev->imgdata);
-	free(dev->mem);
 	free(dev);
 }
 
 /* ---------------------------------------------------------------------------
- * Video streaming
+ * UVC streaming related
  */
 
 static void
@@ -134,13 +906,14 @@ uvc_video_fill_buffer(struct uvc_device *dev, struct v4l2_buffer *buf)
 		/* Fill the buffer with video data. */
 		bpl = dev->width * 2;
 		for (i = 0; i < dev->height; ++i)
-			memset(dev->mem[buf->index] + i*bpl, dev->color++, bpl);
+			memset(dev->mem[buf->index].start + i*bpl,
+					dev->color++, bpl);
 
 		buf->bytesused = bpl * dev->height;
 		break;
 
 	case V4L2_PIX_FMT_MJPEG:
-		memcpy(dev->mem[buf->index], dev->imgdata, dev->imgsize);
+		memcpy(dev->mem[buf->index].start, dev->imgdata, dev->imgsize);
 		buf->bytesused = dev->imgsize;
 		break;
 	}
@@ -149,191 +922,470 @@ uvc_video_fill_buffer(struct uvc_device *dev, struct v4l2_buffer *buf)
 static int
 uvc_video_process(struct uvc_device *dev)
 {
-	struct v4l2_buffer buf;
+	struct v4l2_buffer ubuf;
+	struct v4l2_buffer vbuf;
+	unsigned int i;
 	int ret;
 
-	memset(&buf, 0, sizeof buf);
-	buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-	buf.memory = V4L2_MEMORY_MMAP;
+	/*
+	 * Return immediately if UVC video output device has not started
+	 * streaming yet.
+	 */
+	if (!dev->is_streaming)
+		return 0;
 
-	if ((ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf)) < 0) {
-		printf("Unable to dequeue buffer: %s (%d).\n", strerror(errno),
-			errno);
-		return ret;
+	/* Prepare a v4l2 buffer to be dequeued from UVC domain. */
+	CLEAR(ubuf);
+
+	ubuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		ubuf.memory = V4L2_MEMORY_MMAP;
+		break;
+
+	case IO_METHOD_USERPTR:
+	default:
+		ubuf.memory = V4L2_MEMORY_USERPTR;
+		break;
 	}
 
-	uvc_video_fill_buffer(dev, &buf);
+	if (dev->run_standalone) {
+		/* UVC stanalone setup. */
+		ret = ioctl(dev->uvc_fd, VIDIOC_DQBUF, &ubuf);
+		if (ret < 0) {
+			printf("UVC: Unable to dequeue buffer: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		dev->dqbuf_count++;
 
-	if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) {
-		printf("Unable to requeue buffer: %s (%d).\n", strerror(errno),
-			errno);
-		return ret;
+#ifdef ENABLE_BUFFER_DEBUG
+		printf("DeQueued buffer at UVC side = %d\n", ubuf.index);
+#endif
+		uvc_video_fill_buffer(dev, &ubuf);
+
+		ret = ioctl(dev->uvc_fd, VIDIOC_QBUF, &ubuf);
+		if (ret < 0) {
+			printf("UVC: Unable to queue buffer: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		dev->qbuf_count++;
+
+#ifdef ENABLE_BUFFER_DEBUG
+		printf("ReQueueing buffer at UVC side = %d\n", ubuf.index);
+#endif
+	} else {
+		/* UVC - V4L2 integrated path. */
+
+		/*
+		 * Return immediately if V4L2 video capture device has not
+		 * started streaming yet or if QBUF was not called even once on
+		 * the UVC side.
+		 */
+		if (!dev->vdev->is_streaming || !dev->first_buffer_queued)
+			return 0;
+
+		/*
+		 * Do not dequeue buffers from UVC side until there are atleast
+		 * 2 buffers available at UVC domain.
+		 */
+		if (!dev->uvc_shutdown_requested)
+			if ((dev->dqbuf_count + 1) >= dev->qbuf_count)
+				return 0;
+
+		/* Dequeue the spent buffer from UVC domain */
+		ret = ioctl(dev->uvc_fd, VIDIOC_DQBUF, &ubuf);
+		if (ret < 0) {
+			printf("UVC: Unable to dequeue buffer: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		if (dev->io == IO_METHOD_USERPTR)
+			for (i = 0; i < dev->nbufs; ++i)
+				if (ubuf.m.userptr ==
+				   (unsigned long) dev->vdev->mem[i].start
+				   && ubuf.length == dev->vdev->mem[i].length)
+					break;
+
+		dev->dqbuf_count++;
+
+#ifdef ENABLE_BUFFER_DEBUG
+		printf("DeQueued buffer at UVC side=%d\n", ubuf.index);
+#endif
+
+		/*
+		 * If the dequeued buffer was marked with state ERROR by the
+		 * underlying UVC driver gadget, do not queue the same to V4l2
+		 * and wait for a STREAMOFF event on UVC side corresponding to
+		 * set_alt(0). So, now all buffers pending at UVC end will be
+		 * dequeued one-by-one and we will enter a state where we once
+		 * again wait for a set_alt(1) command from the USB host side.
+		 */
+		if (ubuf.flags & V4L2_BUF_FLAG_ERROR) {
+			dev->uvc_shutdown_requested = 1;
+			printf("UVC: Possible USB shutdown requested from "
+				"Host, seen during VIDIOC_DQBUF\n");
+			return 0;
+		}
+
+		/* Queue the buffer to V4L2 domain */
+		CLEAR(vbuf);
+
+		vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		vbuf.memory = V4L2_MEMORY_MMAP;
+		vbuf.index = ubuf.index;
+
+		ret = ioctl(dev->vdev->v4l2_fd, VIDIOC_QBUF, &vbuf);
+		if (ret < 0) {
+			printf("V4L2: Unable to queue buffer: %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		dev->vdev->qbuf_count++;
+
+#ifdef ENABLE_BUFFER_DEBUG
+		printf("ReQueueing buffer at V4L2 side = %d\n", vbuf.index);
+#endif
 	}
 
 	return 0;
 }
 
 static int
-uvc_video_reqbufs(struct uvc_device *dev, int nbufs)
+uvc_video_qbuf_mmap(struct uvc_device *dev)
 {
-	struct v4l2_requestbuffers rb;
-	struct v4l2_buffer buf;
 	unsigned int i;
 	int ret;
 
-	for (i = 0; i < dev->nbufs; ++i)
-		munmap(dev->mem[i], dev->bufsize);
+	for (i = 0; i < dev->nbufs; ++i) {
+		memset(&dev->mem[i].buf, 0, sizeof(dev->mem[i].buf));
+
+		dev->mem[i].buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		dev->mem[i].buf.memory = V4L2_MEMORY_MMAP;
+		dev->mem[i].buf.index = i;
 
-	free(dev->mem);
-	dev->mem = 0;
-	dev->nbufs = 0;
+		/* UVC standalone setup. */
+		if (dev->run_standalone)
+			uvc_video_fill_buffer(dev, &(dev->mem[i].buf));
+
+		ret = ioctl(dev->uvc_fd, VIDIOC_QBUF, &(dev->mem[i].buf));
+		if (ret < 0) {
+			printf("UVC: VIDIOC_QBUF failed : %s (%d).\n",
+					strerror(errno), errno);
+			return ret;
+		}
+
+		dev->qbuf_count++;
+	}
+
+	return 0;
+}
+
+static int
+uvc_video_qbuf_userptr(struct uvc_device *dev)
+{
+	unsigned int i;
+	int ret;
+
+	/* UVC standalone setup. */
+	if (dev->run_standalone) {
+		for (i = 0; i < dev->nbufs; ++i) {
+			struct v4l2_buffer buf;
+
+			CLEAR(buf);
+			buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+			buf.memory = V4L2_MEMORY_USERPTR;
+			buf.m.userptr = (unsigned long)dev->dummy_buf[i].start;
+			buf.length = dev->dummy_buf[i].length;
+			buf.index = i;
+
+			ret = ioctl(dev->uvc_fd, VIDIOC_QBUF, &buf);
+			if (ret < 0) {
+				printf("UVC: VIDIOC_QBUF failed : %s (%d).\n",
+						strerror(errno), errno);
+				return ret;
+			}
+
+			dev->qbuf_count++;
+		}
+	}
+
+	return 0;
+}
+
+static int
+uvc_video_qbuf(struct uvc_device *dev)
+{
+	int ret = 0;
+
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		ret = uvc_video_qbuf_mmap(dev);
+		break;
+
+	case IO_METHOD_USERPTR:
+		ret = uvc_video_qbuf_userptr(dev);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+uvc_video_reqbufs_mmap(struct uvc_device *dev, int nbufs)
+{
+	struct v4l2_requestbuffers rb;
+	unsigned int i;
+	int ret;
+
+	CLEAR(rb);
 
-	memset(&rb, 0, sizeof rb);
 	rb.count = nbufs;
 	rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 	rb.memory = V4L2_MEMORY_MMAP;
 
-	ret = ioctl(dev->fd, VIDIOC_REQBUFS, &rb);
+	ret = ioctl(dev->uvc_fd, VIDIOC_REQBUFS, &rb);
 	if (ret < 0) {
-		printf("Unable to allocate buffers: %s (%d).\n",
-			strerror(errno), errno);
-		return ret;
+		if (ret == -EINVAL)
+			printf("UVC: does not support memory mapping\n");
+		else
+			printf("UVC: Unable to allocate buffers: %s (%d).\n",
+					strerror(errno), errno);
+		goto err;
 	}
 
-	printf("%u buffers allocated.\n", rb.count);
+	if (!rb.count)
+		return 0;
+
+	if (rb.count < 2) {
+		printf("UVC: Insufficient buffer memory.\n");
+		ret = -EINVAL;
+		goto err;
+	}
 
 	/* Map the buffers. */
-	dev->mem = malloc(rb.count * sizeof dev->mem[0]);
+	dev->mem = calloc(rb.count, sizeof dev->mem[0]);
+	if (!dev->mem) {
+		printf("UVC: Out of memory\n");
+		ret = -ENOMEM;
+		goto err;
+	}
 
 	for (i = 0; i < rb.count; ++i) {
-		memset(&buf, 0, sizeof buf);
-		buf.index = i;
-		buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-		buf.memory = V4L2_MEMORY_MMAP;
-		ret = ioctl(dev->fd, VIDIOC_QUERYBUF, &buf);
+		memset(&dev->mem[i].buf, 0, sizeof(dev->mem[i].buf));
+
+		dev->mem[i].buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+		dev->mem[i].buf.memory = V4L2_MEMORY_MMAP;
+		dev->mem[i].buf.index = i;
+
+		ret = ioctl(dev->uvc_fd, VIDIOC_QUERYBUF, &(dev->mem[i].buf));
 		if (ret < 0) {
-			printf("Unable to query buffer %u: %s (%d).\n", i,
-				strerror(errno), errno);
-			return -1;
+			printf("UVC: VIDIOC_QUERYBUF failed for buf %d: "
+				"%s (%d).\n", i, strerror(errno), errno);
+			ret = -EINVAL;
+			goto err_free;
 		}
-		printf("length: %u offset: %u\n", buf.length, buf.m.offset);
 
-		dev->mem[i] = mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, dev->fd, buf.m.offset);
-		if (dev->mem[i] == MAP_FAILED) {
-			printf("Unable to map buffer %u: %s (%d)\n", i,
+		dev->mem[i].start = mmap(NULL /* start anywhere */,
+					dev->mem[i].buf.length,
+					PROT_READ | PROT_WRITE /* required */,
+					MAP_SHARED /* recommended */,
+					dev->uvc_fd, dev->mem[i].buf.m.offset);
+
+		if (MAP_FAILED == dev->mem[i].start) {
+			printf("UVC: Unable to map buffer %u: %s (%d).\n", i,
 				strerror(errno), errno);
-			return -1;
+			dev->mem[i].length = 0;
+			ret = -EINVAL;
+			goto err_free;
 		}
-		printf("Buffer %u mapped at address %p.\n", i, dev->mem[i]);
+
+		dev->mem[i].length = dev->mem[i].buf.length;
+		printf("UVC: Buffer %u mapped at address %p.\n", i,
+				dev->mem[i].start);
 	}
 
-	dev->bufsize = buf.length;
 	dev->nbufs = rb.count;
+	printf("UVC: %u buffers allocated.\n", rb.count);
 
 	return 0;
+
+err_free:
+	free(dev->mem);
+err:
+	return ret;
 }
 
 static int
-uvc_video_stream(struct uvc_device *dev, int enable)
+uvc_video_reqbufs_userptr(struct uvc_device *dev, int nbufs)
 {
-	struct v4l2_buffer buf;
-	unsigned int i;
-	int type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	struct v4l2_requestbuffers rb;
+	unsigned int i, j, bpl, payload_size;
 	int ret;
 
-	if (!enable) {
-		printf("Stopping video stream.\n");
-		ioctl(dev->fd, VIDIOC_STREAMOFF, &type);
-		return 0;
-	}
+	CLEAR(rb);
 
-	printf("Starting video stream.\n");
+	rb.count = nbufs;
+	rb.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+	rb.memory = V4L2_MEMORY_USERPTR;
 
-	for (i = 0; i < dev->nbufs; ++i) {
-		memset(&buf, 0, sizeof buf);
+	ret = ioctl(dev->uvc_fd, VIDIOC_REQBUFS, &rb);
+	if (ret < 0) {
+		if (ret == -EINVAL)
+			printf("UVC: does not support user pointer i/o\n");
+		else
+			printf("UVC: VIDIOC_REQBUFS error %s (%d).\n",
+					strerror(errno), errno);
+		goto err;
+	}
 
-		buf.index = i;
-		buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-		buf.memory = V4L2_MEMORY_MMAP;
+	if (!rb.count)
+		return 0;
 
-		uvc_video_fill_buffer(dev, &buf);
+	dev->nbufs = rb.count;
+	printf("UVC: %u buffers allocated.\n", rb.count);
+
+	if (dev->run_standalone) {
+		/* Allocate buffers to hold dummy data pattern. */
+		dev->dummy_buf = calloc(rb.count, sizeof dev->dummy_buf[0]);
+		if (!dev->dummy_buf) {
+			printf("UVC: Out of memory\n");
+			ret = -ENOMEM;
+			goto err;
+		}
 
-		printf("Queueing buffer %u.\n", i);
-		if ((ret = ioctl(dev->fd, VIDIOC_QBUF, &buf)) < 0) {
-			printf("Unable to queue buffer: %s (%d).\n",
-				strerror(errno), errno);
+		switch (dev->fcc) {
+		case V4L2_PIX_FMT_YUYV:
+			bpl = dev->width * 2;
+			payload_size = dev->width * dev->height * 2;
 			break;
+		case V4L2_PIX_FMT_MJPEG:
+			payload_size = dev->imgsize;
+			break;
+		}
+
+		for (i = 0; i < rb.count; ++i) {
+			dev->dummy_buf[i].length = payload_size;
+			dev->dummy_buf[i].start = malloc(payload_size);
+			if (!dev->dummy_buf[i].start) {
+				printf("UVC: Out of memory\n");
+				ret = -ENOMEM;
+				goto err;
+			}
+
+			if (V4L2_PIX_FMT_YUYV == dev->fcc)
+				for (j = 0; j < dev->height; ++j)
+					memset(dev->dummy_buf[i].start + j*bpl,
+						dev->color++, bpl);
+
+			if (V4L2_PIX_FMT_MJPEG == dev->fcc)
+				memcpy(dev->dummy_buf[i].start, dev->imgdata,
+						dev->imgsize);
 		}
 	}
 
-	ioctl(dev->fd, VIDIOC_STREAMON, &type);
+	return 0;
+
+err:
 	return ret;
+
 }
 
 static int
-uvc_video_set_format(struct uvc_device *dev)
+uvc_video_reqbufs(struct uvc_device *dev, int nbufs)
 {
-	struct v4l2_format fmt;
-	int ret;
+	int ret = 0;
 
-	printf("Setting format to 0x%08x %ux%u\n",
-		dev->fcc, dev->width, dev->height);
+	switch (dev->io) {
+	case IO_METHOD_MMAP:
+		ret = uvc_video_reqbufs_mmap(dev, nbufs);
+		break;
 
-	memset(&fmt, 0, sizeof fmt);
-	fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
-	fmt.fmt.pix.width = dev->width;
-	fmt.fmt.pix.height = dev->height;
-	fmt.fmt.pix.pixelformat = dev->fcc;
-	fmt.fmt.pix.field = V4L2_FIELD_NONE;
-	if (dev->fcc == V4L2_PIX_FMT_MJPEG)
-		fmt.fmt.pix.sizeimage = dev->imgsize * 1.5;
+	case IO_METHOD_USERPTR:
+		ret = uvc_video_reqbufs_userptr(dev, nbufs);
+		break;
 
-	if ((ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt)) < 0)
-		printf("Unable to set format: %s (%d).\n",
-			strerror(errno), errno);
+	default:
+		ret = -EINVAL;
+		break;
+	}
 
 	return ret;
 }
 
+/*
+ * This function is called in response to either:
+ * 	- A SET_ALT(interface 1, alt setting 1) command from USB host,
+ * 	  if the UVC gadget supports an ISOCHRONOUS video streaming endpoint
+ * 	  or,
+ *
+ *	- A UVC_VS_COMMIT_CONTROL command from USB host, if the UVC gadget
+ *	  supports a BULK type video streaming endpoint.
+ */
 static int
-uvc_video_init(struct uvc_device *dev __attribute__((__unused__)))
+uvc_handle_streamon_event(struct uvc_device *dev)
 {
-	return 0;
-}
+	int ret;
 
-/* ---------------------------------------------------------------------------
- * Request processing
- */
+	ret = uvc_video_reqbufs(dev, dev->nbufs);
+	if (ret < 0)
+		goto err;
+
+	if (!dev->run_standalone) {
+		/* UVC - V4L2 integrated path. */
+		if (IO_METHOD_USERPTR == dev->vdev->io) {
+			/*
+			 * Ensure that the V4L2 video capture device has already
+			 * some buffers queued.
+			 */
+			ret = v4l2_reqbufs(dev->vdev, dev->vdev->nbufs);
+			if (ret < 0)
+				goto err;
+
+			ret = v4l2_qbuf(dev->vdev);
+			if (ret < 0)
+				goto err;
+		}
 
-struct uvc_frame_info
-{
-	unsigned int width;
-	unsigned int height;
-	unsigned int intervals[8];
-};
+		/* Start V4L2 capturing now. */
+		ret = v4l2_start_capturing(dev->vdev);
+		if (ret < 0)
+			goto err;
 
-struct uvc_format_info
-{
-	unsigned int fcc;
-	const struct uvc_frame_info *frames;
-};
+		dev->vdev->is_streaming = 1;
+	}
 
-static const struct uvc_frame_info uvc_frames_yuyv[] = {
-	{  640, 360, { 666666, 10000000, 50000000, 0 }, },
-	{ 1280, 720, { 50000000, 0 }, },
-	{ 0, 0, { 0, }, },
-};
+	/* Common setup. */
 
-static const struct uvc_frame_info uvc_frames_mjpeg[] = {
-	{  640, 360, { 666666, 10000000, 50000000, 0 }, },
-	{ 1280, 720, { 50000000, 0 }, },
-	{ 0, 0, { 0, }, },
-};
+	/* Queue buffers to UVC domain and start streaming. */
+	ret = uvc_video_qbuf(dev);
+	if (ret < 0)
+		goto err;
 
-static const struct uvc_format_info uvc_formats[] = {
-	{ V4L2_PIX_FMT_YUYV, uvc_frames_yuyv },
-	{ V4L2_PIX_FMT_MJPEG, uvc_frames_mjpeg },
-};
+	if (dev->run_standalone) {
+		uvc_video_stream(dev, 1);
+		dev->first_buffer_queued = 1;
+		dev->is_streaming = 1;
+	}
+
+	return 0;
+
+err:
+	return ret;
+}
+
+/* ---------------------------------------------------------------------------
+ * UVC Request processing
+ */
 
 static void
 uvc_fill_streaming_control(struct uvc_device *dev,
@@ -374,14 +1426,24 @@ uvc_fill_streaming_control(struct uvc_device *dev,
 		ctrl->dwMaxVideoFrameSize = dev->imgsize;
 		break;
 	}
-	ctrl->dwMaxPayloadTransferSize = 512;	/* TODO this should be filled by the driver. */
+
+	/* TODO: the UVC maxpayload transfer size should be filled
+	 * by the driver.
+	 */
+	if (!dev->bulk)
+		ctrl->dwMaxPayloadTransferSize = (dev->maxpkt) *
+					(dev->mult + 1) * (dev->burst + 1);
+	else
+		ctrl->dwMaxPayloadTransferSize = ctrl->dwMaxVideoFrameSize;
+
 	ctrl->bmFramingInfo = 3;
 	ctrl->bPreferedVersion = 1;
 	ctrl->bMaxVersion = 1;
 }
 
 static void
-uvc_events_process_standard(struct uvc_device *dev, struct usb_ctrlrequest *ctrl,
+uvc_events_process_standard(struct uvc_device *dev,
+			    struct usb_ctrlrequest *ctrl,
 			    struct uvc_request_data *resp)
 {
 	printf("standard request\n");
@@ -391,12 +1453,258 @@ uvc_events_process_standard(struct uvc_device *dev, struct usb_ctrlrequest *ctrl
 }
 
 static void
-uvc_events_process_control(struct uvc_device *dev, uint8_t req, uint8_t cs,
-			   struct uvc_request_data *resp)
+uvc_events_process_control(struct uvc_device *dev, uint8_t req,
+			   uint8_t cs, uint8_t entity_id,
+			   uint8_t len, struct uvc_request_data *resp)
 {
+	switch (entity_id) {
+	case 0:
+		switch (cs) {
+		case UVC_VC_REQUEST_ERROR_CODE_CONTROL:
+			/* Send the request error code last prepared. */
+			resp->data[0] = dev->request_error_code.data[0];
+			resp->length = dev->request_error_code.length;
+			break;
+
+		default:
+			/*
+			 * If we were not supposed to handle this
+			 * 'cs', prepare an error code response.
+			 */
+			dev->request_error_code.data[0] = 0x06;
+			dev->request_error_code.length = 1;
+			break;
+		}
+		break;
+
+	/* Camera terminal unit 'UVC_VC_INPUT_TERMINAL'. */
+	case 1:
+		switch (cs) {
+		/*
+		 * We support only 'UVC_CT_AE_MODE_CONTROL' for CAMERA
+		 * terminal, as our bmControls[0] = 2 for CT. Also we
+		 * support only auto exposure.
+		 */
+		case UVC_CT_AE_MODE_CONTROL:
+			switch (req) {
+			case UVC_SET_CUR:
+				/* Incase of auto exposure, attempts to
+				 * programmatically set the auto-adjusted
+				 * controls are ignored.
+				 */
+				resp->data[0] = 0x01;
+				resp->length = 1;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+
+			case UVC_GET_INFO:
+				/*
+				 * TODO: We support Set and Get requests, but
+				 * don't support async updates on an video
+				 * status (interrupt) endpoint as of
+				 * now.
+				 */
+				resp->data[0] = 0x03;
+				resp->length = 1;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+
+			case UVC_GET_CUR:
+			case UVC_GET_DEF:
+			case UVC_GET_RES:
+				/* Auto Mode â?? auto Exposure Time, auto Iris. */
+				resp->data[0] = 0x02;
+				resp->length = 1;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			default:
+				/*
+				 * We don't support this control, so STALL the
+				 * control ep.
+				 */
+				resp->length = -EL2HLT;
+				/*
+				 * For every unsupported control request
+				 * set the request error code to appropriate
+				 * value.
+				 */
+				dev->request_error_code.data[0] = 0x07;
+				dev->request_error_code.length = 1;
+				break;
+			}
+			break;
+
+		default:
+			/*
+			 * We don't support this control, so STALL the control
+			 * ep.
+			 */
+			resp->length = -EL2HLT;
+			/*
+			 * If we were not supposed to handle this
+			 * 'cs', prepare a Request Error Code response.
+			 */
+			dev->request_error_code.data[0] = 0x06;
+			dev->request_error_code.length = 1;
+			break;
+		}
+		break;
+
+	/* processing unit 'UVC_VC_PROCESSING_UNIT' */
+	case 2:
+		switch (cs) {
+		/*
+		 * We support only 'UVC_PU_BRIGHTNESS_CONTROL' for Processing
+		 * Unit, as our bmControls[0] = 1 for PU.
+		 */
+		case UVC_PU_BRIGHTNESS_CONTROL:
+			switch (req) {
+			case UVC_SET_CUR:
+				resp->data[0] = 0x0;
+				resp->length = len;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_MIN:
+				resp->data[0] = PU_BRIGHTNESS_MIN_VAL;
+				resp->length = 2;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_MAX:
+				resp->data[0] = PU_BRIGHTNESS_MAX_VAL;
+				resp->length = 2;
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_CUR:
+				resp->length = 2;
+				memcpy(&resp->data[0], &dev->brightness_val,
+						resp->length);
+				/*
+				 * For every successfully handled control
+				 * request set the request error code to no
+				 * error
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_INFO:
+				/*
+				 * We support Set and Get requests and don't
+				 * support async updates on an interrupt endpt
+				 */
+				resp->data[0] = 0x03;
+				resp->length = 1;
+				/*
+				 * For every successfully handled control
+				 * request, set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_DEF:
+				resp->data[0] = PU_BRIGHTNESS_DEFAULT_VAL;
+				resp->length = 2;
+				/*
+				 * For every successfully handled control
+				 * request, set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			case UVC_GET_RES:
+				resp->data[0] = PU_BRIGHTNESS_STEP_SIZE;
+				resp->length = 2;
+				/*
+				 * For every successfully handled control
+				 * request, set the request error code to no
+				 * error.
+				 */
+				dev->request_error_code.data[0] = 0x00;
+				dev->request_error_code.length = 1;
+				break;
+			default:
+				/*
+				 * We don't support this control, so STALL the
+				 * default control ep.
+				 */
+				resp->length = -EL2HLT;
+				/*
+				 * For every unsupported control request
+				 * set the request error code to appropriate
+				 * code.
+				 */
+				dev->request_error_code.data[0] = 0x07;
+				dev->request_error_code.length = 1;
+				break;
+			}
+			break;
+
+		default:
+			/*
+			 * We don't support this control, so STALL the control
+			 * ep.
+			 */
+			resp->length = -EL2HLT;
+			/*
+			 * If we were not supposed to handle this
+			 * 'cs', prepare a Request Error Code response.
+			 */
+			dev->request_error_code.data[0] = 0x06;
+			dev->request_error_code.length = 1;
+			break;
+		}
+
+		break;
+
+	default:
+		/*
+		 * If we were not supposed to handle this
+		 * 'cs', prepare a Request Error Code response.
+		 */
+		dev->request_error_code.data[0] = 0x06;
+		dev->request_error_code.length = 1;
+		break;
+
+	}
+
 	printf("control request (req %02x cs %02x)\n", req, cs);
-	(void)dev;
-	(void)resp;
 }
 
 static void
@@ -434,7 +1742,7 @@ uvc_events_process_streaming(struct uvc_device *dev, uint8_t req, uint8_t cs,
 		break;
 
 	case UVC_GET_RES:
-		memset(ctrl, 0, sizeof *ctrl);
+		CLEAR(ctrl);
 		break;
 
 	case UVC_GET_LEN:
@@ -459,11 +1767,15 @@ uvc_events_process_class(struct uvc_device *dev, struct usb_ctrlrequest *ctrl,
 
 	switch (ctrl->wIndex & 0xff) {
 	case UVC_INTF_CONTROL:
-		uvc_events_process_control(dev, ctrl->bRequest, ctrl->wValue >> 8, resp);
+		uvc_events_process_control(dev, ctrl->bRequest,
+						ctrl->wValue >> 8,
+						ctrl->wIndex >> 8,
+						ctrl->wLength, resp);
 		break;
 
 	case UVC_INTF_STREAMING:
-		uvc_events_process_streaming(dev, ctrl->bRequest, ctrl->wValue >> 8, resp);
+		uvc_events_process_streaming(dev, ctrl->bRequest,
+						ctrl->wValue >> 8, resp);
 		break;
 
 	default:
@@ -477,9 +1789,11 @@ uvc_events_process_setup(struct uvc_device *dev, struct usb_ctrlrequest *ctrl,
 {
 	dev->control = 0;
 
-	printf("bRequestType %02x bRequest %02x wValue %04x wIndex %04x "
+#ifdef ENABLE_USB_REQUEST_DEBUG
+	printf("\nbRequestType %02x bRequest %02x wValue %04x wIndex %04x "
 		"wLength %04x\n", ctrl->bRequestType, ctrl->bRequest,
 		ctrl->wValue, ctrl->wIndex, ctrl->wLength);
+#endif
 
 	switch (ctrl->bRequestType & USB_TYPE_MASK) {
 	case USB_TYPE_STANDARD:
@@ -495,16 +1809,74 @@ uvc_events_process_setup(struct uvc_device *dev, struct usb_ctrlrequest *ctrl,
 	}
 }
 
-static void
+static int
+uvc_events_process_control_data(struct uvc_device *dev,
+				uint8_t cs, uint8_t entity_id,
+				struct uvc_request_data *data)
+{
+	switch (entity_id) {
+	/* Processing unit 'UVC_VC_PROCESSING_UNIT'. */
+	case 2:
+		switch (cs) {
+		/*
+		 * We support only 'UVC_PU_BRIGHTNESS_CONTROL' for Processing
+		 * Unit, as our bmControls[0] = 1 for PU.
+		 */
+		case UVC_PU_BRIGHTNESS_CONTROL:
+			memcpy(&dev->brightness_val, data->data, data->length);
+			/* UVC - V4L2 integrated path. */
+			if (!dev->run_standalone)
+				/*
+				 * Try to change the Brightness attribute on
+				 * Video capture device. Note that this try may
+				 * succeed or end up with some error on the
+				 * video capture side. By default to keep tools
+				 * like USBCV's UVC test suite happy, we are
+				 * maintaining a local copy of the current
+				 * brightness value in 'dev->brightness_val'
+				 * variable and we return the same value to the
+				 * Host on receiving a GET_CUR(BRIGHTNESS)
+				 * control request.
+				 *
+				 * FIXME: Keeping in view the point discussed
+				 * above, notice that we ignore the return value
+				 * from the function call below. To be strictly
+				 * compliant, we should return the same value
+				 * accordingly.
+				 */
+				v4l2_set_ctrl(dev->vdev, dev->brightness_val,
+						V4L2_CID_BRIGHTNESS);
+			break;
+
+		default:
+			break;
+		}
+
+		break;
+
+	default:
+		break;
+	}
+
+	printf("Control Request data phase (cs %02x entity %02x)\n",
+			cs, entity_id);
+
+	return 0;
+}
+
+static int
 uvc_events_process_data(struct uvc_device *dev, struct uvc_request_data *data)
 {
 	struct uvc_streaming_control *target;
 	struct uvc_streaming_control *ctrl;
+	struct v4l2_format fmt;
 	const struct uvc_format_info *format;
 	const struct uvc_frame_info *frame;
 	const unsigned int *interval;
 	unsigned int iformat, iframe;
 	unsigned int nframes;
+	unsigned int *val = (unsigned int *)data->data;
+	int ret;
 
 	switch (dev->control) {
 	case UVC_VS_PROBE_CONTROL:
@@ -519,7 +1891,26 @@ uvc_events_process_data(struct uvc_device *dev, struct uvc_request_data *data)
 
 	default:
 		printf("setting unknown control, length = %d\n", data->length);
-		return;
+
+		/*
+		 * As we support only BRIGHTNESS control, this request is
+		 * for setting BRIGHTNESS control.
+		 * Check for any invalid SET_CUR(BRIGHTNESS) requests
+		 * from Host. Note that we support Brightness levels
+		 * from 0x0 to 0x10 in a step of 0x1. So, any request
+		 * with value greater than 0x10 is invalid.
+		 */
+		if (*val > PU_BRIGHTNESS_MAX_VAL) {
+			return -EINVAL;
+		} else {
+			ret = uvc_events_process_control_data(dev,
+						UVC_PU_BRIGHTNESS_CONTROL,
+						2, data);
+			if (ret < 0)
+				goto err;
+
+			return 0;
+		}
 	}
 
 	ctrl = (struct uvc_streaming_control *)&data->data;
@@ -557,10 +1948,55 @@ uvc_events_process_data(struct uvc_device *dev, struct uvc_request_data *data)
 		dev->width = frame->width;
 		dev->height = frame->height;
 
-		uvc_video_set_format(dev);
-		if (dev->bulk)
-			uvc_video_stream(dev, 1);
+		/*
+		 * Try to set the default format at the V4L2 video capture
+		 * device as requested by the user.
+		 */
+		CLEAR(fmt);
+
+		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		fmt.fmt.pix.field = V4L2_FIELD_ANY;
+		fmt.fmt.pix.width = frame->width;
+		fmt.fmt.pix.height = frame->height;
+		fmt.fmt.pix.pixelformat = format->fcc;
+
+		switch (format->fcc) {
+		case V4L2_PIX_FMT_YUYV:
+			fmt.fmt.pix.sizeimage =
+				(fmt.fmt.pix.width * fmt.fmt.pix.height * 2);
+			break;
+		case V4L2_PIX_FMT_MJPEG:
+			fmt.fmt.pix.sizeimage = dev->imgsize;
+			break;
+		}
+
+		/*
+		 * As per the new commit command received from the UVC host
+		 * change the current format selection at both UVC and V4L2
+		 * sides.
+		 */
+		ret = uvc_video_set_format(dev);
+		if (ret < 0)
+			goto err;
+
+		if (!dev->run_standalone) {
+			/* UVC - V4L2 integrated path. */
+			ret = v4l2_set_format(dev->vdev, &fmt);
+			if (ret < 0)
+				goto err;
+		}
+
+		if (dev->bulk) {
+			ret = uvc_handle_streamon_event(dev);
+			if (ret < 0)
+				goto err;
+		}
 	}
+
+	return 0;
+
+err:
+	return ret;
 }
 
 static void
@@ -571,7 +2007,7 @@ uvc_events_process(struct uvc_device *dev)
 	struct uvc_request_data resp;
 	int ret;
 
-	ret = ioctl(dev->fd, VIDIOC_DQEVENT, &v4l2_event);
+	ret = ioctl(dev->uvc_fd, VIDIOC_DQEVENT, &v4l2_event);
 	if (ret < 0) {
 		printf("VIDIOC_DQEVENT failed: %s (%d)\n", strerror(errno),
 			errno);
@@ -583,7 +2019,12 @@ uvc_events_process(struct uvc_device *dev)
 
 	switch (v4l2_event.type) {
 	case UVC_EVENT_CONNECT:
+		return;
+
 	case UVC_EVENT_DISCONNECT:
+		dev->uvc_shutdown_requested = 1;
+		printf("UVC: Possible USB shutdown requested from "
+				"Host, seen via UVC_EVENT_DISCONNECT\n");
 		return;
 
 	case UVC_EVENT_SETUP:
@@ -591,21 +2032,39 @@ uvc_events_process(struct uvc_device *dev)
 		break;
 
 	case UVC_EVENT_DATA:
-		uvc_events_process_data(dev, &uvc_event->data);
+		ret = uvc_events_process_data(dev, &uvc_event->data);
+		if (ret < 0)
+			break;
+
 		return;
 
 	case UVC_EVENT_STREAMON:
-		uvc_video_reqbufs(dev, 4);
-		uvc_video_stream(dev, 1);
-		break;
+		if (!dev->bulk)
+			uvc_handle_streamon_event(dev);
+		return;
 
 	case UVC_EVENT_STREAMOFF:
-		uvc_video_stream(dev, 0);
-		uvc_video_reqbufs(dev, 0);
-		break;
+		/* Stop V4L2 streaming... */
+		if (!dev->run_standalone && dev->vdev->is_streaming) {
+			/* UVC - V4L2 integrated path. */
+			v4l2_stop_capturing(dev->vdev);
+			v4l2_uninit_device(dev->vdev);
+			v4l2_reqbufs(dev->vdev, 0);
+			dev->vdev->is_streaming = 0;
+		}
+
+		/* ... and now UVC streaming.. */
+		if (dev->is_streaming) {
+			uvc_video_stream(dev, 0);
+			uvc_uninit_device(dev);
+			uvc_video_reqbufs(dev, 0);
+			dev->is_streaming = 0;
+		}
+
+		return;
 	}
 
-	ioctl(dev->fd, UVCIOC_SEND_RESPONSE, &resp);
+	ret = ioctl(dev->uvc_fd, UVCIOC_SEND_RESPONSE, &resp);
 	if (ret < 0) {
 		printf("UVCIOC_S_EVENT failed: %s (%d)\n", strerror(errno),
 			errno);
@@ -617,33 +2076,42 @@ static void
 uvc_events_init(struct uvc_device *dev)
 {
 	struct v4l2_event_subscription sub;
+	unsigned int payload_size;
+
+	switch (dev->fcc) {
+	case V4L2_PIX_FMT_YUYV:
+		payload_size = dev->width * dev->height * 2;
+		break;
+	case V4L2_PIX_FMT_MJPEG:
+		payload_size = dev->imgsize;
+		break;
+	}
 
 	uvc_fill_streaming_control(dev, &dev->probe, 0, 0);
 	uvc_fill_streaming_control(dev, &dev->commit, 0, 0);
 
-	if (dev->bulk) {
+	if (dev->bulk)
 		/* FIXME Crude hack, must be negotiated with the driver. */
-		dev->probe.dwMaxPayloadTransferSize = 16 * 1024;
-		dev->commit.dwMaxPayloadTransferSize = 16 * 1024;
-	}
-
+		dev->probe.dwMaxPayloadTransferSize =
+			dev->commit.dwMaxPayloadTransferSize = payload_size;
 
 	memset(&sub, 0, sizeof sub);
 	sub.type = UVC_EVENT_SETUP;
-	ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
+	ioctl(dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
 	sub.type = UVC_EVENT_DATA;
-	ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
+	ioctl(dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
 	sub.type = UVC_EVENT_STREAMON;
-	ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
+	ioctl(dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
 	sub.type = UVC_EVENT_STREAMOFF;
-	ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
+	ioctl(dev->uvc_fd, VIDIOC_SUBSCRIBE_EVENT, &sub);
 }
 
 /* ---------------------------------------------------------------------------
  * main
  */
 
-static void image_load(struct uvc_device *dev, const char *img)
+static void
+image_load(struct uvc_device *dev, const char *img)
 {
 	int fd = -1;
 
@@ -669,76 +2137,352 @@ static void image_load(struct uvc_device *dev, const char *img)
 	close(fd);
 }
 
-static void usage(const char *argv0)
+static void
+usage(const char *argv0)
 {
 	fprintf(stderr, "Usage: %s [options]\n", argv0);
 	fprintf(stderr, "Available options are\n");
 	fprintf(stderr, " -b		Use bulk mode\n");
-	fprintf(stderr, " -d device	Video device\n");
+	fprintf(stderr, " -d		Do not use any real V4L2 capture device\n");
+	fprintf(stderr, " -f <format>    Select frame format\n\t"
+				"0 = V4L2_PIX_FMT_YUYV\n\t"
+				"1 = V4L2_PIX_FMT_MJPEG\n");
 	fprintf(stderr, " -h		Print this help screen and exit\n");
 	fprintf(stderr, " -i image	MJPEG image\n");
+	fprintf(stderr, " -m		Streaming mult for ISOC (b/w 0 and 2)\n");
+	fprintf(stderr, " -n		Number of Video buffers (b/w 2 and 32)\n");
+	fprintf(stderr, " -o <IO method> Select UVC IO method:\n\t"
+				"0 = MMAP\n\t"
+				"1 = USER_PTR\n");
+	fprintf(stderr, " -r <resolution> Select frame resolution:\n\t"
+				"0 = 360p, VGA (640x360)\n\t"
+				"1 = 720p, WXGA (1280x720)\n");
+	fprintf(stderr, " -s <speed>	Select USB bus speed (b/w 0 and 2)\n\t"
+				"0 = Full Speed (FS)\n\t"
+				"1 = High Speed (HS)\n\t"
+				"2 = Super Speed (SS)\n");
+	fprintf(stderr, " -t		Streaming burst (b/w 0 and 15)\n");
+	fprintf(stderr, " -u device	UVC Video Output device\n");
+	fprintf(stderr, " -v device	V4L2 Video Capture device\n");
+
 }
 
-int main(int argc, char *argv[])
+int
+main(int argc, char *argv[])
 {
-	char *device = "/dev/video0";
-	struct uvc_device *dev;
-	int bulk_mode = 0;
+	struct uvc_device *udev;
+	struct v4l2_device *vdev;
+	struct timeval tv;
+	struct v4l2_format fmt;
+	char *uvc_devname = "/dev/video0";
+	char *v4l2_devname = "/dev/video1";
 	char *mjpeg_image = NULL;
-	fd_set fds;
-	int ret, opt;
-
-	while ((opt = getopt(argc, argv, "bd:hi:")) != -1) {
+	fd_set fdsv, fdsu;
+	int ret, opt, nfds;
+	int bulk_mode = 0;
+	int dummy_data_gen_mode = 0;
+	/* Frame format/resolution related params. */
+	int default_format = 0;		/* V4L2_PIX_FMT_YUYV */
+	int default_resolution = 0;	/* VGA 360p */
+	int nbufs = 2;			/* Ping-Pong buffers */
+	/* USB speed related params */
+	int mult = 0;
+	int burst = 0;
+	enum usb_device_speed speed = USB_SPEED_SUPER;	/* High-Speed */
+	enum io_method uvc_io_method = IO_METHOD_USERPTR;
+
+	while ((opt = getopt(argc, argv, "bdf:hi:m:n:o:r:s:t:u:v:")) != -1) {
 		switch (opt) {
 		case 'b':
 			bulk_mode = 1;
 			break;
 
 		case 'd':
-			device = optarg;
+			dummy_data_gen_mode = 1;
+			break;
+
+		case 'f':
+			if (atoi(optarg) < 0 && atoi(optarg) > 1) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			default_format = atoi(optarg);
 			break;
 
 		case 'h':
 			usage(argv[0]);
-			return 0;
+			return 1;
 
 		case 'i':
 			mjpeg_image = optarg;
 			break;
 
+		case 'm':
+			if (atoi(optarg) < 0 && atoi(optarg) > 2) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			mult = atoi(optarg);
+			printf("Requested Mult value = %d\n", mult);
+			break;
+
+		case 'n':
+			if (atoi(optarg) < 2 && atoi(optarg) > 32) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			nbufs = atoi(optarg);
+			printf("Number of buffers requested = %d\n", nbufs);
+			break;
+
+		case 'o':
+			if (atoi(optarg) < 0 && atoi(optarg) > 1) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			uvc_io_method = atoi(optarg);
+			printf("UVC: IO method requested is %s\n",
+					(uvc_io_method == IO_METHOD_MMAP) ?
+					"MMAP": "USER_PTR");
+			break;
+
+		case 'r':
+			if (atoi(optarg) < 0 && atoi(optarg) > 1) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			default_resolution = atoi(optarg);
+			break;
+
+		case 's':
+			if (atoi(optarg) < 0 && atoi(optarg) > 2) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			speed = atoi(optarg);
+			break;
+
+		case 't':
+			if (atoi(optarg) < 0 && atoi(optarg) > 15) {
+				usage(argv[0]);
+				return 1;
+			}
+
+			burst = atoi(optarg);
+			printf("Requested Burst value = %d\n", burst);
+			break;
+
+		case 'u':
+			uvc_devname = optarg;
+			break;
+
+		case 'v':
+			v4l2_devname = optarg;
+			break;
+
 		default:
-			fprintf(stderr, "Invalid option '-%c'\n", opt);
+			printf("Invalid option '-%c'\n", opt);
 			usage(argv[0]);
 			return 1;
 		}
 	}
 
-	dev = uvc_open(device);
-	if (dev == NULL)
+	if (!dummy_data_gen_mode && !mjpeg_image) {
+		/*
+		 * Try to set the default format at the V4L2 video capture
+		 * device as requested by the user.
+		 */
+		CLEAR(fmt);
+		fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		fmt.fmt.pix.width = (default_resolution == 0) ? 640 : 1280;
+		fmt.fmt.pix.height = (default_resolution == 0) ? 360 : 720;
+		fmt.fmt.pix.sizeimage = (default_format == 0) ?
+			(fmt.fmt.pix.width * fmt.fmt.pix.height * 2) :
+			(fmt.fmt.pix.width * fmt.fmt.pix.height * 1.5);
+		fmt.fmt.pix.pixelformat = (default_format == 0) ?
+			V4L2_PIX_FMT_YUYV : V4L2_PIX_FMT_JPEG;
+		fmt.fmt.pix.field = V4L2_FIELD_ANY;
+
+		/* Open the V4L2 device. */
+		ret = v4l2_open(&vdev, v4l2_devname, &fmt);
+		if (vdev == NULL || ret < 0)
+			return 1;
+	}
+
+	/* Open the UVC device. */
+	ret = uvc_open(&udev, uvc_devname);
+	if (udev == NULL || ret < 0)
 		return 1;
 
-	image_load(dev, mjpeg_image);
+	udev->uvc_devname = uvc_devname;
+
+	if (!dummy_data_gen_mode && !mjpeg_image) {
+		vdev->v4l2_devname = v4l2_devname;
+		/* Bind UVC and V4L2 devices. */
+		udev->vdev = vdev;
+		vdev->udev = udev;
+	}
+
+	/* Set parameters as passed by user. */
+	udev->width = (default_resolution == 0) ? 640 : 1280;
+	udev->height = (default_resolution == 0) ? 360 : 720;
+	udev->imgsize = (default_format == 0) ?
+			(udev->width * udev->height * 2) :
+			(udev->width * udev->height * 1.5);
+	udev->fcc = (default_format == 0) ? V4L2_PIX_FMT_YUYV :
+			V4L2_PIX_FMT_JPEG;
+	udev->io = uvc_io_method;
+	udev->bulk = bulk_mode;
+	udev->nbufs = nbufs;
+	udev->mult = mult;
+	udev->burst = burst;
+	udev->speed = speed;
+
+	if (dummy_data_gen_mode || mjpeg_image)
+		/* UVC standalone setup. */
+		udev->run_standalone = 1;
+
+	if (!dummy_data_gen_mode && !mjpeg_image) {
+		/* UVC - V4L2 integrated path */
+		vdev->nbufs = nbufs;
+
+		/*
+		 * IO methods used at UVC and V4L2 domains must be
+		 * complementary to avoid any memcpy from the CPU.
+		 */
+		switch (uvc_io_method) {
+		case IO_METHOD_MMAP:
+			vdev->io = IO_METHOD_USERPTR;
+			break;
+
+		case IO_METHOD_USERPTR:
+		default:
+			vdev->io = IO_METHOD_MMAP;
+			break;
+		}
+	}
+
+	switch (speed) {
+	case USB_SPEED_FULL:
+		/* Full Speed. */
+		if (bulk_mode)
+			udev->maxpkt = 64;
+		else
+			udev->maxpkt = 1023;
+		break;
+
+	case USB_SPEED_HIGH:
+		/* High Speed. */
+		if (bulk_mode)
+			udev->maxpkt = 512;
+		else
+			udev->maxpkt = 1024;
+		break;
+
+	case USB_SPEED_SUPER:
+	default:
+		/* Super Speed. */
+		if (bulk_mode)
+			udev->maxpkt = 1024;
+		else
+			udev->maxpkt = 1024;
+		break;
+	}
 
-	dev->bulk = bulk_mode;
+	if (!dummy_data_gen_mode && !mjpeg_image &&
+	   (IO_METHOD_MMAP == vdev->io)) {
+		/*
+		 * Ensure that the V4L2 video capture device has already some
+		 * buffers queued.
+		 */
+		v4l2_reqbufs(vdev, vdev->nbufs);
+		v4l2_qbuf(vdev);
+	}
 
-	uvc_events_init(dev);
-	uvc_video_init(dev);
+	if (mjpeg_image)
+		image_load(udev, mjpeg_image);
 
-	FD_ZERO(&fds);
-	FD_SET(dev->fd, &fds);
+	/* Init UVC events. */
+	uvc_events_init(udev);
 
 	while (1) {
-		fd_set efds = fds;
-		fd_set wfds = fds;
+		if (!dummy_data_gen_mode && !mjpeg_image)
+			FD_ZERO(&fdsv);
+
+		FD_ZERO(&fdsu);
+
+		/* We want both setup and data events on UVC interface.. */
+		FD_SET(udev->uvc_fd, &fdsu);
+
+		fd_set efds = fdsu;
+		fd_set dfds = fdsu;
+
+		/* ..but only data events on V4L2 interface */
+		if (!dummy_data_gen_mode && !mjpeg_image)
+			FD_SET(vdev->v4l2_fd, &fdsv);
 
-		ret = select(dev->fd + 1, NULL, &wfds, &efds, NULL);
-		if (FD_ISSET(dev->fd, &efds))
-			uvc_events_process(dev);
-		if (FD_ISSET(dev->fd, &wfds))
-			uvc_video_process(dev);
+		/* Timeout. */
+		tv.tv_sec = 2;
+		tv.tv_usec = 0;
+
+		if (!dummy_data_gen_mode && !mjpeg_image) {
+			nfds = max(vdev->v4l2_fd, udev->uvc_fd);
+			ret = select(nfds + 1, &fdsv, &dfds, &efds, &tv);
+		} else {
+			ret = select(udev->uvc_fd + 1, NULL,
+					&dfds, &efds, NULL);
+		}
+
+		if (-1 == ret) {
+			printf("select error %d, %s\n",
+					errno, strerror (errno));
+			if (EINTR == errno)
+				continue;
+
+			break;
+		}
+
+		if (0 == ret) {
+			printf("select timeout\n");
+			break;
+		}
+
+		if (FD_ISSET(udev->uvc_fd, &efds))
+			uvc_events_process(udev);
+		if (FD_ISSET(udev->uvc_fd, &dfds))
+			uvc_video_process(udev);
+		if (!dummy_data_gen_mode && !mjpeg_image)
+			if (FD_ISSET(vdev->v4l2_fd, &fdsv))
+				v4l2_process_data(vdev);
+	}
+
+	if (!dummy_data_gen_mode && !mjpeg_image && vdev->is_streaming) {
+		/* Stop V4L2 streaming... */
+		v4l2_stop_capturing(vdev);
+		v4l2_uninit_device(vdev);
+		v4l2_reqbufs(vdev, 0);
+		vdev->is_streaming = 0;
 	}
 
-	uvc_close(dev);
+	if (udev->is_streaming) {
+		/* ... and now UVC streaming.. */
+		uvc_video_stream(udev, 0);
+		uvc_uninit_device(udev);
+		uvc_video_reqbufs(udev, 0);
+		udev->is_streaming = 0;
+	}
+
+	if (!dummy_data_gen_mode && !mjpeg_image)
+		v4l2_close(vdev);
+
+	uvc_close(udev);
+
 	return 0;
 }
 
-- 
1.7.2.2

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux