qemu-usb: add usb webcam model

Fixes #4078
This commit is contained in:
Alexander Boettcher 2021-03-16 09:44:23 +01:00 committed by Norman Feske
parent 4826bd82fe
commit 58db8c647a
21 changed files with 1201 additions and 23 deletions

View File

@ -121,7 +121,8 @@ namespace Qemu {
*/
Controller *usb_init(Timer_queue &tq, Pci_device &pd,
Genode::Entrypoint &ep,
Genode::Allocator &, Genode::Env &);
Genode::Allocator &, Genode::Env &,
Genode::Xml_node const &);
/**
* Reset USB libray

View File

@ -5,16 +5,20 @@ CC_WARN=
INC_DIR += $(LIB_DIR) $(QEMU_USB_DIR)
LIBS = qemu-usb_include
LIBS = qemu-usb_include
LIBS += libc
LIBS += libyuv
SRC_CC = dummies.cc qemu_emul.cc host.cc
SRC_CC = dummies.cc qemu_emul.cc host.cc webcam.cc
SRC_C = hcd-xhci.c hcd-xhci-pci.c core.c bus.c
SRC_C = desc.c hcd-xhci.c hcd-xhci-pci.c core.c bus.c
SRC_C += dev-webcam.c
SHARED_LIB = yes
LD_OPT += --version-script=$(LIB_DIR)/symbol.map
vpath dev-webcam.c $(LIB_DIR)
vpath %.c $(QEMU_USB_DIR)
vpath %.cc $(LIB_DIR)

View File

@ -1 +1 @@
d716b4378eb2f243207f868646dfd7e62469228b
4b74867ae1e9383a53edb67fc3665ed8b305e2e6

View File

@ -1,8 +1,9 @@
This library makes the xHCI device model of Qemu available on Genode
and is used as a back end for such for device models in existing VMMs.
Additionally a webcam device model attached to the xHCI device is supported.
Usage
~~~~~
Usage xHCI model
~~~~~~~~~~~~~~~~
The user of this library is required to provide certain back end
functionality, namely a Timer_queue to handle timer events and a Pci_device
@ -28,3 +29,27 @@ needs to be called to reattach USB devices.
Timer callbacks that have been registered using the Timer_queue
interface have to be executed by calling 'Qemu::usb_timer_callback'
when the timer triggers.
Usage webcam model attached to the xHCI model
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If the xHCI model is enabled and used, then the webcam device model can be
enabled by specifying a XML node in the config ROM:
<config ...>
...
<webcam width="640" height="480" fps="15" vertical_flip="false" screen_size="false" report="false"/>
...
</config>
The webcam attributes are optional. The values shown above, are the default ones
when a attribute is not specified. When the webcam node is specified, the model
will open and will use a Genode capture session to obtain the
frames in the rate as specified by the frame per secondes (fps) attribute. The
'vertical_flip' attribute specifies, whether the frames are shown flipped
for guests. For Windows guests the value has to be false, for Linux guests
true. The format supported by the model is in BGR3. If the 'screen_size' is set
to true, the webcam model will try to use the screen size as provided by the
capture session. If the screen size is invalid (e.g. 0x0), the attribute
values of 'width' and 'height' will be used instead. If the 'report' attribute
is set, a report will be generated whenever the guests starts/ends capturing.

View File

@ -0,0 +1,804 @@
/*
* QEMU USB webcam model
*
* Written by Alexander Boettcher
*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include "hw/usb.h"
#include "desc.h"
#include "webcam-backend.h"
enum DeviceConfiguration {
DEVICE_VC_INTERFACE_ID = 0,
DEVICE_VS_INTERFACE_ID = 1,
DEVICE_VS_FORMAT_YUV = 1,
DEVICE_VS_FORMAT_BGR = 2,
DEVICE_VS_BITS_YUV = 16,
DEVICE_VS_BITS_BGR = 24,
DEVICE_VS_FRAME_INDEX = 1,
TERMINAL_ID_INPUT = 1,
TERMINAL_ID_OUTPUT = 2,
DEVICE_EP_ID = 1,
EP_MAX_PACKET_SIZE = 512,
};
typedef struct USBWebcamState {
USBDevice dev;
QEMUTimer *timer;
USBPacket *delayed_packet;
unsigned bytes_frame;
unsigned bytes_payload;
unsigned frame_counter;
uint8_t frame_toggle_bit;
bool delay_packet;
bool capture;
uint8_t watchdog;
uint8_t *frame_pixel;
} USBWebcamState;
#define TYPE_USB_WEBCAM "usb-webcam"
#define USB_WEBCAM(obj) OBJECT_CHECK(USBWebcamState, (obj), TYPE_USB_WEBCAM)
#define U16(x) ((x) & 0xff), (((x) >> 8) & 0xff)
#define U24(x) U16(x), (((x) >> 16) & 0xff)
#define U32(x) U24(x), (((x) >> 24) & 0xff)
enum {
STR_MANUFACTURER = 1,
STR_PRODUCT,
STR_SERIALNUMBER,
STR_CONFIG_HIGH,
STR_VIDEOCONTROL,
STR_VIDEOSTREAM,
STR_CAMERATERMINAL,
};
static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "Genode",
[STR_PRODUCT] = "Genode USB WebCAM",
[STR_SERIALNUMBER] = "1",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
[STR_VIDEOCONTROL] = "Videocontrol",
[STR_VIDEOSTREAM] = "Videostream",
[STR_CAMERATERMINAL] = "Camera Sensor",
};
enum {
USB_CLASS_VIDEO = 0xe,
SC_VIDEO_CONTROL = 1,
SC_VIDEO_STREAMING = 2,
SC_VIDEO_INTERFACE_COLLECTION = 3,
};
enum {
VC_HEADER = 1,
VC_INPUT_TERMINAL = 2,
VC_OUTPUT_TERMINAL = 3,
VS_INPUT_HEADER = 1,
VS_FORMAT_UNCOMPRESSED = 4,
VS_FRAME_UNCOMPRESSED = 5,
VS_FORMAT_FRAME_BASED = 0x10,
VS_FRAME_FRAME_BASED = 0x11,
};
enum {
TT_STREAMING = 0x101,
ITT_CAMERA = 0x201,
};
enum {
UV_SET_CUR = 0x01,
UV_GET_CUR = 0x81,
UV_GET_MIN = 0x82,
UV_GET_MAX = 0x83,
UV_GET_DEF = 0x87,
};
enum {
VS_PROBE_CONTROL = 0x1,
VS_COMMIT_CONTROL = 0x2,
};
struct vs_probe_control {
uint16_t bmHint;
uint8_t bFormatIndex;
uint8_t bFrameIndex;
uint32_t dwFrameInterval;
uint16_t wKeyFrameRate;
uint16_t wPFrameRate;
uint16_t wCompQuality;
uint16_t wCompWindowSize;
uint16_t wDelay;
uint32_t dwMaxVideoFrameSize;
uint32_t dwMaxPayLoadTransferSize;
uint32_t dwClockFrequency;
uint8_t bmFramingInfo;
uint8_t bPreferedVersion;
uint8_t bMinVersion;
uint8_t bMaxVersion;
} QEMU_PACKED;
static struct vs_probe_control vs_commit_state = {
.bFormatIndex = DEVICE_VS_FORMAT_YUV,
.bFrameIndex = DEVICE_VS_FRAME_INDEX,
.bmFramingInfo = 1,
.bPreferedVersion = 1, /* Payload version 1.(1!) for uncompressed and frame based format */
.bMinVersion = 1,
.bMaxVersion = 1,
};
static struct vs_probe_control vs_probe_state;
enum {
BFH_END_OF_FRAME = 1U << 1,
BFH_PRESENT_TIME = 1U << 2,
BFH_END_OF_HEADER = 1U << 7,
};
struct payload_header {
uint8_t length;
uint8_t bfh;
uint32_t timestamp;
} QEMU_PACKED;
static struct bgr3_frame_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFrameIndex;
uint8_t bmCapabilities;
uint16_t wWidth;
uint16_t wHeight;
uint32_t dwMinBitRate;
uint32_t dwMaxBitRate;
uint32_t dwDefaultFrameInterval;
uint8_t bFrameIntervalType;
uint32_t dwBytesPerLine;
uint32_t dwFrameInterval;
} QEMU_PACKED bgr_desc = {
.bLength = 30, /* n=0 ->38, >0 = 26 + 4*n */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = VS_FRAME_FRAME_BASED,
.bFrameIndex = DEVICE_VS_FRAME_INDEX,
.bmCapabilities = 1 | 2, /* D0: Still image, D1: Fixed frame-rate */
.bFrameIntervalType = 1, /* n */
};
static struct yuv_frame_desc {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFrameIndex;
uint8_t bmCapabilities;
uint16_t wWidth;
uint16_t wHeight;
uint32_t dwMinBitRate;
uint32_t dwMaxBitRate;
uint32_t dwMaxVideoFrameBufferSize;
uint32_t dwDefaultFrameInterval;
uint8_t bFrameIntervalType;
uint32_t dwFrameInterval;
} QEMU_PACKED yuv_desc = {
.bLength = 30, /* n=0 ->38, >0 = 26 + 4*n */
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = VS_FRAME_UNCOMPRESSED,
.bFrameIndex = DEVICE_VS_FRAME_INDEX,
.bmCapabilities = 1 | 2, /* D0: Still image, D1: Fixed frame-rate */
.bFrameIntervalType = 1, /* n */
};
static struct {
uint8_t bpp;
uint16_t width;
uint16_t height;
uint32_t interval; /* dwFrameInterval */
void (*capture)(void *);
} formats [2];
static unsigned active_format()
{
return vs_commit_state.bFormatIndex - 1;
}
static const USBDescIface desc_iface_high [] = {
{
.bInterfaceNumber = DEVICE_VC_INTERFACE_ID,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = SC_VIDEO_CONTROL,
.bInterfaceProtocol = 0, /* undefined */
.iInterface = STR_VIDEOCONTROL,
.ndesc = 3,
.descs = (USBDescOther[]) {
{
/* Class-specific VC Interface Header Descriptor */
.data = (uint8_t[]) {
12 + 1, /* u8 bLength 12 + n */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VC_HEADER, /* u8 bDescriptorSubType */
U16(0x0110), /* u16 bcdUVC */
U16(13 + 15 + 9), /* u16 wTotalLength */
U32(1000000), /* u32 dwClockFrequency - deprecated */
0x01, /* u8 bInCollection */
0x01 /* u8 baInterfaceNr(1 .. n) */
}
},
{
/* Camera Terminal Descriptor */
.data = (uint8_t[]) {
15 + 0, /* u8 bLength 15 + n */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VC_INPUT_TERMINAL, /* u8 bDescriptorSubType */
TERMINAL_ID_INPUT, /* u8 bTerminalID */
U16(ITT_CAMERA), /* u16 wTerminalType */
0, /* u8 bAssocTerminal */
STR_CAMERATERMINAL, /* u8 iTerminal */
0, /* u16 wObjectFocalLengthMin */
0, /* u16 wObjectFocalLengthMax */
0, /* u16 wOcularFocalLength */
0 /* u8 bControlSize */
}
},
{
.data = (uint8_t[]) {
9 + 0, /* u8 bLength 9 + n */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VC_OUTPUT_TERMINAL, /* u8 bDescriptorSubType */
TERMINAL_ID_OUTPUT, /* u8 bTerminalID */
U16(TT_STREAMING), /* u16 wTerminalType */
0, /* u8 bAssocTerminal */
TERMINAL_ID_INPUT, /* u8 bSourceID (<- bTerminalID) */
0 /* u8 iTerminal */
}
}
}
},
{
.bInterfaceNumber = DEVICE_VS_INTERFACE_ID,
.bInterfaceClass = USB_CLASS_VIDEO,
.bInterfaceSubClass = SC_VIDEO_STREAMING,
.bInterfaceProtocol = 0, /* undefined */
.iInterface = STR_VIDEOSTREAM,
.ndesc = 5,
.descs = (USBDescOther[]) {
{
/* Class-specific VS Interface Header Descriptor */
.data = (uint8_t[]) {
13 + 2 * 1, /* u8 bLength 13 + p * n */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VS_INPUT_HEADER, /* u8 bDescriptorSubType */
2, /* u8 bNumFormats p */
U16(14 + 27 + 30), /* u16 wTotalLength */
USB_DIR_IN | DEVICE_EP_ID, /* u8 bEndpointAddress */
0, /* u8 bmInfo */
TERMINAL_ID_OUTPUT, /* u8 bTerminalLink <- bTerminalID */
1, /* u8 bStillCaptureMethod */
1, /* u8 bTriggerSupport */
0, /* u8 bTriggerUsage */
1, /* u8 bControlSize n */
0 /* u8 bmaControls (1...n) */
}
},
{
.data = (uint8_t[]) {
27, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VS_FORMAT_UNCOMPRESSED, /* u8 bDescriptorSubType */
DEVICE_VS_FORMAT_YUV, /* u8 bFormatIndex */
1, /* u8 bNumFrameDescriptors */
0x59, 0x55, 0x59, 0x32, /* u8 guidFormat x16 - YUY2 4:2:2 */
0x00, 0x00, 0x10, 0x00,
0x80, 0x00, 0x00, 0xAA,
0x00, 0x38, 0x9B, 0x71,
DEVICE_VS_BITS_YUV, /* u8 bBitsPerPixel */
DEVICE_VS_FRAME_INDEX, /* u8 bDefaultFrameIndex */
0, /* u8 bAspectRadioX */
0, /* u8 bAspectRadioY */
0, /* u8 bmInterlaceFlags */
0 /* u8 bCopyProtect */
}
},
{
.data = (uint8_t *)&yuv_desc
},
{
.data = (uint8_t[]) {
28, /* u8 bLength */
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
VS_FORMAT_FRAME_BASED, /* u8 bDescriptorSubType */
DEVICE_VS_FORMAT_BGR, /* u8 bFormatIndex */
1, /* u8 bNumFrameDescriptors */
0x7d, 0xeb, 0x36, 0xe4, /* u8 guidFormat - BGR */
0x4f, 0x52, 0xce, 0x11,
0x9f, 0x53, 0x00, 0x20,
0xaf, 0x0b, 0xa7, 0x70,
DEVICE_VS_BITS_BGR, /* u8 bBitsPerPixel */
DEVICE_VS_FRAME_INDEX, /* u8 bDefaultFrameIndex */
0, /* u8 bAspectRadioX */
0, /* u8 bAspectRadioY */
0, /* u8 bmInterlaceFlags */
0, /* u8 bCopyProtect */
0 /* u8 bVariableSize */
}
},
{
.data = (uint8_t *)&bgr_desc
}
},
.bNumEndpoints = 1,
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | DEVICE_EP_ID,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = EP_MAX_PACKET_SIZE,
.bInterval = 1,
},
}
}
};
static struct USBDescIfaceAssoc desc_iface_group = {
.bFirstInterface = 0,
.bInterfaceCount = 2,
.bFunctionClass = USB_CLASS_VIDEO,
.bFunctionSubClass = SC_VIDEO_INTERFACE_COLLECTION,
.bFunctionProtocol = 0,
.nif = ARRAY_SIZE(desc_iface_high),
.ifs = desc_iface_high,
};
static const USBDescDevice desc_device_high = {
.bcdUSB = 0x0200,
.bDeviceClass = 0xef, /* Miscellaneous Device Class */
.bDeviceSubClass = 0x02, /* common class */
.bDeviceProtocol = 0x01, /* Interface Association Descriptor */
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 2,
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_HIGH,
.bmAttributes = USB_CFG_ATT_ONE | USB_CFG_ATT_SELFPOWER,
.nif = 0,
.nif_groups = 1,
.if_groups = &desc_iface_group,
},
},
};
static const USBDesc descriptor_webcam = {
.id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
.idProduct = 0x0001,
.bcdDevice = 0,
.iManufacturer = STR_MANUFACTURER,
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
.high = &desc_device_high,
.str = desc_strings,
};
static const VMStateDescription vmstate_usb_webcam = {
.name = TYPE_USB_WEBCAM,
};
static Property webcam_properties[] = {
DEFINE_PROP_END_OF_LIST(),
};
static void webcam_start_timer(USBWebcamState * const state)
{
int64_t const now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
timer_mod(state->timer, now_ns + 100ull * formats[active_format()].interval);
}
static unsigned max_frame_size(unsigned const format)
{
return formats[format].width * formats[format].height *
formats[format].bpp / 8;
}
static void usb_webcam_init_state(USBWebcamState *state)
{
state->delayed_packet = 0;
state->bytes_frame = 0;
state->bytes_payload = 0;
state->frame_counter = 0;
state->frame_toggle_bit = 0;
state->delay_packet = false;
state->capture = false;
state->watchdog = 0;
}
static void usb_webcam_handle_reset(USBDevice *dev)
{
USBWebcamState *state = USB_WEBCAM(dev);
if (!state)
return;
usb_webcam_init_state(state);
}
static void usb_webcam_capture_state_changed(bool const on)
{
char const * format = "unknown";
if (vs_commit_state.bFormatIndex == DEVICE_VS_FORMAT_BGR)
format = "BGR3";
else if (vs_commit_state.bFormatIndex == DEVICE_VS_FORMAT_YUV)
format = "YUY2";
capture_state_changed(on, format);
}
static void usb_webcam_setup_packet(USBWebcamState * const state, USBPacket * const p)
{
unsigned packet_size = vs_commit_state.dwMaxPayLoadTransferSize;
struct payload_header header = { .length = 0, .bfh = 0 };
bool start_timer = !state->bytes_frame;
if (p->iov.size < packet_size)
packet_size = p->iov.size;
if (packet_size <= sizeof(header)) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
if (state->bytes_frame >= max_frame_size(active_format())) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
/* reset capture watchdog */
if (state->watchdog) {
state->watchdog = 0;
start_timer = true;
}
/* check for capture state change */
if (!state->capture) {
state->capture = true;
start_timer = true;
usb_webcam_capture_state_changed(state->capture);
}
if (start_timer)
webcam_start_timer(state);
/* payload header */
if (!state->bytes_payload || state->bytes_payload >= vs_commit_state.dwMaxPayLoadTransferSize) {
header.length = sizeof(header);
header.bfh = BFH_END_OF_HEADER | BFH_PRESENT_TIME | state->frame_toggle_bit;
header.timestamp = state->frame_counter;
state->bytes_payload = 0;
}
/* frame end check */
if (state->bytes_frame + packet_size - header.length >= max_frame_size(active_format())) {
packet_size = header.length + max_frame_size(active_format()) - state->bytes_frame;
header.bfh |= BFH_END_OF_FRAME;
state->bytes_payload = 0;
if (state->frame_toggle_bit)
state->frame_toggle_bit = 0;
else
state->frame_toggle_bit = 1;
state->frame_counter++;
state->delay_packet = true;
} else {
state->bytes_payload += packet_size;
}
/* copy header data in */
if (header.length)
usb_packet_copy(p, &header, header.length);
/* copy frame data in */
usb_packet_copy(p, state->frame_pixel + state->bytes_frame,
packet_size - header.length);
p->status = USB_RET_SUCCESS;
if (state->delay_packet)
state->bytes_frame = 0;
else
state->bytes_frame += packet_size - header.length;
}
static void webcam_timeout(void *opague)
{
USBDevice *dev = (USBDevice *)opague;
USBWebcamState *state = USB_WEBCAM(opague);
if (!state->delayed_packet) {
unsigned const fps = 10000000u / formats[active_format()].interval;
/* capture off detection - after 2s or if in delay_packet state */
if (state->delay_packet || (state->watchdog && state->watchdog >= fps * 2)) {
state->capture = false;
state->delay_packet = false;
usb_webcam_capture_state_changed(state->capture);
} else {
state->watchdog ++;
webcam_start_timer(state);
}
return;
}
USBPacket *p = state->delayed_packet;
state->delayed_packet = 0;
state->delay_packet = false;
/* request next frame pixel buffer */
formats[active_format()].capture(state->frame_pixel);
/* continue usb transmission with new frame */
usb_webcam_setup_packet(state, p);
if (p->status == USB_RET_SUCCESS)
usb_packet_complete(dev, p);
}
static void usb_webcam_realize(USBDevice *dev, Error **errp)
{
USBWebcamState *state = USB_WEBCAM(dev);
usb_desc_create_serial(dev);
usb_desc_init(dev);
/* change target speed, which was set by usb_desc_init to USB_SPEED_FULL */
dev->speed = USB_SPEED_HIGH;
dev->speedmask = USB_SPEED_MASK_HIGH;
/* sets dev->device because of dev->speed* changes */
usb_desc_attach(dev);
state->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, webcam_timeout, dev);
state->frame_pixel = g_malloc(formats[DEVICE_VS_FORMAT_BGR - 1].width *
formats[DEVICE_VS_FORMAT_BGR - 1].height *
formats[DEVICE_VS_FORMAT_BGR - 1].bpp / 8);
}
static void usb_webcam_handle_control(USBDevice * const dev,
USBPacket * const p,
int const request, int const value,
int const index, int const length,
uint8_t * const data)
{
int const ret = usb_desc_handle_control(dev, p, request, value, index,
length, data);
if (ret >= 0) {
p->status = USB_RET_SUCCESS;
/* got handled */
return;
}
bool stall = true;
switch (request) {
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
if (length || (index != (USB_DIR_IN | DEVICE_EP_ID)))
break;
/* release packets on feature == 0 endpoint clear request */
if (!value) {
USBWebcamState *state = USB_WEBCAM(dev);
if (state && state->delayed_packet)
state->delayed_packet = 0;
stall = false;
}
break;
case ClassInterfaceRequest | UV_GET_DEF:
case ClassInterfaceRequest | UV_GET_CUR:
case ClassInterfaceRequest | UV_GET_MIN:
case ClassInterfaceRequest | UV_GET_MAX:
{
if (value & 0xff)
break;
unsigned const cs = (value >> 8) & 0xff; /* control selector */
unsigned const interface = index & 0xff;
unsigned const entity = (index >> 8) & 0xff;
if (interface != DEVICE_VS_INTERFACE_ID)
break;
if (cs != VS_PROBE_CONTROL || length < sizeof(vs_probe_state))
break;
memcpy(data, &vs_probe_state, sizeof(vs_probe_state));
p->actual_length = sizeof(vs_probe_state);
stall = false;
break;
}
case ClassInterfaceOutRequest | UV_SET_CUR:
{
if (value & 0xff)
break;
unsigned const cs = (value >> 8) & 0xff; /* control selector */
unsigned const interface = index & 0xff;
unsigned const entity = (index >> 8) & 0xff;
if (interface != DEVICE_VS_INTERFACE_ID)
break;
if (length < sizeof(vs_probe_state))
break;
struct vs_probe_control * req = (struct vs_probe_control *)data;
if ((cs != VS_COMMIT_CONTROL) && (cs != VS_PROBE_CONTROL))
break;
if ((req->bFormatIndex != DEVICE_VS_FORMAT_BGR) &&
(req->bFormatIndex != DEVICE_VS_FORMAT_YUV))
break;
vs_probe_state.bFormatIndex = req->bFormatIndex;
vs_probe_state.dwMaxVideoFrameSize = max_frame_size(vs_probe_state.bFormatIndex - 1);
vs_probe_state.dwMaxPayLoadTransferSize = max_frame_size(vs_probe_state.bFormatIndex - 1) / 2;
if (cs == VS_COMMIT_CONTROL) {
bool const notify = vs_commit_state.bFormatIndex != vs_probe_state.bFormatIndex;
vs_commit_state = vs_probe_state;
if (notify) {
USBWebcamState *state = USB_WEBCAM(dev);
usb_webcam_capture_state_changed(state->capture);
}
}
stall = false;
break;
}
default:
break;
}
if (stall) {
qemu_printf("%s:%d unhandled request len=%d, request=%x, value=%x,"
" index=%x - stall\n", __func__, __LINE__,
length, request, value, index);
p->status = USB_RET_STALL;
} else
p->status = USB_RET_SUCCESS;
}
static void usb_webcam_handle_data(USBDevice *dev, USBPacket *p)
{
USBWebcamState *state = USB_WEBCAM(dev);
switch (p->pid) {
case USB_TOKEN_IN:
if (!p->ep || p->ep->nr != DEVICE_EP_ID) {
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
break;
default:
p->status = USB_RET_STALL;
if (state->capture)
usb_webcam_capture_state_changed(false);
usb_webcam_init_state(state);
return;
}
if (state->delay_packet) {
p->status = USB_RET_ASYNC;
state->delayed_packet = p;
return;
}
usb_webcam_setup_packet(state, p);
}
static void usb_webcam_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->realize = usb_webcam_realize;
uc->product_desc = desc_strings[STR_PRODUCT];
uc->usb_desc = &descriptor_webcam;
uc->handle_reset = usb_webcam_handle_reset;
uc->handle_control = usb_webcam_handle_control;
uc->handle_data = usb_webcam_handle_data;
dc->vmsd = &vmstate_usb_webcam;
device_class_set_props(dc, webcam_properties);
}
static const TypeInfo webcam_info = {
.name = TYPE_USB_WEBCAM,
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBWebcamState),
.class_init = usb_webcam_class_initfn,
};
static void usb_webcam_register_types(void)
{
/* request host target configuration */
struct webcam_config config;
webcam_backend_config(&config);
unsigned const frame_interval = 10000000u / config.fps; /* in 100ns units */
for (unsigned i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
formats[i].width = config.width;
formats[i].height = config.height;
formats[i].interval = frame_interval;
}
formats[DEVICE_VS_FORMAT_BGR - 1].bpp = DEVICE_VS_BITS_BGR;
formats[DEVICE_VS_FORMAT_BGR - 1].capture = capture_bgr_frame;
formats[DEVICE_VS_FORMAT_YUV - 1].bpp = DEVICE_VS_BITS_YUV;
formats[DEVICE_VS_FORMAT_YUV - 1].capture = capture_yuv_frame;
/* setup model configuration parameters */
{ /* BGR3 */
unsigned const frame_bpl = config.width * DEVICE_VS_BITS_BGR / 8;
unsigned const frame_bitrate = config.width * config.height *
DEVICE_VS_BITS_BGR * config.fps;
bgr_desc.wWidth = config.width;
bgr_desc.wHeight = config.height;
bgr_desc.dwMinBitRate = frame_bitrate;
bgr_desc.dwMaxBitRate = frame_bitrate;
bgr_desc.dwDefaultFrameInterval = frame_interval;
bgr_desc.dwFrameInterval = frame_interval;
bgr_desc.dwBytesPerLine = frame_bpl;
}
{ /* YUV */
unsigned const frame_bpl = config.width * DEVICE_VS_BITS_YUV / 8;
unsigned const frame_bitrate = config.width * config.height *
DEVICE_VS_BITS_YUV * config.fps;
yuv_desc.wWidth = config.width;
yuv_desc.wHeight = config.height;
yuv_desc.dwMinBitRate = frame_bitrate;
yuv_desc.dwMaxBitRate = frame_bitrate;
yuv_desc.dwDefaultFrameInterval = frame_interval;
yuv_desc.dwFrameInterval = frame_interval;
yuv_desc.dwMaxVideoFrameBufferSize = max_frame_size(DEVICE_VS_FORMAT_YUV - 1);
}
vs_commit_state.dwFrameInterval = frame_interval;
vs_commit_state.dwMaxVideoFrameSize = max_frame_size(active_format());
vs_commit_state.dwMaxPayLoadTransferSize = max_frame_size(active_format()) / 2;
vs_commit_state.dwClockFrequency = config.fps;
vs_probe_state = vs_commit_state;
/* register device */
type_register_static(&webcam_info);
}
type_init(usb_webcam_register_types)

View File

@ -212,13 +212,27 @@ void qdev_simple_device_unplug_cb(HotplugHandler*, DeviceState*, Error**)
}
char* qdev_get_dev_path(DeviceState*)
/**
* close to original in hw/core/qdev.c
*/
char *qdev_get_dev_path(DeviceState *dev)
{
TRACE_AND_STOP;
return 0;
BusClass *bc;
if (!dev || !dev->parent_bus) {
return nullptr;
}
bc = BUS_GET_CLASS(dev->parent_bus);
if (bc->get_dev_path) {
return bc->get_dev_path(dev);
}
return nullptr;
}
const char* qdev_fw_name(DeviceState*)
{
TRACE;
@ -256,7 +270,14 @@ gchar* g_strdup(const gchar*)
}
size_t strlen(const char*)
/************************
** hw/usb/desc-msos.c **
************************/
struct USBDesc;
struct USBPacket;
int usb_desc_msos(const USBDesc *desc, USBPacket *p,
int index, uint8_t *dest, size_t len)
{
TRACE_AND_STOP;
return 0;

View File

@ -3,6 +3,8 @@ qemu-x.x.x/include/hw/usb/xhci.h
qemu-x.x.x/include/qemu/queue.h
qemu-x.x.x/hw/usb/bus.c
qemu-x.x.x/hw/usb/core.c
qemu-x.x.x/hw/usb/desc.c
qemu-x.x.x/hw/usb/desc.h
qemu-x.x.x/hw/usb/hcd-xhci.c
qemu-x.x.x/hw/usb/hcd-xhci.h
qemu-x.x.x/hw/usb/hcd-xhci-nec.c

View File

@ -32,6 +32,7 @@ typedef signed long ssize_t;
#ifndef __cplusplus
typedef _Bool bool;
enum { false = 0, true = 1 };
typedef __WCHAR_TYPE__ wchar_t;
#endif
typedef __SIZE_TYPE__ size_t;
typedef unsigned long dma_addr_t;
@ -74,6 +75,7 @@ char *strchr(const char *s, int c);
#define NULL (void *)0
#define QEMU_SENTINEL
#define QEMU_PACKED __attribute__((packed))
#define le32_to_cpu(x) (x)
#define cpu_to_le32(x) (x)
@ -240,6 +242,8 @@ struct BusClass *cast_BusClass(void *);
struct HotplugHandlerClass *cast_HotplugHandlerClass(void *);
struct USBDeviceClass *cast_USBDeviceClass(void *);
struct USBWebcamState;
struct USBWebcamState *cast_USBWebcamState(void *);
struct USBBus *cast_USBBus(void *);
@ -255,6 +259,8 @@ struct USBBus *cast_USBBus(void *);
#define OBJECT_GET_CLASS(klass, obj, str) \
OBJECT_CHECK(klass, obj, str)
#define BUS_GET_CLASS(obj) OBJECT_GET_CLASS(BusClass, (obj), TYPE_BUS)
#define DECLARE_INSTANCE_CHECKER(InstanceType, OBJ_NAME, TYPENAME) \
static inline InstanceType * \
OBJ_NAME(const void *obj) \
@ -853,6 +859,18 @@ typedef struct VMStateDescription
#define trace_usb_xhci_xfer_start(...) TRACE_PRINTF("%s:%d\n", "trace_usb_xhci_xfer_start", __LINE__)
#define trace_usb_xhci_xfer_success(...) TRACE_PRINTF("%s:%d\n", "trace_usb_xhci_xfer_success", __LINE__)
#define trace_usb_desc_device(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_device", __LINE__)
#define trace_usb_desc_config(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_config", __LINE__)
#define trace_usb_desc_string(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_string", __LINE__)
#define trace_usb_desc_device_qualifier(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_device_qualifier", __LINE__)
#define trace_usb_desc_other_speed_config(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_other_speed_config", __LINE__)
#define trace_usb_desc_bos(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_bos", __LINE__)
#define trace_usb_set_addr(...) TRACE_PRINTF("%s:%d\n", "trace_usb_set_addr", __LINE__)
#define trace_usb_set_config(...) TRACE_PRINTF("%s:%d\n", "trace_usb_set_config", __LINE__)
#define trace_usb_clear_device_feature(...) TRACE_PRINTF("%s:%d\n", "trace_usb_clear_device_feature", __LINE__)
#define trace_usb_set_device_feature(...) TRACE_PRINTF("%s:%d\n", "trace_usb_set_device_feature", __LINE__)
#define trace_usb_set_interface(...) TRACE_PRINTF("%s:%d\n", "trace_usb_set_interface", __LINE__)
#define trace_usb_desc_msos(...) TRACE_PRINTF("%s:%d\n", "trace_usb_desc_msos", __LINE__)
/***********************
** library interface **

View File

@ -13,6 +13,7 @@
*/
/* Genode includes */
#include <base/attached_rom_dataspace.h>
#include <base/env.h>
#include <base/log.h>
#include <util/misc_math.h>
@ -68,6 +69,9 @@ extern "C" void _type_init_usb_host_register_types(Genode::Entrypoint*,
extern "C" void _type_init_xhci_register_types();
extern "C" void _type_init_xhci_pci_register_types();
extern "C" void _type_init_host_webcam_register_types(Genode::Env &,
Genode::Xml_node const &);
extern Genode::Mutex _mutex;
Qemu::Controller *qemu_controller();
@ -80,7 +84,9 @@ static Genode::Allocator *_heap = nullptr;
Qemu::Controller *Qemu::usb_init(Timer_queue &tq, Pci_device &pci,
Genode::Entrypoint &ep,
Genode::Allocator &alloc, Genode::Env &env)
Genode::Allocator &alloc,
Genode::Env &env,
Genode::Xml_node const &config)
{
_heap = &alloc;
_timer_queue = &tq;
@ -91,6 +97,10 @@ Qemu::Controller *Qemu::usb_init(Timer_queue &tq, Pci_device &pci,
_type_init_xhci_pci_register_types();
_type_init_usb_host_register_types(&ep, &alloc, &env);
config.with_sub_node("webcam", [&] (Genode::Xml_node const &node) {
_type_init_host_webcam_register_types(env, node);
});
return qemu_controller();
}
@ -210,6 +220,9 @@ struct Wrapper
XHCIPciState *_xhci_pci_state = nullptr;
USBHostDevice _usb_host_device;
USBWebcamState *_webcam_state = nullptr;
unsigned long _webcam_state_size = 0;
ObjectClass _object_class;
DeviceClass _device_class;
PCIDeviceClass _pci_device_class;
@ -239,6 +252,11 @@ struct Wrapper
&& ptr < ((char*)_xhci_pci_state + sizeof(XHCIPciState)))
return true;
if (_webcam_state != nullptr
&& ptr >= _webcam_state
&& ptr < ((char*)_webcam_state + _webcam_state_size))
return true;
using addr_t = Genode::addr_t;
addr_t p = (addr_t)ptr;
@ -259,6 +277,7 @@ struct Object_pool
USB_BUS, /* bus driver */
USB_DEVICE, /* USB device driver */
USB_HOST_DEVICE, /* USB host device driver */
USB_WEBCAM, /* USB host device driver */
MAX = 10 /* host devices (USB_HOST_DEVICE - MAX) */
};
@ -351,6 +370,10 @@ struct DeviceClass *cast_DeviceClass(void *ptr) {
return &Object_pool::p()->find_object(ptr)->_device_class; }
struct USBWebcamState *cast_USBWebcamState(void *ptr) {
return Object_pool::p()->find_object(ptr)->_webcam_state; }
struct USBDeviceClass *cast_USBDeviceClass(void *ptr) {
return &Object_pool::p()->find_object(ptr)->_usb_device_class; }
@ -371,7 +394,8 @@ struct USBBus *cast_DeviceStateToUSBBus(void) {
return &Object_pool::p()->xhci_state()->bus; }
USBHostDevice *create_usbdevice(void *data)
template <typename FUNC>
static USBHostDevice *_create_usbdevice(int const type, FUNC const &fn_init)
{
Wrapper *obj = Object_pool::p()->create_object();
if (!obj) {
@ -379,7 +403,7 @@ USBHostDevice *create_usbdevice(void *data)
return nullptr;
}
obj->_usb_device_class = Object_pool::p()->obj[Object_pool::USB_HOST_DEVICE]._usb_device_class;
obj->_usb_device_class = Object_pool::p()->obj[type]._usb_device_class;
/*
* Set parent bus object
@ -387,8 +411,10 @@ USBHostDevice *create_usbdevice(void *data)
DeviceState *dev_state = &obj->_device_state;
dev_state->parent_bus = Object_pool::p()->bus();
/* set data pointer */
obj->_usb_host_device.data = data;
obj->_usb_device.qdev.parent_bus = dev_state->parent_bus;
/* per type initialization */
fn_init(*obj);
/*
* Attach new USB host device to USB device driver
@ -410,6 +436,16 @@ USBHostDevice *create_usbdevice(void *data)
}
USBHostDevice *create_usbdevice(void *data)
{
return _create_usbdevice(Object_pool::USB_HOST_DEVICE,
[&](Wrapper &obj) {
/* set data pointer */
obj._usb_host_device.data = data;
});
}
void remove_usbdevice(USBHostDevice *device)
{
try {
@ -512,6 +548,16 @@ Type type_register_static(TypeInfo const *t)
t->class_init(c, 0);
}
if (!Genode::strcmp(t->name, "usb-webcam")) {
Wrapper *w = &Object_pool::p()->obj[Object_pool::USB_WEBCAM];
t->class_init(&w->_object_class, 0);
_create_usbdevice(Object_pool::USB_WEBCAM, [&](Wrapper &obj) {
obj._webcam_state = (USBWebcamState *)g_malloc(t->instance_size);
obj._webcam_state_size = t->instance_size;
});
}
return nullptr;
}

View File

@ -0,0 +1,25 @@
/**
* \brief USB webcam model back end definition
* \author Alexander Boettcher
*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#ifndef _WEBCAM_BACKEND_H_
#define _WEBCAM_BACKEND_H_
struct webcam_config {
unsigned width;
unsigned height;
unsigned fps;
};
extern void webcam_backend_config(struct webcam_config *);
extern void capture_bgr_frame(void * pixel);
extern void capture_yuv_frame(void * pixel);
extern void capture_state_changed(bool on, char const * format);
#endif /* _WEBCAM_BACKEND_H_ */

View File

@ -0,0 +1,160 @@
/**
* \brief USB webcam model back end using capture session
* \author Alexander Boettcher
* \date 2021-04-08
*
* Copyright (C) 2021 Genode Labs GmbH
*
* This file is distributed under the terms of the GNU General Public License
* version 2.
*/
#include <base/allocator.h>
#include <base/env.h>
#include <capture_session/connection.h>
#include <gui_session/gui_session.h>
#include <os/reporter.h>
#include <util/xml_node.h>
#include <libyuv/convert_from_argb.h>
extern "C" {
#include "webcam-backend.h"
void _type_init_usb_webcam_register_types();
}
using namespace Genode;
struct Capture_webcam
{
Env &_env;
Capture::Connection _capture { _env, "webcam" };
Gui::Area const _area;
bool const _vflip;
uint8_t const _fps;
Attached_dataspace _ds { _env.rm(), _capture.dataspace() };
Constructible<Reporter> _reporter { };
Gui::Area setup_area(Gui::Area const area_in, bool const auto_area)
{
Gui::Area area = area_in;
if (auto_area) {
area = _capture.screen_size();
if (!area.valid())
area = area_in;
}
/* request setup of dataspace by server */
_capture.buffer(area);
return area;
}
void update_yuv(void *frame)
{
_capture.capture_at(Capture::Point(0, 0));
int const src_stride_argb = _area.w() * 4;
int const dst_stride_yuy2 = _area.w() * 2;
libyuv::ARGBToYUY2(_ds.local_addr<uint8_t>(), src_stride_argb,
reinterpret_cast<uint8_t*>(frame), dst_stride_yuy2,
_area.w(), _area.h());
}
void update_bgr(void *frame)
{
_capture.capture_at(Capture::Point(0, 0));
uint8_t * const bgr = reinterpret_cast<uint8_t *>(frame);
Pixel_rgb888 * data = reinterpret_cast<Pixel_rgb888 *>(_ds.local_addr<void>());
for (int y = 0; y < _area.h(); y++) {
unsigned const row = _vflip ? y : _area.h() - 1 - y;
unsigned const row_byte = (row * _area.w() * 3);
for (int x = 0; x < _area.w(); x++) {
bgr[row_byte + x * 3 + 0] = data->b();
bgr[row_byte + x * 3 + 1] = data->g();
bgr[row_byte + x * 3 + 2] = data->r();
data++;
}
}
}
void capture_state_changed(bool on, char const * format)
{
if (!_reporter.constructed())
return;
Reporter::Xml_generator xml(*_reporter, [&] () {
xml.attribute("enabled", on);
xml.attribute("format", format);
});
}
Capture_webcam (Env &env, Gui::Area area, bool auto_area, bool flip,
uint8_t fps, bool report)
:
_env(env),
_area(setup_area(area, auto_area)),
_vflip(flip),
_fps(fps)
{
if (report) {
_reporter.construct(_env, "capture");
_reporter->enabled(true);
}
log("USB webcam ", _area, " fps=", _fps, " vertical_flip=",
_vflip ? "yes" : "no",
" report=", _reporter.constructed() ? "enabled" : "disabled");
}
};
static Genode::Constructible<Capture_webcam> capture;
extern "C" void capture_state_changed(bool on, char const * format)
{
capture->capture_state_changed(on, format);
}
extern "C" void capture_bgr_frame(void * pixel)
{
capture->update_bgr(pixel);
}
extern "C" void capture_yuv_frame(void * pixel)
{
capture->update_yuv(pixel);
}
extern "C" void webcam_backend_config(struct webcam_config *config)
{
config->fps = capture->_fps;
config->width = capture->_area.w();
config->height = capture->_area.h();
}
/*
* Do not use type_init macro because of name mangling
*/
extern "C" void _type_init_host_webcam_register_types(Env &env,
Xml_node const &webcam)
{
/* initialize capture session */
capture.construct(env, Gui::Area(webcam.attribute_value("width", 640u),
webcam.attribute_value("height", 480u)),
webcam.attribute_value("screen_size", false),
webcam.attribute_value("vertical_flip", false),
webcam.attribute_value("fps", 15u),
webcam.attribute_value("report", false));
/* register webcam model, which will call webcam_backend_config() */
_type_init_usb_webcam_register_types();
}

View File

@ -45,9 +45,15 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc-mem.mk \
src/lib/libc/libc_mem_alloc.cc \
lib/import/import-qemu-usb_include.mk \
lib/mk/qemu-usb_include.mk \
lib/mk/qemu-usb.mk \
lib/mk/qemu-usb.inc \
lib/mk/spec/x86_32/qemu-usb.mk \
lib/mk/spec/x86_64/qemu-usb.mk \
include/qemu \
src/lib/qemu-usb
src/lib/qemu-usb \
lib/import/import-libyuv.mk \
lib/mk/libyuv.inc \
lib/mk/spec/x86_32/libyuv.mk \
lib/mk/spec/x86_64/libyuv.mk
content: $(MIRROR_FROM_LIBPORTS)
@ -65,6 +71,16 @@ $(MIRROR_FROM_QEMU_USB_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(QEMU_USB_PORT_DIR)/$@ $(dir $@)
LIBYUV_PORT_DIR := $(call port_dir,$(GENODE_DIR)/repos/libports/ports/libyuv)
MIRROR_FROM_LIBYUV_PORT_DIR := libyuv
content: $(MIRROR_FROM_LIBYUV_PORT_DIR)
$(MIRROR_FROM_LIBYUV_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(LIBYUV_PORT_DIR)/$@ $(dir $@)
MIRROR_FROM_OS := src/drivers/ps2/scan_code_set_1.h \
include/pointer/shape_report.h \

View File

@ -2,6 +2,7 @@ audio_in_session
audio_out_session
base
base-nova
capture_session
framebuffer_session
input_session
libc

View File

@ -46,9 +46,15 @@ MIRROR_FROM_LIBPORTS := lib/mk/libc-mem.mk \
src/lib/libc/libc_mem_alloc.cc \
lib/import/import-qemu-usb_include.mk \
lib/mk/qemu-usb_include.mk \
lib/mk/qemu-usb.mk \
lib/mk/qemu-usb.inc \
lib/mk/spec/x86_32/qemu-usb.mk \
lib/mk/spec/x86_64/qemu-usb.mk \
include/qemu \
src/lib/qemu-usb
src/lib/qemu-usb \
lib/import/import-libyuv.mk \
lib/mk/libyuv.inc \
lib/mk/spec/x86_32/libyuv.mk \
lib/mk/spec/x86_64/libyuv.mk
content: $(MIRROR_FROM_LIBPORTS)
@ -66,6 +72,16 @@ $(MIRROR_FROM_QEMU_USB_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(QEMU_USB_PORT_DIR)/$@ $(dir $@)
LIBYUV_PORT_DIR := $(call port_dir,$(GENODE_DIR)/repos/libports/ports/libyuv)
MIRROR_FROM_LIBYUV_PORT_DIR := libyuv
content: $(MIRROR_FROM_LIBYUV_PORT_DIR)
$(MIRROR_FROM_LIBYUV_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(LIBYUV_PORT_DIR)/$@ $(dir $@)
MIRROR_FROM_OS := src/drivers/ps2/scan_code_set_1.h \
include/pointer/shape_report.h \

View File

@ -1,6 +1,7 @@
audio_in_session
audio_out_session
base
capture_session
framebuffer_session
input_session
libc

View File

@ -31,11 +31,17 @@ src/virtualbox6_sdk:
MIRROR_FROM_LIBPORTS := \
include/qemu \
lib/import/import-libyuv.mk \
lib/import/import-qemu-usb_include.mk \
lib/mk/libc-common.inc \
lib/mk/libc-mem.mk \
lib/mk/qemu-usb.mk \
lib/mk/libyuv.inc \
lib/mk/qemu-usb.inc \
lib/mk/qemu-usb_include.mk \
lib/mk/spec/x86_32/libyuv.mk \
lib/mk/spec/x86_32/qemu-usb.mk \
lib/mk/spec/x86_64/libyuv.mk \
lib/mk/spec/x86_64/qemu-usb.mk \
src/lib/libc/internal/init.h \
src/lib/libc/internal/mem_alloc.h \
src/lib/libc/internal/monitor.h \
@ -63,6 +69,16 @@ $(MIRROR_FROM_QEMU_USB_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(QEMU_USB_PORT_DIR)/$@ $(dir $@)
LIBYUV_PORT_DIR := $(call port_dir,$(GENODE_DIR)/repos/libports/ports/libyuv)
MIRROR_FROM_LIBYUV_PORT_DIR := libyuv
content: $(MIRROR_FROM_LIBYUV_PORT_DIR)
$(MIRROR_FROM_LIBYUV_PORT_DIR):
mkdir -p $(dir $@)
cp -r $(LIBYUV_PORT_DIR)/$@ $(dir $@)
MIRROR_FROM_OS := src/drivers/ps2/scan_code_set_1.h \
include/pointer/shape_report.h \

View File

@ -1,5 +1,6 @@
base
blit
capture_session
framebuffer_session
gui_session
input_session

View File

@ -28,4 +28,6 @@ set use_cpu_load 0
# use non-generic vbox5 VMM version
set use_vbox5_nova 1
set use_webcam 1
source ${genode_dir}/repos/ports/run/vbox_win.inc

View File

@ -23,6 +23,17 @@ set overlay_image "overlay_${flavor}.vdi"
if {[info exists flavor_extension]} {
set vbox_file "vm_${flavor}${flavor_extension}.vbox"
}
if {![info exists use_webcam]} {
set use_webcam 0
}
if {![info exists webcam_vflip]} {
set webcam_vflip true
if {$use_webcam} {
if {[string match "win*" $flavor] } {
set webcam_vflip false
}
}
}
set build_components {
server/nic_router
@ -267,6 +278,10 @@ for { set i 1} { $i <= $use_vms } { incr i} {
<affinity xpos=\"[expr $i]\" width=\"2\" />
<config ld_verbose=\"yes\" vbox_file=\"${vbox_file}\" vm_name=\"${flavor}\" xhci=\"no\" capslock=\"NOROM\">"
}
append_if [expr $use_webcam] config_of_app {
<webcam vertical_flip="} $webcam_vflip {" fps="15" report="false" screen_size="false" width="640" height="480" />}
append config_of_app {
<libc stdout="/dev/log" stderr="/dev/log" rtc="/dev/rtc" pipe="/pipe"/>
<vfs>

View File

@ -494,8 +494,10 @@ static DECLCALLBACK(int) xhciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
pThis->timer_queue = &timer_queue;
static Pci_device pci_device(pDevIns);
Genode::Attached_rom_dataspace config(genode_env(), "config");
pThis->ctl = Qemu::usb_init(timer_queue, pci_device, *pThis->usb_ep,
vmm_heap(), genode_env());
vmm_heap(), genode_env(), config.xml());
Qemu::Controller::Info const ctl_info = pThis->ctl->info();

View File

@ -476,8 +476,10 @@ static DECLCALLBACK(int) xhciR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFG
pThis->timer_queue = &timer_queue;
static Pci_device pci_device(alloc, pDevIns);
Genode::Attached_rom_dataspace config(*_xhci_genode_env, "config");
pThis->ctl = Qemu::usb_init(timer_queue, pci_device, *pThis->usb_ep,
alloc, *_xhci_genode_env);
alloc, *_xhci_genode_env, config.xml());
Qemu::Controller::Info const ctl_info = pThis->ctl->info();