qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
* [Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation.
@ 2010-06-08 15:34 Natalia Portillo
  2010-06-10 17:46 ` Blue Swirl
  0 siblings, 1 reply; 3+ messages in thread
From: Natalia Portillo @ 2010-06-08 15:34 UTC (permalink / raw)
  To: qemu-devel Developers

Signed-off-by: Natalia Portillo <claunia@claunia.com>
---
 hw/usb-uvc.c | 1096 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1096 insertions(+), 0 deletions(-)
 create mode 100644 hw/usb-uvc.c

diff --git a/hw/usb-uvc.c b/hw/usb-uvc.c
new file mode 100644
index 0000000..b711f51
--- /dev/null
+++ b/hw/usb-uvc.c
@@ -0,0 +1,1096 @@
+/*
+ * 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 "hw.h"
+#include "console.h"
+#include "usb.h"
+#include "qemu-error.h"
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.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
+
+typedef struct USBUVCState {
+    USBDevice dev;
+	char	current_input;
+	char	*v4l2_device;
+} USBUVCState;
+
+static int v4l2_fd;
+static char *frame;
+static char *frame_start;
+static int frame_length;
+static int frame_id;
+static int first_bulk_packet;
+static int frame_remaining_bytes;
+static int frame_max_length;
+
+static const uint8_t qemu_uvc_dev_descriptor[] = {
+	0x12,       /*  u8 bLength; */
+	0x01,       /*  u8 bDescriptorType; Device */
+	0x00, 0x02, /*  u16 bcdUSB; v2.0 */
+	
+	0xEF,	    /*  u8  bDeviceClass; */
+	0x02,	    /*  u8  bDeviceSubClass; */
+	0x01,       /*  u8  bDeviceProtocol; [ low/full speeds only ] */
+	0x08,       /*  u8  bMaxPacketSize0; 8 Bytes */
+	
+	/* Vendor and product id are arbitrary.  */
+	0x00, 0x00, /*  u16 idVendor; */
+ 	0x00, 0x00, /*  u16 idProduct; */
+	0x00, 0x00, /*  u16 bcdDevice */
+	
+	0x01,       /*  u8  iManufacturer; */
+	0x02,       /*  u8  iProduct; */
+	0x00,       /*  u8  iSerialNumber; */
+	0x01        /*  u8  bNumConfigurations; */
+};
+
+static const uint8_t qemu_uvc_config_descriptor[] = {
+	
+	/* one configuration */
+	0x09,       /*  u8  bLength; */
+	0x02,       /*  u8  bDescriptorType; Configuration */
+	0xB7, 0x00, /*  u16 wTotalLength; */
+	0x02,       /*  u8  bNumInterfaces; (2) */
+	0x01,       /*  u8  bConfigurationValue; */
+	0x00,       /*  u8  iConfiguration; */
+	0x80,       /*  u8  bmAttributes;
+				 Bit 7: must be set,
+				 6: Self-powered,
+				 5: Remote wakeup,
+				 4..0: resvd */
+	0xFA,       /*  u8  MaxPower; */
+	
+	/* interface association */
+	0x08,		/*  u8  ifa_bLength; */
+	0x0B,		/*  u8  ifa_bDescriptorType; Interface Association */
+	0x00,		/*  u8  ifa_bFirstInterface; */
+	0x02,		/*  u8  ifa_bInterfaceCount; */
+	0x0E,		/*  u8  ifa_bFunctionClass; CC_VIDEO */
+	0x03,		/*  u8  ifa_bFunctionSubClass; SS_VIDEO_INTERFACE_COLLECTION */
+	0x00,		/*  u8  ifa_bFunctionProtocol; unused */
+	0x02,		/*  u8  ifa_iFunction; */
+	
+	/* video control interface */
+	0x09,       /*  u8  if_bLength; */
+	0x04,       /*  u8  if_bDescriptorType; Interface */
+	0x00,       /*  u8  if_bInterfaceNumber; */
+	0x00,       /*  u8  if_bAlternateSetting; */
+	0x01,       /*  u8  if_bNumEndpoints; */
+	0x0E,       /*  u8  if_bInterfaceClass; CC_VIDEO */
+	0x01,       /*  u8  if_bInterfaceSubClass; SC_VIDEOCONTROL */
+	0x00,       /*  u8  if_bInterfaceProtocol; unused */
+	0x02,       /*  u8  if_iInterface; */
+	
+	/* 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; */
+	
+	/* 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 */
+	
+	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; */
+	
+	/* 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; */
+	
+	/* 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; */
+	
+	/* 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; */
+	
+	/* standard interrupt endpoint */
+	0x07,		/*	u8	ep_bLenght; */
+	0x05,		/*	u8	ep_bDescriptorType; Endpoint */
+	0x81,		/*	u8	ep_bEndpointAddress; IN Endpoint 1 */
+	0x03,		/*	u8	ep_bmAttributes; Interrupt */
+	0x08, 0x00,	/*	u8	ep_wMaxPacketSize; 8 bytes */
+	0xFF,		/*	u8	ep_bInterval; 32ms */
+	
+	/* class-specific interrupt endpoint */
+	0x05,		/*	u8	ep_bLenght; */
+	0x25,		/*	u8	ep_bDescriptorType; CS_ENDPOINT */
+	0x03,		/*	u8	ep_bmAttributes; EP_INTERRUPT */
+	0x08, 0x00,	/*	u8	ep_wMaxPacketSize; 8 bytes */
+	
+	/* standard vs interface descriptor alternate 0 */
+	0x09,		/*	u8	bLength; */
+	0x04,		/*	u8	bDescriptorType; INTERFACE */
+	0x01,		/*	u8	bInterfaceNumber; */
+	0x00,		/*	u8	bAlternateSetting; */
+	0x01,		/*	u8	bNumEndpoints; */
+	0x0E,		/*	u8	bInterfaceClass; CC_VIDEO */
+	0x02,		/*	u8	bInterfaceSubClass; SC_VIDEO_STREAMING */
+	0x00,		/*	u8	bInterfaceProtocol; PC_PROTOCOL_UNDEFINED */
+	0x00,		/*	u8	iInterface; Unused */
+	
+	/* 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; */
+	
+	/* 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; */
+	
+	/* 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; */
+	
+	/* standard vs isochronous video data endpoint descriptor */
+	0x07,		/*	u8	bLength; */
+	0x05,		/*	u8	bDescriptorType; */
+	0x82,		/*	u8	bEndpointAddress; IN endpoint 2 */
+	0x02,		/*	u8	bmAttributes; Isochronous transfer, asynchronous sync */
+	0x40, 0x00,	/*	u16	wMaxPacketSize; 510 bytes */
+	0x00		/*	u8	bInterval; */
+};
+
+static void get_frame_read(void)
+{
+	DPRINTF("Getting frame.\n");
+	frame = frame_start;
+	frame_length = read(v4l2_fd, frame, frame_max_length);
+	
+	if(frame_length == -1)
+	{
+		DPRINTF("Error while reading frame.\n");
+		frame_length = 0;
+	}
+	else
+	{
+		frame_id = frame_id ^ 1;
+		first_bulk_packet = 1;
+		frame_remaining_bytes = frame_length;
+		DPRINTF("Got a frame of %d bytes.\n", frame_length);
+	}
+	
+	return;
+}
+
+static void usb_uvc_handle_reset(USBDevice *dev)
+{
+	DPRINTF("Reset called\n");
+}
+
+static int usb_uvc_handle_control(USBDevice *dev, int request, int value,
+								  int index, int length, uint8_t *data)
+{
+	int ret = 0;
+	USBUVCState *s = (USBUVCState *)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);
+	
+	switch(request)
+	{
+		case DeviceRequest | USB_REQ_GET_STATUS:
+			DPRINTF("USB Request: Get Status\n");
+			data[0] = (1 << USB_DEVICE_SELF_POWERED) |
+            (dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
+			data[1] = 0x00;
+			ret = 2;
+			break;
+		case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
+			DPRINTF("USB Request: Clear feature\n");
+			if (value == USB_DEVICE_REMOTE_WAKEUP) {
+				DPRINTF("USB Request: Unset remote wakeup\n");
+				dev->remote_wakeup = 0;
+			} else {
+				goto fail;
+			}
+			ret = 0;
+			break;
+		case DeviceOutRequest | USB_REQ_SET_FEATURE:
+			DPRINTF("USB Request: Set feature\n");
+			if (value == USB_DEVICE_REMOTE_WAKEUP) {
+				DPRINTF("USB Request: Set remote wakeup\n");
+				dev->remote_wakeup = 1;
+			} else {
+				goto fail;
+			}
+			ret = 0;
+			break;
+		case DeviceOutRequest | USB_REQ_SET_ADDRESS:
+			DPRINTF("USB Request: Set address to 0x%08X\n", value);
+			dev->addr = value;
+			ret = 0;
+			break;			
+		case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+			DPRINTF("USB Request: Get descriptor\n");
+			switch(value >> 8) {
+				case USB_DT_DEVICE:
+					DPRINTF("USB Request: Get device descriptor\n");
+					memcpy(data, qemu_uvc_dev_descriptor,
+						   sizeof(qemu_uvc_dev_descriptor));
+					ret = sizeof(qemu_uvc_dev_descriptor);
+					break;
+				case USB_DT_CONFIG:
+					DPRINTF("USB Request: Get configuration descriptor\n");
+					memcpy(data, qemu_uvc_config_descriptor,
+						   sizeof(qemu_uvc_config_descriptor));
+					ret = sizeof(qemu_uvc_config_descriptor);
+					break;
+				case USB_DT_STRING:
+					DPRINTF("USB Request: Get device strings\n");
+					switch(value & 0xff) {
+						case 0:
+							DPRINTF("USB Request: Get language IDs\n");
+							/* language ids */
+							data[0] = 4;
+							data[1] = 3;
+							data[2] = 0x09;
+							data[3] = 0x04;
+							ret = 4;
+							break;
+						case 1:
+							/* vendor description */
+							DPRINTF("USB Request: Get vendor string\n");
+							ret = set_usb_string(data, "QEMU " QEMU_VERSION);
+							break;
+						case 2:
+							/* product description */
+							DPRINTF("USB Request: Get product string\n");
+							ret = set_usb_string(data, "QEMU USB VIDEO CLASS 2");
+							break;
+						case 3:
+							/* serial number */
+							DPRINTF("USB Request: Get serial number string\n");
+							ret = set_usb_string(data, "1");
+							break;
+						default:
+							goto fail;
+					}
+					break;
+				default:
+					goto fail;
+			}
+			break;
+		case DeviceRequest | USB_REQ_GET_CONFIGURATION:
+			DPRINTF("USB Request: Get configuration\n");
+			data[0] = 1;
+			ret = 1;
+			break;
+		case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
+			DPRINTF("USB Request: Set configuration\n");
+			ret = 0;
+			break;
+		case DeviceRequest | USB_REQ_GET_INTERFACE:
+			DPRINTF("USB Request: Get interface\n");
+			data[0] = 0;
+			ret = 1;
+			break;
+		case DeviceOutRequest | USB_REQ_SET_INTERFACE:
+			DPRINTF("USB Request: Set interface\n");
+			ret = 0;
+			break;
+		case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+			DPRINTF("USB Request: Clear endpoint\n");
+			ret = 0;
+			break;
+		case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
+			DPRINTF("USB Request: Set interface\n");
+			ret = 0;
+			break;
+			/* Class specific requests.  */
+		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;
+				}
+				
+				data[0] = 0; // bmHint
+				data[1] = 0;
+				data[2] = 1; // bFormatIndex
+				data[3] = 1; // bFrameIndex
+				data[4] = 0x2A; // dwFrameInterval
+				data[5] = 0x2C;
+				data[6] = 0x0A;
+				data[7] = 0x00;
+				data[8] = 0; // wKeyFrameRate
+				data[9] = 0;
+				data[10] = 0; // wPFrameRate
+				data[11] = 0;
+				data[12] = 0; // wCompQuality
+				data[13] = 0;
+				data[14] = 1; // wCompWindowSize
+				data[15] = 0;
+				data[16] = 0x20; // wDelay
+				data[17] = 0;
+				data[18] = 0x72; // dwMaxVideoFrameSize
+				data[19] = 0xCE;
+				data[20] = 0x00;
+				data[21] = 0x00;
+				data[22] = 0x72; // dwMaxPayloadTransferSize
+				data[23] = 0xCE;
+				data[24] = 0x00;
+				data[25] = 0x00;
+				ret = 26;
+			}
+			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;
+				}
+				
+				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;
+				}
+				
+				data[0] = 0; // bmHint
+				data[1] = 0;
+				data[2] = 1; // bFormatIndex
+				data[3] = 1; // bFrameIndex
+				data[4] = 0x2A; // dwFrameInterval
+				data[5] = 0x2C;
+				data[6] = 0x0A;
+				data[7] = 0x00;
+				data[8] = 0; // wKeyFrameRate
+				data[9] = 0;
+				data[10] = 0; // wPFrameRate
+				data[11] = 0;
+				data[12] = 0; // wCompQuality
+				data[13] = 0;
+				data[14] = 1; // wCompWindowSize
+				data[15] = 0;
+				data[16] = 0x20; // wDelay
+				data[17] = 0;
+				data[18] = 0x72; // dwMaxVideoFrameSize
+				data[19] = 0xCE;
+				data[20] = 0x00;
+				data[21] = 0x00;
+				data[22] = 0x72; // dwMaxPayloadTransferSize
+				data[23] = 0xCE;
+				data[24] = 0x00;
+				data[25] = 0x00;
+				ret = 26;
+			}
+			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;
+				}
+				
+				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;
+				}
+				
+				data[0] = 0; // bmHint
+				data[1] = 0;
+				data[2] = 1; // bFormatIndex
+				data[3] = 1; // bFrameIndex
+				data[4] = 0x2A; // dwFrameInterval
+				data[5] = 0x2C;
+				data[6] = 0x0A;
+				data[7] = 0x00;
+				data[8] = 0; // wKeyFrameRate
+				data[9] = 0;
+				data[10] = 0; // wPFrameRate
+				data[11] = 0;
+				data[12] = 0; // wCompQuality
+				data[13] = 0;
+				data[14] = 1; // wCompWindowSize
+				data[15] = 0;
+				data[16] = 0x20; // wDelay
+				data[17] = 0;
+				data[18] = 0x72; // dwMaxVideoFrameSize
+				data[19] = 0xCE;
+				data[20] = 0x00;
+				data[21] = 0x00;
+				data[22] = 0x72; // dwMaxPayloadTransferSize
+				data[23] = 0xCE;
+				data[24] = 0x00;
+				data[25] = 0x00;
+				ret = 26;
+			}
+			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;
+				}
+				
+				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;
+				}
+				
+				data[0] = 0; // bmHint
+				data[1] = 0;
+				data[2] = 1; // bFormatIndex
+				data[3] = 1; // bFrameIndex
+				data[4] = 0x2A; // dwFrameInterval
+				data[5] = 0x2C;
+				data[6] = 0x0A;
+				data[7] = 0x00;
+				data[8] = 0; // wKeyFrameRate
+				data[9] = 0;
+				data[10] = 0; // wPFrameRate
+				data[11] = 0;
+				data[12] = 0; // wCompQuality
+				data[13] = 0;
+				data[14] = 1; // wCompWindowSize
+				data[15] = 0;
+				data[16] = 0x20; // wDelay
+				data[17] = 0;
+				data[18] = 0x72; // dwMaxVideoFrameSize
+				data[19] = 0xCE;
+				data[20] = 0x00;
+				data[21] = 0x00;
+				data[22] = 0x72; // dwMaxPayloadTransferSize
+				data[23] = 0xCE;
+				data[24] = 0x00;
+				data[25] = 0x00;
+				ret = 26;
+			}
+			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;
+				}
+				
+				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;
+				}
+				
+				DPRINTF("\tbmHint = 0x%02X%02X\n", data[1], data[0]);
+				DPRINTF("\tbFormatIndex = %d\n", data[2]);
+				DPRINTF("\tbFrameIndex = %d\n", data[3]);
+				DPRINTF("\tdwFrameInterval = 0x%02X%02X%02X%02X\n", data[7], data[6], data[5], data[4]);
+				DPRINTF("\twKeyFrameRate = 0x%02X%02X\n", data[9], data[8]);
+				DPRINTF("\twPFrameRate = 0x%02X%02X\n", data[11], data[10]);
+				DPRINTF("\twCompQuality = 0x%02X%02X\n", data[13], data[12]);
+				DPRINTF("\twCompWindowSize = 0x%02X%02X\n", data[15], data[14]);
+				DPRINTF("\twDelay = 0x%02X%02X\n", data[17], data[16]);
+				DPRINTF("\tdwMaxVideoFrameSize= 0x%02X%02X%02X%02X\n", data[21], data[20], data[19], data[18]);
+				DPRINTF("\tdwMaxPayloadTransferSize= 0x%02X%02X%02X%02X\n", data[25], data[24], data[23], data[22]);
+				
+				frame = frame_start;
+				frame_remaining_bytes = frame_length;
+				first_bulk_packet = 1;
+				
+				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, value stays the same\n");
+				if(length != 2)
+				{
+					DPRINTF("USB Request: Requested %d bytes, expected 2 bytes\n", length);
+					goto fail;
+				}
+				
+				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;
+				}
+				
+				data[0] = 1;
+				data[1] = 0;
+				ret = 2;
+			}
+			else
+				goto fail;
+			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);
+			ret = USB_RET_STALL;
+			break;
+    }
+	
+	return ret;
+}
+
+static int usb_uvc_handle_data(USBDevice *dev, USBPacket *p)
+{
+	int ret = 0;
+	
+	//DPRINTF("Data called\n");
+	//DPRINTF("Packet ID: %d\n", p->pid);
+	//DPRINTF("Device address: %d\n", p->devaddr);
+	//DPRINTF("Device endpoint: %d\n", p->devep);
+	//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:
+			if(p->devep == 1) // IN endpoint 1 (hardware button)
+			{
+				p->data[0] = 2;
+				p->data[1] = 1;
+				p->data[2] = 0;
+				p->data[3] = 0;
+			}
+			else if(p->devep == 2) // IN endpoint 2 (video data)
+			{
+				if(first_bulk_packet)
+				{
+					p->data[0] = 2;
+					p->data[1] = 0x82 | frame_id;
+					memcpy((p->data)+2,frame,62);
+					ret = 64;
+					first_bulk_packet=0;
+					frame = frame + 62;
+					frame_remaining_bytes = frame_remaining_bytes - 62;
+				}
+				else if(frame_remaining_bytes<64)
+				{
+					memcpy(p->data,frame,frame_remaining_bytes); 
+					ret = frame_remaining_bytes;
+					get_frame_read();
+				}
+				else if(frame_remaining_bytes==64)
+				{
+					memcpy(p->data,frame,frame_remaining_bytes); 
+					ret = frame_remaining_bytes;
+					frame_remaining_bytes = 0;
+				}
+				else if(frame_remaining_bytes==0)
+				{
+					ret = 0;
+					get_frame_read();
+				}
+				else
+				{
+					memcpy(p->data,frame,64);
+					frame = frame+64;
+					frame_remaining_bytes = frame_remaining_bytes-64;
+					ret = 64;
+				}
+			}
+			else
+			{
+				DPRINTF("USB Data In requested.\n");
+				DPRINTF("Requested data from endpoint %02X\n", p->devep);
+			}
+			break;
+	    default:
+			DPRINTF("Bad token: %d\n", p->pid);
+			//fail:
+			ret = USB_RET_STALL;
+			break;
+    }
+	
+    return ret;
+}
+
+static void usb_uvc_handle_destroy(USBDevice *dev)
+{
+	DPRINTF("Destroy called\n");
+	close(v4l2_fd);
+}
+
+static int usb_uvc_initfn(USBDevice *dev)
+{
+	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 = (USBUVCState *)dev;
+	
+	s->current_input = 0;
+	s->dev.speed = USB_SPEED_FULL;
+	
+	if (!s->v4l2_device) {
+        error_report("V4L2 device specification needed.\n");
+        return -1;
+    }
+	else
+	{
+		DPRINTF("Trying to open %s\n.", s->v4l2_device);
+	}
+	
+	v4l2_fd = open(s->v4l2_device, O_RDWR);
+	
+	if(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;
+		}
+		
+		return -1;
+	}
+	
+	DPRINTF("Device opened correctly.\n");
+	
+	DPRINTF("Querying capabilities.\n");
+	
+	ret_err = ioctl(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;
+		}
+		
+		return -1;
+	}
+	
+	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(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(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;
+		}
+		
+		return -1;
+	}
+	
+	ioctl(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");
+		return -1;
+	}
+	
+	DPRINTF("Trying to set 320x240 MJPEG.\n");
+	memset(&v_format, 0, sizeof(v_format));
+	v_format.type                = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	v_format.fmt.pix.width       = 320; 
+	v_format.fmt.pix.height      = 240;
+	v_format.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
+	v_format.fmt.pix.field       = V4L2_FIELD_INTERLACED;
+	
+	ret_err = ioctl (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;
+		}
+		
+		return -1;
+	}
+
+	frame_max_length = v_format.fmt.pix.sizeimage;
+	
+	DPRINTF("Format correctly set.\n");
+	DPRINTF("Maximum image size: %d bytes.\n", frame_max_length);
+	
+	DPRINTF("Allocating memory for frames.\n");
+	frame = malloc(frame_max_length);
+	frame_start = frame;
+	
+	frame_id = 1;
+	
+	get_frame_read();
+	
+    return 0;
+}
+
+static USBDevice *usb_uvc_init(const char *filename)
+{
+    USBDevice *dev;
+	
+    dev = usb_create(NULL /* FIXME */, "usb-uvc-webcam");
+    qdev_init_nofail(&dev->qdev);
+	
+	DPRINTF("Filename: %s\n.", filename);
+	
+	if (!*filename) {
+        error_report("character device specification needed");
+        return NULL;
+    }
+	
+    return dev;
+}
+
+static struct USBDeviceInfo usb_uvc_info = {
+    .product_desc   = "QEMU USB Video Class Device",
+    .qdev.name      = "usb-uvc-webcam",
+    .qdev.desc      = "QEMU USB Video Class Device",
+    .usbdevice_name = "uvc-webcam",
+	.usbdevice_init = usb_uvc_init,
+    .qdev.size      = sizeof(USBUVCState),
+    .init           = usb_uvc_initfn,
+    .handle_packet  = usb_generic_handle_packet,
+    .handle_reset   = usb_uvc_handle_reset,
+    .handle_control = usb_uvc_handle_control,
+    .handle_data    = usb_uvc_handle_data,
+    .handle_destroy = usb_uvc_handle_destroy,
+	.qdev.props		= (Property[]) {
+		DEFINE_PROP_STRING("device", USBUVCState, v4l2_device),
+		DEFINE_PROP_END_OF_LIST(),
+	},
+};
+
+static void usb_uvc_register_devices(void)
+{
+    usb_qdev_register(&usb_uvc_info);
+}
+device_init(usb_uvc_register_devices)
-- 

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2010-06-10 18:23 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-06-08 15:34 [Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation Natalia Portillo
2010-06-10 17:46 ` Blue Swirl
2010-06-10 18:23   ` Natalia Portillo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).