Message ID | 4F6AB6D9-39AF-42C9-9CC9-7A87F244A663@claunia.com |
---|---|
State | New |
Headers | show |
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) > -- > > >
Hi Blue, You're right on all things. I'll check CODING_STYLE and do the things. Thanks a lot.
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)
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 --