[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation.
From: |
Natalia Portillo |
Subject: |
[Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation. |
Date: |
Tue, 8 Jun 2010 16:34:12 +0100 |
Signed-off-by: Natalia Portillo <address@hidden>
---
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 <address@hidden>
+ *
+ * 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)
--
- [Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation.,
Natalia Portillo <=