diff --git a/repos/libports/include/qemu/usb.h b/repos/libports/include/qemu/usb.h index 6f8092f5c4..70cf0e46fe 100644 --- a/repos/libports/include/qemu/usb.h +++ b/repos/libports/include/qemu/usb.h @@ -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 diff --git a/repos/libports/lib/mk/qemu-usb.mk b/repos/libports/lib/mk/qemu-usb.mk index c43b8402f9..cc5b6fbc7a 100644 --- a/repos/libports/lib/mk/qemu-usb.mk +++ b/repos/libports/lib/mk/qemu-usb.mk @@ -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) diff --git a/repos/libports/ports/qemu-usb.hash b/repos/libports/ports/qemu-usb.hash index 253f1857ba..6a9daef2fa 100644 --- a/repos/libports/ports/qemu-usb.hash +++ b/repos/libports/ports/qemu-usb.hash @@ -1 +1 @@ -d716b4378eb2f243207f868646dfd7e62469228b +4b74867ae1e9383a53edb67fc3665ed8b305e2e6 diff --git a/repos/libports/src/lib/qemu-usb/README b/repos/libports/src/lib/qemu-usb/README index d37cba9811..03081167fc 100644 --- a/repos/libports/src/lib/qemu-usb/README +++ b/repos/libports/src/lib/qemu-usb/README @@ -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: + + + ... + + ... + + +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. diff --git a/repos/libports/src/lib/qemu-usb/dev-webcam.c b/repos/libports/src/lib/qemu-usb/dev-webcam.c new file mode 100644 index 0000000000..e4aca89150 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/dev-webcam.c @@ -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) diff --git a/repos/libports/src/lib/qemu-usb/dummies.cc b/repos/libports/src/lib/qemu-usb/dummies.cc index f0fb19315b..63bd861291 100644 --- a/repos/libports/src/lib/qemu-usb/dummies.cc +++ b/repos/libports/src/lib/qemu-usb/dummies.cc @@ -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; diff --git a/repos/libports/src/lib/qemu-usb/files.list b/repos/libports/src/lib/qemu-usb/files.list index e108d1c731..8112b1095a 100644 --- a/repos/libports/src/lib/qemu-usb/files.list +++ b/repos/libports/src/lib/qemu-usb/files.list @@ -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 diff --git a/repos/libports/src/lib/qemu-usb/include/qemu_emul.h b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h index d64e9ecc15..82e29694b8 100644 --- a/repos/libports/src/lib/qemu-usb/include/qemu_emul.h +++ b/repos/libports/src/lib/qemu-usb/include/qemu_emul.h @@ -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 ** diff --git a/repos/libports/src/lib/qemu-usb/qemu_emul.cc b/repos/libports/src/lib/qemu-usb/qemu_emul.cc index c358f915d7..f0330086f3 100644 --- a/repos/libports/src/lib/qemu-usb/qemu_emul.cc +++ b/repos/libports/src/lib/qemu-usb/qemu_emul.cc @@ -13,6 +13,7 @@ */ /* Genode includes */ +#include #include #include #include @@ -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 +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; } diff --git a/repos/libports/src/lib/qemu-usb/webcam-backend.h b/repos/libports/src/lib/qemu-usb/webcam-backend.h new file mode 100644 index 0000000000..d2b597818e --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/webcam-backend.h @@ -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_ */ diff --git a/repos/libports/src/lib/qemu-usb/webcam.cc b/repos/libports/src/lib/qemu-usb/webcam.cc new file mode 100644 index 0000000000..af926b3689 --- /dev/null +++ b/repos/libports/src/lib/qemu-usb/webcam.cc @@ -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 +#include +#include +#include +#include +#include + +#include + +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 { }; + + + 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(), src_stride_argb, + reinterpret_cast(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(frame); + Pixel_rgb888 * data = reinterpret_cast(_ds.local_addr()); + + 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; + +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(); +} diff --git a/repos/ports/recipes/src/vbox5-nova/content.mk b/repos/ports/recipes/src/vbox5-nova/content.mk index 30e99b7d79..5ea6109401 100644 --- a/repos/ports/recipes/src/vbox5-nova/content.mk +++ b/repos/ports/recipes/src/vbox5-nova/content.mk @@ -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 \ diff --git a/repos/ports/recipes/src/vbox5-nova/used_apis b/repos/ports/recipes/src/vbox5-nova/used_apis index a5f5a30828..ccc97b5d19 100644 --- a/repos/ports/recipes/src/vbox5-nova/used_apis +++ b/repos/ports/recipes/src/vbox5-nova/used_apis @@ -2,6 +2,7 @@ audio_in_session audio_out_session base base-nova +capture_session framebuffer_session input_session libc diff --git a/repos/ports/recipes/src/vbox5/content.mk b/repos/ports/recipes/src/vbox5/content.mk index 0ffb1fb667..86dff157b9 100644 --- a/repos/ports/recipes/src/vbox5/content.mk +++ b/repos/ports/recipes/src/vbox5/content.mk @@ -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 \ diff --git a/repos/ports/recipes/src/vbox5/used_apis b/repos/ports/recipes/src/vbox5/used_apis index a3279065ad..7a32f33f7a 100644 --- a/repos/ports/recipes/src/vbox5/used_apis +++ b/repos/ports/recipes/src/vbox5/used_apis @@ -1,6 +1,7 @@ audio_in_session audio_out_session base +capture_session framebuffer_session input_session libc diff --git a/repos/ports/recipes/src/vbox6/content.mk b/repos/ports/recipes/src/vbox6/content.mk index 32d3ef2d62..49a759ac03 100644 --- a/repos/ports/recipes/src/vbox6/content.mk +++ b/repos/ports/recipes/src/vbox6/content.mk @@ -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 \ diff --git a/repos/ports/recipes/src/vbox6/used_apis b/repos/ports/recipes/src/vbox6/used_apis index d8ed7e0240..de40389dd7 100644 --- a/repos/ports/recipes/src/vbox6/used_apis +++ b/repos/ports/recipes/src/vbox6/used_apis @@ -1,5 +1,6 @@ base blit +capture_session framebuffer_session gui_session input_session diff --git a/repos/ports/run/vbox5_win10_64.run b/repos/ports/run/vbox5_win10_64.run index 7df52a8e66..6e3a6cf71a 100644 --- a/repos/ports/run/vbox5_win10_64.run +++ b/repos/ports/run/vbox5_win10_64.run @@ -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 diff --git a/repos/ports/run/vbox_win.inc b/repos/ports/run/vbox_win.inc index 46d0cca63a..a76bb1a569 100644 --- a/repos/ports/run/vbox_win.inc +++ b/repos/ports/run/vbox_win.inc @@ -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} { " } + + append_if [expr $use_webcam] config_of_app { + } + append config_of_app { diff --git a/repos/ports/src/virtualbox5/devxhci.cc b/repos/ports/src/virtualbox5/devxhci.cc index c128628d08..19b32427ac 100644 --- a/repos/ports/src/virtualbox5/devxhci.cc +++ b/repos/ports/src/virtualbox5/devxhci.cc @@ -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(); diff --git a/repos/ports/src/virtualbox6/devxhci.cc b/repos/ports/src/virtualbox6/devxhci.cc index 3d5f2dc567..2d1b8d509c 100644 --- a/repos/ports/src/virtualbox6/devxhci.cc +++ b/repos/ports/src/virtualbox6/devxhci.cc @@ -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();