qemu-devel
[Top][All Lists]
Advanced

[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)
-- 




reply via email to

[Prev in Thread] Current Thread [Next in Thread]