mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
Merge.
This commit is contained in:
commit
e812bdf5c4
22 files changed
+2902
-17
No files matched your search
@@ -1867,9 +1867,11 @@ void __init setup_arch(char **cmdline_p)
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
extern int console_use_vt;
|
||||
console_use_vt = 0;
|
||||
#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
}
|
||||
xencons_early_setup();
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -550,14 +550,16 @@ setup_arch (char **cmdline_p)
|
||||
xen_start_info->nr_pages, xen_start_info->flags);
|
||||
|
||||
if (!is_initial_xendomain()) {
|
||||
extern int console_use_vt;
|
||||
#if !defined(CONFIG_VT) || !defined(CONFIG_DUMMY_CONSOLE)
|
||||
conswitchp = NULL;
|
||||
console_use_vt = 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
xencons_early_setup();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* enable IA-64 Machine Check Abort Handling unless disabled */
|
||||
if (!strstr(saved_command_line, "nomca"))
|
||||
ia64_mca_init();
|
||||
|
||||
@@ -983,10 +983,12 @@ void __init setup_arch(char **cmdline_p)
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
extern int console_use_vt;
|
||||
console_use_vt = 0;
|
||||
}
|
||||
#if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE)
|
||||
conswitchp = &dummy_con;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
xencons_early_setup();
|
||||
#else /* CONFIG_XEN */
|
||||
|
||||
#ifdef CONFIG_VT
|
||||
|
||||
@@ -172,6 +172,29 @@ config XEN_NETDEV_FRONTEND
|
||||
dedicated device-driver domain, or your master control domain
|
||||
(domain 0), then you almost certainly want to say Y here.
|
||||
|
||||
config XEN_FRAMEBUFFER
|
||||
tristate "Framebuffer-device frontend driver"
|
||||
depends on XEN && FB
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_IMAGEBLIT
|
||||
default y
|
||||
help
|
||||
The framebuffer-device frontend drivers allows the kernel to create a
|
||||
virtual framebuffer. This framebuffer can be viewed in another
|
||||
domain. Unless this domain has access to a real video card, you
|
||||
probably want to say Y here.
|
||||
|
||||
config XEN_KEYBOARD
|
||||
tristate "Keyboard-device frontend driver"
|
||||
depends on XEN && XEN_FRAMEBUFFER && INPUT
|
||||
default y
|
||||
help
|
||||
The keyboard-device frontend driver allows the kernel to create a
|
||||
virtual keyboard. This keyboard can then be driven by another
|
||||
domain. If you've said Y to CONFIG_XEN_FRAMEBUFFER, you probably
|
||||
want to say Y here.
|
||||
|
||||
config XEN_SCRUB_PAGES
|
||||
bool "Scrub memory before freeing it to Xen"
|
||||
default y
|
||||
|
||||
@@ -15,3 +15,5 @@ obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += blkfront/
|
||||
obj-$(CONFIG_XEN_NETDEV_FRONTEND) += netfront/
|
||||
obj-$(CONFIG_XEN_PCIDEV_BACKEND) += pciback/
|
||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += pcifront/
|
||||
obj-$(CONFIG_XEN_FRAMEBUFFER) += fbfront/
|
||||
obj-$(CONFIG_XEN_KEYBOARD) += fbfront/
|
||||
@@ -57,6 +57,7 @@
|
||||
#include <xen/interface/event_channel.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <xen/evtchn.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <xen/xencons.h>
|
||||
|
||||
/*
|
||||
@@ -65,14 +66,14 @@
|
||||
* 'xencons=tty' [XC_TTY]: Console attached to '/dev/tty[0-9]+'.
|
||||
* 'xencons=ttyS' [XC_SERIAL]: Console attached to '/dev/ttyS[0-9]+'.
|
||||
* 'xencons=xvc' [XC_XVC]: Console attached to '/dev/xvc0'.
|
||||
* [XC_DEFAULT]: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
|
||||
* default: DOM0 -> XC_SERIAL ; all others -> XC_TTY.
|
||||
*
|
||||
* NB. In mode XC_TTY, we create dummy consoles for tty2-63. This suppresses
|
||||
* warnings from standard distro startup scripts.
|
||||
*/
|
||||
static enum {
|
||||
XC_OFF, XC_DEFAULT, XC_TTY, XC_SERIAL, XC_XVC
|
||||
} xc_mode = XC_DEFAULT;
|
||||
XC_OFF, XC_TTY, XC_SERIAL, XC_XVC
|
||||
} xc_mode;
|
||||
static int xc_num = -1;
|
||||
|
||||
/* /dev/xvc0 device number allocated by lanana.org. */
|
||||
@@ -84,17 +85,32 @@ static unsigned long sysrq_requested;
|
||||
extern int sysrq_enabled;
|
||||
#endif
|
||||
|
||||
void xencons_early_setup(void)
|
||||
{
|
||||
extern int console_use_vt;
|
||||
|
||||
if (is_initial_xendomain()) {
|
||||
xc_mode = XC_SERIAL;
|
||||
} else {
|
||||
xc_mode = XC_TTY;
|
||||
console_use_vt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init xencons_setup(char *str)
|
||||
{
|
||||
char *q;
|
||||
int n;
|
||||
extern int console_use_vt;
|
||||
|
||||
console_use_vt = 1;
|
||||
if (!strncmp(str, "ttyS", 4)) {
|
||||
xc_mode = XC_SERIAL;
|
||||
str += 4;
|
||||
} else if (!strncmp(str, "tty", 3)) {
|
||||
xc_mode = XC_TTY;
|
||||
str += 3;
|
||||
console_use_vt = 0;
|
||||
} else if (!strncmp(str, "xvc", 3)) {
|
||||
xc_mode = XC_XVC;
|
||||
str += 3;
|
||||
@@ -192,14 +208,10 @@ static int __init xen_console_init(void)
|
||||
goto out;
|
||||
|
||||
if (is_initial_xendomain()) {
|
||||
if (xc_mode == XC_DEFAULT)
|
||||
xc_mode = XC_SERIAL;
|
||||
kcons_info.write = kcons_write_dom0;
|
||||
} else {
|
||||
if (!xen_start_info->console.domU.evtchn)
|
||||
goto out;
|
||||
if (xc_mode == XC_DEFAULT)
|
||||
xc_mode = XC_TTY;
|
||||
kcons_info.write = kcons_write;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_XEN_FRAMEBUFFER) := xenfb.o
|
||||
obj-$(CONFIG_XEN_KEYBOARD) += xenkbd.o
|
||||
@@ -0,0 +1,682 @@
|
||||
/*
|
||||
* linux/drivers/video/xenfb.c -- Xen para-virtual frame buffer device
|
||||
*
|
||||
* Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
|
||||
*
|
||||
* Based on linux/drivers/video/q40fb.c
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* Switch to grant tables when they become capable of dealing with the
|
||||
* frame buffer.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <xen/evtchn.h>
|
||||
#include <xen/interface/io/fbif.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
struct xenfb_mapping
|
||||
{
|
||||
struct list_head link;
|
||||
struct vm_area_struct *vma;
|
||||
atomic_t map_refs;
|
||||
int faults;
|
||||
struct xenfb_info *info;
|
||||
};
|
||||
|
||||
struct xenfb_info
|
||||
{
|
||||
struct task_struct *kthread;
|
||||
wait_queue_head_t wq;
|
||||
|
||||
unsigned char *fb;
|
||||
struct fb_info *fb_info;
|
||||
struct timer_list refresh;
|
||||
int dirty;
|
||||
int x1, y1, x2, y2; /* dirty rectangle,
|
||||
protected by mm_lock */
|
||||
spinlock_t mm_lock;
|
||||
int nr_pages;
|
||||
struct page **pages;
|
||||
struct list_head mappings; /* protected by mm_lock */
|
||||
|
||||
unsigned evtchn;
|
||||
int irq;
|
||||
struct xenfb_page *page;
|
||||
unsigned long *mfns;
|
||||
int update_wanted; /* XENFB_TYPE_UPDATE wanted */
|
||||
|
||||
struct xenbus_device *xbdev;
|
||||
};
|
||||
|
||||
static int xenfb_fps = 20;
|
||||
static unsigned long xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8;
|
||||
|
||||
static int xenfb_remove(struct xenbus_device *);
|
||||
static void xenfb_init_shared_page(struct xenfb_info *);
|
||||
static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *);
|
||||
static void xenfb_disconnect_backend(struct xenfb_info *);
|
||||
|
||||
static void xenfb_do_update(struct xenfb_info *info,
|
||||
int x, int y, int w, int h)
|
||||
{
|
||||
union xenfb_out_event event;
|
||||
__u32 prod;
|
||||
|
||||
event.type = XENFB_TYPE_UPDATE;
|
||||
event.update.x = x;
|
||||
event.update.y = y;
|
||||
event.update.width = w;
|
||||
event.update.height = h;
|
||||
|
||||
prod = info->page->out_prod;
|
||||
/* caller ensures !xenfb_queue_full() */
|
||||
mb(); /* ensure ring space available */
|
||||
XENFB_OUT_RING_REF(info->page, prod) = event;
|
||||
wmb(); /* ensure ring contents visible */
|
||||
info->page->out_prod = prod + 1;
|
||||
|
||||
notify_remote_via_evtchn(info->evtchn);
|
||||
}
|
||||
|
||||
static int xenfb_queue_full(struct xenfb_info *info)
|
||||
{
|
||||
__u32 cons, prod;
|
||||
|
||||
prod = info->page->out_prod;
|
||||
cons = info->page->out_cons;
|
||||
return prod - cons == XENFB_OUT_RING_LEN;
|
||||
}
|
||||
|
||||
static void xenfb_update_screen(struct xenfb_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
int y1, y2, x1, x2;
|
||||
struct xenfb_mapping *map;
|
||||
|
||||
if (!info->update_wanted)
|
||||
return;
|
||||
if (xenfb_queue_full(info))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&info->mm_lock, flags);
|
||||
|
||||
y1 = info->y1;
|
||||
y2 = info->y2;
|
||||
x1 = info->x1;
|
||||
x2 = info->x2;
|
||||
info->x1 = info->y1 = INT_MAX;
|
||||
info->x2 = info->y2 = 0;
|
||||
|
||||
list_for_each_entry(map, &info->mappings, link) {
|
||||
if (!map->faults)
|
||||
continue;
|
||||
zap_page_range(map->vma, map->vma->vm_start,
|
||||
map->vma->vm_end - map->vma->vm_start, NULL);
|
||||
map->faults = 0;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&info->mm_lock, flags);
|
||||
|
||||
xenfb_do_update(info, x1, y1, x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
static int xenfb_thread(void *data)
|
||||
{
|
||||
struct xenfb_info *info = data;
|
||||
|
||||
while (!kthread_should_stop()) {
|
||||
if (info->dirty) {
|
||||
info->dirty = 0;
|
||||
xenfb_update_screen(info);
|
||||
}
|
||||
wait_event_interruptible(info->wq,
|
||||
kthread_should_stop() || info->dirty);
|
||||
try_to_freeze();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
||||
unsigned blue, unsigned transp,
|
||||
struct fb_info *info)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
if (regno > info->cmap.len)
|
||||
return 1;
|
||||
|
||||
red >>= (16 - info->var.red.length);
|
||||
green >>= (16 - info->var.green.length);
|
||||
blue >>= (16 - info->var.blue.length);
|
||||
|
||||
v = (red << info->var.red.offset) |
|
||||
(green << info->var.green.offset) |
|
||||
(blue << info->var.blue.offset);
|
||||
|
||||
/* FIXME is this sane? check against xxxfb_setcolreg()! */
|
||||
switch (info->var.bits_per_pixel) {
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
((u32 *)info->pseudo_palette)[regno] = v;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenfb_timer(unsigned long data)
|
||||
{
|
||||
struct xenfb_info *info = (struct xenfb_info *)data;
|
||||
info->dirty = 1;
|
||||
wake_up(&info->wq);
|
||||
}
|
||||
|
||||
static void __xenfb_refresh(struct xenfb_info *info,
|
||||
int x1, int y1, int w, int h)
|
||||
{
|
||||
int y2, x2;
|
||||
|
||||
y2 = y1 + h;
|
||||
x2 = x1 + w;
|
||||
|
||||
if (info->y1 > y1)
|
||||
info->y1 = y1;
|
||||
if (info->y2 < y2)
|
||||
info->y2 = y2;
|
||||
if (info->x1 > x1)
|
||||
info->x1 = x1;
|
||||
if (info->x2 < x2)
|
||||
info->x2 = x2;
|
||||
|
||||
if (timer_pending(&info->refresh))
|
||||
return;
|
||||
|
||||
mod_timer(&info->refresh, jiffies + HZ/xenfb_fps);
|
||||
}
|
||||
|
||||
static void xenfb_refresh(struct xenfb_info *info,
|
||||
int x1, int y1, int w, int h)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->mm_lock, flags);
|
||||
__xenfb_refresh(info, x1, y1, w, h);
|
||||
spin_unlock_irqrestore(&info->mm_lock, flags);
|
||||
}
|
||||
|
||||
static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
|
||||
{
|
||||
struct xenfb_info *info = p->par;
|
||||
|
||||
cfb_fillrect(p, rect);
|
||||
xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height);
|
||||
}
|
||||
|
||||
static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image)
|
||||
{
|
||||
struct xenfb_info *info = p->par;
|
||||
|
||||
cfb_imageblit(p, image);
|
||||
xenfb_refresh(info, image->dx, image->dy, image->width, image->height);
|
||||
}
|
||||
|
||||
static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
|
||||
{
|
||||
struct xenfb_info *info = p->par;
|
||||
|
||||
cfb_copyarea(p, area);
|
||||
xenfb_refresh(info, area->dx, area->dy, area->width, area->height);
|
||||
}
|
||||
|
||||
static void xenfb_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
struct xenfb_mapping *map = vma->vm_private_data;
|
||||
atomic_inc(&map->map_refs);
|
||||
}
|
||||
|
||||
static void xenfb_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct xenfb_mapping *map = vma->vm_private_data;
|
||||
struct xenfb_info *info = map->info;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->mm_lock, flags);
|
||||
if (atomic_dec_and_test(&map->map_refs)) {
|
||||
list_del(&map->link);
|
||||
kfree(map);
|
||||
}
|
||||
spin_unlock_irqrestore(&info->mm_lock, flags);
|
||||
}
|
||||
|
||||
static struct page *xenfb_vm_nopage(struct vm_area_struct *vma,
|
||||
unsigned long vaddr, int *type)
|
||||
{
|
||||
struct xenfb_mapping *map = vma->vm_private_data;
|
||||
struct xenfb_info *info = map->info;
|
||||
int pgnr = (vaddr - vma->vm_start) >> PAGE_SHIFT;
|
||||
unsigned long flags;
|
||||
struct page *page;
|
||||
int y1, y2;
|
||||
|
||||
if (pgnr >= info->nr_pages)
|
||||
return NOPAGE_SIGBUS;
|
||||
|
||||
spin_lock_irqsave(&info->mm_lock, flags);
|
||||
page = info->pages[pgnr];
|
||||
get_page(page);
|
||||
map->faults++;
|
||||
|
||||
y1 = pgnr * PAGE_SIZE / info->fb_info->fix.line_length;
|
||||
y2 = (pgnr * PAGE_SIZE + PAGE_SIZE - 1) / info->fb_info->fix.line_length;
|
||||
if (y2 > info->fb_info->var.yres)
|
||||
y2 = info->fb_info->var.yres;
|
||||
__xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1);
|
||||
spin_unlock_irqrestore(&info->mm_lock, flags);
|
||||
|
||||
if (type)
|
||||
*type = VM_FAULT_MINOR;
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static struct vm_operations_struct xenfb_vm_ops = {
|
||||
.open = xenfb_vm_open,
|
||||
.close = xenfb_vm_close,
|
||||
.nopage = xenfb_vm_nopage,
|
||||
};
|
||||
|
||||
static int xenfb_mmap(struct fb_info *fb_info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct xenfb_info *info = fb_info->par;
|
||||
unsigned long flags;
|
||||
struct xenfb_mapping *map;
|
||||
int map_pages;
|
||||
|
||||
if (!(vma->vm_flags & VM_WRITE))
|
||||
return -EINVAL;
|
||||
if (!(vma->vm_flags & VM_SHARED))
|
||||
return -EINVAL;
|
||||
if (vma->vm_pgoff != 0)
|
||||
return -EINVAL;
|
||||
|
||||
map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
|
||||
if (map_pages > info->nr_pages)
|
||||
return -EINVAL;
|
||||
|
||||
map = kzalloc(sizeof(*map), GFP_KERNEL);
|
||||
if (map == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
map->vma = vma;
|
||||
map->faults = 0;
|
||||
map->info = info;
|
||||
atomic_set(&map->map_refs, 1);
|
||||
|
||||
spin_lock_irqsave(&info->mm_lock, flags);
|
||||
list_add(&map->link, &info->mappings);
|
||||
spin_unlock_irqrestore(&info->mm_lock, flags);
|
||||
|
||||
vma->vm_ops = &xenfb_vm_ops;
|
||||
vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED);
|
||||
vma->vm_private_data = map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fb_ops xenfb_fb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_setcolreg = xenfb_setcolreg,
|
||||
.fb_fillrect = xenfb_fillrect,
|
||||
.fb_copyarea = xenfb_copyarea,
|
||||
.fb_imageblit = xenfb_imageblit,
|
||||
.fb_mmap = xenfb_mmap,
|
||||
};
|
||||
|
||||
static irqreturn_t xenfb_event_handler(int rq, void *dev_id,
|
||||
struct pt_regs *regs)
|
||||
{
|
||||
/*
|
||||
* No in events recognized, simply ignore them all.
|
||||
* If you need to recognize some, see xenbkd's input_handler()
|
||||
* for how to do that.
|
||||
*/
|
||||
struct xenfb_info *info = dev_id;
|
||||
struct xenfb_page *page = info->page;
|
||||
|
||||
if (page->in_cons != page->in_prod) {
|
||||
info->page->in_cons = info->page->in_prod;
|
||||
notify_remote_via_evtchn(info->evtchn);
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned long vmalloc_to_mfn(void *address)
|
||||
{
|
||||
return pfn_to_mfn(vmalloc_to_pfn(address));
|
||||
}
|
||||
|
||||
static int __devinit xenfb_probe(struct xenbus_device *dev,
|
||||
const struct xenbus_device_id *id)
|
||||
{
|
||||
struct xenfb_info *info;
|
||||
struct fb_info *fb_info;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (info == NULL) {
|
||||
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev->dev.driver_data = info;
|
||||
info->xbdev = dev;
|
||||
info->irq = -1;
|
||||
info->x1 = info->y1 = INT_MAX;
|
||||
spin_lock_init(&info->mm_lock);
|
||||
init_waitqueue_head(&info->wq);
|
||||
init_timer(&info->refresh);
|
||||
info->refresh.function = xenfb_timer;
|
||||
info->refresh.data = (unsigned long)info;
|
||||
INIT_LIST_HEAD(&info->mappings);
|
||||
|
||||
info->fb = vmalloc(xenfb_mem_len);
|
||||
if (info->fb == NULL)
|
||||
goto error_nomem;
|
||||
memset(info->fb, 0, xenfb_mem_len);
|
||||
|
||||
info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
|
||||
info->pages = kmalloc(sizeof(struct page *) * info->nr_pages,
|
||||
GFP_KERNEL);
|
||||
if (info->pages == NULL)
|
||||
goto error_nomem;
|
||||
|
||||
info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages);
|
||||
if (!info->mfns)
|
||||
goto error_nomem;
|
||||
|
||||
/* set up shared page */
|
||||
info->page = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!info->page)
|
||||
goto error_nomem;
|
||||
|
||||
xenfb_init_shared_page(info);
|
||||
|
||||
fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL);
|
||||
/* see fishy hackery below */
|
||||
if (fb_info == NULL)
|
||||
goto error_nomem;
|
||||
|
||||
/* FIXME fishy hackery */
|
||||
fb_info->pseudo_palette = fb_info->par;
|
||||
fb_info->par = info;
|
||||
/* /FIXME */
|
||||
fb_info->screen_base = info->fb;
|
||||
|
||||
fb_info->fbops = &xenfb_fb_ops;
|
||||
fb_info->var.xres_virtual = fb_info->var.xres = info->page->width;
|
||||
fb_info->var.yres_virtual = fb_info->var.yres = info->page->height;
|
||||
fb_info->var.bits_per_pixel = info->page->depth;
|
||||
|
||||
fb_info->var.red = (struct fb_bitfield){16, 8, 0};
|
||||
fb_info->var.green = (struct fb_bitfield){8, 8, 0};
|
||||
fb_info->var.blue = (struct fb_bitfield){0, 8, 0};
|
||||
|
||||
fb_info->var.activate = FB_ACTIVATE_NOW;
|
||||
fb_info->var.height = -1;
|
||||
fb_info->var.width = -1;
|
||||
fb_info->var.vmode = FB_VMODE_NONINTERLACED;
|
||||
|
||||
fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
|
||||
fb_info->fix.line_length = info->page->line_length;
|
||||
fb_info->fix.smem_start = 0;
|
||||
fb_info->fix.smem_len = xenfb_mem_len;
|
||||
strcpy(fb_info->fix.id, "xen");
|
||||
fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
|
||||
fb_info->fix.accel = FB_ACCEL_NONE;
|
||||
|
||||
fb_info->flags = FBINFO_FLAG_DEFAULT;
|
||||
|
||||
ret = fb_alloc_cmap(&fb_info->cmap, 256, 0);
|
||||
if (ret < 0) {
|
||||
framebuffer_release(fb_info);
|
||||
xenbus_dev_fatal(dev, ret, "fb_alloc_cmap");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = register_framebuffer(fb_info);
|
||||
if (ret) {
|
||||
fb_dealloc_cmap(&info->fb_info->cmap);
|
||||
framebuffer_release(fb_info);
|
||||
xenbus_dev_fatal(dev, ret, "register_framebuffer");
|
||||
goto error;
|
||||
}
|
||||
info->fb_info = fb_info;
|
||||
|
||||
/* FIXME should this be delayed until backend XenbusStateConnected? */
|
||||
info->kthread = kthread_run(xenfb_thread, info, "xenfb thread");
|
||||
if (IS_ERR(info->kthread)) {
|
||||
ret = PTR_ERR(info->kthread);
|
||||
info->kthread = NULL;
|
||||
xenbus_dev_fatal(dev, ret, "register_framebuffer");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = xenfb_connect_backend(dev, info);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error_nomem:
|
||||
ret = -ENOMEM;
|
||||
xenbus_dev_fatal(dev, ret, "allocating device memory");
|
||||
error:
|
||||
xenfb_remove(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xenfb_resume(struct xenbus_device *dev)
|
||||
{
|
||||
struct xenfb_info *info = dev->dev.driver_data;
|
||||
|
||||
xenfb_disconnect_backend(info);
|
||||
xenfb_init_shared_page(info);
|
||||
return xenfb_connect_backend(dev, info);
|
||||
}
|
||||
|
||||
static int xenfb_remove(struct xenbus_device *dev)
|
||||
{
|
||||
struct xenfb_info *info = dev->dev.driver_data;
|
||||
|
||||
del_timer(&info->refresh);
|
||||
if (info->kthread)
|
||||
kthread_stop(info->kthread);
|
||||
xenfb_disconnect_backend(info);
|
||||
if (info->fb_info) {
|
||||
unregister_framebuffer(info->fb_info);
|
||||
fb_dealloc_cmap(&info->fb_info->cmap);
|
||||
framebuffer_release(info->fb_info);
|
||||
}
|
||||
free_page((unsigned long)info->page);
|
||||
vfree(info->mfns);
|
||||
kfree(info->pages);
|
||||
vfree(info->fb);
|
||||
kfree(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenfb_init_shared_page(struct xenfb_info *info)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < info->nr_pages; i++)
|
||||
info->pages[i] = vmalloc_to_page(info->fb + i * PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < info->nr_pages; i++)
|
||||
info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE);
|
||||
|
||||
info->page->pd[0] = vmalloc_to_mfn(info->mfns);
|
||||
info->page->pd[1] = 0;
|
||||
info->page->width = XENFB_WIDTH;
|
||||
info->page->height = XENFB_HEIGHT;
|
||||
info->page->depth = XENFB_DEPTH;
|
||||
info->page->line_length = (info->page->depth / 8) * info->page->width;
|
||||
info->page->mem_length = xenfb_mem_len;
|
||||
info->page->in_cons = info->page->in_prod = 0;
|
||||
info->page->out_cons = info->page->out_prod = 0;
|
||||
}
|
||||
|
||||
static int xenfb_connect_backend(struct xenbus_device *dev,
|
||||
struct xenfb_info *info)
|
||||
{
|
||||
int ret;
|
||||
struct xenbus_transaction xbt;
|
||||
|
||||
ret = xenbus_alloc_evtchn(dev, &info->evtchn);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = bind_evtchn_to_irqhandler(info->evtchn, xenfb_event_handler,
|
||||
0, "xenfb", info);
|
||||
if (ret < 0) {
|
||||
xenbus_free_evtchn(dev, info->evtchn);
|
||||
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
|
||||
return ret;
|
||||
}
|
||||
info->irq = ret;
|
||||
|
||||
again:
|
||||
ret = xenbus_transaction_start(&xbt);
|
||||
if (ret) {
|
||||
xenbus_dev_fatal(dev, ret, "starting transaction");
|
||||
return ret;
|
||||
}
|
||||
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
|
||||
virt_to_mfn(info->page));
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
|
||||
info->evtchn);
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1");
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_transaction_end(xbt, 0);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
goto again;
|
||||
xenbus_dev_fatal(dev, ret, "completing transaction");
|
||||
return ret;
|
||||
}
|
||||
|
||||
xenbus_switch_state(dev, XenbusStateInitialised);
|
||||
return 0;
|
||||
|
||||
error_xenbus:
|
||||
xenbus_transaction_end(xbt, 1);
|
||||
xenbus_dev_fatal(dev, ret, "writing xenstore");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xenfb_disconnect_backend(struct xenfb_info *info)
|
||||
{
|
||||
if (info->irq >= 0)
|
||||
unbind_from_irqhandler(info->irq, info);
|
||||
info->irq = -1;
|
||||
}
|
||||
|
||||
static void xenfb_backend_changed(struct xenbus_device *dev,
|
||||
enum xenbus_state backend_state)
|
||||
{
|
||||
struct xenfb_info *info = dev->dev.driver_data;
|
||||
int val;
|
||||
|
||||
switch (backend_state) {
|
||||
case XenbusStateInitialising:
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateUnknown:
|
||||
case XenbusStateClosed:
|
||||
break;
|
||||
|
||||
case XenbusStateInitWait:
|
||||
InitWait:
|
||||
xenbus_switch_state(dev, XenbusStateConnected);
|
||||
break;
|
||||
|
||||
case XenbusStateConnected:
|
||||
/*
|
||||
* Work around xenbus race condition: If backend goes
|
||||
* through InitWait to Connected fast enough, we can
|
||||
* get Connected twice here.
|
||||
*/
|
||||
if (dev->state != XenbusStateConnected)
|
||||
goto InitWait; /* no InitWait seen yet, fudge it */
|
||||
|
||||
if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
|
||||
"request-update", "%d", &val) < 0)
|
||||
val = 0;
|
||||
if (val)
|
||||
info->update_wanted = 1;
|
||||
break;
|
||||
|
||||
case XenbusStateClosing:
|
||||
// FIXME is this safe in any dev->state?
|
||||
xenbus_frontend_closed(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct xenbus_device_id xenfb_ids[] = {
|
||||
{ "vfb" },
|
||||
{ "" }
|
||||
};
|
||||
|
||||
static struct xenbus_driver xenfb = {
|
||||
.name = "vfb",
|
||||
.owner = THIS_MODULE,
|
||||
.ids = xenfb_ids,
|
||||
.probe = xenfb_probe,
|
||||
.remove = xenfb_remove,
|
||||
.resume = xenfb_resume,
|
||||
.otherend_changed = xenfb_backend_changed,
|
||||
};
|
||||
|
||||
static int __init xenfb_init(void)
|
||||
{
|
||||
if (!is_running_on_xen())
|
||||
return -ENODEV;
|
||||
|
||||
/* Nothing to do if running in dom0. */
|
||||
if (is_initial_xendomain())
|
||||
return -ENODEV;
|
||||
|
||||
return xenbus_register_frontend(&xenfb);
|
||||
}
|
||||
|
||||
static void __exit xenfb_cleanup(void)
|
||||
{
|
||||
return xenbus_unregister_driver(&xenfb);
|
||||
}
|
||||
|
||||
module_init(xenfb_init);
|
||||
module_exit(xenfb_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device
|
||||
*
|
||||
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
|
||||
*
|
||||
* Based on linux/drivers/input/mouse/sermouse.c
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO:
|
||||
*
|
||||
* Switch to grant tables together with xenfb.c.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/hypervisor.h>
|
||||
#include <xen/evtchn.h>
|
||||
#include <xen/interface/io/fbif.h>
|
||||
#include <xen/interface/io/kbdif.h>
|
||||
#include <xen/xenbus.h>
|
||||
|
||||
struct xenkbd_info
|
||||
{
|
||||
struct input_dev *dev;
|
||||
struct xenkbd_page *page;
|
||||
unsigned evtchn;
|
||||
int irq;
|
||||
struct xenbus_device *xbdev;
|
||||
};
|
||||
|
||||
static int xenkbd_remove(struct xenbus_device *);
|
||||
static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
|
||||
static void xenkbd_disconnect_backend(struct xenkbd_info *);
|
||||
|
||||
/*
|
||||
* Note: if you need to send out events, see xenfb_do_update() for how
|
||||
* to do that.
|
||||
*/
|
||||
|
||||
static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs)
|
||||
{
|
||||
struct xenkbd_info *info = dev_id;
|
||||
struct xenkbd_page *page = info->page;
|
||||
__u32 cons, prod;
|
||||
|
||||
prod = page->in_prod;
|
||||
if (prod == page->out_cons)
|
||||
return IRQ_HANDLED;
|
||||
rmb(); /* ensure we see ring contents up to prod */
|
||||
for (cons = page->in_cons; cons != prod; cons++) {
|
||||
union xenkbd_in_event *event;
|
||||
event = &XENKBD_IN_RING_REF(page, cons);
|
||||
|
||||
switch (event->type) {
|
||||
case XENKBD_TYPE_MOTION:
|
||||
input_report_rel(info->dev, REL_X, event->motion.rel_x);
|
||||
input_report_rel(info->dev, REL_Y, event->motion.rel_y);
|
||||
break;
|
||||
case XENKBD_TYPE_KEY:
|
||||
input_report_key(info->dev, event->key.keycode, event->key.pressed);
|
||||
break;
|
||||
case XENKBD_TYPE_POS:
|
||||
input_report_abs(info->dev, ABS_X, event->pos.abs_x);
|
||||
input_report_abs(info->dev, ABS_Y, event->pos.abs_y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
input_sync(info->dev);
|
||||
mb(); /* ensure we got ring contents */
|
||||
page->in_cons = cons;
|
||||
notify_remote_via_evtchn(info->evtchn);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int __devinit xenkbd_probe(struct xenbus_device *dev,
|
||||
const struct xenbus_device_id *id)
|
||||
{
|
||||
int ret, i;
|
||||
struct xenkbd_info *info;
|
||||
struct input_dev *input_dev;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info) {
|
||||
xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
|
||||
return -ENOMEM;
|
||||
}
|
||||
dev->dev.driver_data = info;
|
||||
info->xbdev = dev;
|
||||
|
||||
info->page = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!info->page)
|
||||
goto error_nomem;
|
||||
info->page->in_cons = info->page->in_prod = 0;
|
||||
info->page->out_cons = info->page->out_prod = 0;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
goto error_nomem;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS);
|
||||
input_dev->keybit[LONG(BTN_MOUSE)]
|
||||
= BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
|
||||
/* TODO additional buttons */
|
||||
input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
|
||||
|
||||
/* FIXME not sure this is quite right */
|
||||
for (i = 0; i < 256; i++)
|
||||
set_bit(i, input_dev->keybit);
|
||||
|
||||
input_dev->name = "Xen Virtual Keyboard/Mouse";
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X, 0, XENFB_WIDTH, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
|
||||
|
||||
ret = input_register_device(input_dev);
|
||||
if (ret) {
|
||||
input_free_device(input_dev);
|
||||
xenbus_dev_fatal(dev, ret, "input_register_device");
|
||||
goto error;
|
||||
}
|
||||
info->dev = input_dev;
|
||||
|
||||
ret = xenkbd_connect_backend(dev, info);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error_nomem:
|
||||
ret = -ENOMEM;
|
||||
xenbus_dev_fatal(dev, ret, "allocating device memory");
|
||||
error:
|
||||
xenkbd_remove(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xenkbd_resume(struct xenbus_device *dev)
|
||||
{
|
||||
struct xenkbd_info *info = dev->dev.driver_data;
|
||||
|
||||
xenkbd_disconnect_backend(info);
|
||||
return xenkbd_connect_backend(dev, info);
|
||||
}
|
||||
|
||||
static int xenkbd_remove(struct xenbus_device *dev)
|
||||
{
|
||||
struct xenkbd_info *info = dev->dev.driver_data;
|
||||
|
||||
xenkbd_disconnect_backend(info);
|
||||
input_unregister_device(info->dev);
|
||||
free_page((unsigned long)info->page);
|
||||
kfree(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenkbd_connect_backend(struct xenbus_device *dev,
|
||||
struct xenkbd_info *info)
|
||||
{
|
||||
int ret;
|
||||
struct xenbus_transaction xbt;
|
||||
|
||||
ret = xenbus_alloc_evtchn(dev, &info->evtchn);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = bind_evtchn_to_irqhandler(info->evtchn, input_handler, 0,
|
||||
"xenkbd", info);
|
||||
if (ret < 0) {
|
||||
xenbus_free_evtchn(dev, info->evtchn);
|
||||
xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
|
||||
return ret;
|
||||
}
|
||||
info->irq = ret;
|
||||
|
||||
again:
|
||||
ret = xenbus_transaction_start(&xbt);
|
||||
if (ret) {
|
||||
xenbus_dev_fatal(dev, ret, "starting transaction");
|
||||
return ret;
|
||||
}
|
||||
ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
|
||||
virt_to_mfn(info->page));
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
|
||||
info->evtchn);
|
||||
if (ret)
|
||||
goto error_xenbus;
|
||||
ret = xenbus_transaction_end(xbt, 0);
|
||||
if (ret) {
|
||||
if (ret == -EAGAIN)
|
||||
goto again;
|
||||
xenbus_dev_fatal(dev, ret, "completing transaction");
|
||||
return ret;
|
||||
}
|
||||
|
||||
xenbus_switch_state(dev, XenbusStateInitialised);
|
||||
return 0;
|
||||
|
||||
error_xenbus:
|
||||
xenbus_transaction_end(xbt, 1);
|
||||
xenbus_dev_fatal(dev, ret, "writing xenstore");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void xenkbd_disconnect_backend(struct xenkbd_info *info)
|
||||
{
|
||||
if (info->irq >= 0)
|
||||
unbind_from_irqhandler(info->irq, info);
|
||||
info->irq = -1;
|
||||
}
|
||||
|
||||
static void xenkbd_backend_changed(struct xenbus_device *dev,
|
||||
enum xenbus_state backend_state)
|
||||
{
|
||||
struct xenkbd_info *info = dev->dev.driver_data;
|
||||
int ret, val;
|
||||
|
||||
switch (backend_state) {
|
||||
case XenbusStateInitialising:
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateUnknown:
|
||||
case XenbusStateClosed:
|
||||
break;
|
||||
|
||||
case XenbusStateInitWait:
|
||||
InitWait:
|
||||
ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
|
||||
"feature-abs-pointer", "%d", &val);
|
||||
if (ret < 0)
|
||||
val = 0;
|
||||
if (val) {
|
||||
ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
|
||||
"request-abs-pointer", "1");
|
||||
if (ret)
|
||||
; /* FIXME */
|
||||
}
|
||||
xenbus_switch_state(dev, XenbusStateConnected);
|
||||
break;
|
||||
|
||||
case XenbusStateConnected:
|
||||
/*
|
||||
* Work around xenbus race condition: If backend goes
|
||||
* through InitWait to Connected fast enough, we can
|
||||
* get Connected twice here.
|
||||
*/
|
||||
if (dev->state != XenbusStateConnected)
|
||||
goto InitWait; /* no InitWait seen yet, fudge it */
|
||||
break;
|
||||
|
||||
case XenbusStateClosing:
|
||||
xenbus_frontend_closed(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static struct xenbus_device_id xenkbd_ids[] = {
|
||||
{ "vkbd" },
|
||||
{ "" }
|
||||
};
|
||||
|
||||
static struct xenbus_driver xenkbd = {
|
||||
.name = "vkbd",
|
||||
.owner = THIS_MODULE,
|
||||
.ids = xenkbd_ids,
|
||||
.probe = xenkbd_probe,
|
||||
.remove = xenkbd_remove,
|
||||
.resume = xenkbd_resume,
|
||||
.otherend_changed = xenkbd_backend_changed,
|
||||
};
|
||||
|
||||
static int __init xenkbd_init(void)
|
||||
{
|
||||
if (!is_running_on_xen())
|
||||
return -ENODEV;
|
||||
|
||||
/* Nothing to do if running in dom0. */
|
||||
if (is_initial_xendomain())
|
||||
return -ENODEV;
|
||||
|
||||
return xenbus_register_frontend(&xenkbd);
|
||||
}
|
||||
|
||||
static void __exit xenkbd_cleanup(void)
|
||||
{
|
||||
return xenbus_unregister_driver(&xenkbd);
|
||||
}
|
||||
|
||||
module_init(xenkbd_init);
|
||||
module_exit(xenkbd_cleanup);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -14,4 +14,6 @@ void xencons_tx(void);
|
||||
int xencons_ring_init(void);
|
||||
int xencons_ring_send(const char *data, unsigned len);
|
||||
|
||||
void xencons_early_setup(void);
|
||||
|
||||
#endif /* __ASM_XENCONS_H__ */
|
||||
@@ -882,6 +882,7 @@ unsigned long zap_page_range(struct vm_area_struct *vma, unsigned long address,
|
||||
tlb_finish_mmu(tlb, address, end);
|
||||
return end;
|
||||
}
|
||||
EXPORT_SYMBOL(zap_page_range);
|
||||
|
||||
/*
|
||||
* Do a quick page-table lookup for a single page.
|
||||
|
||||
@@ -19,6 +19,7 @@ SUBDIRS-y += xenstat
|
||||
SUBDIRS-y += libaio
|
||||
SUBDIRS-y += blktap
|
||||
SUBDIRS-y += libfsimage
|
||||
SUBDIRS-y += xenfb
|
||||
SUBDIRS-$(LIBXENAPI_BINDINGS) += libxen
|
||||
|
||||
# These don't cross-compile
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
# A collection of DevControllers
|
||||
#
|
||||
|
||||
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
|
||||
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif
|
||||
from xen.xend.server.BlktapController import BlktapController
|
||||
|
||||
class XendDevices:
|
||||
@@ -41,6 +41,8 @@ class XendDevices:
|
||||
'irq': irqif.IRQController,
|
||||
'usb': usbif.UsbifController,
|
||||
'tap': BlktapController,
|
||||
'vfb': vfbif.VfbifController,
|
||||
'vkbd': vfbif.VkbdifController,
|
||||
}
|
||||
|
||||
#@classmethod
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
from xen.xend.server.DevController import DevController
|
||||
|
||||
from xen.xend.XendError import VmError
|
||||
import xen.xend
|
||||
import os
|
||||
|
||||
def spawn_detached(path, args, env):
|
||||
p = os.fork()
|
||||
if p == 0:
|
||||
os.spawnve(os.P_NOWAIT, path, args, env)
|
||||
os._exit(0)
|
||||
else:
|
||||
os.waitpid(p, 0)
|
||||
|
||||
class VfbifController(DevController):
|
||||
"""Virtual frame buffer controller. Handles all vfb devices for a domain.
|
||||
Note that we only support a single vfb per domain at the moment.
|
||||
"""
|
||||
|
||||
def __init__(self, vm):
|
||||
DevController.__init__(self, vm)
|
||||
self.config = {}
|
||||
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
devid = 0
|
||||
back = {}
|
||||
front = {}
|
||||
return (devid, back, front)
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
r = DevController.getDeviceConfiguration(self, devid)
|
||||
for (k,v) in self.config.iteritems():
|
||||
r[k] = v
|
||||
return r
|
||||
|
||||
def createDevice(self, config):
|
||||
DevController.createDevice(self, config)
|
||||
self.config = config
|
||||
std_args = [ "--domid", "%d" % self.vm.getDomid(),
|
||||
"--title", self.vm.getName() ]
|
||||
t = config.get("type", None)
|
||||
if t == "vnc":
|
||||
# Try to start the vnc backend
|
||||
args = [xen.util.auxbin.pathTo("xen-vncfb")]
|
||||
if config.has_key("vncunused"):
|
||||
args += ["--unused"]
|
||||
elif config.has_key("vncdisplay"):
|
||||
args += ["--vncport", "%d" % (5900 + config["vncdisplay"])]
|
||||
vnclisten = config.get("vnclisten",
|
||||
xen.xend.XendRoot.instance().get_vnclisten_address())
|
||||
args += [ "--listen", vnclisten ]
|
||||
spawn_detached(args[0], args + std_args, os.environ)
|
||||
elif t == "sdl":
|
||||
args = [xen.util.auxbin.pathTo("xen-sdlfb")]
|
||||
env = dict(os.environ)
|
||||
if config.has_key("display"):
|
||||
env['DISPLAY'] = config["display"]
|
||||
if config.has_key("xauthority"):
|
||||
env['XAUTHORITY'] = config["xauthority"]
|
||||
spawn_detached(args[0], args + std_args, env)
|
||||
else:
|
||||
raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
|
||||
|
||||
class VkbdifController(DevController):
|
||||
"""Virtual keyboard controller. Handles all vkbd devices for a domain.
|
||||
"""
|
||||
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
devid = 0
|
||||
back = {}
|
||||
front = {}
|
||||
return (devid, back, front)
|
||||
@@ -284,6 +284,18 @@ gopts.var('usbport', val='PATH',
|
||||
use="""Add a physical USB port to a domain, as specified by the path
|
||||
to that port. This option may be repeated to add more than one port.""")
|
||||
|
||||
gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY",
|
||||
fn=append_value, default=[],
|
||||
use="""Make the domain a framebuffer backend.
|
||||
The backend type should be either sdl or vnc.
|
||||
For type=vnc, connect an external vncviewer. The server will listen
|
||||
on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
|
||||
domain id. If vncunused=1, the server will try to find an arbitrary
|
||||
unused port above 5900.
|
||||
For type=sdl, a viewer will be started automatically using the
|
||||
given DISPLAY and XAUTHORITY, which default to the current user's
|
||||
ones.""")
|
||||
|
||||
gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
|
||||
fn=append_value, default=[],
|
||||
use="""Add a network interface with the given MAC address and bridge.
|
||||
@@ -512,8 +524,8 @@ def configure_image(vals):
|
||||
config_image.append(['args', vals.extra])
|
||||
|
||||
if vals.builder == 'hvm':
|
||||
configure_hvm(config_image, vals)
|
||||
|
||||
configure_hvm(config_image, vals)
|
||||
|
||||
return config_image
|
||||
|
||||
def configure_disks(config_devs, vals):
|
||||
@@ -564,6 +576,23 @@ def configure_usb(config_devs, vals):
|
||||
config_usb = ['usbport', ['path', path]]
|
||||
config_devs.append(['device', config_usb])
|
||||
|
||||
def configure_vfbs(config_devs, vals):
|
||||
for f in vals.vfb:
|
||||
d = comma_sep_kv_to_dict(f)
|
||||
config = ['vfb']
|
||||
if not d.has_key("type"):
|
||||
d['type'] = 'sdl'
|
||||
for (k,v) in d.iteritems():
|
||||
if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
|
||||
'xauthority', 'type' ]:
|
||||
err("configuration option %s unknown to vfbs" % k)
|
||||
config.append([k,v])
|
||||
if not d.has_key("display") and os.environ.has_key("DISPLAY"):
|
||||
config.append(["display", os.environ['DISPLAY']])
|
||||
if not d.has_key("xauthority"):
|
||||
config.append(["xauthority", get_xauthority()])
|
||||
config_devs.append(['device', ['vkbd']])
|
||||
config_devs.append(['device', config])
|
||||
|
||||
def configure_security(config, vals):
|
||||
"""Create the config for ACM security labels.
|
||||
@@ -742,6 +771,7 @@ def make_config(vals):
|
||||
configure_vifs(config_devs, vals)
|
||||
configure_usb(config_devs, vals)
|
||||
configure_vtpm(config_devs, vals)
|
||||
configure_vfbs(config_devs, vals)
|
||||
configure_security(config, vals)
|
||||
config += config_devs
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
XEN_ROOT=../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
CFLAGS += -I$(XEN_LIBXC) -I$(XEN_XENSTORE) -I$(XEN_ROOT)/linux-2.6-xen-sparse/include
|
||||
LDFLAGS += -L$(XEN_LIBXC) -L$(XEN_XENSTORE)
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PROG = $(INSTALL) -m0755
|
||||
INSTALL_DIR = $(INSTALL) -d -m0755
|
||||
|
||||
.PHONY: all
|
||||
all: build
|
||||
|
||||
.PHONY: build
|
||||
build: mk-symlinks
|
||||
$(MAKE) vncfb sdlfb
|
||||
|
||||
install: all
|
||||
$(INSTALL_DIR) -p $(DESTDIR)/usr/$(LIBDIR)/xen/bin
|
||||
$(INSTALL_PROG) vncfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-vncfb
|
||||
$(INSTALL_PROG) sdlfb $(DESTDIR)/usr/$(LIBDIR)/xen/bin/xen-sdlfb
|
||||
|
||||
sdlfb: sdlfb.o xenfb.o
|
||||
|
||||
sdlfb.o: CFLAGS += $(shell sdl-config --cflags)
|
||||
sdlfb: LDLIBS += $(shell sdl-config --libs) -lxenctrl -lxenstore
|
||||
|
||||
clean:
|
||||
$(RM) *.o *~ vncfb sdlfb
|
||||
|
||||
vncfb: vncfb.o xenfb.o
|
||||
vncfb.o: CFLAGS += $(shell libvncserver-config --cflags)
|
||||
vncfb: LDLIBS += $(shell libvncserver-config --libs) -lxenctrl -lxenstore
|
||||
|
||||
sdlfb.o xenfb.o vncfb.o: xenfb.h
|
||||
@@ -0,0 +1,342 @@
|
||||
#include <SDL.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/input.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include "xenfb.h"
|
||||
|
||||
struct SDLFBData
|
||||
{
|
||||
SDL_Surface *dst;
|
||||
SDL_Surface *src;
|
||||
};
|
||||
|
||||
/*
|
||||
* Map from scancode to Linux input layer keycode. Scancodes are
|
||||
* hardware-specific. This map assumes a standard AT or PS/2
|
||||
* keyboard.
|
||||
*
|
||||
* Why use scancodes? We can't use key symbols, because they don't
|
||||
* identify keys --- they're what keys are mapped to. The standard
|
||||
* German keymap, for instance, maps both KEY_COMMA and KEY_102ND to
|
||||
* SDLK_LESS.
|
||||
*/
|
||||
static int keymap[256] = {
|
||||
[9] = KEY_ESC,
|
||||
[10] = KEY_1,
|
||||
[11] = KEY_2,
|
||||
[12] = KEY_3,
|
||||
[13] = KEY_4,
|
||||
[14] = KEY_5,
|
||||
[15] = KEY_6,
|
||||
[16] = KEY_7,
|
||||
[17] = KEY_8,
|
||||
[18] = KEY_9,
|
||||
[19] = KEY_0,
|
||||
[20] = KEY_MINUS,
|
||||
[21] = KEY_EQUAL,
|
||||
[22] = KEY_BACKSPACE,
|
||||
[23] = KEY_TAB,
|
||||
[24] = KEY_Q,
|
||||
[25] = KEY_W,
|
||||
[26] = KEY_E,
|
||||
[27] = KEY_R,
|
||||
[28] = KEY_T,
|
||||
[29] = KEY_Y,
|
||||
[30] = KEY_U,
|
||||
[31] = KEY_I,
|
||||
[32] = KEY_O,
|
||||
[33] = KEY_P,
|
||||
[34] = KEY_LEFTBRACE,
|
||||
[35] = KEY_RIGHTBRACE,
|
||||
[36] = KEY_ENTER,
|
||||
[37] = KEY_LEFTCTRL,
|
||||
[38] = KEY_A,
|
||||
[39] = KEY_S,
|
||||
[40] = KEY_D,
|
||||
[41] = KEY_F,
|
||||
[42] = KEY_G,
|
||||
[43] = KEY_H,
|
||||
[44] = KEY_J,
|
||||
[45] = KEY_K,
|
||||
[46] = KEY_L,
|
||||
[47] = KEY_SEMICOLON,
|
||||
[48] = KEY_APOSTROPHE,
|
||||
[49] = KEY_GRAVE,
|
||||
[50] = KEY_LEFTSHIFT,
|
||||
[51] = KEY_BACKSLASH,
|
||||
[52] = KEY_Z,
|
||||
[53] = KEY_X,
|
||||
[54] = KEY_C,
|
||||
[55] = KEY_V,
|
||||
[56] = KEY_B,
|
||||
[57] = KEY_N,
|
||||
[58] = KEY_M,
|
||||
[59] = KEY_COMMA,
|
||||
[60] = KEY_DOT,
|
||||
[61] = KEY_SLASH,
|
||||
[62] = KEY_RIGHTSHIFT,
|
||||
[63] = KEY_KPASTERISK,
|
||||
[64] = KEY_LEFTALT,
|
||||
[65] = KEY_SPACE,
|
||||
[66] = KEY_CAPSLOCK,
|
||||
[67] = KEY_F1,
|
||||
[68] = KEY_F2,
|
||||
[69] = KEY_F3,
|
||||
[70] = KEY_F4,
|
||||
[71] = KEY_F5,
|
||||
[72] = KEY_F6,
|
||||
[73] = KEY_F7,
|
||||
[74] = KEY_F8,
|
||||
[75] = KEY_F9,
|
||||
[76] = KEY_F10,
|
||||
[77] = KEY_NUMLOCK,
|
||||
[78] = KEY_SCROLLLOCK,
|
||||
[79] = KEY_KP7,
|
||||
[80] = KEY_KP8,
|
||||
[81] = KEY_KP9,
|
||||
[82] = KEY_KPMINUS,
|
||||
[83] = KEY_KP4,
|
||||
[84] = KEY_KP5,
|
||||
[85] = KEY_KP6,
|
||||
[86] = KEY_KPPLUS,
|
||||
[87] = KEY_KP1,
|
||||
[88] = KEY_KP2,
|
||||
[89] = KEY_KP3,
|
||||
[90] = KEY_KP0,
|
||||
[91] = KEY_KPDOT,
|
||||
[94] = KEY_102ND, /* FIXME is this correct? */
|
||||
[95] = KEY_F11,
|
||||
[96] = KEY_F12,
|
||||
[108] = KEY_KPENTER,
|
||||
[109] = KEY_RIGHTCTRL,
|
||||
[112] = KEY_KPSLASH,
|
||||
[111] = KEY_SYSRQ,
|
||||
[113] = KEY_RIGHTALT,
|
||||
[97] = KEY_HOME,
|
||||
[98] = KEY_UP,
|
||||
[99] = KEY_PAGEUP,
|
||||
[100] = KEY_LEFT,
|
||||
[102] = KEY_RIGHT,
|
||||
[103] = KEY_END,
|
||||
[104] = KEY_DOWN,
|
||||
[105] = KEY_PAGEDOWN,
|
||||
[106] = KEY_INSERT,
|
||||
[107] = KEY_DELETE,
|
||||
[110] = KEY_PAUSE,
|
||||
[115] = KEY_LEFTMETA,
|
||||
[116] = KEY_RIGHTMETA,
|
||||
[117] = KEY_MENU,
|
||||
};
|
||||
|
||||
static int btnmap[] = {
|
||||
[SDL_BUTTON_LEFT] = BTN_LEFT,
|
||||
[SDL_BUTTON_MIDDLE] = BTN_MIDDLE,
|
||||
[SDL_BUTTON_RIGHT] = BTN_RIGHT,
|
||||
/* FIXME not 100% sure about these: */
|
||||
[SDL_BUTTON_WHEELUP] = BTN_FORWARD,
|
||||
[SDL_BUTTON_WHEELDOWN] BTN_BACK
|
||||
};
|
||||
|
||||
static void sdl_update(struct xenfb *xenfb, int x, int y, int width, int height)
|
||||
{
|
||||
struct SDLFBData *data = xenfb->user_data;
|
||||
SDL_Rect r = { x, y, width, height };
|
||||
SDL_BlitSurface(data->src, &r, data->dst, &r);
|
||||
SDL_UpdateRect(data->dst, x, y, width, height);
|
||||
}
|
||||
|
||||
static int sdl_on_event(struct xenfb *xenfb, SDL_Event *event)
|
||||
{
|
||||
int x, y, ret;
|
||||
|
||||
switch (event->type) {
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
if (keymap[event->key.keysym.scancode] == 0)
|
||||
break;
|
||||
ret = xenfb_send_key(xenfb,
|
||||
event->type == SDL_KEYDOWN,
|
||||
keymap[event->key.keysym.scancode]);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Key %d %s lost (%s)\n",
|
||||
keymap[event->key.keysym.scancode],
|
||||
event->type == SDL_KEYDOWN ? "down" : "up",
|
||||
strerror(errno));
|
||||
break;
|
||||
case SDL_MOUSEMOTION:
|
||||
if (xenfb->abs_pointer_wanted) {
|
||||
SDL_GetMouseState(&x, &y);
|
||||
ret = xenfb_send_position(xenfb, x, y);
|
||||
} else {
|
||||
SDL_GetRelativeMouseState(&x, &y);
|
||||
ret = xenfb_send_motion(xenfb, x, y);
|
||||
}
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
|
||||
x, y, strerror(errno));
|
||||
break;
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
if (event->button.button >= sizeof(btnmap) / sizeof(*btnmap))
|
||||
break;
|
||||
if (btnmap[event->button.button] == 0)
|
||||
break;
|
||||
ret = xenfb_send_key(xenfb,
|
||||
event->type == SDL_MOUSEBUTTONDOWN,
|
||||
btnmap[event->button.button]);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Button %d %s lost (%s)\n",
|
||||
btnmap[event->button.button] - BTN_MOUSE,
|
||||
event->type == SDL_MOUSEBUTTONDOWN ? "down" : "up",
|
||||
strerror(errno));
|
||||
break;
|
||||
case SDL_QUIT:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct option options[] = {
|
||||
{ "domid", 1, NULL, 'd' },
|
||||
{ "title", 1, NULL, 't' },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct xenfb *xenfb;
|
||||
int domid = -1;
|
||||
char * title = NULL;
|
||||
fd_set readfds;
|
||||
int nfds;
|
||||
struct SDLFBData data;
|
||||
SDL_Rect r;
|
||||
struct timeval tv;
|
||||
SDL_Event event;
|
||||
int do_quit = 0;
|
||||
int opt;
|
||||
char *endp;
|
||||
int retval;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "d:t:", options,
|
||||
NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domid = strtol(optarg, &endp, 10);
|
||||
if (endp == optarg || *endp) {
|
||||
fprintf(stderr, "Invalid domain id specified\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
title = strdup(optarg);
|
||||
break;
|
||||
case '?':
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (optind != argc) {
|
||||
fprintf(stderr, "Invalid options!\n");
|
||||
exit(1);
|
||||
}
|
||||
if (domid <= 0) {
|
||||
fprintf(stderr, "Domain ID must be specified!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
xenfb = xenfb_new();
|
||||
if (xenfb == NULL) {
|
||||
fprintf(stderr, "Could not create framebuffer (%s)\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (xenfb_attach_dom(xenfb, domid) < 0) {
|
||||
fprintf(stderr, "Could not connect to domain (%s)\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
|
||||
fprintf(stderr, "Could not initialize SDL\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
data.dst = SDL_SetVideoMode(xenfb->width, xenfb->height, xenfb->depth,
|
||||
SDL_SWSURFACE);
|
||||
if (!data.dst) {
|
||||
fprintf(stderr, "SDL_SetVideoMode failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
data.src = SDL_CreateRGBSurfaceFrom(xenfb->pixels,
|
||||
xenfb->width, xenfb->height,
|
||||
xenfb->depth, xenfb->row_stride,
|
||||
0xFF0000, 0xFF00, 0xFF, 0);
|
||||
|
||||
if (!data.src) {
|
||||
fprintf(stderr, "SDL_CreateRGBSurfaceFrom failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (title == NULL)
|
||||
title = strdup("xen-sdlfb");
|
||||
SDL_WM_SetCaption(title, title);
|
||||
|
||||
r.x = r.y = 0;
|
||||
r.w = xenfb->width;
|
||||
r.h = xenfb->height;
|
||||
SDL_BlitSurface(data.src, &r, data.dst, &r);
|
||||
SDL_UpdateRect(data.dst, 0, 0, xenfb->width, xenfb->height);
|
||||
|
||||
xenfb->update = sdl_update;
|
||||
xenfb->user_data = &data;
|
||||
|
||||
SDL_ShowCursor(0);
|
||||
|
||||
/*
|
||||
* We need to wait for fds becoming ready or SDL events to
|
||||
* arrive. We time out the select after 10ms to poll for SDL
|
||||
* events. Clunky, but works. Could avoid the clunkiness
|
||||
* with a separate thread.
|
||||
*/
|
||||
for (;;) {
|
||||
FD_ZERO(&readfds);
|
||||
nfds = xenfb_select_fds(xenfb, &readfds);
|
||||
tv = (struct timeval){0, 10000};
|
||||
|
||||
if (select(nfds, &readfds, NULL, NULL, &tv) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"Can't select() on event channel (%s)\n",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
while (SDL_PollEvent(&event)) {
|
||||
if (!sdl_on_event(xenfb, &event))
|
||||
do_quit = 1;
|
||||
}
|
||||
|
||||
if (do_quit)
|
||||
break;
|
||||
|
||||
retval = xenfb_poll(xenfb, &readfds);
|
||||
if (retval == -2)
|
||||
xenfb_teardown(xenfb);
|
||||
if (retval < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
xenfb_delete(xenfb);
|
||||
|
||||
SDL_Quit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <malloc.h>
|
||||
#include <rfb/rfb.h>
|
||||
#include <rfb/keysym.h>
|
||||
#include <linux/input.h>
|
||||
#include <xs.h>
|
||||
#include "xenfb.h"
|
||||
|
||||
static int xk2linux[0x10000] = {
|
||||
[XK_a] = KEY_A,
|
||||
[XK_b] = KEY_B,
|
||||
[XK_c] = KEY_C,
|
||||
[XK_d] = KEY_D,
|
||||
[XK_e] = KEY_E,
|
||||
[XK_f] = KEY_F,
|
||||
[XK_g] = KEY_G,
|
||||
[XK_h] = KEY_H,
|
||||
[XK_i] = KEY_I,
|
||||
[XK_j] = KEY_J,
|
||||
[XK_k] = KEY_K,
|
||||
[XK_l] = KEY_L,
|
||||
[XK_m] = KEY_M,
|
||||
[XK_n] = KEY_N,
|
||||
[XK_o] = KEY_O,
|
||||
[XK_p] = KEY_P,
|
||||
[XK_q] = KEY_Q,
|
||||
[XK_r] = KEY_R,
|
||||
[XK_s] = KEY_S,
|
||||
[XK_t] = KEY_T,
|
||||
[XK_u] = KEY_U,
|
||||
[XK_v] = KEY_V,
|
||||
[XK_w] = KEY_W,
|
||||
[XK_x] = KEY_X,
|
||||
[XK_y] = KEY_Y,
|
||||
[XK_z] = KEY_Z,
|
||||
[XK_A] = KEY_A,
|
||||
[XK_B] = KEY_B,
|
||||
[XK_C] = KEY_C,
|
||||
[XK_D] = KEY_D,
|
||||
[XK_E] = KEY_E,
|
||||
[XK_F] = KEY_F,
|
||||
[XK_G] = KEY_G,
|
||||
[XK_H] = KEY_H,
|
||||
[XK_I] = KEY_I,
|
||||
[XK_J] = KEY_J,
|
||||
[XK_K] = KEY_K,
|
||||
[XK_L] = KEY_L,
|
||||
[XK_M] = KEY_M,
|
||||
[XK_N] = KEY_N,
|
||||
[XK_O] = KEY_O,
|
||||
[XK_P] = KEY_P,
|
||||
[XK_Q] = KEY_Q,
|
||||
[XK_R] = KEY_R,
|
||||
[XK_S] = KEY_S,
|
||||
[XK_T] = KEY_T,
|
||||
[XK_U] = KEY_U,
|
||||
[XK_V] = KEY_V,
|
||||
[XK_W] = KEY_W,
|
||||
[XK_X] = KEY_X,
|
||||
[XK_Y] = KEY_Y,
|
||||
[XK_Z] = KEY_Z,
|
||||
[XK_0] = KEY_0,
|
||||
[XK_1] = KEY_1,
|
||||
[XK_2] = KEY_2,
|
||||
[XK_3] = KEY_3,
|
||||
[XK_4] = KEY_4,
|
||||
[XK_5] = KEY_5,
|
||||
[XK_6] = KEY_6,
|
||||
[XK_7] = KEY_7,
|
||||
[XK_8] = KEY_8,
|
||||
[XK_9] = KEY_9,
|
||||
[XK_Return] = KEY_ENTER,
|
||||
[XK_BackSpace] = KEY_BACKSPACE,
|
||||
[XK_Tab] = KEY_TAB,
|
||||
[XK_Pause] = KEY_PAUSE,
|
||||
[XK_Delete] = KEY_DELETE,
|
||||
[XK_slash] = KEY_SLASH,
|
||||
[XK_minus] = KEY_MINUS,
|
||||
[XK_equal] = KEY_EQUAL,
|
||||
[XK_Escape] = KEY_ESC,
|
||||
[XK_braceleft] = KEY_LEFTBRACE,
|
||||
[XK_braceright] = KEY_RIGHTBRACE,
|
||||
[XK_bracketleft] = KEY_LEFTMETA,
|
||||
[XK_bracketright] = KEY_RIGHTMETA,
|
||||
[XK_Control_L] = KEY_LEFTCTRL,
|
||||
[XK_Control_R] = KEY_RIGHTCTRL,
|
||||
[XK_Shift_L] = KEY_LEFTSHIFT,
|
||||
[XK_Shift_R] = KEY_RIGHTSHIFT,
|
||||
[XK_Alt_L] = KEY_LEFTALT,
|
||||
[XK_Alt_R] = KEY_RIGHTALT,
|
||||
[XK_semicolon] = KEY_SEMICOLON,
|
||||
[XK_apostrophe] = KEY_APOSTROPHE,
|
||||
[XK_grave] = KEY_GRAVE,
|
||||
[XK_backslash] = KEY_BACKSLASH,
|
||||
[XK_comma] = KEY_COMMA,
|
||||
[XK_period] = KEY_DOT,
|
||||
[XK_space] = KEY_SPACE,
|
||||
[XK_Caps_Lock] = KEY_CAPSLOCK,
|
||||
[XK_Num_Lock] = KEY_NUMLOCK,
|
||||
[XK_Scroll_Lock] = KEY_SCROLLLOCK,
|
||||
[XK_Sys_Req] = KEY_SYSRQ,
|
||||
[XK_Linefeed] = KEY_LINEFEED,
|
||||
[XK_Home] = KEY_HOME,
|
||||
[XK_Pause] = KEY_PAUSE,
|
||||
[XK_F1] = KEY_F1,
|
||||
[XK_F2] = KEY_F2,
|
||||
[XK_F3] = KEY_F3,
|
||||
[XK_F4] = KEY_F4,
|
||||
[XK_F5] = KEY_F5,
|
||||
[XK_F6] = KEY_F6,
|
||||
[XK_F7] = KEY_F7,
|
||||
[XK_F8] = KEY_F8,
|
||||
[XK_F9] = KEY_F9,
|
||||
[XK_F10] = KEY_F10,
|
||||
[XK_F11] = KEY_F11,
|
||||
[XK_F12] = KEY_F12,
|
||||
[XK_Up] = KEY_UP,
|
||||
[XK_Page_Up] = KEY_PAGEUP,
|
||||
[XK_Left] = KEY_LEFT,
|
||||
[XK_Right] = KEY_RIGHT,
|
||||
[XK_End] = KEY_END,
|
||||
[XK_Down] = KEY_DOWN,
|
||||
[XK_Page_Down] = KEY_PAGEDOWN,
|
||||
[XK_Insert] = KEY_INSERT,
|
||||
[XK_colon] = KEY_SEMICOLON,
|
||||
[XK_quotedbl] = KEY_APOSTROPHE,
|
||||
[XK_less] = KEY_COMMA,
|
||||
[XK_greater] = KEY_DOT,
|
||||
[XK_question] = KEY_SLASH,
|
||||
[XK_bar] = KEY_BACKSLASH,
|
||||
[XK_asciitilde] = KEY_GRAVE,
|
||||
[XK_exclam] = KEY_1,
|
||||
[XK_at] = KEY_2,
|
||||
[XK_numbersign] = KEY_3,
|
||||
[XK_dollar] = KEY_4,
|
||||
[XK_percent] = KEY_5,
|
||||
[XK_asciicircum] = KEY_6,
|
||||
[XK_ampersand] = KEY_7,
|
||||
[XK_asterisk] = KEY_8,
|
||||
[XK_parenleft] = KEY_9,
|
||||
[XK_parenright] = KEY_0,
|
||||
[XK_underscore] = KEY_MINUS,
|
||||
[XK_plus] = KEY_EQUAL,
|
||||
};
|
||||
|
||||
static void on_kbd_event(rfbBool down, rfbKeySym keycode, rfbClientPtr cl)
|
||||
{
|
||||
/*
|
||||
* We need to map to the key's Linux input layer keycode.
|
||||
* Unfortunately, we don't get the key here, only the
|
||||
* rfbKeySym, which is what the key is mapped to. Mapping
|
||||
* back to the key is impossible in general, even when you
|
||||
* know the keymap. For instance, the standard German keymap
|
||||
* maps both KEY_COMMA and KEY_102ND to XK_less. We simply
|
||||
* assume standard US layout. This sucks.
|
||||
*/
|
||||
rfbScreenInfoPtr server = cl->screen;
|
||||
struct xenfb *xenfb = server->screenData;
|
||||
if (keycode >= sizeof(xk2linux) / sizeof(*xk2linux))
|
||||
return;
|
||||
if (xk2linux[keycode] == 0)
|
||||
return;
|
||||
if (xenfb_send_key(xenfb, down, xk2linux[keycode]) < 0)
|
||||
fprintf(stderr, "Key %d %s lost (%s)\n",
|
||||
xk2linux[keycode], down ? "down" : "up",
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
static void on_ptr_event(int buttonMask, int x, int y, rfbClientPtr cl)
|
||||
{
|
||||
/* initial pointer state: at (0,0), buttons up */
|
||||
static int last_x, last_y, last_button;
|
||||
rfbScreenInfoPtr server = cl->screen;
|
||||
struct xenfb *xenfb = server->screenData;
|
||||
int i, last_down, down, ret;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
last_down = last_button & (1 << i);
|
||||
down = buttonMask & (1 << i);
|
||||
if (down == last_down)
|
||||
continue;
|
||||
/* FIXME this assumes buttons are numbered the same; verify they are */
|
||||
if (xenfb_send_key(xenfb, down != 0, BTN_MOUSE + i) < 0)
|
||||
fprintf(stderr, "Button %d %s lost (%s)\n",
|
||||
i, down ? "down" : "up", strerror(errno));
|
||||
}
|
||||
|
||||
if (x != last_x || y != last_y) {
|
||||
if (xenfb->abs_pointer_wanted)
|
||||
ret = xenfb_send_position(xenfb, x, y);
|
||||
else
|
||||
ret = xenfb_send_motion(xenfb, x - last_x, y - last_y);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Pointer to %d,%d lost (%s)\n",
|
||||
x, y, strerror(errno));
|
||||
}
|
||||
|
||||
last_button = buttonMask;
|
||||
last_x = x;
|
||||
last_y = y;
|
||||
}
|
||||
|
||||
static void xenstore_write_vncport(int port, int domid)
|
||||
{
|
||||
char *buf = NULL, *path;
|
||||
char portstr[10];
|
||||
struct xs_handle *xsh = NULL;
|
||||
|
||||
xsh = xs_daemon_open();
|
||||
if (xsh == NULL)
|
||||
return;
|
||||
|
||||
path = xs_get_domain_path(xsh, domid);
|
||||
if (path == NULL) {
|
||||
fprintf(stderr, "Can't get domain path (%s)\n",
|
||||
strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (asprintf(&buf, "%s/console/vnc-port", path) == -1) {
|
||||
fprintf(stderr, "Can't make vncport path\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
|
||||
fprintf(stderr, "Can't make vncport value\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!xs_write(xsh, XBT_NULL, buf, portstr, strlen(portstr)))
|
||||
fprintf(stderr, "Can't set vncport (%s)\n",
|
||||
strerror(errno));
|
||||
|
||||
out:
|
||||
free(buf);
|
||||
}
|
||||
|
||||
|
||||
static void vnc_update(struct xenfb *xenfb, int x, int y, int w, int h)
|
||||
{
|
||||
rfbScreenInfoPtr server = xenfb->user_data;
|
||||
rfbMarkRectAsModified(server, x, y, x + w, y + h);
|
||||
}
|
||||
|
||||
static struct option options[] = {
|
||||
{ "domid", 1, NULL, 'd' },
|
||||
{ "vncport", 1, NULL, 'p' },
|
||||
{ "title", 1, NULL, 't' },
|
||||
{ "unused", 0, NULL, 'u' },
|
||||
{ "listen", 1, NULL, 'l' },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
rfbScreenInfoPtr server;
|
||||
char *fake_argv[7] = { "vncfb", "-rfbport", "5901",
|
||||
"-desktop", "xen-vncfb",
|
||||
"-listen", "127.0.0.1" };
|
||||
int fake_argc = sizeof(fake_argv) / sizeof(fake_argv[0]);
|
||||
int domid = -1, port = -1;
|
||||
char *title = NULL;
|
||||
char *listen = NULL;
|
||||
bool unused = false;
|
||||
int opt;
|
||||
struct xenfb *xenfb;
|
||||
fd_set readfds;
|
||||
int nfds;
|
||||
char portstr[10];
|
||||
char *endp;
|
||||
int r;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "d:p:t:u", options,
|
||||
NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
errno = 0;
|
||||
domid = strtol(optarg, &endp, 10);
|
||||
if (endp == optarg || *endp || errno) {
|
||||
fprintf(stderr, "Invalid domain id specified\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
errno = 0;
|
||||
port = strtol(optarg, &endp, 10);
|
||||
if (endp == optarg || *endp || errno) {
|
||||
fprintf(stderr, "Invalid port specified\n");
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
title = strdup(optarg);
|
||||
break;
|
||||
case 'u':
|
||||
unused = true;
|
||||
break;
|
||||
case 'l':
|
||||
listen = strdup(optarg);
|
||||
break;
|
||||
case '?':
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (optind != argc) {
|
||||
fprintf(stderr, "Invalid options!\n");
|
||||
exit(1);
|
||||
}
|
||||
if (domid <= 0) {
|
||||
fprintf(stderr, "Domain ID must be specified!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (port <= 0)
|
||||
port = 5900 + domid;
|
||||
if (snprintf(portstr, sizeof(portstr), "%d", port) == -1) {
|
||||
fprintf(stderr, "Invalid port specified\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fake_argv[2] = portstr;
|
||||
|
||||
if (title != NULL)
|
||||
fake_argv[4] = title;
|
||||
|
||||
if (listen != NULL)
|
||||
fake_argv[6] = listen;
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
xenfb = xenfb_new();
|
||||
if (xenfb == NULL) {
|
||||
fprintf(stderr, "Could not create framebuffer (%s)\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (xenfb_attach_dom(xenfb, domid) < 0) {
|
||||
fprintf(stderr, "Could not connect to domain (%s)\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
server = rfbGetScreen(&fake_argc, fake_argv,
|
||||
xenfb->width, xenfb->height,
|
||||
8, 3, xenfb->depth / 8);
|
||||
if (server == NULL) {
|
||||
fprintf(stderr, "Could not create VNC server\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
xenfb->user_data = server;
|
||||
xenfb->update = vnc_update;
|
||||
|
||||
if (unused)
|
||||
server->autoPort = true;
|
||||
|
||||
server->serverFormat.redShift = 16;
|
||||
server->serverFormat.greenShift = 8;
|
||||
server->serverFormat.blueShift = 0;
|
||||
server->kbdAddEvent = on_kbd_event;
|
||||
server->ptrAddEvent = on_ptr_event;
|
||||
server->frameBuffer = xenfb->pixels;
|
||||
server->screenData = xenfb;
|
||||
server->cursor = NULL;
|
||||
rfbInitServer(server);
|
||||
|
||||
rfbRunEventLoop(server, -1, true);
|
||||
|
||||
xenstore_write_vncport(server->port, domid);
|
||||
|
||||
for (;;) {
|
||||
FD_ZERO(&readfds);
|
||||
nfds = xenfb_select_fds(xenfb, &readfds);
|
||||
|
||||
if (select(nfds, &readfds, NULL, NULL, NULL) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
fprintf(stderr,
|
||||
"Can't select() on event channel (%s)\n",
|
||||
strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
r = xenfb_poll(xenfb, &readfds);
|
||||
if (r == -2)
|
||||
xenfb_teardown(xenfb);
|
||||
if (r < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
rfbScreenCleanup(server);
|
||||
xenfb_delete(xenfb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,711 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <xenctrl.h>
|
||||
#include <xen/io/xenbus.h>
|
||||
#include <xen/io/fbif.h>
|
||||
#include <xen/io/kbdif.h>
|
||||
#include <sys/select.h>
|
||||
#include <stdbool.h>
|
||||
#include <xen/linux/evtchn.h>
|
||||
#include <xen/event_channel.h>
|
||||
#include <sys/mman.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <xs.h>
|
||||
|
||||
#include "xenfb.h"
|
||||
|
||||
// FIXME defend against malicious frontend?
|
||||
|
||||
struct xenfb_device {
|
||||
const char *devicetype;
|
||||
char nodename[64]; /* backend xenstore dir */
|
||||
char otherend[64]; /* frontend xenstore dir */
|
||||
int otherend_id; /* frontend domid */
|
||||
enum xenbus_state state; /* backend state */
|
||||
void *page; /* shared page */
|
||||
evtchn_port_t port;
|
||||
struct xenfb_private *xenfb;
|
||||
};
|
||||
|
||||
struct xenfb_private {
|
||||
struct xenfb pub;
|
||||
int evt_xch; /* event channel driver handle */
|
||||
int xc; /* hypervisor interface handle */
|
||||
struct xs_handle *xsh; /* xs daemon handle */
|
||||
struct xenfb_device fb, kbd;
|
||||
size_t fb_len; /* size of framebuffer */
|
||||
};
|
||||
|
||||
static void xenfb_detach_dom(struct xenfb_private *);
|
||||
|
||||
static char *xenfb_path_in_dom(struct xs_handle *xsh,
|
||||
char *buf, size_t size,
|
||||
unsigned domid, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char *domp = xs_get_domain_path(xsh, domid);
|
||||
int n;
|
||||
|
||||
if (domp == NULL)
|
||||
return NULL;
|
||||
|
||||
n = snprintf(buf, size, "%s/", domp);
|
||||
free(domp);
|
||||
if (n >= size)
|
||||
return NULL;
|
||||
|
||||
va_start(ap, fmt);
|
||||
n += vsnprintf(buf + n, size - n, fmt, ap);
|
||||
va_end(ap);
|
||||
if (n >= size)
|
||||
return NULL;
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int xenfb_xs_scanf1(struct xs_handle *xsh,
|
||||
const char *dir, const char *node,
|
||||
const char *fmt, void *dest)
|
||||
{
|
||||
char buf[1024];
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
if (snprintf(buf, sizeof(buf), "%s/%s", dir, node) >= sizeof(buf)) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
p = xs_read(xsh, XBT_NULL, buf, NULL);
|
||||
if (!p) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
ret = sscanf(p, fmt, dest);
|
||||
free(p);
|
||||
if (ret != 1) {
|
||||
errno = EDOM;
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int xenfb_xs_printf(struct xs_handle *xsh,
|
||||
const char *dir, const char *node, char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
char key[1024];
|
||||
char val[1024];
|
||||
int n;
|
||||
|
||||
if (snprintf(key, sizeof(key), "%s/%s", dir, node) >= sizeof(key)) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = vsnprintf(val, sizeof(val), fmt, ap);
|
||||
va_end(ap);
|
||||
if (n >= sizeof(val)) {
|
||||
errno = ENOSPC; /* close enough */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!xs_write(xsh, XBT_NULL, key, val, n))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenfb_device_init(struct xenfb_device *dev,
|
||||
const char *type,
|
||||
struct xenfb_private *xenfb)
|
||||
{
|
||||
dev->devicetype = type;
|
||||
dev->otherend_id = -1;
|
||||
dev->port = -1;
|
||||
dev->xenfb = xenfb;
|
||||
}
|
||||
|
||||
int xenfb_device_set_domain(struct xenfb_device *dev, int domid)
|
||||
{
|
||||
struct xenfb_private *xenfb = dev->xenfb;
|
||||
|
||||
dev->otherend_id = domid;
|
||||
|
||||
if (!xenfb_path_in_dom(xenfb->xsh,
|
||||
dev->otherend, sizeof(dev->otherend),
|
||||
domid, "device/%s/0", dev->devicetype)) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
if (!xenfb_path_in_dom(xenfb->xsh,
|
||||
dev->nodename, sizeof(dev->nodename),
|
||||
0, "backend/%s/%d/0", dev->devicetype, domid)) {
|
||||
errno = ENOENT;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct xenfb *xenfb_new(void)
|
||||
{
|
||||
struct xenfb_private *xenfb = malloc(sizeof(*xenfb));
|
||||
int serrno;
|
||||
|
||||
if (xenfb == NULL)
|
||||
return NULL;
|
||||
|
||||
memset(xenfb, 0, sizeof(*xenfb));
|
||||
xenfb->evt_xch = xenfb->xc = -1;
|
||||
xenfb_device_init(&xenfb->fb, "vfb", xenfb);
|
||||
xenfb_device_init(&xenfb->kbd, "vkbd", xenfb);
|
||||
|
||||
xenfb->evt_xch = xc_evtchn_open();
|
||||
if (xenfb->evt_xch == -1)
|
||||
goto fail;
|
||||
|
||||
xenfb->xc = xc_interface_open();
|
||||
if (xenfb->xc == -1)
|
||||
goto fail;
|
||||
|
||||
xenfb->xsh = xs_daemon_open();
|
||||
if (!xenfb->xsh)
|
||||
goto fail;
|
||||
|
||||
return &xenfb->pub;
|
||||
|
||||
fail:
|
||||
serrno = errno;
|
||||
xenfb_delete(&xenfb->pub);
|
||||
errno = serrno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Remove the backend area in xenbus since the framebuffer really is
|
||||
going away. */
|
||||
void xenfb_teardown(struct xenfb *xenfb_pub)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
|
||||
xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename);
|
||||
xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename);
|
||||
}
|
||||
|
||||
|
||||
void xenfb_delete(struct xenfb *xenfb_pub)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
|
||||
xenfb_detach_dom(xenfb);
|
||||
if (xenfb->xc >= 0)
|
||||
xc_interface_close(xenfb->xc);
|
||||
if (xenfb->evt_xch >= 0)
|
||||
xc_evtchn_close(xenfb->evt_xch);
|
||||
if (xenfb->xsh)
|
||||
xs_daemon_close(xenfb->xsh);
|
||||
free(xenfb);
|
||||
}
|
||||
|
||||
static enum xenbus_state xenfb_read_state(struct xs_handle *xsh,
|
||||
const char *dir)
|
||||
{
|
||||
int ret, state;
|
||||
|
||||
ret = xenfb_xs_scanf1(xsh, dir, "state", "%d", &state);
|
||||
if (ret < 0)
|
||||
return XenbusStateUnknown;
|
||||
|
||||
if ((unsigned)state > XenbusStateClosed)
|
||||
state = XenbusStateUnknown;
|
||||
return state;
|
||||
}
|
||||
|
||||
static int xenfb_switch_state(struct xenfb_device *dev,
|
||||
enum xenbus_state state)
|
||||
{
|
||||
struct xs_handle *xsh = dev->xenfb->xsh;
|
||||
|
||||
if (xenfb_xs_printf(xsh, dev->nodename, "state", "%d", state) < 0)
|
||||
return -1;
|
||||
dev->state = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenfb_wait_for_state(struct xs_handle *xsh, const char *dir,
|
||||
unsigned awaited)
|
||||
{
|
||||
unsigned state, dummy;
|
||||
char **vec;
|
||||
|
||||
for (;;) {
|
||||
state = xenfb_read_state(xsh, dir);
|
||||
if (state < 0)
|
||||
return -1;
|
||||
|
||||
if ((1 << state) & awaited)
|
||||
return state;
|
||||
|
||||
vec = xs_read_watch(xsh, &dummy);
|
||||
if (!vec)
|
||||
return -1;
|
||||
free(vec);
|
||||
}
|
||||
}
|
||||
|
||||
static int xenfb_wait_for_backend_creation(struct xenfb_device *dev)
|
||||
{
|
||||
struct xs_handle *xsh = dev->xenfb->xsh;
|
||||
int state;
|
||||
|
||||
if (!xs_watch(xsh, dev->nodename, ""))
|
||||
return -1;
|
||||
state = xenfb_wait_for_state(xsh, dev->nodename,
|
||||
(1 << XenbusStateInitialising)
|
||||
| (1 << XenbusStateClosed)
|
||||
#if 1 /* TODO fudging state to permit restarting; to be removed */
|
||||
| (1 << XenbusStateInitWait)
|
||||
| (1 << XenbusStateConnected)
|
||||
| (1 << XenbusStateClosing)
|
||||
#endif
|
||||
);
|
||||
xs_unwatch(xsh, dev->nodename, "");
|
||||
|
||||
switch (state) {
|
||||
#if 1
|
||||
case XenbusStateInitWait:
|
||||
case XenbusStateConnected:
|
||||
printf("Fudging state to %d\n", XenbusStateInitialising); /* FIXME */
|
||||
#endif
|
||||
case XenbusStateInitialising:
|
||||
case XenbusStateClosing:
|
||||
case XenbusStateClosed:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenfb_hotplug(struct xenfb_device *dev)
|
||||
{
|
||||
if (xenfb_xs_printf(dev->xenfb->xsh, dev->nodename,
|
||||
"hotplug-status", "connected"))
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenfb_wait_for_frontend_initialised(struct xenfb_device *dev)
|
||||
{
|
||||
switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
|
||||
#if 1 /* TODO fudging state to permit restarting; to be removed */
|
||||
(1 << XenbusStateInitialised)
|
||||
| (1 << XenbusStateConnected)
|
||||
#else
|
||||
1 << XenbusStateInitialised,
|
||||
#endif
|
||||
)) {
|
||||
#if 1
|
||||
case XenbusStateConnected:
|
||||
printf("Fudging state to %d\n", XenbusStateInitialised); /* FIXME */
|
||||
#endif
|
||||
case XenbusStateInitialised:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xenfb_map_fb(struct xenfb_private *xenfb, int domid)
|
||||
{
|
||||
struct xenfb_page *page = xenfb->fb.page;
|
||||
int n_fbmfns;
|
||||
int n_fbdirs;
|
||||
unsigned long *fbmfns;
|
||||
|
||||
n_fbmfns = (xenfb->fb_len + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
|
||||
n_fbdirs = n_fbmfns * sizeof(unsigned long);
|
||||
n_fbdirs = (n_fbdirs + (XC_PAGE_SIZE - 1)) / XC_PAGE_SIZE;
|
||||
|
||||
/*
|
||||
* Bug alert: xc_map_foreign_batch() can fail partly and
|
||||
* return a non-null value. This is a design flaw. When it
|
||||
* happens, we happily continue here, and later crash on
|
||||
* access.
|
||||
*/
|
||||
fbmfns = xc_map_foreign_batch(xenfb->xc, domid,
|
||||
PROT_READ, page->pd, n_fbdirs);
|
||||
if (fbmfns == NULL)
|
||||
return -1;
|
||||
|
||||
xenfb->pub.pixels = xc_map_foreign_batch(xenfb->xc, domid,
|
||||
PROT_READ | PROT_WRITE, fbmfns, n_fbmfns);
|
||||
if (xenfb->pub.pixels == NULL) {
|
||||
munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return munmap(fbmfns, n_fbdirs * XC_PAGE_SIZE);
|
||||
}
|
||||
|
||||
static int xenfb_bind(struct xenfb_device *dev)
|
||||
{
|
||||
struct xenfb_private *xenfb = dev->xenfb;
|
||||
unsigned long mfn;
|
||||
evtchn_port_t evtchn;
|
||||
|
||||
if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "page-ref", "%lu",
|
||||
&mfn) < 0)
|
||||
return -1;
|
||||
if (xenfb_xs_scanf1(xenfb->xsh, dev->otherend, "event-channel", "%u",
|
||||
&evtchn) < 0)
|
||||
return -1;
|
||||
|
||||
dev->port = xc_evtchn_bind_interdomain(xenfb->evt_xch,
|
||||
dev->otherend_id, evtchn);
|
||||
if (dev->port == -1)
|
||||
return -1;
|
||||
|
||||
dev->page = xc_map_foreign_range(xenfb->xc, dev->otherend_id,
|
||||
XC_PAGE_SIZE, PROT_READ | PROT_WRITE, mfn);
|
||||
if (dev->page == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenfb_unbind(struct xenfb_device *dev)
|
||||
{
|
||||
if (dev->page) {
|
||||
munmap(dev->page, XC_PAGE_SIZE);
|
||||
dev->page = NULL;
|
||||
}
|
||||
if (dev->port >= 0) {
|
||||
xc_evtchn_unbind(dev->xenfb->evt_xch, dev->port);
|
||||
dev->port = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int xenfb_wait_for_frontend_connected(struct xenfb_device *dev)
|
||||
{
|
||||
switch (xenfb_wait_for_state(dev->xenfb->xsh, dev->otherend,
|
||||
1 << XenbusStateConnected)) {
|
||||
case XenbusStateConnected:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void xenfb_dev_fatal(struct xenfb_device *dev, int err,
|
||||
const char *fmt, ...)
|
||||
{
|
||||
struct xs_handle *xsh = dev->xenfb->xsh;
|
||||
va_list ap;
|
||||
char errdir[80];
|
||||
char buf[1024];
|
||||
int n;
|
||||
|
||||
fprintf(stderr, "%s ", dev->nodename); /* somewhat crude */
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
if (err)
|
||||
fprintf(stderr, " (%s)", strerror(err));
|
||||
putc('\n', stderr);
|
||||
|
||||
if (!xenfb_path_in_dom(xsh, errdir, sizeof(errdir), 0,
|
||||
"error/%s", dev->nodename))
|
||||
goto out; /* FIXME complain */
|
||||
|
||||
va_start(ap, fmt);
|
||||
n = snprintf(buf, sizeof(buf), "%d ", err);
|
||||
snprintf(buf + n, sizeof(buf) - n, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (xenfb_xs_printf(xsh, buf, "error", "%s", buf) < 0)
|
||||
goto out; /* FIXME complain */
|
||||
|
||||
out:
|
||||
xenfb_switch_state(dev, XenbusStateClosing);
|
||||
}
|
||||
|
||||
int xenfb_attach_dom(struct xenfb *xenfb_pub, int domid)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
struct xs_handle *xsh = xenfb->xsh;
|
||||
int val, serrno;
|
||||
struct xenfb_page *fb_page;
|
||||
|
||||
xenfb_detach_dom(xenfb);
|
||||
|
||||
xenfb_device_set_domain(&xenfb->fb, domid);
|
||||
xenfb_device_set_domain(&xenfb->kbd, domid);
|
||||
|
||||
if (xenfb_wait_for_backend_creation(&xenfb->fb) < 0)
|
||||
goto error;
|
||||
if (xenfb_wait_for_backend_creation(&xenfb->kbd) < 0)
|
||||
goto error;
|
||||
|
||||
if (xenfb_xs_printf(xsh, xenfb->kbd.nodename, "feature-abs-pointer", "1"))
|
||||
goto error;
|
||||
if (xenfb_switch_state(&xenfb->fb, XenbusStateInitWait))
|
||||
goto error;
|
||||
if (xenfb_switch_state(&xenfb->kbd, XenbusStateInitWait))
|
||||
goto error;
|
||||
|
||||
if (xenfb_hotplug(&xenfb->fb) < 0)
|
||||
goto error;
|
||||
if (xenfb_hotplug(&xenfb->kbd) < 0)
|
||||
goto error;
|
||||
|
||||
if (!xs_watch(xsh, xenfb->fb.otherend, ""))
|
||||
goto error;
|
||||
if (!xs_watch(xsh, xenfb->kbd.otherend, ""))
|
||||
goto error;
|
||||
|
||||
if (xenfb_wait_for_frontend_initialised(&xenfb->fb) < 0)
|
||||
goto error;
|
||||
if (xenfb_wait_for_frontend_initialised(&xenfb->kbd) < 0)
|
||||
goto error;
|
||||
|
||||
if (xenfb_bind(&xenfb->fb) < 0)
|
||||
goto error;
|
||||
if (xenfb_bind(&xenfb->kbd) < 0)
|
||||
goto error;
|
||||
|
||||
if (xenfb_xs_scanf1(xsh, xenfb->fb.otherend, "feature-update",
|
||||
"%d", &val) < 0)
|
||||
val = 0;
|
||||
if (!val) {
|
||||
errno = ENOTSUP;
|
||||
goto error;
|
||||
}
|
||||
xenfb_xs_printf(xsh, xenfb->fb.nodename, "request-update", "1");
|
||||
|
||||
/* TODO check for permitted ranges */
|
||||
fb_page = xenfb->fb.page;
|
||||
xenfb->pub.depth = fb_page->depth;
|
||||
xenfb->pub.width = fb_page->width;
|
||||
xenfb->pub.height = fb_page->height;
|
||||
/* TODO check for consistency with the above */
|
||||
xenfb->fb_len = fb_page->mem_length;
|
||||
xenfb->pub.row_stride = fb_page->line_length;
|
||||
|
||||
if (xenfb_map_fb(xenfb, domid) < 0)
|
||||
goto error;
|
||||
|
||||
if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected))
|
||||
goto error;
|
||||
if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected))
|
||||
goto error;
|
||||
|
||||
if (xenfb_wait_for_frontend_connected(&xenfb->kbd) < 0)
|
||||
goto error;
|
||||
if (xenfb_xs_scanf1(xsh, xenfb->kbd.otherend, "request-abs-pointer",
|
||||
"%d", &val) < 0)
|
||||
val = 0;
|
||||
xenfb->pub.abs_pointer_wanted = val;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
serrno = errno;
|
||||
xenfb_detach_dom(xenfb);
|
||||
xenfb_dev_fatal(&xenfb->fb, serrno, "on fire");
|
||||
xenfb_dev_fatal(&xenfb->kbd, serrno, "on fire");
|
||||
errno = serrno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void xenfb_detach_dom(struct xenfb_private *xenfb)
|
||||
{
|
||||
xenfb_unbind(&xenfb->fb);
|
||||
xenfb_unbind(&xenfb->kbd);
|
||||
if (xenfb->pub.pixels) {
|
||||
munmap(xenfb->pub.pixels, xenfb->fb_len);
|
||||
xenfb->pub.pixels = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void xenfb_on_fb_event(struct xenfb_private *xenfb)
|
||||
{
|
||||
uint32_t prod, cons;
|
||||
struct xenfb_page *page = xenfb->fb.page;
|
||||
|
||||
prod = page->out_prod;
|
||||
if (prod == page->out_cons)
|
||||
return;
|
||||
rmb(); /* ensure we see ring contents up to prod */
|
||||
for (cons = page->out_cons; cons != prod; cons++) {
|
||||
union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
|
||||
|
||||
switch (event->type) {
|
||||
case XENFB_TYPE_UPDATE:
|
||||
if (xenfb->pub.update)
|
||||
xenfb->pub.update(&xenfb->pub,
|
||||
event->update.x, event->update.y,
|
||||
event->update.width, event->update.height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mb(); /* ensure we're done with ring contents */
|
||||
page->out_cons = cons;
|
||||
xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);
|
||||
}
|
||||
|
||||
static void xenfb_on_kbd_event(struct xenfb_private *xenfb)
|
||||
{
|
||||
struct xenkbd_page *page = xenfb->kbd.page;
|
||||
|
||||
/* We don't understand any keyboard events, so just ignore them. */
|
||||
if (page->out_prod == page->out_cons)
|
||||
return;
|
||||
page->out_cons = page->out_prod;
|
||||
xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
|
||||
}
|
||||
|
||||
static int xenfb_on_state_change(struct xenfb_device *dev)
|
||||
{
|
||||
enum xenbus_state state;
|
||||
|
||||
state = xenfb_read_state(dev->xenfb->xsh, dev->otherend);
|
||||
|
||||
switch (state) {
|
||||
case XenbusStateUnknown:
|
||||
/* There was an error reading the frontend state. The
|
||||
domain has probably gone away; in any case, there's
|
||||
not much point in us continuing. */
|
||||
return -1;
|
||||
case XenbusStateInitialising:
|
||||
case XenbusStateInitWait:
|
||||
case XenbusStateInitialised:
|
||||
case XenbusStateConnected:
|
||||
break;
|
||||
case XenbusStateClosing:
|
||||
xenfb_unbind(dev);
|
||||
xenfb_switch_state(dev, state);
|
||||
break;
|
||||
case XenbusStateClosed:
|
||||
xenfb_switch_state(dev, state);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 0 normally, -1 on error, or -2 if the domain went away. */
|
||||
int xenfb_poll(struct xenfb *xenfb_pub, fd_set *readfds)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
evtchn_port_t port;
|
||||
unsigned dummy;
|
||||
char **vec;
|
||||
int r;
|
||||
|
||||
if (FD_ISSET(xc_evtchn_fd(xenfb->evt_xch), readfds)) {
|
||||
port = xc_evtchn_pending(xenfb->evt_xch);
|
||||
if (port == -1)
|
||||
return -1;
|
||||
|
||||
if (port == xenfb->fb.port)
|
||||
xenfb_on_fb_event(xenfb);
|
||||
else if (port == xenfb->kbd.port)
|
||||
xenfb_on_kbd_event(xenfb);
|
||||
|
||||
if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET(xs_fileno(xenfb->xsh), readfds)) {
|
||||
vec = xs_read_watch(xenfb->xsh, &dummy);
|
||||
free(vec);
|
||||
r = xenfb_on_state_change(&xenfb->fb);
|
||||
if (r == 0)
|
||||
r = xenfb_on_state_change(&xenfb->kbd);
|
||||
if (r == -1)
|
||||
return -2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xenfb_select_fds(struct xenfb *xenfb_pub, fd_set *readfds)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
int fd1 = xc_evtchn_fd(xenfb->evt_xch);
|
||||
int fd2 = xs_fileno(xenfb->xsh);
|
||||
|
||||
FD_SET(fd1, readfds);
|
||||
FD_SET(fd2, readfds);
|
||||
return fd1 > fd2 ? fd1 + 1 : fd2 + 1;
|
||||
}
|
||||
|
||||
static int xenfb_kbd_event(struct xenfb_private *xenfb,
|
||||
union xenkbd_in_event *event)
|
||||
{
|
||||
uint32_t prod;
|
||||
struct xenkbd_page *page = xenfb->kbd.page;
|
||||
|
||||
if (xenfb->kbd.state != XenbusStateConnected)
|
||||
return 0;
|
||||
|
||||
prod = page->in_prod;
|
||||
if (prod - page->in_cons == XENKBD_IN_RING_LEN) {
|
||||
errno = EAGAIN;
|
||||
return -1;
|
||||
}
|
||||
|
||||
mb(); /* ensure ring space available */
|
||||
XENKBD_IN_RING_REF(page, prod) = *event;
|
||||
wmb(); /* ensure ring contents visible */
|
||||
page->in_prod = prod + 1;
|
||||
return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);
|
||||
}
|
||||
|
||||
int xenfb_send_key(struct xenfb *xenfb_pub, bool down, int keycode)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
union xenkbd_in_event event;
|
||||
|
||||
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
|
||||
event.type = XENKBD_TYPE_KEY;
|
||||
event.key.pressed = down ? 1 : 0;
|
||||
event.key.keycode = keycode;
|
||||
|
||||
return xenfb_kbd_event(xenfb, &event);
|
||||
}
|
||||
|
||||
int xenfb_send_motion(struct xenfb *xenfb_pub, int rel_x, int rel_y)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
union xenkbd_in_event event;
|
||||
|
||||
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
|
||||
event.type = XENKBD_TYPE_MOTION;
|
||||
event.motion.rel_x = rel_x;
|
||||
event.motion.rel_y = rel_y;
|
||||
|
||||
return xenfb_kbd_event(xenfb, &event);
|
||||
}
|
||||
|
||||
int xenfb_send_position(struct xenfb *xenfb_pub, int abs_x, int abs_y)
|
||||
{
|
||||
struct xenfb_private *xenfb = (struct xenfb_private *)xenfb_pub;
|
||||
union xenkbd_in_event event;
|
||||
|
||||
memset(&event, 0, XENKBD_IN_EVENT_SIZE);
|
||||
event.type = XENKBD_TYPE_POS;
|
||||
event.pos.abs_x = abs_x;
|
||||
event.pos.abs_y = abs_y;
|
||||
|
||||
return xenfb_kbd_event(xenfb, &event);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef _XENFB_H_
|
||||
#define _XENFB_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
struct xenfb
|
||||
{
|
||||
void *pixels;
|
||||
|
||||
int row_stride;
|
||||
int depth;
|
||||
int width;
|
||||
int height;
|
||||
int abs_pointer_wanted;
|
||||
|
||||
void *user_data;
|
||||
|
||||
void (*update)(struct xenfb *xenfb, int x, int y, int width, int height);
|
||||
};
|
||||
|
||||
struct xenfb *xenfb_new(void);
|
||||
void xenfb_delete(struct xenfb *xenfb);
|
||||
void xenfb_teardown(struct xenfb *xenfb);
|
||||
|
||||
int xenfb_attach_dom(struct xenfb *xenfb, int domid);
|
||||
|
||||
int xenfb_select_fds(struct xenfb *xenfb, fd_set *readfds);
|
||||
int xenfb_poll(struct xenfb *xenfb, fd_set *readfds);
|
||||
|
||||
int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode);
|
||||
int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y);
|
||||
int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* fbif.h -- Xen virtual frame buffer device
|
||||
*
|
||||
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_IO_FBIF_H__
|
||||
#define __XEN_PUBLIC_IO_FBIF_H__
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
/* Out events (frontend -> backend) */
|
||||
|
||||
/*
|
||||
* Out events may be sent only when requested by backend, and receipt
|
||||
* of an unknown out event is an error.
|
||||
*/
|
||||
|
||||
/* Event type 1 currently not used */
|
||||
/*
|
||||
* Framebuffer update notification event
|
||||
* Capable frontend sets feature-update in xenstore.
|
||||
* Backend requests it by setting request-update in xenstore.
|
||||
*/
|
||||
#define XENFB_TYPE_UPDATE 2
|
||||
|
||||
struct xenfb_update
|
||||
{
|
||||
__u8 type; /* XENFB_TYPE_UPDATE */
|
||||
__s32 x; /* source x */
|
||||
__s32 y; /* source y */
|
||||
__s32 width; /* rect width */
|
||||
__s32 height; /* rect height */
|
||||
};
|
||||
|
||||
#define XENFB_OUT_EVENT_SIZE 40
|
||||
|
||||
union xenfb_out_event
|
||||
{
|
||||
__u8 type;
|
||||
struct xenfb_update update;
|
||||
char pad[XENFB_OUT_EVENT_SIZE];
|
||||
};
|
||||
|
||||
/* In events (backend -> frontend) */
|
||||
|
||||
/*
|
||||
* Frontends should ignore unknown in events.
|
||||
* No in events currently defined.
|
||||
*/
|
||||
|
||||
#define XENFB_IN_EVENT_SIZE 40
|
||||
|
||||
union xenfb_in_event
|
||||
{
|
||||
__u8 type;
|
||||
char pad[XENFB_IN_EVENT_SIZE];
|
||||
};
|
||||
|
||||
/* shared page */
|
||||
|
||||
#define XENFB_IN_RING_SIZE 1024
|
||||
#define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE)
|
||||
#define XENFB_IN_RING_OFFS 1024
|
||||
#define XENFB_IN_RING(page) \
|
||||
((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS))
|
||||
#define XENFB_IN_RING_REF(page, idx) \
|
||||
(XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN])
|
||||
|
||||
#define XENFB_OUT_RING_SIZE 2048
|
||||
#define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE)
|
||||
#define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE)
|
||||
#define XENFB_OUT_RING(page) \
|
||||
((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS))
|
||||
#define XENFB_OUT_RING_REF(page, idx) \
|
||||
(XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN])
|
||||
|
||||
struct xenfb_page
|
||||
{
|
||||
__u32 in_cons, in_prod;
|
||||
__u32 out_cons, out_prod;
|
||||
|
||||
__s32 width; /* the width of the framebuffer (in pixels) */
|
||||
__s32 height; /* the height of the framebuffer (in pixels) */
|
||||
__u32 line_length; /* the length of a row of pixels (in bytes) */
|
||||
__u32 mem_length; /* the length of the framebuffer (in bytes) */
|
||||
__u8 depth; /* the depth of a pixel (in bits) */
|
||||
|
||||
/*
|
||||
* Framebuffer page directory
|
||||
*
|
||||
* Each directory page holds PAGE_SIZE / sizeof(*pd)
|
||||
* framebuffer pages, and can thus map up to PAGE_SIZE *
|
||||
* PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and
|
||||
* sizeof(unsigned long) == 4, that's 4 Megs. Two directory
|
||||
* pages should be enough for a while.
|
||||
*/
|
||||
unsigned long pd[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* Wart: xenkbd needs to know resolution. Put it here until a better
|
||||
* solution is found, but don't leak it to the backend.
|
||||
*/
|
||||
#ifdef __KERNEL__
|
||||
#define XENFB_WIDTH 800
|
||||
#define XENFB_HEIGHT 600
|
||||
#define XENFB_DEPTH 32
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* kbdif.h -- Xen virtual keyboard/mouse
|
||||
*
|
||||
* Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
|
||||
* Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#ifndef __XEN_PUBLIC_IO_KBDIF_H__
|
||||
#define __XEN_PUBLIC_IO_KBDIF_H__
|
||||
|
||||
#include <asm/types.h>
|
||||
|
||||
/* In events (backend -> frontend) */
|
||||
|
||||
/*
|
||||
* Frontends should ignore unknown in events.
|
||||
*/
|
||||
|
||||
/* Pointer movement event */
|
||||
#define XENKBD_TYPE_MOTION 1
|
||||
/* Event type 2 currently not used */
|
||||
/* Key event (includes pointer buttons) */
|
||||
#define XENKBD_TYPE_KEY 3
|
||||
/*
|
||||
* Pointer position event
|
||||
* Capable backend sets feature-abs-pointer in xenstore.
|
||||
* Frontend requests ot instead of XENKBD_TYPE_MOTION by setting
|
||||
* request-abs-update in xenstore.
|
||||
*/
|
||||
#define XENKBD_TYPE_POS 4
|
||||
|
||||
struct xenkbd_motion
|
||||
{
|
||||
__u8 type; /* XENKBD_TYPE_MOTION */
|
||||
__s32 rel_x; /* relative X motion */
|
||||
__s32 rel_y; /* relative Y motion */
|
||||
};
|
||||
|
||||
struct xenkbd_key
|
||||
{
|
||||
__u8 type; /* XENKBD_TYPE_KEY */
|
||||
__u8 pressed; /* 1 if pressed; 0 otherwise */
|
||||
__u32 keycode; /* KEY_* from linux/input.h */
|
||||
};
|
||||
|
||||
struct xenkbd_position
|
||||
{
|
||||
__u8 type; /* XENKBD_TYPE_POS */
|
||||
__s32 abs_x; /* absolute X position (in FB pixels) */
|
||||
__s32 abs_y; /* absolute Y position (in FB pixels) */
|
||||
};
|
||||
|
||||
#define XENKBD_IN_EVENT_SIZE 40
|
||||
|
||||
union xenkbd_in_event
|
||||
{
|
||||
__u8 type;
|
||||
struct xenkbd_motion motion;
|
||||
struct xenkbd_key key;
|
||||
struct xenkbd_position pos;
|
||||
char pad[XENKBD_IN_EVENT_SIZE];
|
||||
};
|
||||
|
||||
/* Out events (frontend -> backend) */
|
||||
|
||||
/*
|
||||
* Out events may be sent only when requested by backend, and receipt
|
||||
* of an unknown out event is an error.
|
||||
* No out events currently defined.
|
||||
*/
|
||||
|
||||
#define XENKBD_OUT_EVENT_SIZE 40
|
||||
|
||||
union xenkbd_out_event
|
||||
{
|
||||
__u8 type;
|
||||
char pad[XENKBD_OUT_EVENT_SIZE];
|
||||
};
|
||||
|
||||
/* shared page */
|
||||
|
||||
#define XENKBD_IN_RING_SIZE 2048
|
||||
#define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE)
|
||||
#define XENKBD_IN_RING_OFFS 1024
|
||||
#define XENKBD_IN_RING(page) \
|
||||
((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS))
|
||||
#define XENKBD_IN_RING_REF(page, idx) \
|
||||
(XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN])
|
||||
|
||||
#define XENKBD_OUT_RING_SIZE 1024
|
||||
#define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE)
|
||||
#define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE)
|
||||
#define XENKBD_OUT_RING(page) \
|
||||
((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS))
|
||||
#define XENKBD_OUT_RING_REF(page, idx) \
|
||||
(XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN])
|
||||
|
||||
struct xenkbd_page
|
||||
{
|
||||
__u32 in_cons, in_prod;
|
||||
__u32 out_cons, out_prod;
|
||||
};
|
||||
|
||||
#endif
|
||||