All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC] USB Video Class device emulation.
@ 2021-06-06 17:30 Raphael Lisicki
  0 siblings, 0 replies; only message in thread
From: Raphael Lisicki @ 2021-06-06 17:30 UTC (permalink / raw
  To: qemu-devel; +Cc: claunia

This continues work started by Natalia Portillo <claunia@claunia.com>, as
submitted here: https://lists.nongnu.org/archive/html/qemu-devel/2010-06/msg01126.html

I have updated the changes so that the patch now applies to v4.2.1. Video
input has been expanded to support user-mode buffers so that an UVC device
itself can be used as input for this module.

My work on this has already stalled some weeks ago and I am submitting it in
case somebody might be interested.

Best regards
Raphael
---
 hw/usb/Makefile.objs |    1 +
 hw/usb/usb-uvc.c     | 1435 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1436 insertions(+)
 create mode 100644 hw/usb/usb-uvc.c

diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 303ac084a0..83e460af8a 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -26,6 +26,7 @@ common-obj-$(CONFIG_USB_AUDIO)        += dev-audio.o
 common-obj-$(CONFIG_USB_SERIAL)       += dev-serial.o
 common-obj-$(CONFIG_USB_NETWORK)      += dev-network.o
 common-obj-$(CONFIG_USB_BLUETOOTH)    += dev-bluetooth.o
+common-obj-$(CONFIG_LINUX)            += usb-uvc.o
 
 ifeq ($(CONFIG_USB_SMARTCARD),y)
 common-obj-y                          += dev-smartcard-reader.o
diff --git a/hw/usb/usb-uvc.c b/hw/usb/usb-uvc.c
new file mode 100644
index 0000000000..a7ffba68d8
--- /dev/null
+++ b/hw/usb/usb-uvc.c
@@ -0,0 +1,1435 @@
+/*
+ * USB Video Class Device emulation.
+ *
+ * Copyright (c) 2010 Claunia.com
+ * Written by Natalia Portillo <natalia@claunia.com>
+ *
+ * Based on hw/usb-hid.c:
+ * Copyright (c) 2005 Fabrice Bellard
+ *
+ * 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 in its version 2.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "usb.h"
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/qdev-properties.h"
+#include "hw/hw.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "qapi/error.h"
+// V4L2 ioctls
+#include <sys/ioctl.h>
+#include <linux/videodev2.h>
+
+#define DEBUG_UVC
+
+#ifdef DEBUG_UVC
+#define DPRINTF(fmt, ...) \
+do { printf("usb-uvc: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while(0)
+#endif
+
+/* USB Video Class Request codes */
+#define USB_UVC_RC_UNDEFINED	0x00
+#define USB_UVC_SET_CUR			0x01
+#define USB_UVC_GET_CUR			0x81
+#define USB_UVC_GET_MIN			0x82
+#define USB_UVC_GET_MAX			0x83
+#define USB_UVC_GET_RES			0x84
+#define USB_UVC_GET_LEN			0x85
+#define USB_UVC_GET_INFO		0x86
+#define USB_UVC_GET_DEF			0x87
+
+/* USB Video Class Request types */
+#define UVCSetVideoControl		0x2100
+#define UVCSetVideoStreaming	0x2200
+#define UVCGetVideoControl		0xA100
+#define UVCGetVideoStreaming	0xA200
+
+
+enum v4l2_api {
+	V4L2_API_UNKOWN = 0,
+	V4L2_API_READ = 1,
+	V4L2_API_USERPTRS = 2,
+};
+
+
+
+typedef struct USBUVCState {
+    USBDevice dev;
+	char	current_input;
+	char	*v4l2_device;
+	size_t	height;
+	size_t	width;
+
+
+	int v4l2_fd;
+	enum v4l2_api v4l2_api;
+	char *frame;
+	char *frame_start;
+	char* frame_storage[3];
+	int frame_id;
+	int frame_remaining_bytes;
+	int frame_max_length;
+
+	/* values in 16bit, as by UVC. Lets assume the 64bit values from V4L2 fit. Report error if the assumption breaks. */
+	struct {
+		bool supported;
+		int16_t minimum;
+		int16_t maximum;
+		int16_t resolution;
+		int16_t default_value;
+		int16_t current_value;
+	} brightness;
+} USBUVCState;
+
+
+
+static void get_frame_read_api(USBUVCState *s)
+{
+	DPRINTF("Getting frame.\n");
+	s->frame = s->frame_start;
+	int frame_length = read(s->v4l2_fd, s->frame+2, s->frame_max_length);
+	
+	if(frame_length == -1)
+	{
+		DPRINTF("Error while reading frame. errno %d\n", errno);
+	}
+	else
+	{
+		s->frame[0] = 2;
+		s->frame_id = s->frame_id ^ 1;
+		s->frame[1] = 0x82 | s->frame_id;
+		s->frame_remaining_bytes = frame_length+2;
+		DPRINTF("Got a frame of %d bytes.\n", frame_length);
+	}
+	
+	return;
+}
+
+
+
+static void get_frame_userptrs_api(USBUVCState *s)
+{
+	struct v4l2_buffer buf;
+	memset(&buf, 0, sizeof(buf));
+	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	buf.memory = V4L2_MEMORY_USERPTR;
+	//printf("starting VIDIOC_DQBUF ioctl\n");
+	int ret_err = ioctl(s->v4l2_fd,  VIDIOC_DQBUF, &buf);
+	//printf("ended VIDIOC_DQBUF ioctl\n");
+	if(ret_err==-1)
+	{
+		switch(errno)
+		{
+			case EINVAL:
+				printf("VIDIOC_DQBUF einval.\n");
+				break;
+			default:
+				printf("VIDIOC_DQBUF returned unknown error %d.\n", errno);
+				break;
+		}
+		printf("V4L2 IOCTL VIDIOC_DQBUF failed\n");
+		return;
+	}
+
+	char* ptr = (void*)buf.m.userptr;
+	//printf("got frame of length %d from DMA buf %d, starting at %p\n", buf.bytesused, buf.index, ptr);
+
+
+	buf.m.userptr = (unsigned long)s->frame_start+2;
+
+	s->frame_start = ptr - 2;
+	s->frame = s->frame_start;
+
+	int frame_length = buf.bytesused;
+
+	s->frame[0] = 2;
+	s->frame_id = s->frame_id ^ 1;
+	s->frame[1] = 0x82 | s->frame_id;
+	s->frame_remaining_bytes = frame_length+2;
+
+
+	//printf("starting VIDIOC_QBUF ioctl\n");
+	ret_err = ioctl(s->v4l2_fd,  VIDIOC_QBUF, &buf);
+	//printf("ended VIDIOC_QBUF ioctl\n");
+	if(ret_err==-1)
+	{
+		printf("VIDIOC_QBUF failed\n");
+		return;
+	}
+}
+
+static void get_frame_read(USBUVCState *s)
+{
+	switch (s->v4l2_api) {
+		case V4L2_API_READ:
+			return get_frame_read_api(s);
+		case V4L2_API_USERPTRS:
+			return get_frame_userptrs_api(s);
+		default:
+			DPRINTF("Unhandled api type %d\n", s->v4l2_api);
+	}
+}
+
+
+
+static void usb_uvc_handle_reset(USBDevice *dev)
+{
+	DPRINTF("Reset called\n");
+}
+
+
+
+static int assign_query_control(int fd, int cid, struct v4l2_queryctrl* queryctrl)
+{
+
+
+memset(queryctrl, 0, sizeof(struct v4l2_queryctrl));
+queryctrl->id = cid;
+
+if (-1 == ioctl(fd, VIDIOC_QUERYCTRL, queryctrl)) {
+    if (errno != EINVAL) {
+        perror("assign_query_control");
+        exit(EXIT_FAILURE);
+    } else {
+        printf("V4L2_CID_BRIGHTNESS is not supportedn");
+    }
+} else if (queryctrl->flags & V4L2_CTRL_FLAG_DISABLED) {
+    printf("V4L2_CID_BRIGHTNESS is not supportedn");
+} else {
+	return 0;
+}
+	return -1;
+}
+
+
+static void usb_uvc_handle_control(USBDevice *dev, USBPacket *p, int request, int value,
+								  int index, int length, uint8_t *data)
+{
+	int ret = 0;
+	USBUVCState *s = DO_UPCAST(USBUVCState, dev, dev);
+	
+	DPRINTF("Control called\n");
+	//	DPRINTF("Request: 0x%08X\n", request);
+	//	DPRINTF("Value: 0x%08X\n", value);
+	//	DPRINTF("Index: 0x%08X\n", index);
+	//	DPRINTF("Length: 0x%08X\n", length);
+
+      ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+      if (ret >= 0) {
+          DPRINTF("Control handled generically\n");
+          return;
+      }
+	ret = 0;
+
+	struct commit_control_msg {
+		uint16_t bmHint;
+		uint8_t bFormatIndex;
+		uint8_t bFrameIndex;
+		uint32_t dwFrameInterval;
+		uint16_t wKeyFrameRate;
+		uint16_t wPFrameRate;
+		uint16_t wCompQuality;
+		uint16_t wCompWindowSize;
+		uint16_t wDelay;
+		uint32_t dwMaxVideoFrameSize;
+		uint32_t dwMaxPayloadTransferSize;
+	} QEMU_PACKED;
+	QEMU_BUILD_BUG_ON(sizeof (struct commit_control_msg) != 26);
+
+	
+	switch(request)
+	{
+		case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_CUR:
+			ret = 0;
+			
+			if((index&0xFF) == 0x01 && ((value&0xFF00) == 0x0100 || (value&0xFF00) == 0x0200))
+			{
+				DPRINTF("USB Request: Get video control current setting attribute for interface %d\n", index&0xFF);
+				if((value&0xFF00) == 0x0100)
+					DPRINTF("\tVS_PROBE_CONTROL\n");
+				else
+					DPRINTF("\tVS_COMMIT_CONTROL\n");
+				
+				if(length != 26)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 26 bytes\n", length);
+					goto fail;
+				}
+
+				struct commit_control_msg msg;
+				memset(&msg, 0, sizeof(msg));
+				memcpy(&msg, data, MIN(length, sizeof(msg)));
+
+
+				msg.dwFrameInterval = cpu_to_le32(666666);
+				msg.wKeyFrameRate = cpu_to_le16(1);
+				msg.wPFrameRate = cpu_to_le16(0);
+				msg.wCompQuality = cpu_to_le16(0);
+				msg.wCompWindowSize = cpu_to_le16(1);
+				msg.wDelay = cpu_to_le16(0x20);
+				msg.dwMaxVideoFrameSize = cpu_to_le32(s->frame_max_length);
+				msg.dwMaxPayloadTransferSize = cpu_to_le32(s->frame_max_length);
+				memcpy(data, &msg, sizeof(msg));
+				ret = sizeof(msg);
+			}
+			else if((index&0xFF00) == 0x0400 && (value&0xFF00) == 0x0100) // Setting input
+			{
+				DPRINTF("USB Request: Asking for current input\n");
+				if(length != 1)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 1 byte\n", length);
+					goto fail;
+				}
+				
+				data[0] = s->current_input;
+				ret = 1;
+			}
+			else if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Asking for current brightness\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				
+				if (s->brightness.supported) {
+					printf("current brightness value is %d\n", s->brightness.current_value);
+					int16_t for_writing = cpu_to_le16(s->brightness.current_value);
+					memcpy(data, &for_writing, 2);
+					ret = 2;
+				}
+				else {
+					data[0] = 1;
+					data[1] = 0;
+					ret = 2;
+				}
+			}
+			else
+				goto fail;
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_MIN:
+			ret = 0;
+			
+			if((index&0xFF) == 0x01 && ((value&0xFF00) == 0x0100 || (value&0xFF00) == 0x0200))
+			{
+				DPRINTF("USB Request: Get video control minimum setting attribute for interface %d\n", index&0xFF);
+				
+				if(length != 26)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 26 bytes\n", length);
+					goto fail;
+				}
+				
+				struct commit_control_msg msg;
+				memset(&msg, 0, sizeof(msg));
+				memcpy(&msg, data, MIN(length, sizeof(msg)));
+
+
+				msg.dwFrameInterval = cpu_to_le32(666666);
+				msg.wKeyFrameRate = cpu_to_le16(1);
+				msg.wPFrameRate = cpu_to_le16(0);
+				msg.wCompQuality = cpu_to_le16(0);
+				msg.wCompWindowSize = cpu_to_le16(1);
+				msg.wDelay = cpu_to_le16(0x20);
+				msg.dwMaxVideoFrameSize = cpu_to_le32(s->frame_max_length);
+				msg.dwMaxPayloadTransferSize = cpu_to_le32(s->frame_max_length);
+				memcpy(data, &msg, sizeof(msg));
+				ret = sizeof(msg);
+			}
+			else if((index&0xFF00) == 0x0400 && (value&0xFF00) == 0x0100) // Setting input
+			{
+				DPRINTF("USB Request: Asking for minimum input\n");
+				if(length != 1)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 1 byte\n", length);
+					goto fail;
+				}
+				
+				data[0] = 0;
+				ret = 1;
+			}
+			else if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Asking for minimum brightness\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				
+				if (s->brightness.supported) {
+					printf("min brightness value is %d\n", s->brightness.minimum);
+					int16_t for_writing = cpu_to_le16(s->brightness.minimum);
+					memcpy(data, &for_writing, 2);
+					ret = 2;
+				}
+				else {
+					data[0] = 1;
+					data[1] = 0;
+					ret = 2;
+				}
+			}
+			else
+				goto fail;
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_MAX:
+			if((index&0xFF) == 0x01 && ((value&0xFF00) == 0x0100 || (value&0xFF00) == 0x0200))
+			{
+				DPRINTF("USB Request: Get video control maximum setting attribute for interface %d\n", index&0xFF);
+				
+				if(length != 26)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 26 bytes\n", length);
+					goto fail;
+				}
+				
+				struct commit_control_msg msg;
+				memset(&msg, 0, sizeof(msg));
+				memcpy(&msg, data, MIN(length, sizeof(msg)));
+
+
+				msg.dwFrameInterval = cpu_to_le32(666666);
+				msg.wKeyFrameRate = cpu_to_le16(1);
+				msg.wPFrameRate = cpu_to_le16(0);
+				msg.wCompQuality = cpu_to_le16(0);
+				msg.wCompWindowSize = cpu_to_le16(1);
+				msg.wDelay = cpu_to_le16(0x20);
+				msg.dwMaxVideoFrameSize = cpu_to_le32(s->frame_max_length);
+				msg.dwMaxPayloadTransferSize = cpu_to_le32(s->frame_max_length);
+				memcpy(data, &msg, sizeof(msg));
+				ret = sizeof(msg);
+			}
+			else if((index&0xFF00) == 0x0400 && (value&0xFF00) == 0x0100) // Setting input
+			{
+				DPRINTF("USB Request: Asking maximum input\n");
+				if(length != 1)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 1 byte\n", length);
+					goto fail;
+				}
+				
+				data[0] = 1;
+				ret = 1;
+			}					
+			else if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Asking for maximum brightness\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				
+				if (s->brightness.supported) {
+					printf("max brightness value is %d\n", s->brightness.maximum);
+					int16_t for_writing = cpu_to_le16(s->brightness.maximum);
+					memcpy(data, &for_writing, 2);
+					ret = 2;
+				}
+				else {
+					data[0] = 1;
+					data[1] = 0;
+					ret = 2;
+				}
+			}
+			else
+				goto fail;
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_DEF:
+			if((index&0xFF) == 0x01 && ((value&0xFF00) == 0x0100 || (value&0xFF00) == 0x0200))
+			{
+				DPRINTF("USB Request: Get video control default setting attribute for interface %d\n", index&0xFF);
+				
+				if(length != 26)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 26 bytes\n", length);
+					goto fail;
+				}
+				
+				struct commit_control_msg msg;
+				memset(&msg, 0, sizeof(msg));
+				memcpy(&msg, data, MIN(length, sizeof(msg)));
+
+
+				msg.dwFrameInterval = cpu_to_le32(666666);
+				msg.wKeyFrameRate = cpu_to_le16(1);
+				msg.wPFrameRate = cpu_to_le16(0);
+				msg.wCompQuality = cpu_to_le16(0);
+				msg.wCompWindowSize = cpu_to_le16(1);
+				msg.wDelay = cpu_to_le16(0x20);
+				msg.dwMaxVideoFrameSize = cpu_to_le32(s->frame_max_length);
+				msg.dwMaxPayloadTransferSize = cpu_to_le32(s->frame_max_length);
+				memcpy(data, &msg, sizeof(msg));
+				ret = sizeof(msg);
+			}
+			else if((index&0xFF00) == 0x0400 && (value&0xFF00) == 0x0100) // Setting input
+			{
+				DPRINTF("USB Request: Asking for default input\n");
+				if(length != 1)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 1 byte\n", length);
+					goto fail;
+				}
+				
+				data[0] = 0;
+				ret = 1;
+			}
+			else if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Asking for default brightness\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				if (s->brightness.supported) {
+					printf("default value is %d\n", s->brightness.default_value);
+					int16_t for_writing = cpu_to_le16(s->brightness.default_value);
+					memcpy(data, &for_writing, 2);
+					ret = 2;
+				}
+				else {
+					data[0] = 1;
+					data[1] = 0;
+					ret = 2;
+				}
+			}
+			else
+				goto fail;
+			break;
+		case UVCSetVideoControl | USB_UVC_SET_CUR:
+			DPRINTF("USB Request: Set video control setting attribute for interface %d\n", index&0xFF);
+			
+			ret = 0;
+			
+			if((index&0xFF) == 0x01 && ((value&0xFF00) == 0x0100 || (value&0xFF00) == 0x0200))
+			{
+				if((value&0xFF00) == 0x0100)
+					DPRINTF("\tVS_PROBE_CONTROL\n");
+				else
+					DPRINTF("\tVS_COMMIT_CONTROL\n");
+				
+				if(length != 26)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 26 bytes\n", length);
+					goto fail;
+				}
+
+				struct commit_control_msg msg;
+				memset(&msg, 0, sizeof(msg));
+				memcpy(&msg, data, MIN(length, sizeof(msg)));
+
+				
+				DPRINTF("\tbmHint = 0x%04X\n", le16_to_cpu(msg.bmHint));
+				DPRINTF("\tbFormatIndex = %d\n", msg.bFormatIndex);
+				DPRINTF("\tbFrameIndex = %d\n", msg.bFrameIndex);
+				DPRINTF("\tdwFrameInterval = 0x%08X\n", le32_to_cpu(msg.dwFrameInterval));
+				DPRINTF("\twKeyFrameRate = 0x%04X\n", le16_to_cpu(msg.wKeyFrameRate));
+				DPRINTF("\twPFrameRate = 0x%04X\n", le16_to_cpu(msg.wPFrameRate));
+				DPRINTF("\twCompQuality = 0x%04X\n", le16_to_cpu(msg.wCompQuality));
+				DPRINTF("\twCompWindowSize = 0x%04X\n", le16_to_cpu(msg.wCompWindowSize));
+				DPRINTF("\twDelay = 0x%04X\n", le16_to_cpu(msg.wDelay));
+				DPRINTF("\tdwMaxVideoFrameSize= 0x%08X\n", le32_to_cpu(msg.dwMaxVideoFrameSize));
+				DPRINTF("\tdwMaxPayloadTransferSize= 0x%08X\n", le32_to_cpu(msg.dwMaxPayloadTransferSize));
+				
+				
+				ret = 26;
+			}
+			else if((index&0xFF00) == 0x0400 && (value&0xFF00) == 0x0100) // Setting input
+			{
+				DPRINTF("Setting input to %d\n", data[0]);
+				if(length != 1)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 1 byte\n", length);
+					goto fail;
+				}
+				
+				s->current_input = data[0];
+				ret = 1;
+			}
+			else if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Setting brightness\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+
+
+				if (s->brightness.supported) {
+					int16_t brightness_field;
+					memcpy(&brightness_field, data, 2);
+					s->brightness.current_value = le16_to_cpu(brightness_field);
+				}
+				ret = 2;
+			}
+			else
+				goto fail;
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_RES:
+			if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				DPRINTF("USB Resquest: Asking for brightness resolution\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				
+				if (s->brightness.supported) {
+					printf("resolution value is %d\n", s->brightness.resolution);
+					int16_t for_writing = cpu_to_le16(s->brightness.resolution);
+					memcpy(data, &for_writing, 2);
+					ret = 2;
+				}
+				else {
+					data[0] = 1;
+					data[1] = 0;
+					ret = 2;
+				}
+			}
+			else
+				goto fail;
+			break;
+		case UVCGetVideoControl | USB_UVC_GET_INFO:
+			if(length != 1)
+			{
+				DPRINTF("USB Request: Requested %d bytes, expected 1 bytes\n", length);
+				goto fail;
+			}
+			if((index&0xFF00) == 0x0500 && (value&0xFF00) == 0x0200 && 0) // PU_BRIGHTNESS_CONTROL of PROCESSING_UNIT
+			{
+				data[0] = 0x3; //support GET & SET
+				ret = 1;
+			}
+			else {
+				data[0] = 0;
+				printf("reporting no support for index 0x%08x and value 0x%08x\n", index, value);
+				ret = 1;
+			}
+			break;
+		default:
+		fail:
+			DPRINTF("USB Request: Unhandled control request\n");
+			DPRINTF("\tRequest: 0x%08X\n", request);
+			DPRINTF("\tValue: 0x%08X\n", value);
+			DPRINTF("\tIndex: 0x%08X\n", index);
+			DPRINTF("\tLength: 0x%08X\n", length);
+			p->status = USB_RET_STALL;
+			return;
+    }
+	p->actual_length = ret;
+}
+
+static void usb_uvc_handle_data(USBDevice *dev, USBPacket *p)
+{
+	USBUVCState *s = DO_UPCAST(USBUVCState, dev, dev);
+	//DPRINTF("Data called\n");
+	//DPRINTF("Packet ID: %d\n", p->pid);
+	//DPRINTF("Device address: %d\n", p->devaddr);
+	//DPRINTF("Device endpoint: %d\n", p->ep->nr);
+	//DPRINTF("Data length: %d\n", p->len);
+	
+	switch (p->pid)
+	{
+		case USB_TOKEN_OUT:
+			DPRINTF("USB Data Out requested.\n");
+			break;
+		case USB_TOKEN_IN:
+			//printf("frame_remaining_bytes: %d\n", frame_remaining_bytes);
+			if(p->ep->nr == 1) // IN endpoint 1 (hardware button)
+			{
+				uint8_t buf[p->iov.size];
+				buf[0] = 2;
+				buf[1] = 1;
+				buf[2] = 0;
+				buf[3] = 0;
+				usb_packet_copy(p, buf, sizeof(buf));
+			}
+			else if(p->ep->nr == 2) // IN endpoint 2 (video data)
+			{
+				if(s->frame_remaining_bytes==0)
+				{
+					get_frame_read(s);
+				}
+				uint32_t len;
+				len = p->iov.size - p->actual_length;
+				//printf("iov has size %zu\n", len);
+				int to_copy = MIN(512, MIN(len, s->frame_remaining_bytes));
+				usb_packet_copy(p, s->frame, to_copy);
+				s->frame += to_copy;
+				s->frame_remaining_bytes -= to_copy;
+			}
+			else
+			{
+				DPRINTF("USB Data In requested.\n");
+				DPRINTF("Requested data from endpoint %02X\n", p->ep->nr);
+			}
+			break;
+	    default:
+			DPRINTF("Bad token: %d\n", p->pid);
+			//fail:
+			p->status = USB_RET_STALL;
+			break;
+    }
+	
+    return;
+}
+
+static void usb_uvc_unrealize(USBDevice *dev, Error **errp)
+{
+	USBUVCState *s = DO_UPCAST(USBUVCState, dev, dev);
+	DPRINTF("Unrealize called\n");
+	close(s->v4l2_fd);
+}
+
+
+
+
+
+
+uint8_t frame_descriptor[] = {
+	/* class-specific vs frame descriptor alternate 0 */
+	0x26,		/*	u8	bLength; */
+	0x24,		/*	u8	bDescriptorType; CS_INTERFACE */
+	0x07,		/*	u8	bDescriptorSubtype; VS_FRAME_MJPEG */
+	0x01,		/*	u8	bFrameIndex; */
+	0x01,		/*	u8	bmCapabilities; */
+	0x40, 0x01,	/*	u8	wWidth; 320 */
+	0xF0, 0x00,	/*	u8	wHeight; 240 */
+	0x00, 0xEC,
+	0x0D, 0x00,	/*	u32	dwMinBitRate; */
+	0x00, 0xEC,
+	0x0D, 0x00,	/*	u32	dwMaxBitRate; */
+	0x72, 0xCE,
+	0x00, 0x00,	/*	u32	dwMaxVideoFrameBufSize; */
+	0x2A, 0x2C,
+	0x0A, 0x00,	/*	u32	dwDefaultFrameInterval; */
+	0x00,		/*	u8	bFrameIntervalType;	*/
+	0x2A, 0x2C,
+	0x0A, 0x00,	/*	u32	dwMinFrameInterval; */
+	0x2A, 0x2C,
+	0x0A, 0x00,	/*	u32	dwMaxFrameInterval; */
+	0x00, 0x00,
+	0x00, 0x00,	/*	u32	dwFrameIntervalStep; */
+	};
+
+
+
+
+static int usb_uvc_try_userpointers_setup(USBUVCState *s)
+{
+struct v4l2_requestbuffers reqbuf;
+
+memset (&reqbuf, 0, sizeof (reqbuf));
+reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+reqbuf.memory = V4L2_MEMORY_USERPTR;
+reqbuf.count = 2;
+
+if (ioctl (s->v4l2_fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
+    if (errno == EINVAL)
+        printf ("Video capturing or user pointer streaming is not supported\n");
+    else
+        perror ("VIDIOC_REQBUFS");
+
+    exit (EXIT_FAILURE);
+}
+
+
+	DPRINTF("Allocating memory for frames.\n");
+	s->frame_storage[0] = malloc(s->frame_max_length+2);
+	s->frame_storage[1] = malloc(s->frame_max_length+2);
+	s->frame_storage[2] = malloc(s->frame_max_length+2);
+
+
+
+
+        for (int i = 0; i < 2; ++i) {
+                struct v4l2_buffer buf;
+
+                memset(&buf,0,sizeof(buf));
+                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+                buf.memory = V4L2_MEMORY_USERPTR;
+                buf.index = i;
+                buf.m.userptr = (unsigned long)s->frame_storage[i]+2;
+                buf.length = s->frame_max_length;
+
+
+			if (ioctl (s->v4l2_fd, VIDIOC_QBUF, &buf) == -1) {
+				perror ("VIDIOC_REQBUFS");
+			}
+        }
+
+	s->frame = s->frame_storage[2];
+
+
+
+	s->frame_start = s->frame;
+	s->frame_remaining_bytes = 0;
+	s->frame_id = 0;
+
+
+
+
+
+
+
+
+struct v4l2_streamparm parm;
+
+    parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+    parm.parm.capture.timeperframe.numerator = 1;
+    parm.parm.capture.timeperframe.denominator = 1;
+
+
+
+
+
+
+
+
+if (ioctl (s->v4l2_fd, VIDIOC_S_PARM, &parm) == -1) {
+    if (errno == EINVAL)
+        printf ("VIDIOC_S_PARM is not supported\\n");
+    else
+        perror ("VIDIOC_S_PARM");
+
+    exit (EXIT_FAILURE);
+}
+
+
+
+
+
+
+
+
+
+int cap_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+if (ioctl (s->v4l2_fd, VIDIOC_STREAMON, &cap_type) == -1) {
+    if (errno == EINVAL)
+        printf ("VIDIOC_STREAMON is not supported\\n");
+    else
+        perror ("VIDIOC_STREAMON");
+
+    exit (EXIT_FAILURE);
+}
+
+	return 0;
+}
+
+
+
+
+
+static void usb_uvc_realize(USBDevice *dev, Error **errp)
+{
+	struct v4l2_capability capabilities;
+	struct v4l2_input video_input;
+	struct v4l2_format v_format;
+	int video_input_index;
+	int ret_err;
+	
+	DPRINTF("Init called\n");
+	
+	USBUVCState *s = DO_UPCAST(USBUVCState, dev, dev);
+
+	usb_desc_create_serial(dev);
+	usb_desc_init(dev);
+	
+	s->current_input = 0;
+	s->height = 240;
+	s->width = 320;
+
+
+
+	*(uint16_t*)&frame_descriptor[5] = s->width;
+	*(uint16_t*)&frame_descriptor[7] = s->height;
+	
+	if (!s->v4l2_device) {
+        error_setg(errp, "V4L2 device specification needed.");
+        return;
+    }
+	else
+	{
+		DPRINTF("Trying to open %s\n.", s->v4l2_device);
+	}
+	
+	s->v4l2_fd = open(s->v4l2_device, O_RDWR);
+	
+	if(s->v4l2_fd==-1)
+	{
+		switch(errno)
+		{
+			case EACCES:
+				error_report("Access denied.");
+				break;
+			case EBUSY:
+				error_report("Device busy.");
+				break;
+			case ENXIO:
+				error_report("Device does not exist.");
+				break;
+			case ENOMEM:
+				error_report("Not enough memory to open device.");
+				break;
+			case EMFILE:
+				error_report("Process reached maximum files opened.");
+				break;
+			case ENFILE:
+				error_report("System reached maximum files opened.");
+				break;
+			default:
+				error_report("Unknown error %d opening device.", errno);
+				break;
+		}
+		error_setg(errp, "V4L2 device open failed.");
+		return;
+	}
+	
+	DPRINTF("Device opened correctly.\n");
+	
+	DPRINTF("Querying capabilities.\n");
+	
+	ret_err = ioctl(s->v4l2_fd, VIDIOC_QUERYCAP, &capabilities);
+	
+	if(ret_err==-1)
+	{
+		switch(errno)
+		{
+			case EINVAL:
+				error_report("Device is not V4L2 device.\n");
+				break;
+			default:
+				error_report("Device returned unknown error %d.\n", errno);
+				break;
+		}
+		error_setg(errp, "V4L2 IOCTL VIDIOC_QUERYCAP failed");
+		return;
+	}
+	
+	DPRINTF("Device driver: %s\n", capabilities.driver);
+	DPRINTF("Device name: %s\n", capabilities.card);
+	DPRINTF("Device bus: %s\n", capabilities.bus_info);
+	DPRINTF("Driver version: %u.%u.%u\n",(capabilities.version >> 16) & 0xFF,(capabilities.version >> 8) & 0xFF,	capabilities.version & 0xFF);
+	DPRINTF("Device capabilities: 0x%08X\n", capabilities.capabilities);
+
+
+
+
+
+	
+	DPRINTF("Enumerating video inputs.\n");
+	memset(&video_input, 0, sizeof(video_input));
+	video_input.index=0;
+	while((ioctl(s->v4l2_fd, VIDIOC_ENUMINPUT, &video_input)==0))
+	{
+		if(video_input.type == V4L2_INPUT_TYPE_CAMERA)
+		{
+			video_input_index = video_input.index;
+			break;
+		}
+		
+		video_input.index++;
+	}
+	
+	DPRINTF("Setting video input to index %d\n", video_input_index);
+	ret_err = ioctl(s->v4l2_fd, VIDIOC_S_INPUT, &video_input_index);
+	
+	if(ret_err==-1)
+	{
+		switch(errno)
+		{
+			case EINVAL:
+				error_report("Incorrect video input selected.\n");
+				break;
+			case EBUSY:
+				error_report("Input cannot be switched.\n");
+				break;
+			default:
+				error_report("Unknown error %d.\n", errno);
+				break;
+		}
+		error_setg(errp, "V4L2 IOCTL VIDIOC_S_INPUT failed");
+		return;
+	}
+	
+	ioctl(s->v4l2_fd, VIDIOC_G_INPUT, &ret_err);
+	
+	if(ret_err==video_input_index)
+		DPRINTF("Video input correctly set.\n");
+	else
+	{
+		error_report("Some error happened while setting video input.\n");
+		error_setg(errp, "V4L2 IOCTL VIDIOC_G_INPUT failed");
+		return;
+	}
+	
+	DPRINTF("Trying to set %zux%zu MJPEG.\n", s->width, s->height);
+	memset(&v_format, 0, sizeof(v_format));
+	v_format.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	v_format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+
+
+	ret_err = ioctl (s->v4l2_fd, VIDIOC_G_FMT, &v_format);
+	if(ret_err == -1)
+	{
+		error_setg(errp, "V4L2 IOCTL VIDIOC_G_FMT failed %d", errno);
+		return;
+	}
+
+	v_format.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	v_format.fmt.pix.width       = s->width; 
+	v_format.fmt.pix.height      = s->height;
+	v_format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+	v_format.fmt.pix.field       = V4L2_FIELD_INTERLACED;
+	
+	ret_err = ioctl (s->v4l2_fd, VIDIOC_S_FMT, &v_format);
+	
+	if(ret_err == -1)
+	{
+		switch(errno)
+		{
+			case EBUSY:
+				error_report("Device busy while changing format.\n");
+				break;
+			case EINVAL:
+				error_report("Invalid format.\n");
+				break;
+			default:
+				error_report("Unknown error %d while changing format.\n", errno);
+				break;
+		}
+		error_setg(errp, "V4L2 IOCTL VIDIOC_S_FMT failed");
+		return;
+	}
+
+	s->frame_max_length = v_format.fmt.pix.sizeimage;
+	
+	DPRINTF("Format correctly set.\n");
+
+	printf("%d %d\n", v_format.fmt.pix.width , v_format.fmt.pix.height);
+	DPRINTF("Maximum image size: %d bytes.\n", s->frame_max_length);
+
+
+	struct v4l2_queryctrl queryctrl;
+	if (assign_query_control(s->v4l2_fd, V4L2_CID_BRIGHTNESS, &queryctrl) == 0) {
+		s->brightness.supported = true;
+		s->brightness.minimum = queryctrl.minimum;
+		s->brightness.maximum = queryctrl.maximum;
+		s->brightness.resolution = queryctrl.step;
+		s->brightness.default_value = queryctrl.default_value;
+	}
+
+
+
+
+
+
+	if (!s->v4l2_api && capabilities.capabilities & V4L2_CAP_READWRITE) {
+		printf("Device supports read/write\n");
+		s->frame_storage[0] = malloc(s->frame_max_length+2);
+		s->frame_storage[1] = NULL;
+		s->frame_storage[2] = NULL;
+		s->frame = s->frame_storage[0];
+		s->frame_start = s->frame;
+		s->frame_remaining_bytes = 0;
+		s->frame_id = 0;
+		s->v4l2_api = V4L2_API_READ;
+	}
+
+
+	if (!s->v4l2_api && capabilities.capabilities & V4L2_CAP_STREAMING) {
+		printf("Device supports V4L2_CAP_STREAMING\n");
+		int retval = usb_uvc_try_userpointers_setup(s);
+		if (retval == 0) {
+			printf("Selecting  V4L2_API_USERPTRS\n");
+			s->v4l2_api = V4L2_API_USERPTRS;
+		}
+	}
+
+
+	if (!s->v4l2_api) {
+		error_setg(errp, "No possible V4L2 API found.");
+		return;
+	}
+}
+
+
+enum usb_audio_strings {
+    STRING_NULL,
+    STRING_MANUFACTURER,
+    STRING_PRODUCT,
+    STRING_SERIALNUMBER,
+};
+
+static const USBDescStrings usb_uvc_stringtable = {
+    [STRING_MANUFACTURER]       = "QEMU",
+    [STRING_PRODUCT]            = "QEMU USB VIDEO CLASS 2",
+    [STRING_SERIALNUMBER]       = "1",
+};
+
+
+static const USBDescIface video_control_iface =
+
+{
+        .bInterfaceNumber              = 0,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = 0x0e,
+        .bInterfaceSubClass            = 0x01,
+        .bInterfaceProtocol            = 0x00,
+        .iInterface                    = 0x02,
+        .ndesc                         = 6,
+        .descs = (USBDescOther[]) {
+             {
+		.data = (uint8_t[]) {
+			/* class specific vc interface descriptor */
+			0x0D,		/*  u8  cif_bLength; */
+			0x24,		/*  u8  cif_bDescriptorType; CS_INTERFACE */
+			0x01,		/*  u8  cif_bDescriptorSubType; VC_HEADER */
+			0x00, 0x01, /*  u16 cif_bcdUVC; 1.0 */
+			0x42, 0x00, /*  u16 cif_wTotalLength */
+			0x80, 0x8D, /*  u32 cif_dwClockFrequency; Deprecated, 6Mhz */
+			0x5B, 0x00,
+			0x01,		/*  u8  cif_bInCollection; */
+			0x01,		/*  u8  cif_baInterfaceNr; */
+                }
+            },{
+                .data = (uint8_t[]) {
+	/* input terminal descriptor */
+			0x11,		/*  u8  itd_bLength; */
+			0x24,		/*  u8  itd_bDescriptorType; CS_INTERFACE */
+			0x02,		/*  u8  itd_bDescriptorSubtype; VC_INPUT_TERMINAL */
+			0x01,		/*  u8  itd_bTerminalID; */
+			0x01, 0x02, /*  u16  itd_wTerminalType; ITT_CAMERA */
+			0x00,		/*  u8  itd_bAssocTerminal; No association */
+			0x00,		/*  u8  itd_iTerminal; Unused */
+			0x00, 0x00, /*  u16  itd_wObjectiveFocalLengthMin; No optical zoom */
+			0x00, 0x00, /*  u16  itd_wObjectiveFocalLengthMax; No optical zoom */
+			0x00, 0x00, /*  u16  itd_wOcularFocalLength; No optical zoom */
+			0x02,		/*  u8  itd_bControlSize; No controls implemented */
+			0x00, 0x00, /*  u16  itd_bmControls; No controls supported */
+                }
+            },{
+                .data = (uint8_t[]) {
+		0x08,		/*	u8	itd_bLength; */
+		0x24,		/*	u8	itd_bDescriptorType; CS_INTERFACE */
+		0x02,		/*	u8	itd_bDescriptorSubtype; VC_INPUT_TERMINAL */
+		0x02,		/*	u8	itd_bTerminalID; */
+		0x01, 0x04,	/*	u16	itd_wTerminalType; ITT_COMPOSITE */
+		0x00,		/*	u8	itd_bAssocTerminal; */
+		0x00,		/*	u8	itd_iTerminal; */
+                }
+            },{
+                .data = (uint8_t[]) {
+		/* output terminal descriptor */
+		0x09,		/*	u8	otd_bLength; */
+		0x24,		/*	u8	otd_bDescriptorType; CS_INTERFACE */
+		0x03,		/*	u8	otd_bDescriptorSubtype; VC_OUTPUT_TERMINAL */
+		0x03,		/*	u8	otd_bTerminalID; */
+		0x01, 0x01,	/*	u16	otd_wTerminalType; TT_STREAMING */
+		0x00,		/*	u8	otd_bAssocTerminal; No association */
+		0x05,		/*	u8	otd_bSourceID; */
+		0x00,		/*	u8	otd_iTerminal; */
+                }
+            },{
+                .data = (uint8_t[]) {
+		/* selector unit descriptor */
+		0x08,		/*	u8	sud_bLength; */
+		0x24,		/*	u8	sud_bDescriptorType; CS_INTERFACE */
+		0x04,		/*	u8	sud_bDescriptorSubtype; VC_SELECTOR_UNIT */
+		0x04,		/*	u8	sud_bUnitID; */
+		0x02,		/*	u8	sud_bNrInPins; */
+		0x01,		/*	u8	sud_baSourceID; */
+		0x02,
+		0x00,		/*	u8	sud_iSelector; */
+                }
+            },{
+                .data = (uint8_t[]) {
+		/* processing unit descriptor */
+		0x0B,		/*	u8	pud_bLength; */
+		0x24,		/*	u8	pud_bDescriptorType; CS_INTERFACE */
+		0x05,		/*	u8	pud_bDescriptorSubtype; VC_PROCESSING_UNIT */
+		0x05,		/*	u8	pud_bUnitID; */
+		0x04,		/*	u8	pud_bSourceID; */
+		0x00, 0x00,	/*	u16	pud_wMaxMultiplier; */
+		0x02,		/*	u8	pud_bControlSize; */
+		0x01, 0x00,	/*	u16	pud_bmControls; Brightness control supported */
+		0x00,		/*	u8	pud_iProcessing; */
+		}
+            }
+         },
+
+        .eps = (USBDescEndpoint[]) {
+            {
+                /* standard interrupt endpoint */
+                .bEndpointAddress      = 0x81,
+                .bmAttributes          = 0x03,
+                .wMaxPacketSize        = 0x08,
+                .bInterval             = 0xff,
+            },
+        },
+        
+    };
+
+
+
+
+
+
+static const USBDescIface desc_iface_fullspeed[] = {
+    {
+         /* standard vs interface descriptor alternate 0 */
+        .bInterfaceNumber              = 1,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = 0x0e,
+        .bInterfaceSubClass            = 0x02,
+        .bInterfaceProtocol            = 0x00,
+        .iInterface                    = 0x00,
+        .ndesc                         = 3,
+        .descs = (USBDescOther[]) {
+             {
+		.data = (uint8_t[]) {
+		/* class-specific vs header descriptor input alternate 0 */
+		0x0E,		/*	u8	bLength; */
+		0x24,		/*	u8	bDescriptorType; CS_INTERFACE */
+		0x01,		/*	u8	bDescriptorSubtype; VS_INPUT_HEADER */
+		0x01,		/*	u8	bNumFormats; */
+		0x46, 0x00,	/*	u8	wTotalLength; */
+		0x82,		/*	u8	bEndpointAddress; */
+		0x00,		/*	u8	bmInfo; */
+		0x03,		/*	u8	bTerminalLink; */
+		0x00,		/*	u8	bStillCaptureMethod; */
+		0x00,		/*	u8	bTriggerSupport; */
+		0x00,		/*	u8	bTriggerUsage; */
+		0x01,		/*	u8	bControlSize; */
+		0x00,		/*	u8	bmaControls; */
+                }
+            },{
+                .data = (uint8_t[]) {
+		/* class-specific vs format descriptor alternate 0 */
+		0x0B,		/*	u8	bLength; */
+		0x24,		/*	u8	bDescriptorType; CS_INTERFACE */
+		0x06,		/*	u8	bDescriptorSubtype; VS_FORMAT_MJPEG */
+		0x01,		/*	u8	bFormatIndex; */
+		0x01,		/*	u8	bNumFrameDescriptors; */
+		0x01,		/*	u8	bmFlags; */
+		0x01,		/*	u8	bDefaultFrameIndex; */
+		0x00,		/*	u8	bAspectRatioX; */
+		0x00,		/*	u8	bAspectRatioY; */
+		0x02,		/*	u8	bmInterlaceFlags; */
+		0x00,		/*	u8	bCopyProtect; */
+                }
+            },{
+                .data = frame_descriptor
+            }
+         },
+
+        .eps = (USBDescEndpoint[]) {
+            {
+                /* standard vs isochronous video data endpoint descriptor */
+                .bEndpointAddress      = 0x82,
+                .bmAttributes          = 0x02,
+                .wMaxPacketSize        = 0x40,
+                .bInterval             = 0xff,
+            },
+        },
+    },
+    
+};
+
+
+
+
+static const USBDescIface desc_iface_highspeed[] = {
+    {
+         /* standard vs interface descriptor alternate 0 */
+        .bInterfaceNumber              = 1,
+        .bNumEndpoints                 = 1,
+        .bInterfaceClass               = 0x0e,
+        .bInterfaceSubClass            = 0x02,
+        .bInterfaceProtocol            = 0x00,
+        .iInterface                    = 0x00,
+        .ndesc                         = 3,
+        .descs = (USBDescOther[]) {
+             {
+		.data = (uint8_t[]) {
+		/* class-specific vs header descriptor input alternate 0 */
+		0x0E,		/*	u8	bLength; */
+		0x24,		/*	u8	bDescriptorType; CS_INTERFACE */
+		0x01,		/*	u8	bDescriptorSubtype; VS_INPUT_HEADER */
+		0x01,		/*	u8	bNumFormats; */
+		0x46, 0x00,	/*	u8	wTotalLength; */
+		0x82,		/*	u8	bEndpointAddress; */
+		0x00,		/*	u8	bmInfo; */
+		0x03,		/*	u8	bTerminalLink; */
+		0x00,		/*	u8	bStillCaptureMethod; */
+		0x00,		/*	u8	bTriggerSupport; */
+		0x00,		/*	u8	bTriggerUsage; */
+		0x01,		/*	u8	bControlSize; */
+		0x00,		/*	u8	bmaControls; */
+                }
+            },{
+                .data = (uint8_t[]) {
+		/* class-specific vs format descriptor alternate 0 */
+		0x0B,		/*	u8	bLength; */
+		0x24,		/*	u8	bDescriptorType; CS_INTERFACE */
+		0x06,		/*	u8	bDescriptorSubtype; VS_FORMAT_MJPEG */
+		0x01,		/*	u8	bFormatIndex; */
+		0x01,		/*	u8	bNumFrameDescriptors; */
+		0x01,		/*	u8	bmFlags; */
+		0x01,		/*	u8	bDefaultFrameIndex; */
+		0x00,		/*	u8	bAspectRatioX; */
+		0x00,		/*	u8	bAspectRatioY; */
+		0x02,		/*	u8	bmInterlaceFlags; */
+		0x00,		/*	u8	bCopyProtect; */
+                }
+            },{
+                .data = frame_descriptor
+            }
+         },
+
+        .eps = (USBDescEndpoint[]) {
+            {
+                /* standard vs isochronous video data endpoint descriptor */
+                .bEndpointAddress      = 0x82,
+                .bmAttributes          = USB_ENDPOINT_XFER_BULK,
+                .wMaxPacketSize        = 512,
+                .bInterval             = 0xff,
+            },
+        },
+    },
+    
+};
+
+
+
+
+
+static const USBDescIfaceAssoc desc_iface_groups[] = {
+  {
+/* interface association */
+    .bFirstInterface = 0x00,
+    .bInterfaceCount = 0x02,
+    .bFunctionClass = 0x0e,
+    .bFunctionSubClass = 0x03,
+    .bFunctionProtocol = 0x00,
+    .iFunction = 0x02,
+    .nif = 1,
+    .ifs = &video_control_iface,
+
+
+  },
+};
+
+
+static const USBDescDevice desc_device_highspeed = {
+    .bcdUSB                        = 0x0200,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = 0,
+            .bmAttributes          = 0x80,
+            .bMaxPower             = 0xfa,
+            .nif_groups = ARRAY_SIZE(desc_iface_groups),
+            .if_groups = desc_iface_groups,
+            .nif = ARRAY_SIZE(desc_iface_highspeed),
+            .ifs = desc_iface_highspeed,
+        },
+    },
+};
+
+static const USBDescDevice desc_device_fullspeed = {
+    .bcdUSB                        = 0x0110,
+    .bMaxPacketSize0               = 64,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 2,
+            .bConfigurationValue   = 1,
+            .iConfiguration        = 0,
+            .bmAttributes          = 0x80,
+            .bMaxPower             = 0xfa,
+            .nif_groups = ARRAY_SIZE(desc_iface_groups),
+            .if_groups = desc_iface_groups,
+            .nif = ARRAY_SIZE(desc_iface_fullspeed),
+            .ifs = desc_iface_fullspeed,
+        },
+    },
+};
+
+static const USBDesc desc_uvc = {
+    .id = {
+        .idVendor          = 0,
+        .idProduct         = 0,
+        .bcdDevice         = 0,
+        .iManufacturer     = STRING_MANUFACTURER,
+        .iProduct          = STRING_PRODUCT,
+        .iSerialNumber     = STRING_SERIALNUMBER,
+    },
+    .full = &desc_device_fullspeed,
+    //.high = &desc_device_highspeed,
+    .str  = usb_uvc_stringtable,
+};
+
+
+
+static Property usb_uvc_properties[] = {
+    DEFINE_PROP_STRING("device", USBUVCState, v4l2_device),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_uvc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *k = USB_DEVICE_CLASS(klass);
+
+    dc->props         = usb_uvc_properties;
+    k->product_desc   = "QEMU USB Video Class Device";
+    k->usb_desc       = &desc_uvc;
+    k->realize        = usb_uvc_realize;
+    k->handle_reset   = usb_uvc_handle_reset;
+    k->handle_control = usb_uvc_handle_control;
+    k->handle_data    = usb_uvc_handle_data;
+    k->unrealize      = usb_uvc_unrealize;
+}
+
+static TypeInfo usb_uvc_info = {
+    .name          = "usb-uvc-webcam",
+    .parent        = TYPE_USB_DEVICE,
+    .instance_size = sizeof(USBUVCState),
+    .class_init    = usb_uvc_class_init,
+};
+
+static void usb_uvc_register_types(void)
+{
+    type_register_static(&usb_uvc_info);
+}
+
+type_init(usb_uvc_register_types)
-- 
2.17.1



^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2021-06-06 20:49 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2021-06-06 17:30 [PATCH RFC] USB Video Class device emulation Raphael Lisicki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.