diff mbox

[RFC,1/2] USB Video Class device emulation.

Message ID 4F6AB6D9-39AF-42C9-9CC9-7A87F244A663@claunia.com
State New
Headers show

Commit Message

Natalia Portillo June 8, 2010, 3:34 p.m. UTC
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

--

Comments

Blue Swirl June 10, 2010, 5:46 p.m. UTC | #1
On Tue, Jun 8, 2010 at 3:34 PM, Natalia Portillo <claunia@claunia.com> wrote:

Please add a short description of the patch.

> 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

This should be commented out.

> +
> +#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;

The formatting seems to be off. Please check that indentation is 4
spaces, no tabs.

> +       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;

Couldn't these belong to USBUVCState?

> +
> +static const uint8_t qemu_uvc_dev_descriptor[] = {
> +       0x12,       /*  u8 bLength; */
> +       0x01,       /*  u8 bDescriptorType; Device */

It would be more readable if you defined structures for all the
descriptors and some #defines or enums for the magic numbers. Then
this would become something like:
    .bLength = 0x12,
    .bDescriptorType = USB_DESCTYPE_DEVICE,

so very few comments would be needed anymore. It's a big task though
and could be taken as later cleanup.

> +       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; */

I'm not familiar with USB at all, is the endianness specified to be
little endian, or should this be different on a big endian host or
guest?

> +       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 */

It's MHz.

> +       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)
> +       {

There must be a space between 'if' and '(' and the braces must be on
the same line as 'if' etc. Please read CODING_STYLE document.

> +               DPRINTF("Error while reading frame.\n");
> +               frame_length = 0;
> +       }
> +       else
> +       {

} 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);

Are these needed? If they are only used in specific debugging, please
add a separate DPRINTF_xyz function which can be enabled with
DEBUG_xyz.

> +
> +       switch(request)
> +       {
> +               case DeviceRequest | USB_REQ_GET_STATUS:

I think the formatting for 'case' is also not in line.

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

Please add #defines or enums for the magic values. There should be
spaces around '&'.

> +                       {
> +                               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:

Probably should be removed.

> +                       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);

Please remove the space between 'ioctl' and '(' for consistency.

> +
> +       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);

qemu_malloc()

> +       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)
> --
>
>
>
Natalia Portillo June 10, 2010, 6:23 p.m. UTC | #2
Hi Blue,

You're right on all things.
I'll check CODING_STYLE and do the things.

Thanks a lot.
diff mbox

Patch

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)