[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation.
From: |
Blue Swirl |
Subject: |
Re: [Qemu-devel] [RFC PATCH 1/2] USB Video Class device emulation. |
Date: |
Thu, 10 Jun 2010 17:46:16 +0000 |
On Tue, Jun 8, 2010 at 3:34 PM, Natalia Portillo <address@hidden> wrote:
Please add a short description of the patch.
> 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
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)
> --
>
>
>