aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/display/vga.c50
-rw-r--r--hw/display/vga_int.h1
-rw-r--r--hw/display/virtio-gpu-3d.c5
-rw-r--r--hw/display/virtio-gpu.c28
-rw-r--r--hw/display/virtio-vga.c8
-rw-r--r--hw/usb/Makefile.objs4
-rw-r--r--hw/usb/hcd-ohci.c6
-rw-r--r--hw/usb/xen-usb.c1080
-rw-r--r--hw/xen/xen_backend.c63
-rw-r--r--hw/xen/xen_devconfig.c52
-rw-r--r--hw/xenpv/xen_machine_pv.c43
-rw-r--r--include/hw/virtio/virtio-gpu.h6
-rw-r--r--include/hw/xen/xen_backend.h6
-rw-r--r--include/hw/xen/xen_common.h2
14 files changed, 1263 insertions, 91 deletions
diff --git a/hw/display/vga.c b/hw/display/vga.c
index 4a55ec6..9ebc54f 100644
--- a/hw/display/vga.c
+++ b/hw/display/vga.c
@@ -149,6 +149,11 @@ static inline bool vbe_enabled(VGACommonState *s)
return s->vbe_regs[VBE_DISPI_INDEX_ENABLE] & VBE_DISPI_ENABLED;
}
+static inline uint8_t sr(VGACommonState *s, int idx)
+{
+ return vbe_enabled(s) ? s->sr_vbe[idx] : s->sr[idx];
+}
+
static void vga_update_memory_access(VGACommonState *s)
{
hwaddr base, offset, size;
@@ -163,8 +168,8 @@ static void vga_update_memory_access(VGACommonState *s)
s->has_chain4_alias = false;
s->plane_updated = 0xf;
}
- if ((s->sr[VGA_SEQ_PLANE_WRITE] & VGA_SR02_ALL_PLANES) ==
- VGA_SR02_ALL_PLANES && s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) {
+ if ((sr(s, VGA_SEQ_PLANE_WRITE) & VGA_SR02_ALL_PLANES) ==
+ VGA_SR02_ALL_PLANES && sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) {
offset = 0;
switch ((s->gr[VGA_GFX_MISC] >> 2) & 3) {
case 0:
@@ -234,7 +239,7 @@ static void vga_precise_update_retrace_info(VGACommonState *s)
((s->cr[VGA_CRTC_OVERFLOW] >> 6) & 2)) << 8);
vretr_end_line = s->cr[VGA_CRTC_V_SYNC_END] & 0xf;
- clocking_mode = (s->sr[VGA_SEQ_CLOCK_MODE] >> 3) & 1;
+ clocking_mode = (sr(s, VGA_SEQ_CLOCK_MODE) >> 3) & 1;
clock_sel = (s->msr >> 2) & 3;
dots = (s->msr & 1) ? 8 : 9;
@@ -486,7 +491,6 @@ void vga_ioport_write(void *opaque, uint32_t addr, uint32_t val)
printf("vga: write SR%x = 0x%02x\n", s->sr_index, val);
#endif
s->sr[s->sr_index] = val & sr_mask[s->sr_index];
- vbe_update_vgaregs(s);
if (s->sr_index == VGA_SEQ_CLOCK_MODE) {
s->update_retrace_info(s);
}
@@ -680,13 +684,13 @@ static void vbe_update_vgaregs(VGACommonState *s)
if (s->vbe_regs[VBE_DISPI_INDEX_BPP] == 4) {
shift_control = 0;
- s->sr[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
+ s->sr_vbe[VGA_SEQ_CLOCK_MODE] &= ~8; /* no double line */
} else {
shift_control = 2;
/* set chain 4 mode */
- s->sr[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
+ s->sr_vbe[VGA_SEQ_MEMORY_MODE] |= VGA_SR04_CHN_4M;
/* activate all planes */
- s->sr[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
+ s->sr_vbe[VGA_SEQ_PLANE_WRITE] |= VGA_SR02_ALL_PLANES;
}
s->gr[VGA_GFX_MODE] = (s->gr[VGA_GFX_MODE] & ~0x60) |
(shift_control << 5);
@@ -836,7 +840,7 @@ uint32_t vga_mem_readb(VGACommonState *s, hwaddr addr)
break;
}
- if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) {
+ if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) {
/* chain 4 mode : simplest access */
assert(addr < s->vram_size);
ret = s->vram_ptr[addr];
@@ -904,11 +908,11 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
break;
}
- if (s->sr[VGA_SEQ_MEMORY_MODE] & VGA_SR04_CHN_4M) {
+ if (sr(s, VGA_SEQ_MEMORY_MODE) & VGA_SR04_CHN_4M) {
/* chain 4 mode : simplest access */
plane = addr & 3;
mask = (1 << plane);
- if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
+ if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) {
assert(addr < s->vram_size);
s->vram_ptr[addr] = val;
#ifdef DEBUG_VGA_MEM
@@ -921,7 +925,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
/* odd/even mode (aka text mode mapping) */
plane = (s->gr[VGA_GFX_PLANE_READ] & 2) | (addr & 1);
mask = (1 << plane);
- if (s->sr[VGA_SEQ_PLANE_WRITE] & mask) {
+ if (sr(s, VGA_SEQ_PLANE_WRITE) & mask) {
addr = ((addr & ~1) << 1) | plane;
if (addr >= s->vram_size) {
return;
@@ -996,7 +1000,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val)
do_write:
/* mask data according to sr[2] */
- mask = s->sr[VGA_SEQ_PLANE_WRITE];
+ mask = sr(s, VGA_SEQ_PLANE_WRITE);
s->plane_updated |= mask; /* only used to detect font change */
write_mask = mask16[mask];
if (addr * sizeof(uint32_t) >= s->vram_size) {
@@ -1152,10 +1156,10 @@ static void vga_get_text_resolution(VGACommonState *s, int *pwidth, int *pheight
/* total width & height */
cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1;
cwidth = 8;
- if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) {
+ if (!(sr(s, VGA_SEQ_CLOCK_MODE) & VGA_SR01_CHAR_CLK_8DOTS)) {
cwidth = 9;
}
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 0x08) {
cwidth = 16; /* NOTE: no 18 pixel wide */
}
width = (s->cr[VGA_CRTC_H_DISP] + 1);
@@ -1197,7 +1201,7 @@ static void vga_draw_text(VGACommonState *s, int full_update)
int64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL);
/* compute font data address (in plane 2) */
- v = s->sr[VGA_SEQ_CHARACTER_MAP];
+ v = sr(s, VGA_SEQ_CHARACTER_MAP);
offset = (((v >> 4) & 1) | ((v << 1) & 6)) * 8192 * 4 + 2;
if (offset != s->font_offsets[0]) {
s->font_offsets[0] = offset;
@@ -1506,11 +1510,11 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
}
if (shift_control == 0) {
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) {
disp_width <<= 1;
}
} else if (shift_control == 1) {
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) {
disp_width <<= 1;
}
}
@@ -1574,7 +1578,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
if (shift_control == 0) {
full_update |= update_palette16(s);
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) {
v = VGA_DRAW_LINE4D2;
} else {
v = VGA_DRAW_LINE4;
@@ -1582,7 +1586,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
bits = 4;
} else if (shift_control == 1) {
full_update |= update_palette16(s);
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 8) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 8) {
v = VGA_DRAW_LINE2D2;
} else {
v = VGA_DRAW_LINE2;
@@ -1629,7 +1633,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update)
#if 0
printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n",
width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE],
- s->line_compare, s->sr[VGA_SEQ_CLOCK_MODE]);
+ s->line_compare, sr(s, VGA_SEQ_CLOCK_MODE));
#endif
addr1 = (s->start_addr * 4);
bwidth = (width * bits + 7) / 8;
@@ -1781,6 +1785,7 @@ void vga_common_reset(VGACommonState *s)
{
s->sr_index = 0;
memset(s->sr, '\0', sizeof(s->sr));
+ memset(s->sr_vbe, '\0', sizeof(s->sr_vbe));
s->gr_index = 0;
memset(s->gr, '\0', sizeof(s->gr));
s->ar_index = 0;
@@ -1883,10 +1888,10 @@ static void vga_update_text(void *opaque, console_ch_t *chardata)
/* total width & height */
cheight = (s->cr[VGA_CRTC_MAX_SCAN] & 0x1f) + 1;
cw = 8;
- if (!(s->sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_CHAR_CLK_8DOTS)) {
+ if (!(sr(s, VGA_SEQ_CLOCK_MODE) & VGA_SR01_CHAR_CLK_8DOTS)) {
cw = 9;
}
- if (s->sr[VGA_SEQ_CLOCK_MODE] & 0x08) {
+ if (sr(s, VGA_SEQ_CLOCK_MODE) & 0x08) {
cw = 16; /* NOTE: no 18 pixel wide */
}
width = (s->cr[VGA_CRTC_H_DISP] + 1);
@@ -2053,6 +2058,7 @@ static int vga_common_post_load(void *opaque, int version_id)
/* force refresh */
s->graphic_mode = -1;
+ vbe_update_vgaregs(s);
return 0;
}
diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h
index bdb43a5..3ce5544 100644
--- a/hw/display/vga_int.h
+++ b/hw/display/vga_int.h
@@ -98,6 +98,7 @@ typedef struct VGACommonState {
MemoryRegion chain4_alias;
uint8_t sr_index;
uint8_t sr[256];
+ uint8_t sr_vbe[256];
uint8_t gr_index;
uint8_t gr[256];
uint8_t ar_index;
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
index fa19294..433bf93 100644
--- a/hw/display/virtio-gpu-3d.c
+++ b/hw/display/virtio-gpu-3d.c
@@ -17,6 +17,7 @@
#include "trace.h"
#include "hw/virtio/virtio.h"
#include "hw/virtio/virtio-gpu.h"
+#include "qapi/error.h"
#ifdef CONFIG_VIRGL
@@ -127,7 +128,7 @@ static void virgl_cmd_resource_flush(VirtIOGPU *g,
trace_virtio_gpu_cmd_res_flush(rf.resource_id,
rf.r.width, rf.r.height, rf.r.x, rf.r.y);
- for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ for (i = 0; i < g->conf.max_outputs; i++) {
if (g->scanout[i].resource_id != rf.resource_id) {
continue;
}
@@ -146,7 +147,7 @@ static void virgl_cmd_set_scanout(VirtIOGPU *g,
trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
ss.r.width, ss.r.height, ss.r.x, ss.r.y);
- if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) {
+ if (ss.scanout_id >= g->conf.max_outputs) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
__func__, ss.scanout_id);
cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 91345bd..f3b0f14 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -20,6 +20,7 @@
#include "hw/virtio/virtio-gpu.h"
#include "hw/virtio/virtio-bus.h"
#include "qemu/log.h"
+#include "qapi/error.h"
static struct virtio_gpu_simple_resource*
virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
@@ -465,7 +466,7 @@ static void virtio_gpu_resource_flush(VirtIOGPU *g,
pixman_region_init_rect(&flush_region,
rf.r.x, rf.r.y, rf.r.width, rf.r.height);
- for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ for (i = 0; i < g->conf.max_outputs; i++) {
struct virtio_gpu_scanout *scanout;
pixman_region16_t region, finalregion;
pixman_box16_t *extents;
@@ -508,6 +509,13 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
ss.r.width, ss.r.height, ss.r.x, ss.r.y);
+ if (ss.scanout_id >= g->conf.max_outputs) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+
g->enable = 1;
if (ss.resource_id == 0) {
scanout = &g->scanout[ss.scanout_id];
@@ -517,8 +525,7 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
res->scanout_bitmask &= ~(1 << ss.scanout_id);
}
}
- if (ss.scanout_id == 0 ||
- ss.scanout_id >= g->conf.max_outputs) {
+ if (ss.scanout_id == 0) {
qemu_log_mask(LOG_GUEST_ERROR,
"%s: illegal scanout id specified %d",
__func__, ss.scanout_id);
@@ -533,14 +540,6 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g,
}
/* create a surface for this scanout */
- if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT ||
- ss.scanout_id >= g->conf.max_outputs) {
- qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
- __func__, ss.scanout_id);
- cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
- return;
- }
-
res = virtio_gpu_find_resource(g, ss.resource_id);
if (!res) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
@@ -880,7 +879,7 @@ static int virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info)
{
VirtIOGPU *g = opaque;
- if (idx > g->conf.max_outputs) {
+ if (idx >= g->conf.max_outputs) {
return -1;
}
@@ -930,6 +929,11 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
bool have_virgl;
int i;
+ if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) {
+ error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS);
+ return;
+ }
+
g->config_size = sizeof(struct virtio_gpu_config);
g->virtio_config.num_scanouts = g->conf.max_outputs;
virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU,
diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c
index e58b165..f49f8de 100644
--- a/hw/display/virtio-vga.c
+++ b/hw/display/virtio-vga.c
@@ -4,6 +4,7 @@
#include "ui/console.h"
#include "vga_int.h"
#include "hw/virtio/virtio-pci.h"
+#include "qapi/error.h"
/*
* virtio-vga: This extends VirtioPCIProxy.
@@ -89,6 +90,7 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
VirtIOVGA *vvga = VIRTIO_VGA(vpci_dev);
VirtIOGPU *g = &vvga->vdev;
VGACommonState *vga = &vvga->vga;
+ Error *err = NULL;
uint32_t offset;
int i;
@@ -124,7 +126,11 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp)
/* force virtio-1.0 */
vpci_dev->flags &= ~VIRTIO_PCI_FLAG_DISABLE_MODERN;
vpci_dev->flags |= VIRTIO_PCI_FLAG_DISABLE_LEGACY;
- object_property_set_bool(OBJECT(g), true, "realized", errp);
+ object_property_set_bool(OBJECT(g), true, "realized", &err);
+ if (err) {
+ error_propagate(errp, err);
+ return;
+ }
/* add stdvga mmio regions */
pci_std_vga_mmio_region_init(vga, &vpci_dev->modern_bar,
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 2717027..98b5c9d 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -38,3 +38,7 @@ common-obj-$(CONFIG_USB_REDIR) += redirect.o quirks.o
# usb pass-through
common-obj-y += $(patsubst %,host-%.o,$(HOST_USB))
+
+ifeq ($(CONFIG_USB_LIBUSB),y)
+common-obj-$(CONFIG_XEN_BACKEND) += xen-usb.o
+endif
diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c
index ffab561..16d9ff7 100644
--- a/hw/usb/hcd-ohci.c
+++ b/hw/usb/hcd-ohci.c
@@ -1848,6 +1848,12 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
ohci->as = as;
+ if (num_ports > OHCI_MAX_PORTS) {
+ error_setg(errp, "OHCI num-ports=%d is too big (limit is %d ports)",
+ num_ports, OHCI_MAX_PORTS);
+ return;
+ }
+
if (usb_frame_time == 0) {
#ifdef OHCI_TIME_WARP
usb_frame_time = NANOSECONDS_PER_SECOND;
diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c
new file mode 100644
index 0000000..664df04
--- /dev/null
+++ b/hw/usb/xen-usb.c
@@ -0,0 +1,1080 @@
+/*
+ * xen paravirt usb device backend
+ *
+ * (c) Juergen Gross <jgross@suse.com>
+ *
+ * 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; under version 2 of the License.
+ *
+ * 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/>.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include <libusb.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/config-file.h"
+#include "hw/sysbus.h"
+#include "hw/usb.h"
+#include "hw/xen/xen_backend.h"
+#include "monitor/qdev.h"
+#include "qapi/qmp/qbool.h"
+#include "qapi/qmp/qint.h"
+#include "qapi/qmp/qstring.h"
+#include "sys/user.h"
+
+#include <xen/io/ring.h>
+#include <xen/io/usbif.h>
+
+/*
+ * Check for required support of usbif.h: USBIF_SHORT_NOT_OK was the last
+ * macro added we rely on.
+ */
+#ifdef USBIF_SHORT_NOT_OK
+
+#define TR(xendev, lvl, fmt, args...) \
+ { \
+ struct timeval tv; \
+ \
+ gettimeofday(&tv, NULL); \
+ xen_be_printf(xendev, lvl, "%8ld.%06ld xen-usb(%s):" fmt, \
+ tv.tv_sec, tv.tv_usec, __func__, ##args); \
+ }
+#define TR_BUS(xendev, fmt, args...) TR(xendev, 2, fmt, ##args)
+#define TR_REQ(xendev, fmt, args...) TR(xendev, 3, fmt, ##args)
+
+#define USBBACK_MAXPORTS USBIF_PIPE_PORT_MASK
+#define USB_DEV_ADDR_SIZE (USBIF_PIPE_DEV_MASK + 1)
+
+/* USB wire protocol: structure describing control request parameter. */
+struct usbif_ctrlrequest {
+ uint8_t bRequestType;
+ uint8_t bRequest;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+};
+
+struct usbback_info;
+struct usbback_req;
+
+struct usbback_stub {
+ USBDevice *dev;
+ USBPort port;
+ unsigned int speed;
+ bool attached;
+ QTAILQ_HEAD(submit_q_head, usbback_req) submit_q;
+};
+
+struct usbback_req {
+ struct usbback_info *usbif;
+ struct usbback_stub *stub;
+ struct usbif_urb_request req;
+ USBPacket packet;
+
+ unsigned int nr_buffer_segs; /* # of transfer_buffer segments */
+ unsigned int nr_extra_segs; /* # of iso_frame_desc segments */
+
+ QTAILQ_ENTRY(usbback_req) q;
+
+ void *buffer;
+ void *isoc_buffer;
+ struct libusb_transfer *xfer;
+};
+
+struct usbback_hotplug {
+ QSIMPLEQ_ENTRY(usbback_hotplug) q;
+ unsigned port;
+};
+
+struct usbback_info {
+ struct XenDevice xendev; /* must be first */
+ USBBus bus;
+ void *urb_sring;
+ void *conn_sring;
+ struct usbif_urb_back_ring urb_ring;
+ struct usbif_conn_back_ring conn_ring;
+ int num_ports;
+ int usb_ver;
+ bool ring_error;
+ QTAILQ_HEAD(req_free_q_head, usbback_req) req_free_q;
+ QSIMPLEQ_HEAD(hotplug_q_head, usbback_hotplug) hotplug_q;
+ struct usbback_stub ports[USBBACK_MAXPORTS];
+ struct usbback_stub *addr_table[USB_DEV_ADDR_SIZE];
+ QEMUBH *bh;
+};
+
+static struct usbback_req *usbback_get_req(struct usbback_info *usbif)
+{
+ struct usbback_req *usbback_req;
+
+ if (QTAILQ_EMPTY(&usbif->req_free_q)) {
+ usbback_req = g_new0(struct usbback_req, 1);
+ } else {
+ usbback_req = QTAILQ_FIRST(&usbif->req_free_q);
+ QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q);
+ }
+ return usbback_req;
+}
+
+static void usbback_put_req(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+
+ usbif = usbback_req->usbif;
+ memset(usbback_req, 0, sizeof(*usbback_req));
+ QTAILQ_INSERT_HEAD(&usbif->req_free_q, usbback_req, q);
+}
+
+static int usbback_gnttab_map(struct usbback_req *usbback_req)
+{
+ unsigned int nr_segs, i, prot;
+ uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST];
+ struct usbback_info *usbif = usbback_req->usbif;
+ struct XenDevice *xendev = &usbif->xendev;
+ struct usbif_request_segment *seg;
+ void *addr;
+
+ nr_segs = usbback_req->nr_buffer_segs + usbback_req->nr_extra_segs;
+ if (!nr_segs) {
+ return 0;
+ }
+
+ if (nr_segs > USBIF_MAX_SEGMENTS_PER_REQUEST) {
+ xen_be_printf(xendev, 0, "bad number of segments in request (%d)\n",
+ nr_segs);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < nr_segs; i++) {
+ if ((unsigned)usbback_req->req.seg[i].offset +
+ (unsigned)usbback_req->req.seg[i].length > PAGE_SIZE) {
+ xen_be_printf(xendev, 0, "segment crosses page boundary\n");
+ return -EINVAL;
+ }
+ }
+
+ if (usbback_req->nr_buffer_segs) {
+ prot = PROT_READ;
+ if (usbif_pipein(usbback_req->req.pipe)) {
+ prot |= PROT_WRITE;
+ }
+ for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
+ ref[i] = usbback_req->req.seg[i].gref;
+ }
+ usbback_req->buffer = xengnttab_map_domain_grant_refs(xendev->gnttabdev,
+ usbback_req->nr_buffer_segs, xendev->dom, ref, prot);
+
+ if (!usbback_req->buffer) {
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < usbback_req->nr_buffer_segs; i++) {
+ seg = usbback_req->req.seg + i;
+ addr = usbback_req->buffer + i * PAGE_SIZE + seg->offset;
+ qemu_iovec_add(&usbback_req->packet.iov, addr, seg->length);
+ }
+ }
+
+ if (!usbif_pipeisoc(usbback_req->req.pipe)) {
+ return 0;
+ }
+
+ /*
+ * Right now isoc requests are not supported.
+ * Prepare supporting those by doing the work needed on the guest
+ * interface side.
+ */
+
+ if (!usbback_req->nr_extra_segs) {
+ xen_be_printf(xendev, 0, "iso request without descriptor segments\n");
+ return -EINVAL;
+ }
+
+ prot = PROT_READ | PROT_WRITE;
+ for (i = 0; i < usbback_req->nr_extra_segs; i++) {
+ ref[i] = usbback_req->req.seg[i + usbback_req->req.nr_buffer_segs].gref;
+ }
+ usbback_req->isoc_buffer = xengnttab_map_domain_grant_refs(
+ xendev->gnttabdev, usbback_req->nr_extra_segs, xendev->dom, ref, prot);
+
+ if (!usbback_req->isoc_buffer) {
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int usbback_init_packet(struct usbback_req *usbback_req)
+{
+ struct XenDevice *xendev = &usbback_req->usbif->xendev;
+ USBPacket *packet = &usbback_req->packet;
+ USBDevice *dev = usbback_req->stub->dev;
+ USBEndpoint *ep;
+ unsigned int pid, ep_nr;
+ bool sok;
+ int ret = 0;
+
+ qemu_iovec_init(&packet->iov, USBIF_MAX_SEGMENTS_PER_REQUEST);
+ pid = usbif_pipein(usbback_req->req.pipe) ? USB_TOKEN_IN : USB_TOKEN_OUT;
+ ep_nr = usbif_pipeendpoint(usbback_req->req.pipe);
+ sok = !!(usbback_req->req.transfer_flags & USBIF_SHORT_NOT_OK);
+ if (usbif_pipectrl(usbback_req->req.pipe)) {
+ ep_nr = 0;
+ sok = false;
+ }
+ ep = usb_ep_get(dev, pid, ep_nr);
+ usb_packet_setup(packet, pid, ep, 0, 1, sok, true);
+
+ switch (usbif_pipetype(usbback_req->req.pipe)) {
+ case USBIF_PIPE_TYPE_ISOC:
+ TR_REQ(xendev, "iso transfer %s: buflen: %x, %d frames\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length,
+ usbback_req->req.u.isoc.nr_frame_desc_segs);
+ ret = -EINVAL; /* isoc not implemented yet */
+ break;
+
+ case USBIF_PIPE_TYPE_INT:
+ TR_REQ(xendev, "int transfer %s: buflen: %x\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length);
+ break;
+
+ case USBIF_PIPE_TYPE_CTRL:
+ packet->parameter = *(uint64_t *)usbback_req->req.u.ctrl;
+ TR_REQ(xendev, "ctrl parameter: %lx, buflen: %x\n", packet->parameter,
+ usbback_req->req.buffer_length);
+ break;
+
+ case USBIF_PIPE_TYPE_BULK:
+ TR_REQ(xendev, "bulk transfer %s: buflen: %x\n",
+ (pid == USB_TOKEN_IN) ? "in" : "out",
+ usbback_req->req.buffer_length);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static void usbback_do_response(struct usbback_req *usbback_req, int32_t status,
+ int32_t actual_length, int32_t error_count)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_response *res;
+ struct XenDevice *xendev;
+ unsigned int notify;
+
+ usbif = usbback_req->usbif;
+ xendev = &usbif->xendev;
+
+ TR_REQ(xendev, "id %d, status %d, length %d, errcnt %d\n",
+ usbback_req->req.id, status, actual_length, error_count);
+
+ if (usbback_req->packet.iov.iov) {
+ qemu_iovec_destroy(&usbback_req->packet.iov);
+ }
+
+ if (usbback_req->buffer) {
+ xengnttab_unmap(xendev->gnttabdev, usbback_req->buffer,
+ usbback_req->nr_buffer_segs);
+ usbback_req->buffer = NULL;
+ }
+
+ if (usbback_req->isoc_buffer) {
+ xengnttab_unmap(xendev->gnttabdev, usbback_req->isoc_buffer,
+ usbback_req->nr_extra_segs);
+ usbback_req->isoc_buffer = NULL;
+ }
+
+ res = RING_GET_RESPONSE(&usbif->urb_ring, usbif->urb_ring.rsp_prod_pvt);
+ res->id = usbback_req->req.id;
+ res->status = status;
+ res->actual_length = actual_length;
+ res->error_count = error_count;
+ res->start_frame = 0;
+ usbif->urb_ring.rsp_prod_pvt++;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&usbif->urb_ring, notify);
+
+ if (notify) {
+ xen_be_send_notify(xendev);
+ }
+
+ usbback_put_req(usbback_req);
+}
+
+static void usbback_do_response_ret(struct usbback_req *usbback_req,
+ int32_t status)
+{
+ usbback_do_response(usbback_req, status, 0, 0);
+}
+
+static int32_t usbback_xlat_status(int status)
+{
+ switch (status) {
+ case USB_RET_SUCCESS:
+ return 0;
+ case USB_RET_NODEV:
+ return -ENODEV;
+ case USB_RET_STALL:
+ return -EPIPE;
+ case USB_RET_BABBLE:
+ return -EOVERFLOW;
+ case USB_RET_IOERROR:
+ return -EPROTO;
+ }
+
+ return -ESHUTDOWN;
+}
+
+static void usbback_packet_complete(USBPacket *packet)
+{
+ struct usbback_req *usbback_req;
+ int32_t status;
+
+ usbback_req = container_of(packet, struct usbback_req, packet);
+
+ QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q);
+
+ status = usbback_xlat_status(packet->status);
+ usbback_do_response(usbback_req, status, packet->actual_length, 0);
+}
+
+static void usbback_set_address(struct usbback_info *usbif,
+ struct usbback_stub *stub,
+ unsigned int cur_addr, unsigned int new_addr)
+{
+ if (cur_addr) {
+ usbif->addr_table[cur_addr] = NULL;
+ }
+ if (new_addr) {
+ usbif->addr_table[new_addr] = stub;
+ }
+}
+
+static bool usbback_cancel_req(struct usbback_req *usbback_req)
+{
+ bool ret = false;
+
+ if (usb_packet_is_inflight(&usbback_req->packet)) {
+ usb_cancel_packet(&usbback_req->packet);
+ ret = true;
+ }
+ return ret;
+}
+
+static void usbback_process_unlink_req(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+ struct usbback_req *unlink_req;
+ unsigned int id, devnum;
+ int ret;
+
+ usbif = usbback_req->usbif;
+ ret = 0;
+ id = usbback_req->req.u.unlink.unlink_id;
+ TR_REQ(&usbif->xendev, "unlink id %d\n", id);
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ if (unlikely(devnum == 0)) {
+ usbback_req->stub = usbif->ports +
+ usbif_pipeportnum(usbback_req->req.pipe);
+ if (unlikely(!usbback_req->stub)) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ } else {
+ if (unlikely(!usbif->addr_table[devnum])) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ usbback_req->stub = usbif->addr_table[devnum];
+ }
+
+ QTAILQ_FOREACH(unlink_req, &usbback_req->stub->submit_q, q) {
+ if (unlink_req->req.id == id) {
+ if (usbback_cancel_req(unlink_req)) {
+ usbback_do_response_ret(unlink_req, -EPROTO);
+ }
+ break;
+ }
+ }
+
+fail_response:
+ usbback_do_response_ret(usbback_req, ret);
+}
+
+/*
+ * Checks whether a request can be handled at once or should be forwarded
+ * to the usb framework.
+ * Return value is:
+ * 0 in case of usb framework is needed
+ * 1 in case of local handling (no error)
+ * The request response has been queued already if return value not 0.
+ */
+static int usbback_check_and_submit(struct usbback_req *usbback_req)
+{
+ struct usbback_info *usbif;
+ unsigned int devnum;
+ struct usbback_stub *stub;
+ struct usbif_ctrlrequest *ctrl;
+ int ret;
+ uint16_t wValue;
+
+ usbif = usbback_req->usbif;
+ stub = NULL;
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ ctrl = (struct usbif_ctrlrequest *)usbback_req->req.u.ctrl;
+ wValue = le16_to_cpu(ctrl->wValue);
+
+ /*
+ * When the device is first connected or resetted, USB device has no
+ * address. In this initial state, following requests are sent to device
+ * address (#0),
+ *
+ * 1. GET_DESCRIPTOR (with Descriptor Type is "DEVICE") is sent,
+ * and OS knows what device is connected to.
+ *
+ * 2. SET_ADDRESS is sent, and then device has its address.
+ *
+ * In the next step, SET_CONFIGURATION is sent to addressed device, and
+ * then the device is finally ready to use.
+ */
+ if (unlikely(devnum == 0)) {
+ stub = usbif->ports + usbif_pipeportnum(usbback_req->req.pipe) - 1;
+ if (!stub->dev || !stub->attached) {
+ ret = -ENODEV;
+ goto do_response;
+ }
+
+ switch (ctrl->bRequest) {
+ case USB_REQ_GET_DESCRIPTOR:
+ /*
+ * GET_DESCRIPTOR request to device #0.
+ * through normal transfer.
+ */
+ TR_REQ(&usbif->xendev, "devnum 0 GET_DESCRIPTOR\n");
+ usbback_req->stub = stub;
+ return 0;
+ case USB_REQ_SET_ADDRESS:
+ /*
+ * SET_ADDRESS request to device #0.
+ * add attached device to addr_table.
+ */
+ TR_REQ(&usbif->xendev, "devnum 0 SET_ADDRESS\n");
+ usbback_set_address(usbif, stub, 0, wValue);
+ ret = 0;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ goto do_response;
+ }
+
+ if (unlikely(!usbif->addr_table[devnum])) {
+ ret = -ENODEV;
+ goto do_response;
+ }
+ usbback_req->stub = usbif->addr_table[devnum];
+
+ /*
+ * Check special request
+ */
+ if (ctrl->bRequest != USB_REQ_SET_ADDRESS) {
+ return 0;
+ }
+
+ /*
+ * SET_ADDRESS request to addressed device.
+ * change addr or remove from addr_table.
+ */
+ usbback_set_address(usbif, usbback_req->stub, devnum, wValue);
+ ret = 0;
+
+do_response:
+ usbback_do_response_ret(usbback_req, ret);
+ return 1;
+}
+
+static void usbback_dispatch(struct usbback_req *usbback_req)
+{
+ int ret;
+ unsigned int devnum;
+ struct usbback_info *usbif;
+
+ usbif = usbback_req->usbif;
+
+ TR_REQ(&usbif->xendev, "start req_id %d pipe %08x\n", usbback_req->req.id,
+ usbback_req->req.pipe);
+
+ /* unlink request */
+ if (unlikely(usbif_pipeunlink(usbback_req->req.pipe))) {
+ usbback_process_unlink_req(usbback_req);
+ return;
+ }
+
+ if (usbif_pipectrl(usbback_req->req.pipe)) {
+ if (usbback_check_and_submit(usbback_req)) {
+ return;
+ }
+ } else {
+ devnum = usbif_pipedevice(usbback_req->req.pipe);
+ usbback_req->stub = usbif->addr_table[devnum];
+
+ if (!usbback_req->stub || !usbback_req->stub->attached) {
+ ret = -ENODEV;
+ goto fail_response;
+ }
+ }
+
+ QTAILQ_INSERT_TAIL(&usbback_req->stub->submit_q, usbback_req, q);
+
+ usbback_req->nr_buffer_segs = usbback_req->req.nr_buffer_segs;
+ usbback_req->nr_extra_segs = usbif_pipeisoc(usbback_req->req.pipe) ?
+ usbback_req->req.u.isoc.nr_frame_desc_segs : 0;
+
+ ret = usbback_init_packet(usbback_req);
+ if (ret) {
+ xen_be_printf(&usbif->xendev, 0, "invalid request\n");
+ ret = -ESHUTDOWN;
+ goto fail_free_urb;
+ }
+
+ ret = usbback_gnttab_map(usbback_req);
+ if (ret) {
+ xen_be_printf(&usbif->xendev, 0, "invalid buffer, ret=%d\n", ret);
+ ret = -ESHUTDOWN;
+ goto fail_free_urb;
+ }
+
+ usb_handle_packet(usbback_req->stub->dev, &usbback_req->packet);
+ if (usbback_req->packet.status != USB_RET_ASYNC) {
+ usbback_packet_complete(&usbback_req->packet);
+ }
+ return;
+
+fail_free_urb:
+ QTAILQ_REMOVE(&usbback_req->stub->submit_q, usbback_req, q);
+
+fail_response:
+ usbback_do_response_ret(usbback_req, ret);
+}
+
+static void usbback_hotplug_notify(struct usbback_info *usbif)
+{
+ struct usbif_conn_back_ring *ring = &usbif->conn_ring;
+ struct usbif_conn_request req;
+ struct usbif_conn_response *res;
+ struct usbback_hotplug *usb_hp;
+ unsigned int notify;
+
+ if (!usbif->conn_sring) {
+ return;
+ }
+
+ /* Check for full ring. */
+ if ((RING_SIZE(ring) - ring->rsp_prod_pvt - ring->req_cons) == 0) {
+ xen_be_send_notify(&usbif->xendev);
+ return;
+ }
+
+ usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q);
+ QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q);
+
+ RING_COPY_REQUEST(ring, ring->req_cons, &req);
+ ring->req_cons++;
+ ring->sring->req_event = ring->req_cons + 1;
+
+ res = RING_GET_RESPONSE(ring, ring->rsp_prod_pvt);
+ res->id = req.id;
+ res->portnum = usb_hp->port;
+ res->speed = usbif->ports[usb_hp->port - 1].speed;
+ ring->rsp_prod_pvt++;
+ RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(ring, notify);
+
+ if (notify) {
+ xen_be_send_notify(&usbif->xendev);
+ }
+
+ TR_BUS(&usbif->xendev, "hotplug port %d speed %d\n", usb_hp->port,
+ res->speed);
+
+ g_free(usb_hp);
+
+ if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ qemu_bh_schedule(usbif->bh);
+ }
+}
+
+static void usbback_bh(void *opaque)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_back_ring *urb_ring;
+ struct usbback_req *usbback_req;
+ RING_IDX rc, rp;
+ unsigned int more_to_do;
+
+ usbif = opaque;
+ if (usbif->ring_error) {
+ return;
+ }
+
+ if (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ usbback_hotplug_notify(usbif);
+ }
+
+ urb_ring = &usbif->urb_ring;
+ rc = urb_ring->req_cons;
+ rp = urb_ring->sring->req_prod;
+ xen_rmb(); /* Ensure we see queued requests up to 'rp'. */
+
+ if (RING_REQUEST_PROD_OVERFLOW(urb_ring, rp)) {
+ rc = urb_ring->rsp_prod_pvt;
+ xen_be_printf(&usbif->xendev, 0, "domU provided bogus ring requests "
+ "(%#x - %#x = %u). Halting ring processing.\n",
+ rp, rc, rp - rc);
+ usbif->ring_error = true;
+ return;
+ }
+
+ while (rc != rp) {
+ if (RING_REQUEST_CONS_OVERFLOW(urb_ring, rc)) {
+ break;
+ }
+ usbback_req = usbback_get_req(usbif);
+
+ RING_COPY_REQUEST(urb_ring, rc, &usbback_req->req);
+ usbback_req->usbif = usbif;
+
+ usbback_dispatch(usbback_req);
+
+ urb_ring->req_cons = ++rc;
+ }
+
+ RING_FINAL_CHECK_FOR_REQUESTS(urb_ring, more_to_do);
+ if (more_to_do) {
+ qemu_bh_schedule(usbif->bh);
+ }
+}
+
+static void usbback_hotplug_enq(struct usbback_info *usbif, unsigned port)
+{
+ struct usbback_hotplug *usb_hp;
+
+ usb_hp = g_new0(struct usbback_hotplug, 1);
+ usb_hp->port = port;
+ QSIMPLEQ_INSERT_TAIL(&usbif->hotplug_q, usb_hp, q);
+ usbback_hotplug_notify(usbif);
+}
+
+static void usbback_portid_remove(struct usbback_info *usbif, unsigned port)
+{
+ USBPort *p;
+
+ if (!usbif->ports[port - 1].dev) {
+ return;
+ }
+
+ p = &(usbif->ports[port - 1].port);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+
+ object_unparent(OBJECT(usbif->ports[port - 1].dev));
+ usbif->ports[port - 1].dev = NULL;
+ usbif->ports[port - 1].speed = USBIF_SPEED_NONE;
+ usbif->ports[port - 1].attached = false;
+ usbback_hotplug_enq(usbif, port);
+
+ TR_BUS(&usbif->xendev, "port %d removed\n", port);
+}
+
+static void usbback_portid_add(struct usbback_info *usbif, unsigned port,
+ char *busid)
+{
+ unsigned speed;
+ char *portname;
+ USBPort *p;
+ Error *local_err = NULL;
+ QDict *qdict;
+ QemuOpts *opts;
+
+ if (usbif->ports[port - 1].dev) {
+ return;
+ }
+
+ portname = strchr(busid, '-');
+ if (!portname) {
+ xen_be_printf(&usbif->xendev, 0, "device %s illegal specification\n",
+ busid);
+ return;
+ }
+ portname++;
+ p = &(usbif->ports[port - 1].port);
+ snprintf(p->path, sizeof(p->path), "%s", portname);
+
+ qdict = qdict_new();
+ qdict_put(qdict, "driver", qstring_from_str("usb-host"));
+ qdict_put(qdict, "hostbus", qint_from_int(atoi(busid)));
+ qdict_put(qdict, "hostport", qstring_from_str(portname));
+ opts = qemu_opts_from_qdict(qemu_find_opts("device"), qdict, &local_err);
+ if (local_err) {
+ goto err;
+ }
+ usbif->ports[port - 1].dev = USB_DEVICE(qdev_device_add(opts, &local_err));
+ if (!usbif->ports[port - 1].dev) {
+ goto err;
+ }
+ QDECREF(qdict);
+ snprintf(p->path, sizeof(p->path), "%d", port);
+ speed = usbif->ports[port - 1].dev->speed;
+ switch (speed) {
+ case USB_SPEED_LOW:
+ speed = USBIF_SPEED_LOW;
+ break;
+ case USB_SPEED_FULL:
+ speed = USBIF_SPEED_FULL;
+ break;
+ case USB_SPEED_HIGH:
+ speed = (usbif->usb_ver < USB_VER_USB20) ?
+ USBIF_SPEED_NONE : USBIF_SPEED_HIGH;
+ break;
+ default:
+ speed = USBIF_SPEED_NONE;
+ break;
+ }
+ if (speed == USBIF_SPEED_NONE) {
+ xen_be_printf(&usbif->xendev, 0, "device %s wrong speed\n", busid);
+ object_unparent(OBJECT(usbif->ports[port - 1].dev));
+ usbif->ports[port - 1].dev = NULL;
+ return;
+ }
+ usb_device_reset(usbif->ports[port - 1].dev);
+ usbif->ports[port - 1].speed = speed;
+ usbif->ports[port - 1].attached = true;
+ QTAILQ_INIT(&usbif->ports[port - 1].submit_q);
+ usbback_hotplug_enq(usbif, port);
+
+ TR_BUS(&usbif->xendev, "port %d attached\n", port);
+ return;
+
+err:
+ QDECREF(qdict);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+ xen_be_printf(&usbif->xendev, 0, "device %s could not be opened\n", busid);
+}
+
+static void usbback_process_port(struct usbback_info *usbif, unsigned port)
+{
+ char node[8];
+ char *busid;
+
+ snprintf(node, sizeof(node), "port/%d", port);
+ busid = xenstore_read_be_str(&usbif->xendev, node);
+ if (busid == NULL) {
+ xen_be_printf(&usbif->xendev, 0, "xenstore_read %s failed\n", node);
+ return;
+ }
+
+ /* Remove portid, if the port is not connected. */
+ if (strlen(busid) == 0) {
+ usbback_portid_remove(usbif, port);
+ } else {
+ usbback_portid_add(usbif, port, busid);
+ }
+
+ g_free(busid);
+}
+
+static void usbback_disconnect(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ struct usbback_req *req, *tmp;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ xen_be_unbind_evtchn(xendev);
+
+ if (usbif->urb_sring) {
+ xengnttab_unmap(xendev->gnttabdev, usbif->urb_sring, 1);
+ usbif->urb_sring = NULL;
+ }
+ if (usbif->conn_sring) {
+ xengnttab_unmap(xendev->gnttabdev, usbif->conn_sring, 1);
+ usbif->conn_sring = NULL;
+ }
+
+ for (i = 0; i < usbif->num_ports; i++) {
+ if (!usbif->ports[i].dev) {
+ continue;
+ }
+ QTAILQ_FOREACH_SAFE(req, &usbif->ports[i].submit_q, q, tmp) {
+ usbback_cancel_req(req);
+ }
+ }
+
+ TR_BUS(xendev, "finished\n");
+}
+
+static int usbback_connect(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ struct usbif_urb_sring *urb_sring;
+ struct usbif_conn_sring *conn_sring;
+ int urb_ring_ref;
+ int conn_ring_ref;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) {
+ xen_be_printf(xendev, 0, "error reading urb-ring-ref\n");
+ return -1;
+ }
+ if (xenstore_read_fe_int(xendev, "conn-ring-ref", &conn_ring_ref)) {
+ xen_be_printf(xendev, 0, "error reading conn-ring-ref\n");
+ return -1;
+ }
+ if (xenstore_read_fe_int(xendev, "event-channel", &xendev->remote_port)) {
+ xen_be_printf(xendev, 0, "error reading event-channel\n");
+ return -1;
+ }
+
+ usbif->urb_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom,
+ urb_ring_ref,
+ PROT_READ | PROT_WRITE);
+ usbif->conn_sring = xengnttab_map_grant_ref(xendev->gnttabdev, xendev->dom,
+ conn_ring_ref,
+ PROT_READ | PROT_WRITE);
+ if (!usbif->urb_sring || !usbif->conn_sring) {
+ xen_be_printf(xendev, 0, "error mapping rings\n");
+ usbback_disconnect(xendev);
+ return -1;
+ }
+
+ urb_sring = usbif->urb_sring;
+ conn_sring = usbif->conn_sring;
+ BACK_RING_INIT(&usbif->urb_ring, urb_sring, XC_PAGE_SIZE);
+ BACK_RING_INIT(&usbif->conn_ring, conn_sring, XC_PAGE_SIZE);
+
+ xen_be_bind_evtchn(xendev);
+
+ xen_be_printf(xendev, 1, "urb-ring-ref %d, conn-ring-ref %d, "
+ "remote port %d, local port %d\n", urb_ring_ref,
+ conn_ring_ref, xendev->remote_port, xendev->local_port);
+
+ for (i = 1; i <= usbif->num_ports; i++) {
+ if (usbif->ports[i - 1].dev) {
+ usbback_hotplug_enq(usbif, i);
+ }
+ }
+
+ return 0;
+}
+
+static void usbback_backend_changed(struct XenDevice *xendev, const char *node)
+{
+ struct usbback_info *usbif;
+ unsigned int i;
+
+ TR_BUS(xendev, "path %s\n", node);
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ for (i = 1; i <= usbif->num_ports; i++) {
+ usbback_process_port(usbif, i);
+ }
+}
+
+static int usbback_init(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+
+ TR_BUS(xendev, "start\n");
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ if (xenstore_read_be_int(xendev, "num-ports", &usbif->num_ports) ||
+ usbif->num_ports < 1 || usbif->num_ports > USBBACK_MAXPORTS) {
+ xen_be_printf(xendev, 0, "num-ports not readable or out of bounds\n");
+ return -1;
+ }
+ if (xenstore_read_be_int(xendev, "usb-ver", &usbif->usb_ver) ||
+ (usbif->usb_ver != USB_VER_USB11 && usbif->usb_ver != USB_VER_USB20)) {
+ xen_be_printf(xendev, 0, "usb-ver not readable or out of bounds\n");
+ return -1;
+ }
+
+ usbback_backend_changed(xendev, "port");
+
+ TR_BUS(xendev, "finished\n");
+
+ return 0;
+}
+
+static void xen_bus_attach(USBPort *port)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+ usbif->ports[port->index].attached = true;
+ usbback_hotplug_enq(usbif, port->index + 1);
+}
+
+static void xen_bus_detach(USBPort *port)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+ usbif->ports[port->index].attached = false;
+ usbback_hotplug_enq(usbif, port->index + 1);
+}
+
+static void xen_bus_child_detach(USBPort *port, USBDevice *child)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_BUS(&usbif->xendev, "\n");
+}
+
+static void xen_bus_complete(USBPort *port, USBPacket *packet)
+{
+ struct usbback_info *usbif;
+
+ usbif = port->opaque;
+ TR_REQ(&usbif->xendev, "\n");
+ usbback_packet_complete(packet);
+}
+
+static USBPortOps xen_usb_port_ops = {
+ .attach = xen_bus_attach,
+ .detach = xen_bus_detach,
+ .child_detach = xen_bus_child_detach,
+ .complete = xen_bus_complete,
+};
+
+static USBBusOps xen_usb_bus_ops = {
+};
+
+static void usbback_alloc(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ USBPort *p;
+ unsigned int i, max_grants;
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+
+ usb_bus_new(&usbif->bus, sizeof(usbif->bus), &xen_usb_bus_ops, xen_sysdev);
+ for (i = 0; i < USBBACK_MAXPORTS; i++) {
+ p = &(usbif->ports[i].port);
+ usb_register_port(&usbif->bus, p, usbif, i, &xen_usb_port_ops,
+ USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL |
+ USB_SPEED_MASK_HIGH);
+ snprintf(p->path, sizeof(p->path), "%d", 99);
+ }
+
+ QTAILQ_INIT(&usbif->req_free_q);
+ QSIMPLEQ_INIT(&usbif->hotplug_q);
+ usbif->bh = qemu_bh_new(usbback_bh, usbif);
+
+ /* max_grants: for each request and for the rings (request and connect). */
+ max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2;
+ if (xengnttab_set_max_grants(xendev->gnttabdev, max_grants) < 0) {
+ xen_be_printf(xendev, 0, "xengnttab_set_max_grants failed: %s\n",
+ strerror(errno));
+ }
+}
+
+static int usbback_free(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+ struct usbback_req *usbback_req;
+ struct usbback_hotplug *usb_hp;
+ unsigned int i;
+
+ TR_BUS(xendev, "start\n");
+
+ usbback_disconnect(xendev);
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ for (i = 1; i <= usbif->num_ports; i++) {
+ usbback_portid_remove(usbif, i);
+ }
+
+ while (!QTAILQ_EMPTY(&usbif->req_free_q)) {
+ usbback_req = QTAILQ_FIRST(&usbif->req_free_q);
+ QTAILQ_REMOVE(&usbif->req_free_q, usbback_req, q);
+ g_free(usbback_req);
+ }
+ while (!QSIMPLEQ_EMPTY(&usbif->hotplug_q)) {
+ usb_hp = QSIMPLEQ_FIRST(&usbif->hotplug_q);
+ QSIMPLEQ_REMOVE_HEAD(&usbif->hotplug_q, q);
+ g_free(usb_hp);
+ }
+
+ qemu_bh_delete(usbif->bh);
+
+ for (i = 0; i < USBBACK_MAXPORTS; i++) {
+ usb_unregister_port(&usbif->bus, &(usbif->ports[i].port));
+ }
+
+ usb_bus_release(&usbif->bus);
+
+ TR_BUS(xendev, "finished\n");
+
+ return 0;
+}
+
+static void usbback_event(struct XenDevice *xendev)
+{
+ struct usbback_info *usbif;
+
+ usbif = container_of(xendev, struct usbback_info, xendev);
+ qemu_bh_schedule(usbif->bh);
+}
+
+struct XenDevOps xen_usb_ops = {
+ .size = sizeof(struct usbback_info),
+ .flags = DEVOPS_FLAG_NEED_GNTDEV,
+ .init = usbback_init,
+ .alloc = usbback_alloc,
+ .free = usbback_free,
+ .backend_changed = usbback_backend_changed,
+ .initialise = usbback_connect,
+ .disconnect = usbback_disconnect,
+ .event = usbback_event,
+};
+
+#else /* USBIF_SHORT_NOT_OK */
+
+static int usbback_not_supported(void)
+{
+ return -EINVAL;
+}
+
+struct XenDevOps xen_usb_ops = {
+ .backend_register = usbback_not_supported,
+};
+
+#endif
diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c
index 60575ad..c63f9df 100644
--- a/hw/xen/xen_backend.c
+++ b/hw/xen/xen_backend.c
@@ -42,11 +42,36 @@ struct xs_handle *xenstore = NULL;
const char *xen_protocol;
/* private */
+struct xs_dirs {
+ char *xs_dir;
+ QTAILQ_ENTRY(xs_dirs) list;
+};
+static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup =
+ QTAILQ_HEAD_INITIALIZER(xs_cleanup);
+
static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs);
static int debug = 0;
/* ------------------------------------------------------------- */
+static void xenstore_cleanup_dir(char *dir)
+{
+ struct xs_dirs *d;
+
+ d = g_malloc(sizeof(*d));
+ d->xs_dir = dir;
+ QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
+}
+
+void xen_config_cleanup(void)
+{
+ struct xs_dirs *d;
+
+ QTAILQ_FOREACH(d, &xs_cleanup, list) {
+ xs_rm(xenstore, 0, d->xs_dir);
+ }
+}
+
int xenstore_write_str(const char *base, const char *node, const char *val)
{
char abspath[XEN_BUFSIZE];
@@ -75,6 +100,30 @@ char *xenstore_read_str(const char *base, const char *node)
return ret;
}
+int xenstore_mkdir(char *path, int p)
+{
+ struct xs_permissions perms[2] = {
+ {
+ .id = 0, /* set owner: dom0 */
+ }, {
+ .id = xen_domid,
+ .perms = p,
+ }
+ };
+
+ if (!xs_mkdir(xenstore, 0, path)) {
+ xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", path);
+ return -1;
+ }
+ xenstore_cleanup_dir(g_strdup(path));
+
+ if (!xs_set_permissions(xenstore, 0, path, perms, 2)) {
+ xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", path);
+ return -1;
+ }
+ return 0;
+}
+
int xenstore_write_int(const char *base, const char *node, int ival)
{
char val[12];
@@ -726,6 +775,20 @@ err:
int xen_be_register(const char *type, struct XenDevOps *ops)
{
+ char path[50];
+ int rc;
+
+ if (ops->backend_register) {
+ rc = ops->backend_register();
+ if (rc) {
+ return rc;
+ }
+ }
+
+ snprintf(path, sizeof(path), "device-model/%u/backends/%s", xen_domid,
+ type);
+ xenstore_mkdir(path, XS_PERM_NONE);
+
return xenstore_scan(type, xen_domid, ops);
}
diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c
index 1f30fe4..b7d290d 100644
--- a/hw/xen/xen_devconfig.c
+++ b/hw/xen/xen_devconfig.c
@@ -5,54 +5,6 @@
/* ------------------------------------------------------------- */
-struct xs_dirs {
- char *xs_dir;
- QTAILQ_ENTRY(xs_dirs) list;
-};
-static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup);
-
-static void xen_config_cleanup_dir(char *dir)
-{
- struct xs_dirs *d;
-
- d = g_malloc(sizeof(*d));
- d->xs_dir = dir;
- QTAILQ_INSERT_TAIL(&xs_cleanup, d, list);
-}
-
-void xen_config_cleanup(void)
-{
- struct xs_dirs *d;
-
- QTAILQ_FOREACH(d, &xs_cleanup, list) {
- xs_rm(xenstore, 0, d->xs_dir);
- }
-}
-
-/* ------------------------------------------------------------- */
-
-static int xen_config_dev_mkdir(char *dev, int p)
-{
- struct xs_permissions perms[2] = {{
- .id = 0, /* set owner: dom0 */
- },{
- .id = xen_domid,
- .perms = p,
- }};
-
- if (!xs_mkdir(xenstore, 0, dev)) {
- xen_be_printf(NULL, 0, "xs_mkdir %s: failed\n", dev);
- return -1;
- }
- xen_config_cleanup_dir(g_strdup(dev));
-
- if (!xs_set_permissions(xenstore, 0, dev, perms, 2)) {
- xen_be_printf(NULL, 0, "xs_set_permissions %s: failed\n", dev);
- return -1;
- }
- return 0;
-}
-
static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev,
char *fe, char *be, int len)
{
@@ -66,8 +18,8 @@ static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev,
snprintf(be, len, "%s/backend/%s/%d/%d", dom, btype, xen_domid, vdev);
free(dom);
- xen_config_dev_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE);
- xen_config_dev_mkdir(be, XS_PERM_READ);
+ xenstore_mkdir(fe, XS_PERM_READ | XS_PERM_WRITE);
+ xenstore_mkdir(be, XS_PERM_READ);
return 0;
}
diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c
index fc13535..f68cf48 100644
--- a/hw/xenpv/xen_machine_pv.c
+++ b/hw/xenpv/xen_machine_pv.c
@@ -25,10 +25,15 @@
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/boards.h"
+#include "hw/sysbus.h"
#include "hw/xen/xen_backend.h"
#include "xen_domainbuild.h"
#include "sysemu/block-backend.h"
+#define TYPE_XENSYSDEV "xensysdev"
+
+DeviceState *xen_sysdev;
+
static void xen_init_pv(MachineState *machine)
{
DriveInfo *dinfo;
@@ -67,11 +72,17 @@ static void xen_init_pv(MachineState *machine)
break;
}
+ xen_sysdev = qdev_create(NULL, TYPE_XENSYSDEV);
+ qdev_init_nofail(xen_sysdev);
+
xen_be_register("console", &xen_console_ops);
xen_be_register("vkbd", &xen_kbdmouse_ops);
xen_be_register("vfb", &xen_framebuffer_ops);
xen_be_register("qdisk", &xen_blkdev_ops);
xen_be_register("qnic", &xen_netdev_ops);
+#ifdef CONFIG_USB_LIBUSB
+ xen_be_register("qusb", &xen_usb_ops);
+#endif
/* configure framebuffer */
if (xenfb_enabled) {
@@ -101,6 +112,38 @@ static void xen_init_pv(MachineState *machine)
xen_init_display(xen_domid);
}
+static int xen_sysdev_init(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static Property xen_sysdev_properties[] = {
+ {/* end of property list */},
+};
+
+static void xen_sysdev_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = xen_sysdev_init;
+ dc->props = xen_sysdev_properties;
+}
+
+static const TypeInfo xensysdev_info = {
+ .name = TYPE_XENSYSDEV,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(SysBusDevice),
+ .class_init = xen_sysdev_class_init,
+};
+
+static void xenpv_register_types(void)
+{
+ type_register_static(&xensysdev_info);
+}
+
+type_init(xenpv_register_types);
+
static void xenpv_machine_init(MachineClass *mc)
{
mc->desc = "Xen Para-virtualized PC";
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 13b0ab0..1602a13 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -27,8 +27,6 @@
#define VIRTIO_ID_GPU 16
-#define VIRTIO_GPU_MAX_SCANOUT 4
-
struct virtio_gpu_simple_resource {
uint32_t resource_id;
uint32_t width;
@@ -98,8 +96,8 @@ typedef struct VirtIOGPU {
QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq;
QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq;
- struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUT];
- struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUT];
+ struct virtio_gpu_scanout scanout[VIRTIO_GPU_MAX_SCANOUTS];
+ struct virtio_gpu_requested_state req_state[VIRTIO_GPU_MAX_SCANOUTS];
struct virtio_gpu_conf conf;
int enabled_output_bitmask;
diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen_backend.h
index c839eeb..6e18a46 100644
--- a/include/hw/xen/xen_backend.h
+++ b/include/hw/xen/xen_backend.h
@@ -28,6 +28,7 @@ struct XenDevOps {
int (*free)(struct XenDevice *xendev);
void (*backend_changed)(struct XenDevice *xendev, const char *node);
void (*frontend_changed)(struct XenDevice *xendev, const char *node);
+ int (*backend_register)(void);
};
struct XenDevice {
@@ -60,8 +61,10 @@ extern xc_interface *xen_xc;
extern xenforeignmemory_handle *xen_fmem;
extern struct xs_handle *xenstore;
extern const char *xen_protocol;
+extern DeviceState *xen_sysdev;
/* xenstore helper functions */
+int xenstore_mkdir(char *path, int p);
int xenstore_write_str(const char *base, const char *node, const char *val);
int xenstore_write_int(const char *base, const char *node, int ival);
int xenstore_write_int64(const char *base, const char *node, int64_t ival);
@@ -98,6 +101,9 @@ extern struct XenDevOps xen_kbdmouse_ops; /* xen_framebuffer.c */
extern struct XenDevOps xen_framebuffer_ops; /* xen_framebuffer.c */
extern struct XenDevOps xen_blkdev_ops; /* xen_disk.c */
extern struct XenDevOps xen_netdev_ops; /* xen_nic.c */
+#ifdef CONFIG_USB_LIBUSB
+extern struct XenDevOps xen_usb_ops; /* xen-usb.c */
+#endif
void xen_init_display(int domid);
diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h
index 7b52e8f..5eabf37 100644
--- a/include/hw/xen/xen_common.h
+++ b/include/hw/xen/xen_common.h
@@ -49,6 +49,8 @@ typedef xc_gnttab xengnttab_handle;
#define xengnttab_unmap(h, a, n) xc_gnttab_munmap(h, a, n)
#define xengnttab_map_grant_refs(h, c, d, r, p) \
xc_gnttab_map_grant_refs(h, c, d, r, p)
+#define xengnttab_map_domain_grant_refs(h, c, d, r, p) \
+ xc_gnttab_map_domain_grant_refs(h, c, d, r, p)
#define xenforeignmemory_open(l, f) xen_xc