diff options
| -rw-r--r-- | drivers/input/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/input/Makefile | 2 | ||||
| -rw-r--r-- | drivers/input/xen-kbdfront.c | 340 | ||||
| -rw-r--r-- | drivers/video/Kconfig | 14 | ||||
| -rw-r--r-- | drivers/video/Makefile | 1 | ||||
| -rw-r--r-- | drivers/video/xen-fbfront.c | 550 | ||||
| -rw-r--r-- | include/xen/interface/io/fbif.h | 124 | ||||
| -rw-r--r-- | include/xen/interface/io/kbdif.h | 114 |
8 files changed, 1154 insertions, 0 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 9dea14db72..5f9d860925 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig | |||
| @@ -149,6 +149,15 @@ config INPUT_APMPOWER | |||
| 149 | To compile this driver as a module, choose M here: the | 149 | To compile this driver as a module, choose M here: the |
| 150 | module will be called apm-power. | 150 | module will be called apm-power. |
| 151 | 151 | ||
| 152 | config XEN_KBDDEV_FRONTEND | ||
| 153 | tristate "Xen virtual keyboard and mouse support" | ||
| 154 | depends on XEN_FBDEV_FRONTEND | ||
| 155 | default y | ||
| 156 | help | ||
| 157 | This driver implements the front-end of the Xen virtual | ||
| 158 | keyboard and mouse device driver. It communicates with a back-end | ||
| 159 | in another domain. | ||
| 160 | |||
| 152 | comment "Input Device Drivers" | 161 | comment "Input Device Drivers" |
| 153 | 162 | ||
| 154 | source "drivers/input/keyboard/Kconfig" | 163 | source "drivers/input/keyboard/Kconfig" |
diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 2ae87b19ca..98c4f9a778 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile | |||
| @@ -23,3 +23,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ | |||
| 23 | obj-$(CONFIG_INPUT_MISC) += misc/ | 23 | obj-$(CONFIG_INPUT_MISC) += misc/ |
| 24 | 24 | ||
| 25 | obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o | 25 | obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o |
| 26 | |||
| 27 | obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o | ||
diff --git a/drivers/input/xen-kbdfront.c b/drivers/input/xen-kbdfront.c new file mode 100644 index 0000000000..0f47f4697c --- /dev/null +++ b/drivers/input/xen-kbdfront.c | |||
| @@ -0,0 +1,340 @@ | |||
| 1 | /* | ||
| 2 | * Xen para-virtual input device | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> | ||
| 5 | * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> | ||
| 6 | * | ||
| 7 | * Based on linux/drivers/input/mouse/sermouse.c | ||
| 8 | * | ||
| 9 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 10 | * License. See the file COPYING in the main directory of this archive for | ||
| 11 | * more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | /* | ||
| 15 | * TODO: | ||
| 16 | * | ||
| 17 | * Switch to grant tables together with xen-fbfront.c. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/errno.h> | ||
| 22 | #include <linux/module.h> | ||
| 23 | #include <linux/input.h> | ||
| 24 | #include <asm/xen/hypervisor.h> | ||
| 25 | #include <xen/events.h> | ||
| 26 | #include <xen/page.h> | ||
| 27 | #include <xen/interface/io/fbif.h> | ||
| 28 | #include <xen/interface/io/kbdif.h> | ||
| 29 | #include <xen/xenbus.h> | ||
| 30 | |||
| 31 | struct xenkbd_info { | ||
| 32 | struct input_dev *kbd; | ||
| 33 | struct input_dev *ptr; | ||
| 34 | struct xenkbd_page *page; | ||
| 35 | int irq; | ||
| 36 | struct xenbus_device *xbdev; | ||
| 37 | char phys[32]; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static int xenkbd_remove(struct xenbus_device *); | ||
| 41 | static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *); | ||
| 42 | static void xenkbd_disconnect_backend(struct xenkbd_info *); | ||
| 43 | |||
| 44 | /* | ||
| 45 | * Note: if you need to send out events, see xenfb_do_update() for how | ||
| 46 | * to do that. | ||
| 47 | */ | ||
| 48 | |||
| 49 | static irqreturn_t input_handler(int rq, void *dev_id) | ||
| 50 | { | ||
| 51 | struct xenkbd_info *info = dev_id; | ||
| 52 | struct xenkbd_page *page = info->page; | ||
| 53 | __u32 cons, prod; | ||
| 54 | |||
| 55 | prod = page->in_prod; | ||
| 56 | if (prod == page->in_cons) | ||
| 57 | return IRQ_HANDLED; | ||
| 58 | rmb(); /* ensure we see ring contents up to prod */ | ||
| 59 | for (cons = page->in_cons; cons != prod; cons++) { | ||
| 60 | union xenkbd_in_event *event; | ||
| 61 | struct input_dev *dev; | ||
| 62 | event = &XENKBD_IN_RING_REF(page, cons); | ||
| 63 | |||
| 64 | dev = info->ptr; | ||
| 65 | switch (event->type) { | ||
| 66 | case XENKBD_TYPE_MOTION: | ||
| 67 | input_report_rel(dev, REL_X, event->motion.rel_x); | ||
| 68 | input_report_rel(dev, REL_Y, event->motion.rel_y); | ||
| 69 | break; | ||
| 70 | case XENKBD_TYPE_KEY: | ||
| 71 | dev = NULL; | ||
| 72 | if (test_bit(event->key.keycode, info->kbd->keybit)) | ||
| 73 | dev = info->kbd; | ||
| 74 | if (test_bit(event->key.keycode, info->ptr->keybit)) | ||
| 75 | dev = info->ptr; | ||
| 76 | if (dev) | ||
| 77 | input_report_key(dev, event->key.keycode, | ||
| 78 | event->key.pressed); | ||
| 79 | else | ||
| 80 | printk(KERN_WARNING | ||
| 81 | "xenkbd: unhandled keycode 0x%x\n", | ||
| 82 | event->key.keycode); | ||
| 83 | break; | ||
| 84 | case XENKBD_TYPE_POS: | ||
| 85 | input_report_abs(dev, ABS_X, event->pos.abs_x); | ||
| 86 | input_report_abs(dev, ABS_Y, event->pos.abs_y); | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | if (dev) | ||
| 90 | input_sync(dev); | ||
| 91 | } | ||
| 92 | mb(); /* ensure we got ring contents */ | ||
| 93 | page->in_cons = cons; | ||
| 94 | notify_remote_via_irq(info->irq); | ||
| 95 | |||
| 96 | return IRQ_HANDLED; | ||
| 97 | } | ||
| 98 | |||
| 99 | static int __devinit xenkbd_probe(struct xenbus_device *dev, | ||
| 100 | const struct xenbus_device_id *id) | ||
| 101 | { | ||
| 102 | int ret, i; | ||
| 103 | struct xenkbd_info *info; | ||
| 104 | struct input_dev *kbd, *ptr; | ||
| 105 | |||
| 106 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
| 107 | if (!info) { | ||
| 108 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); | ||
| 109 | return -ENOMEM; | ||
| 110 | } | ||
| 111 | dev->dev.driver_data = info; | ||
| 112 | info->xbdev = dev; | ||
| 113 | info->irq = -1; | ||
| 114 | snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename); | ||
| 115 | |||
| 116 | info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
| 117 | if (!info->page) | ||
| 118 | goto error_nomem; | ||
| 119 | |||
| 120 | /* keyboard */ | ||
| 121 | kbd = input_allocate_device(); | ||
| 122 | if (!kbd) | ||
| 123 | goto error_nomem; | ||
| 124 | kbd->name = "Xen Virtual Keyboard"; | ||
| 125 | kbd->phys = info->phys; | ||
| 126 | kbd->id.bustype = BUS_PCI; | ||
| 127 | kbd->id.vendor = 0x5853; | ||
| 128 | kbd->id.product = 0xffff; | ||
| 129 | kbd->evbit[0] = BIT(EV_KEY); | ||
| 130 | for (i = KEY_ESC; i < KEY_UNKNOWN; i++) | ||
| 131 | set_bit(i, kbd->keybit); | ||
| 132 | for (i = KEY_OK; i < KEY_MAX; i++) | ||
| 133 | set_bit(i, kbd->keybit); | ||
| 134 | |||
| 135 | ret = input_register_device(kbd); | ||
| 136 | if (ret) { | ||
| 137 | input_free_device(kbd); | ||
| 138 | xenbus_dev_fatal(dev, ret, "input_register_device(kbd)"); | ||
| 139 | goto error; | ||
| 140 | } | ||
| 141 | info->kbd = kbd; | ||
| 142 | |||
| 143 | /* pointing device */ | ||
| 144 | ptr = input_allocate_device(); | ||
| 145 | if (!ptr) | ||
| 146 | goto error_nomem; | ||
| 147 | ptr->name = "Xen Virtual Pointer"; | ||
| 148 | ptr->phys = info->phys; | ||
| 149 | ptr->id.bustype = BUS_PCI; | ||
| 150 | ptr->id.vendor = 0x5853; | ||
| 151 | ptr->id.product = 0xfffe; | ||
| 152 | ptr->evbit[0] = BIT(EV_KEY) | BIT(EV_REL) | BIT(EV_ABS); | ||
| 153 | for (i = BTN_LEFT; i <= BTN_TASK; i++) | ||
| 154 | set_bit(i, ptr->keybit); | ||
| 155 | ptr->relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
| 156 | input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0); | ||
| 157 | input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0); | ||
| 158 | |||
| 159 | ret = input_register_device(ptr); | ||
| 160 | if (ret) { | ||
| 161 | input_free_device(ptr); | ||
| 162 | xenbus_dev_fatal(dev, ret, "input_register_device(ptr)"); | ||
| 163 | goto error; | ||
| 164 | } | ||
| 165 | info->ptr = ptr; | ||
| 166 | |||
| 167 | ret = xenkbd_connect_backend(dev, info); | ||
| 168 | if (ret < 0) | ||
| 169 | goto error; | ||
| 170 | |||
| 171 | return 0; | ||
| 172 | |||
| 173 | error_nomem: | ||
| 174 | ret = -ENOMEM; | ||
| 175 | xenbus_dev_fatal(dev, ret, "allocating device memory"); | ||
| 176 | error: | ||
| 177 | xenkbd_remove(dev); | ||
| 178 | return ret; | ||
| 179 | } | ||
| 180 | |||
| 181 | static int xenkbd_resume(struct xenbus_device *dev) | ||
| 182 | { | ||
| 183 | struct xenkbd_info *info = dev->dev.driver_data; | ||
| 184 | |||
| 185 | xenkbd_disconnect_backend(info); | ||
| 186 | memset(info->page, 0, PAGE_SIZE); | ||
| 187 | return xenkbd_connect_backend(dev, info); | ||
| 188 | } | ||
| 189 | |||
| 190 | static int xenkbd_remove(struct xenbus_device *dev) | ||
| 191 | { | ||
| 192 | struct xenkbd_info *info = dev->dev.driver_data; | ||
| 193 | |||
| 194 | xenkbd_disconnect_backend(info); | ||
| 195 | if (info->kbd) | ||
| 196 | input_unregister_device(info->kbd); | ||
| 197 | if (info->ptr) | ||
| 198 | input_unregister_device(info->ptr); | ||
| 199 | free_page((unsigned long)info->page); | ||
| 200 | kfree(info); | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | static int xenkbd_connect_backend(struct xenbus_device *dev, | ||
| 205 | struct xenkbd_info *info) | ||
| 206 | { | ||
| 207 | int ret, evtchn; | ||
| 208 | struct xenbus_transaction xbt; | ||
| 209 | |||
| 210 | ret = xenbus_alloc_evtchn(dev, &evtchn); | ||
| 211 | if (ret) | ||
| 212 | return ret; | ||
| 213 | ret = bind_evtchn_to_irqhandler(evtchn, input_handler, | ||
| 214 | 0, dev->devicetype, info); | ||
| 215 | if (ret < 0) { | ||
| 216 | xenbus_free_evtchn(dev, evtchn); | ||
| 217 | xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); | ||
| 218 | return ret; | ||
| 219 | } | ||
| 220 | info->irq = ret; | ||
| 221 | |||
| 222 | again: | ||
| 223 | ret = xenbus_transaction_start(&xbt); | ||
| 224 | if (ret) { | ||
| 225 | xenbus_dev_fatal(dev, ret, "starting transaction"); | ||
| 226 | return ret; | ||
| 227 | } | ||
| 228 | ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", | ||
| 229 | virt_to_mfn(info->page)); | ||
| 230 | if (ret) | ||
| 231 | goto error_xenbus; | ||
| 232 | ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", | ||
| 233 | evtchn); | ||
| 234 | if (ret) | ||
| 235 | goto error_xenbus; | ||
| 236 | ret = xenbus_transaction_end(xbt, 0); | ||
| 237 | if (ret) { | ||
| 238 | if (ret == -EAGAIN) | ||
| 239 | goto again; | ||
| 240 | xenbus_dev_fatal(dev, ret, "completing transaction"); | ||
| 241 | return ret; | ||
| 242 | } | ||
| 243 | |||
| 244 | xenbus_switch_state(dev, XenbusStateInitialised); | ||
| 245 | return 0; | ||
| 246 | |||
| 247 | error_xenbus: | ||
| 248 | xenbus_transaction_end(xbt, 1); | ||
| 249 | xenbus_dev_fatal(dev, ret, "writing xenstore"); | ||
| 250 | return ret; | ||
| 251 | } | ||
| 252 | |||
| 253 | static void xenkbd_disconnect_backend(struct xenkbd_info *info) | ||
| 254 | { | ||
| 255 | if (info->irq >= 0) | ||
| 256 | unbind_from_irqhandler(info->irq, info); | ||
| 257 | info->irq = -1; | ||
| 258 | } | ||
| 259 | |||
| 260 | static void xenkbd_backend_changed(struct xenbus_device *dev, | ||
| 261 | enum xenbus_state backend_state) | ||
| 262 | { | ||
| 263 | struct xenkbd_info *info = dev->dev.driver_data; | ||
| 264 | int ret, val; | ||
| 265 | |||
| 266 | switch (backend_state) { | ||
| 267 | case XenbusStateInitialising: | ||
| 268 | case XenbusStateInitialised: | ||
| 269 | case XenbusStateUnknown: | ||
| 270 | case XenbusStateClosed: | ||
| 271 | break; | ||
| 272 | |||
| 273 | case XenbusStateInitWait: | ||
| 274 | InitWait: | ||
| 275 | ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend, | ||
| 276 | "feature-abs-pointer", "%d", &val); | ||
| 277 | if (ret < 0) | ||
| 278 | val = 0; | ||
| 279 | if (val) { | ||
| 280 | ret = xenbus_printf(XBT_NIL, info->xbdev->nodename, | ||
| 281 | "request-abs-pointer", "1"); | ||
| 282 | if (ret) | ||
| 283 | printk(KERN_WARNING | ||
| 284 | "xenkbd: can't request abs-pointer"); | ||
| 285 | } | ||
| 286 | xenbus_switch_state(dev, XenbusStateConnected); | ||
| 287 | break; | ||
| 288 | |||
| 289 | case XenbusStateConnected: | ||
| 290 | /* | ||
| 291 | * Work around xenbus race condition: If backend goes | ||
| 292 | * through InitWait to Connected fast enough, we can | ||
| 293 | * get Connected twice here. | ||
| 294 | */ | ||
| 295 | if (dev->state != XenbusStateConnected) | ||
| 296 | goto InitWait; /* no InitWait seen yet, fudge it */ | ||
| 297 | break; | ||
| 298 | |||
| 299 | case XenbusStateClosing: | ||
| 300 | xenbus_frontend_closed(dev); | ||
| 301 | break; | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | static struct xenbus_device_id xenkbd_ids[] = { | ||
| 306 | { "vkbd" }, | ||
| 307 | { "" } | ||
| 308 | }; | ||
| 309 | |||
| 310 | static struct xenbus_driver xenkbd = { | ||
| 311 | .name = "vkbd", | ||
| 312 | .owner = THIS_MODULE, | ||
| 313 | .ids = xenkbd_ids, | ||
| 314 | .probe = xenkbd_probe, | ||
| 315 | .remove = xenkbd_remove, | ||
| 316 | .resume = xenkbd_resume, | ||
| 317 | .otherend_changed = xenkbd_backend_changed, | ||
| 318 | }; | ||
| 319 | |||
| 320 | static int __init xenkbd_init(void) | ||
| 321 | { | ||
| 322 | if (!is_running_on_xen()) | ||
| 323 | return -ENODEV; | ||
| 324 | |||
| 325 | /* Nothing to do if running in dom0. */ | ||
| 326 | if (is_initial_xendomain()) | ||
| 327 | return -ENODEV; | ||
| 328 | |||
| 329 | return xenbus_register_frontend(&xenkbd); | ||
| 330 | } | ||
| 331 | |||
| 332 | static void __exit xenkbd_cleanup(void) | ||
| 333 | { | ||
| 334 | xenbus_unregister_driver(&xenkbd); | ||
| 335 | } | ||
| 336 | |||
| 337 | module_init(xenkbd_init); | ||
| 338 | module_exit(xenkbd_cleanup); | ||
| 339 | |||
| 340 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 1bd5fb3023..e3dc8f8d0c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
| @@ -1930,6 +1930,20 @@ config FB_VIRTUAL | |||
| 1930 | 1930 | ||
| 1931 | If unsure, say N. | 1931 | If unsure, say N. |
| 1932 | 1932 | ||
| 1933 | config XEN_FBDEV_FRONTEND | ||
| 1934 | tristate "Xen virtual frame buffer support" | ||
| 1935 | depends on FB && XEN | ||
| 1936 | select FB_SYS_FILLRECT | ||
| 1937 | select FB_SYS_COPYAREA | ||
| 1938 | select FB_SYS_IMAGEBLIT | ||
| 1939 | select FB_SYS_FOPS | ||
| 1940 | select FB_DEFERRED_IO | ||
| 1941 | default y | ||
| 1942 | help | ||
| 1943 | This driver implements the front-end of the Xen virtual | ||
| 1944 | frame buffer driver. It communicates with a back-end | ||
| 1945 | in another domain. | ||
| 1946 | |||
| 1933 | source "drivers/video/omap/Kconfig" | 1947 | source "drivers/video/omap/Kconfig" |
| 1934 | 1948 | ||
| 1935 | source "drivers/video/backlight/Kconfig" | 1949 | source "drivers/video/backlight/Kconfig" |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 11c0e5e05f..f172b9b733 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
| @@ -114,6 +114,7 @@ obj-$(CONFIG_FB_PS3) += ps3fb.o | |||
| 114 | obj-$(CONFIG_FB_SM501) += sm501fb.o | 114 | obj-$(CONFIG_FB_SM501) += sm501fb.o |
| 115 | obj-$(CONFIG_FB_XILINX) += xilinxfb.o | 115 | obj-$(CONFIG_FB_XILINX) += xilinxfb.o |
| 116 | obj-$(CONFIG_FB_OMAP) += omap/ | 116 | obj-$(CONFIG_FB_OMAP) += omap/ |
| 117 | obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o | ||
| 117 | 118 | ||
| 118 | # Platform or fallback drivers go here | 119 | # Platform or fallback drivers go here |
| 119 | obj-$(CONFIG_FB_UVESA) += uvesafb.o | 120 | obj-$(CONFIG_FB_UVESA) += uvesafb.o |
diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c new file mode 100644 index 0000000000..619a6f8d65 --- /dev/null +++ b/drivers/video/xen-fbfront.c | |||
| @@ -0,0 +1,550 @@ | |||
| 1 | /* | ||
| 2 | * Xen para-virtual frame buffer device | ||
| 3 | * | ||
| 4 | * Copyright (C) 2005-2006 Anthony Liguori <aliguori@us.ibm.com> | ||
| 5 | * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> | ||
| 6 | * | ||
| 7 | * Based on linux/drivers/video/q40fb.c | ||
| 8 | * | ||
| 9 | * This file is subject to the terms and conditions of the GNU General Public | ||
| 10 | * License. See the file COPYING in the main directory of this archive for | ||
| 11 | * more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | /* | ||
| 15 | * TODO: | ||
| 16 | * | ||
| 17 | * Switch to grant tables when they become capable of dealing with the | ||
| 18 | * frame buffer. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/kernel.h> | ||
| 22 | #include <linux/errno.h> | ||
| 23 | #include <linux/fb.h> | ||
| 24 | #include <linux/module.h> | ||
| 25 | #include <linux/vmalloc.h> | ||
| 26 | #include <linux/mm.h> | ||
| 27 | #include <asm/xen/hypervisor.h> | ||
| 28 | #include <xen/events.h> | ||
| 29 | #include <xen/page.h> | ||
| 30 | #include <xen/interface/io/fbif.h> | ||
| 31 | #include <xen/interface/io/protocols.h> | ||
| 32 | #include <xen/xenbus.h> | ||
| 33 | |||
| 34 | struct xenfb_info { | ||
| 35 | unsigned char *fb; | ||
| 36 | struct fb_info *fb_info; | ||
| 37 | int x1, y1, x2, y2; /* dirty rectangle, | ||
| 38 | protected by dirty_lock */ | ||
| 39 | spinlock_t dirty_lock; | ||
| 40 | int nr_pages; | ||
| 41 | int irq; | ||
| 42 | struct xenfb_page *page; | ||
| 43 | unsigned long *mfns; | ||
| 44 | int update_wanted; /* XENFB_TYPE_UPDATE wanted */ | ||
| 45 | |||
| 46 | struct xenbus_device *xbdev; | ||
| 47 | }; | ||
| 48 | |||
| 49 | static u32 xenfb_mem_len = XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8; | ||
| 50 | |||
| 51 | static int xenfb_remove(struct xenbus_device *); | ||
| 52 | static void xenfb_init_shared_page(struct xenfb_info *); | ||
| 53 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); | ||
| 54 | static void xenfb_disconnect_backend(struct xenfb_info *); | ||
| 55 | |||
| 56 | static void xenfb_do_update(struct xenfb_info *info, | ||
| 57 | int x, int y, int w, int h) | ||
| 58 | { | ||
| 59 | union xenfb_out_event event; | ||
| 60 | u32 prod; | ||
| 61 | |||
| 62 | event.type = XENFB_TYPE_UPDATE; | ||
| 63 | event.update.x = x; | ||
| 64 | event.update.y = y; | ||
| 65 | event.update.width = w; | ||
| 66 | event.update.height = h; | ||
| 67 | |||
| 68 | prod = info->page->out_prod; | ||
| 69 | /* caller ensures !xenfb_queue_full() */ | ||
| 70 | mb(); /* ensure ring space available */ | ||
| 71 | XENFB_OUT_RING_REF(info->page, prod) = event; | ||
| 72 | wmb(); /* ensure ring contents visible */ | ||
| 73 | info->page->out_prod = prod + 1; | ||
| 74 | |||
| 75 | notify_remote_via_irq(info->irq); | ||
| 76 | } | ||
| 77 | |||
| 78 | static int xenfb_queue_full(struct xenfb_info *info) | ||
| 79 | { | ||
| 80 | u32 cons, prod; | ||
| 81 | |||
| 82 | prod = info->page->out_prod; | ||
| 83 | cons = info->page->out_cons; | ||
| 84 | return prod - cons == XENFB_OUT_RING_LEN; | ||
| 85 | } | ||
| 86 | |||
| 87 | static void xenfb_refresh(struct xenfb_info *info, | ||
| 88 | int x1, int y1, int w, int h) | ||
| 89 | { | ||
| 90 | unsigned long flags; | ||
| 91 | int y2 = y1 + h - 1; | ||
| 92 | int x2 = x1 + w - 1; | ||
| 93 | |||
| 94 | if (!info->update_wanted) | ||
| 95 | return; | ||
| 96 | |||
| 97 | spin_lock_irqsave(&info->dirty_lock, flags); | ||
| 98 | |||
| 99 | /* Combine with dirty rectangle: */ | ||
| 100 | if (info->y1 < y1) | ||
| 101 | y1 = info->y1; | ||
| 102 | if (info->y2 > y2) | ||
| 103 | y2 = info->y2; | ||
| 104 | if (info->x1 < x1) | ||
| 105 | x1 = info->x1; | ||
| 106 | if (info->x2 > x2) | ||
| 107 | x2 = info->x2; | ||
| 108 | |||
| 109 | if (xenfb_queue_full(info)) { | ||
| 110 | /* Can't send right now, stash it in the dirty rectangle */ | ||
| 111 | info->x1 = x1; | ||
| 112 | info->x2 = x2; | ||
| 113 | info->y1 = y1; | ||
| 114 | info->y2 = y2; | ||
| 115 | spin_unlock_irqrestore(&info->dirty_lock, flags); | ||
| 116 | return; | ||
| 117 | } | ||
| 118 | |||
| 119 | /* Clear dirty rectangle: */ | ||
| 120 | info->x1 = info->y1 = INT_MAX; | ||
| 121 | info->x2 = info->y2 = 0; | ||
| 122 | |||
| 123 | spin_unlock_irqrestore(&info->dirty_lock, flags); | ||
| 124 | |||
| 125 | if (x1 <= x2 && y1 <= y2) | ||
| 126 | xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1); | ||
| 127 | } | ||
| 128 | |||
| 129 | static void xenfb_deferred_io(struct fb_info *fb_info, | ||
| 130 | struct list_head *pagelist) | ||
| 131 | { | ||
| 132 | struct xenfb_info *info = fb_info->par; | ||
| 133 | struct page *page; | ||
| 134 | unsigned long beg, end; | ||
| 135 | int y1, y2, miny, maxy; | ||
| 136 | |||
| 137 | miny = INT_MAX; | ||
| 138 | maxy = 0; | ||
| 139 | list_for_each_entry(page, pagelist, lru) { | ||
| 140 | beg = page->index << PAGE_SHIFT; | ||
| 141 | end = beg + PAGE_SIZE - 1; | ||
| 142 | y1 = beg / fb_info->fix.line_length; | ||
| 143 | y2 = end / fb_info->fix.line_length; | ||
| 144 | if (y2 >= fb_info->var.yres) | ||
| 145 | y2 = fb_info->var.yres - 1; | ||
| 146 | if (miny > y1) | ||
| 147 | miny = y1; | ||
| 148 | if (maxy < y2) | ||
| 149 | maxy = y2; | ||
| 150 | } | ||
| 151 | xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1); | ||
| 152 | } | ||
| 153 | |||
| 154 | static struct fb_deferred_io xenfb_defio = { | ||
| 155 | .delay = HZ / 20, | ||
| 156 | .deferred_io = xenfb_deferred_io, | ||
| 157 | }; | ||
| 158 | |||
| 159 | static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
| 160 | unsigned blue, unsigned transp, | ||
| 161 | struct fb_info *info) | ||
| 162 | { | ||
| 163 | u32 v; | ||
| 164 | |||
| 165 | if (regno > info->cmap.len) | ||
| 166 | return 1; | ||
| 167 | |||
| 168 | #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) | ||
| 169 | red = CNVT_TOHW(red, info->var.red.length); | ||
| 170 | green = CNVT_TOHW(green, info->var.green.length); | ||
| 171 | blue = CNVT_TOHW(blue, info->var.blue.length); | ||
| 172 | transp = CNVT_TOHW(transp, info->var.transp.length); | ||
| 173 | #undef CNVT_TOHW | ||
| 174 | |||
| 175 | v = (red << info->var.red.offset) | | ||
| 176 | (green << info->var.green.offset) | | ||
| 177 | (blue << info->var.blue.offset); | ||
| 178 | |||
| 179 | switch (info->var.bits_per_pixel) { | ||
| 180 | case 16: | ||
| 181 | case 24: | ||
| 182 | case 32: | ||
| 183 | ((u32 *)info->pseudo_palette)[regno] = v; | ||
| 184 | break; | ||
| 185 | } | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) | ||
| 191 | { | ||
| 192 | struct xenfb_info *info = p->par; | ||
| 193 | |||
| 194 | sys_fillrect(p, rect); | ||
| 195 | xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); | ||
| 196 | } | ||
| 197 | |||
| 198 | static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) | ||
| 199 | { | ||
| 200 | struct xenfb_info *info = p->par; | ||
| 201 | |||
| 202 | sys_imageblit(p, image); | ||
| 203 | xenfb_refresh(info, image->dx, image->dy, image->width, image->height); | ||
| 204 | } | ||
| 205 | |||
| 206 | static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) | ||
| 207 | { | ||
| 208 | struct xenfb_info *info = p->par; | ||
| 209 | |||
| 210 | sys_copyarea(p, area); | ||
| 211 | xenfb_refresh(info, area->dx, area->dy, area->width, area->height); | ||
| 212 | } | ||
| 213 | |||
| 214 | static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, | ||
| 215 | size_t count, loff_t *ppos) | ||
| 216 | { | ||
| 217 | struct xenfb_info *info = p->par; | ||
| 218 | ssize_t res; | ||
| 219 | |||
| 220 | res = fb_sys_write(p, buf, count, ppos); | ||
| 221 | xenfb_refresh(info, 0, 0, info->page->width, info->page->height); | ||
| 222 | return res; | ||
| 223 | } | ||
| 224 | |||
| 225 | static struct fb_ops xenfb_fb_ops = { | ||
| 226 | .owner = THIS_MODULE, | ||
| 227 | .fb_read = fb_sys_read, | ||
| 228 | .fb_write = xenfb_write, | ||
| 229 | .fb_setcolreg = xenfb_setcolreg, | ||
| 230 | .fb_fillrect = xenfb_fillrect, | ||
| 231 | .fb_copyarea = xenfb_copyarea, | ||
| 232 | .fb_imageblit = xenfb_imageblit, | ||
| 233 | }; | ||
| 234 | |||
| 235 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) | ||
| 236 | { | ||
| 237 | /* | ||
| 238 | * No in events recognized, simply ignore them all. | ||
| 239 | * If you need to recognize some, see xen-kbdfront's | ||
| 240 | * input_handler() for how to do that. | ||
| 241 | */ | ||
| 242 | struct xenfb_info *info = dev_id; | ||
| 243 | struct xenfb_page *page = info->page; | ||
| 244 | |||
| 245 | if (page->in_cons != page->in_prod) { | ||
| 246 | info->page->in_cons = info->page->in_prod; | ||
| 247 | notify_remote_via_irq(info->irq); | ||
| 248 | } | ||
| 249 | |||
| 250 | /* Flush dirty rectangle: */ | ||
| 251 | xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX); | ||
| 252 | |||
| 253 | return IRQ_HANDLED; | ||
| 254 | } | ||
| 255 | |||
| 256 | static int __devinit xenfb_probe(struct xenbus_device *dev, | ||
| 257 | const struct xenbus_device_id *id) | ||
| 258 | { | ||
| 259 | struct xenfb_info *info; | ||
| 260 | struct fb_info *fb_info; | ||
| 261 | int ret; | ||
| 262 | |||
| 263 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
| 264 | if (info == NULL) { | ||
| 265 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); | ||
| 266 | return -ENOMEM; | ||
| 267 | } | ||
| 268 | dev->dev.driver_data = info; | ||
| 269 | info->xbdev = dev; | ||
| 270 | info->irq = -1; | ||
| 271 | info->x1 = info->y1 = INT_MAX; | ||
| 272 | spin_lock_init(&info->dirty_lock); | ||
| 273 | |||
| 274 | info->fb = vmalloc(xenfb_mem_len); | ||
| 275 | if (info->fb == NULL) | ||
| 276 | goto error_nomem; | ||
| 277 | memset(info->fb, 0, xenfb_mem_len); | ||
| 278 | |||
| 279 | info->nr_pages = (xenfb_mem_len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 280 | |||
| 281 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); | ||
| 282 | if (!info->mfns) | ||
| 283 | goto error_nomem; | ||
| 284 | |||
| 285 | /* set up shared page */ | ||
| 286 | info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
| 287 | if (!info->page) | ||
| 288 | goto error_nomem; | ||
| 289 | |||
| 290 | xenfb_init_shared_page(info); | ||
| 291 | |||
| 292 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ | ||
| 293 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); | ||
| 294 | if (fb_info == NULL) | ||
| 295 | goto error_nomem; | ||
| 296 | |||
| 297 | /* complete the abuse: */ | ||
| 298 | fb_info->pseudo_palette = fb_info->par; | ||
| 299 | fb_info->par = info; | ||
| 300 | |||
| 301 | fb_info->screen_base = info->fb; | ||
| 302 | |||
| 303 | fb_info->fbops = &xenfb_fb_ops; | ||
| 304 | fb_info->var.xres_virtual = fb_info->var.xres = info->page->width; | ||
| 305 | fb_info->var.yres_virtual = fb_info->var.yres = info->page->height; | ||
| 306 | fb_info->var.bits_per_pixel = info->page->depth; | ||
| 307 | |||
| 308 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; | ||
| 309 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; | ||
| 310 | fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; | ||
| 311 | |||
| 312 | fb_info->var.activate = FB_ACTIVATE_NOW; | ||
| 313 | fb_info->var.height = -1; | ||
| 314 | fb_info->var.width = -1; | ||
| 315 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; | ||
| 316 | |||
| 317 | fb_info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
| 318 | fb_info->fix.line_length = info->page->line_length; | ||
| 319 | fb_info->fix.smem_start = 0; | ||
| 320 | fb_info->fix.smem_len = xenfb_mem_len; | ||
| 321 | strcpy(fb_info->fix.id, "xen"); | ||
| 322 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
| 323 | fb_info->fix.accel = FB_ACCEL_NONE; | ||
| 324 | |||
| 325 | fb_info->flags = FBINFO_FLAG_DEFAULT; | ||
| 326 | |||
| 327 | ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); | ||
| 328 | if (ret < 0) { | ||
| 329 | framebuffer_release(fb_info); | ||
| 330 | xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); | ||
| 331 | goto error; | ||
| 332 | } | ||
| 333 | |||
| 334 | fb_info->fbdefio = &xenfb_defio; | ||
| 335 | fb_deferred_io_init(fb_info); | ||
| 336 | |||
| 337 | ret = register_framebuffer(fb_info); | ||
| 338 | if (ret) { | ||
| 339 | fb_deferred_io_cleanup(fb_info); | ||
| 340 | fb_dealloc_cmap(&fb_info->cmap); | ||
| 341 | framebuffer_release(fb_info); | ||
| 342 | xenbus_dev_fatal(dev, ret, "register_framebuffer"); | ||
| 343 | goto error; | ||
| 344 | } | ||
| 345 | info->fb_info = fb_info; | ||
| 346 | |||
| 347 | ret = xenfb_connect_backend(dev, info); | ||
| 348 | if (ret < 0) | ||
| 349 | goto error; | ||
| 350 | |||
| 351 | return 0; | ||
| 352 | |||
| 353 | error_nomem: | ||
| 354 | ret = -ENOMEM; | ||
| 355 | xenbus_dev_fatal(dev, ret, "allocating device memory"); | ||
| 356 | error: | ||
| 357 | xenfb_remove(dev); | ||
| 358 | return ret; | ||
| 359 | } | ||
| 360 | |||
| 361 | static int xenfb_resume(struct xenbus_device *dev) | ||
| 362 | { | ||
| 363 | struct xenfb_info *info = dev->dev.driver_data; | ||
| 364 | |||
| 365 | xenfb_disconnect_backend(info); | ||
| 366 | xenfb_init_shared_page(info); | ||
| 367 | return xenfb_connect_backend(dev, info); | ||
| 368 | } | ||
| 369 | |||
| 370 | static int xenfb_remove(struct xenbus_device *dev) | ||
| 371 | { | ||
| 372 | struct xenfb_info *info = dev->dev.driver_data; | ||
| 373 | |||
| 374 | xenfb_disconnect_backend(info); | ||
| 375 | if (info->fb_info) { | ||
| 376 | fb_deferred_io_cleanup(info->fb_info); | ||
| 377 | unregister_framebuffer(info->fb_info); | ||
| 378 | fb_dealloc_cmap(&info->fb_info->cmap); | ||
| 379 | framebuffer_release(info->fb_info); | ||
| 380 | } | ||
| 381 | free_page((unsigned long)info->page); | ||
| 382 | vfree(info->mfns); | ||
| 383 | vfree(info->fb); | ||
| 384 | kfree(info); | ||
| 385 | |||
| 386 | return 0; | ||
| 387 | } | ||
| 388 | |||
| 389 | static unsigned long vmalloc_to_mfn(void *address) | ||
| 390 | { | ||
| 391 | return pfn_to_mfn(vmalloc_to_pfn(address)); | ||
| 392 | } | ||
| 393 | |||
| 394 | static void xenfb_init_shared_page(struct xenfb_info *info) | ||
| 395 | { | ||
| 396 | int i; | ||
| 397 | |||
| 398 | for (i = 0; i < info->nr_pages; i++) | ||
| 399 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); | ||
| 400 | |||
| 401 | info->page->pd[0] = vmalloc_to_mfn(info->mfns); | ||
| 402 | info->page->pd[1] = 0; | ||
| 403 | info->page->width = XENFB_WIDTH; | ||
| 404 | info->page->height = XENFB_HEIGHT; | ||
| 405 | info->page->depth = XENFB_DEPTH; | ||
| 406 | info->page->line_length = (info->page->depth / 8) * info->page->width; | ||
| 407 | info->page->mem_length = xenfb_mem_len; | ||
| 408 | info->page->in_cons = info->page->in_prod = 0; | ||
| 409 | info->page->out_cons = info->page->out_prod = 0; | ||
| 410 | } | ||
| 411 | |||
| 412 | static int xenfb_connect_backend(struct xenbus_device *dev, | ||
| 413 | struct xenfb_info *info) | ||
| 414 | { | ||
| 415 | int ret, evtchn; | ||
| 416 | struct xenbus_transaction xbt; | ||
| 417 | |||
| 418 | ret = xenbus_alloc_evtchn(dev, &evtchn); | ||
| 419 | if (ret) | ||
| 420 | return ret; | ||
| 421 | ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, | ||
| 422 | 0, dev->devicetype, info); | ||
| 423 | if (ret < 0) { | ||
| 424 | xenbus_free_evtchn(dev, evtchn); | ||
| 425 | xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); | ||
| 426 | return ret; | ||
| 427 | } | ||
| 428 | info->irq = ret; | ||
| 429 | |||
| 430 | again: | ||
| 431 | ret = xenbus_transaction_start(&xbt); | ||
| 432 | if (ret) { | ||
| 433 | xenbus_dev_fatal(dev, ret, "starting transaction"); | ||
| 434 | return ret; | ||
| 435 | } | ||
| 436 | ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", | ||
| 437 | virt_to_mfn(info->page)); | ||
| 438 | if (ret) | ||
| 439 | goto error_xenbus; | ||
| 440 | ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", | ||
| 441 | evtchn); | ||
| 442 | if (ret) | ||
| 443 | goto error_xenbus; | ||
| 444 | ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", | ||
| 445 | XEN_IO_PROTO_ABI_NATIVE); | ||
| 446 | if (ret) | ||
| 447 | goto error_xenbus; | ||
| 448 | ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); | ||
| 449 | if (ret) | ||
| 450 | goto error_xenbus; | ||
| 451 | ret = xenbus_transaction_end(xbt, 0); | ||
| 452 | if (ret) { | ||
| 453 | if (ret == -EAGAIN) | ||
| 454 | goto again; | ||
| 455 | xenbus_dev_fatal(dev, ret, "completing transaction"); | ||
| 456 | return ret; | ||
| 457 | } | ||
| 458 | |||
| 459 | xenbus_switch_state(dev, XenbusStateInitialised); | ||
| 460 | return 0; | ||
| 461 | |||
| 462 | error_xenbus: | ||
| 463 | xenbus_transaction_end(xbt, 1); | ||
| 464 | xenbus_dev_fatal(dev, ret, "writing xenstore"); | ||
| 465 | return ret; | ||
| 466 | } | ||
| 467 | |||
| 468 | static void xenfb_disconnect_backend(struct xenfb_info *info) | ||
| 469 | { | ||
| 470 | if (info->irq >= 0) | ||
| 471 | unbind_from_irqhandler(info->irq, info); | ||
| 472 | info->irq = -1; | ||
| 473 | } | ||
| 474 | |||
| 475 | static void xenfb_backend_changed(struct xenbus_device *dev, | ||
| 476 | enum xenbus_state backend_state) | ||
| 477 | { | ||
| 478 | struct xenfb_info *info = dev->dev.driver_data; | ||
| 479 | int val; | ||
| 480 | |||
| 481 | switch (backend_state) { | ||
| 482 | case XenbusStateInitialising: | ||
| 483 | case XenbusStateInitialised: | ||
| 484 | case XenbusStateUnknown: | ||
| 485 | case XenbusStateClosed: | ||
| 486 | break; | ||
| 487 | |||
| 488 | case XenbusStateInitWait: | ||
| 489 | InitWait: | ||
| 490 | xenbus_switch_state(dev, XenbusStateConnected); | ||
| 491 | break; | ||
| 492 | |||
| 493 | case XenbusStateConnected: | ||
| 494 | /* | ||
| 495 | * Work around xenbus race condition: If backend goes | ||
| 496 | * through InitWait to Connected fast enough, we can | ||
| 497 | * get Connected twice here. | ||
| 498 | */ | ||
| 499 | if (dev->state != XenbusStateConnected) | ||
| 500 | goto InitWait; /* no InitWait seen yet, fudge it */ | ||
| 501 | |||
| 502 | if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, | ||
| 503 | "request-update", "%d", &val) < 0) | ||
| 504 | val = 0; | ||
| 505 | if (val) | ||
| 506 | info->update_wanted = 1; | ||
| 507 | break; | ||
| 508 | |||
| 509 | case XenbusStateClosing: | ||
| 510 | xenbus_frontend_closed(dev); | ||
| 511 | break; | ||
| 512 | } | ||
| 513 | } | ||
| 514 | |||
| 515 | static struct xenbus_device_id xenfb_ids[] = { | ||
| 516 | { "vfb" }, | ||
| 517 | { "" } | ||
| 518 | }; | ||
| 519 | |||
| 520 | static struct xenbus_driver xenfb = { | ||
| 521 | .name = "vfb", | ||
| 522 | .owner = THIS_MODULE, | ||
| 523 | .ids = xenfb_ids, | ||
| 524 | .probe = xenfb_probe, | ||
| 525 | .remove = xenfb_remove, | ||
| 526 | .resume = xenfb_resume, | ||
| 527 | .otherend_changed = xenfb_backend_changed, | ||
| 528 | }; | ||
| 529 | |||
| 530 | static int __init xenfb_init(void) | ||
| 531 | { | ||
| 532 | if (!is_running_on_xen()) | ||
| 533 | return -ENODEV; | ||
| 534 | |||
| 535 | /* Nothing to do if running in dom0. */ | ||
| 536 | if (is_initial_xendomain()) | ||
| 537 | return -ENODEV; | ||
| 538 | |||
| 539 | return xenbus_register_frontend(&xenfb); | ||
| 540 | } | ||
| 541 | |||
| 542 | static void __exit xenfb_cleanup(void) | ||
| 543 | { | ||
| 544 | xenbus_unregister_driver(&xenfb); | ||
| 545 | } | ||
| 546 | |||
| 547 | module_init(xenfb_init); | ||
| 548 | module_exit(xenfb_cleanup); | ||
| 549 | |||
| 550 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/xen/interface/io/fbif.h b/include/xen/interface/io/fbif.h new file mode 100644 index 0000000000..5a934dd779 --- /dev/null +++ b/include/xen/interface/io/fbif.h | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* | ||
| 2 | * fbif.h -- Xen virtual frame buffer device | ||
| 3 | * | ||
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 5 | * of this software and associated documentation files (the "Software"), to | ||
| 6 | * deal in the Software without restriction, including without limitation the | ||
| 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| 8 | * sell copies of the Software, and to permit persons to whom the Software is | ||
| 9 | * furnished to do so, subject to the following conditions: | ||
| 10 | * | ||
| 11 | * The above copyright notice and this permission notice shall be included in | ||
| 12 | * all copies or substantial portions of the Software. | ||
| 13 | * | ||
| 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
| 20 | * DEALINGS IN THE SOFTWARE. | ||
| 21 | * | ||
| 22 | * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> | ||
| 23 | * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> | ||
| 24 | */ | ||
| 25 | |||
| 26 | #ifndef __XEN_PUBLIC_IO_FBIF_H__ | ||
| 27 | #define __XEN_PUBLIC_IO_FBIF_H__ | ||
| 28 | |||
| 29 | /* Out events (frontend -> backend) */ | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Out events may be sent only when requested by backend, and receipt | ||
| 33 | * of an unknown out event is an error. | ||
| 34 | */ | ||
| 35 | |||
| 36 | /* Event type 1 currently not used */ | ||
| 37 | /* | ||
| 38 | * Framebuffer update notification event | ||
| 39 | * Capable frontend sets feature-update in xenstore. | ||
| 40 | * Backend requests it by setting request-update in xenstore. | ||
| 41 | */ | ||
| 42 | #define XENFB_TYPE_UPDATE 2 | ||
| 43 | |||
| 44 | struct xenfb_update { | ||
| 45 | uint8_t type; /* XENFB_TYPE_UPDATE */ | ||
| 46 | int32_t x; /* source x */ | ||
| 47 | int32_t y; /* source y */ | ||
| 48 | int32_t width; /* rect width */ | ||
| 49 | int32_t height; /* rect height */ | ||
| 50 | }; | ||
| 51 | |||
| 52 | #define XENFB_OUT_EVENT_SIZE 40 | ||
| 53 | |||
| 54 | union xenfb_out_event { | ||
| 55 | uint8_t type; | ||
| 56 | struct xenfb_update update; | ||
| 57 | char pad[XENFB_OUT_EVENT_SIZE]; | ||
| 58 | }; | ||
| 59 | |||
| 60 | /* In events (backend -> frontend) */ | ||
| 61 | |||
| 62 | /* | ||
| 63 | * Frontends should ignore unknown in events. | ||
| 64 | * No in events currently defined. | ||
| 65 | */ | ||
| 66 | |||
| 67 | #define XENFB_IN_EVENT_SIZE 40 | ||
| 68 | |||
| 69 | union xenfb_in_event { | ||
| 70 | uint8_t type; | ||
| 71 | char pad[XENFB_IN_EVENT_SIZE]; | ||
| 72 | }; | ||
| 73 | |||
| 74 | /* shared page */ | ||
| 75 | |||
| 76 | #define XENFB_IN_RING_SIZE 1024 | ||
| 77 | #define XENFB_IN_RING_LEN (XENFB_IN_RING_SIZE / XENFB_IN_EVENT_SIZE) | ||
| 78 | #define XENFB_IN_RING_OFFS 1024 | ||
| 79 | #define XENFB_IN_RING(page) \ | ||
| 80 | ((union xenfb_in_event *)((char *)(page) + XENFB_IN_RING_OFFS)) | ||
| 81 | #define XENFB_IN_RING_REF(page, idx) \ | ||
| 82 | (XENFB_IN_RING((page))[(idx) % XENFB_IN_RING_LEN]) | ||
| 83 | |||
| 84 | #define XENFB_OUT_RING_SIZE 2048 | ||
| 85 | #define XENFB_OUT_RING_LEN (XENFB_OUT_RING_SIZE / XENFB_OUT_EVENT_SIZE) | ||
| 86 | #define XENFB_OUT_RING_OFFS (XENFB_IN_RING_OFFS + XENFB_IN_RING_SIZE) | ||
| 87 | #define XENFB_OUT_RING(page) \ | ||
| 88 | ((union xenfb_out_event *)((char *)(page) + XENFB_OUT_RING_OFFS)) | ||
| 89 | #define XENFB_OUT_RING_REF(page, idx) \ | ||
| 90 | (XENFB_OUT_RING((page))[(idx) % XENFB_OUT_RING_LEN]) | ||
| 91 | |||
| 92 | struct xenfb_page { | ||
| 93 | uint32_t in_cons, in_prod; | ||
| 94 | uint32_t out_cons, out_prod; | ||
| 95 | |||
| 96 | int32_t width; /* width of the framebuffer (in pixels) */ | ||
| 97 | int32_t height; /* height of the framebuffer (in pixels) */ | ||
| 98 | uint32_t line_length; /* length of a row of pixels (in bytes) */ | ||
| 99 | uint32_t mem_length; /* length of the framebuffer (in bytes) */ | ||
| 100 | uint8_t depth; /* depth of a pixel (in bits) */ | ||
| 101 | |||
| 102 | /* | ||
| 103 | * Framebuffer page directory | ||
| 104 | * | ||
| 105 | * Each directory page holds PAGE_SIZE / sizeof(*pd) | ||
| 106 | * framebuffer pages, and can thus map up to PAGE_SIZE * | ||
| 107 | * PAGE_SIZE / sizeof(*pd) bytes. With PAGE_SIZE == 4096 and | ||
| 108 | * sizeof(unsigned long) == 4, that's 4 Megs. Two directory | ||
| 109 | * pages should be enough for a while. | ||
| 110 | */ | ||
| 111 | unsigned long pd[2]; | ||
| 112 | }; | ||
| 113 | |||
| 114 | /* | ||
| 115 | * Wart: xenkbd needs to know resolution. Put it here until a better | ||
| 116 | * solution is found, but don't leak it to the backend. | ||
| 117 | */ | ||
| 118 | #ifdef __KERNEL__ | ||
| 119 | #define XENFB_WIDTH 800 | ||
| 120 | #define XENFB_HEIGHT 600 | ||
| 121 | #define XENFB_DEPTH 32 | ||
| 122 | #endif | ||
| 123 | |||
| 124 | #endif | ||
diff --git a/include/xen/interface/io/kbdif.h b/include/xen/interface/io/kbdif.h new file mode 100644 index 0000000000..fb97f4284f --- /dev/null +++ b/include/xen/interface/io/kbdif.h | |||
| @@ -0,0 +1,114 @@ | |||
| 1 | /* | ||
| 2 | * kbdif.h -- Xen virtual keyboard/mouse | ||
| 3 | * | ||
| 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 5 | * of this software and associated documentation files (the "Software"), to | ||
| 6 | * deal in the Software without restriction, including without limitation the | ||
| 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||
| 8 | * sell copies of the Software, and to permit persons to whom the Software is | ||
| 9 | * furnished to do so, subject to the following conditions: | ||
| 10 | * | ||
| 11 | * The above copyright notice and this permission notice shall be included in | ||
| 12 | * all copies or substantial portions of the Software. | ||
| 13 | * | ||
| 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||
| 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||
| 20 | * DEALINGS IN THE SOFTWARE. | ||
| 21 | * | ||
| 22 | * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com> | ||
| 23 | * Copyright (C) 2006 Red Hat, Inc., Markus Armbruster <armbru@redhat.com> | ||
| 24 | */ | ||
| 25 | |||
| 26 | #ifndef __XEN_PUBLIC_IO_KBDIF_H__ | ||
| 27 | #define __XEN_PUBLIC_IO_KBDIF_H__ | ||
| 28 | |||
| 29 | /* In events (backend -> frontend) */ | ||
| 30 | |||
| 31 | /* | ||
| 32 | * Frontends should ignore unknown in events. | ||
| 33 | */ | ||
| 34 | |||
| 35 | /* Pointer movement event */ | ||
| 36 | #define XENKBD_TYPE_MOTION 1 | ||
| 37 | /* Event type 2 currently not used */ | ||
| 38 | /* Key event (includes pointer buttons) */ | ||
| 39 | #define XENKBD_TYPE_KEY 3 | ||
| 40 | /* | ||
| 41 | * Pointer position event | ||
| 42 | * Capable backend sets feature-abs-pointer in xenstore. | ||
| 43 | * Frontend requests ot instead of XENKBD_TYPE_MOTION by setting | ||
| 44 | * request-abs-update in xenstore. | ||
| 45 | */ | ||
| 46 | #define XENKBD_TYPE_POS 4 | ||
| 47 | |||
| 48 | struct xenkbd_motion { | ||
| 49 | uint8_t type; /* XENKBD_TYPE_MOTION */ | ||
| 50 | int32_t rel_x; /* relative X motion */ | ||
| 51 | int32_t rel_y; /* relative Y motion */ | ||
| 52 | }; | ||
| 53 | |||
| 54 | struct xenkbd_key { | ||
| 55 | uint8_t type; /* XENKBD_TYPE_KEY */ | ||
| 56 | uint8_t pressed; /* 1 if pressed; 0 otherwise */ | ||
| 57 | uint32_t keycode; /* KEY_* from linux/input.h */ | ||
| 58 | }; | ||
| 59 | |||
| 60 | struct xenkbd_position { | ||
| 61 | uint8_t type; /* XENKBD_TYPE_POS */ | ||
| 62 | int32_t abs_x; /* absolute X position (in FB pixels) */ | ||
| 63 | int32_t abs_y; /* absolute Y position (in FB pixels) */ | ||
| 64 | }; | ||
| 65 | |||
| 66 | #define XENKBD_IN_EVENT_SIZE 40 | ||
| 67 | |||
| 68 | union xenkbd_in_event { | ||
| 69 | uint8_t type; | ||
| 70 | struct xenkbd_motion motion; | ||
| 71 | struct xenkbd_key key; | ||
| 72 | struct xenkbd_position pos; | ||
| 73 | char pad[XENKBD_IN_EVENT_SIZE]; | ||
| 74 | }; | ||
| 75 | |||
| 76 | /* Out events (frontend -> backend) */ | ||
| 77 | |||
| 78 | /* | ||
| 79 | * Out events may be sent only when requested by backend, and receipt | ||
| 80 | * of an unknown out event is an error. | ||
| 81 | * No out events currently defined. | ||
| 82 | */ | ||
| 83 | |||
| 84 | #define XENKBD_OUT_EVENT_SIZE 40 | ||
| 85 | |||
| 86 | union xenkbd_out_event { | ||
| 87 | uint8_t type; | ||
| 88 | char pad[XENKBD_OUT_EVENT_SIZE]; | ||
| 89 | }; | ||
| 90 | |||
| 91 | /* shared page */ | ||
| 92 | |||
| 93 | #define XENKBD_IN_RING_SIZE 2048 | ||
| 94 | #define XENKBD_IN_RING_LEN (XENKBD_IN_RING_SIZE / XENKBD_IN_EVENT_SIZE) | ||
| 95 | #define XENKBD_IN_RING_OFFS 1024 | ||
| 96 | #define XENKBD_IN_RING(page) \ | ||
| 97 | ((union xenkbd_in_event *)((char *)(page) + XENKBD_IN_RING_OFFS)) | ||
| 98 | #define XENKBD_IN_RING_REF(page, idx) \ | ||
| 99 | (XENKBD_IN_RING((page))[(idx) % XENKBD_IN_RING_LEN]) | ||
| 100 | |||
| 101 | #define XENKBD_OUT_RING_SIZE 1024 | ||
| 102 | #define XENKBD_OUT_RING_LEN (XENKBD_OUT_RING_SIZE / XENKBD_OUT_EVENT_SIZE) | ||
| 103 | #define XENKBD_OUT_RING_OFFS (XENKBD_IN_RING_OFFS + XENKBD_IN_RING_SIZE) | ||
| 104 | #define XENKBD_OUT_RING(page) \ | ||
| 105 | ((union xenkbd_out_event *)((char *)(page) + XENKBD_OUT_RING_OFFS)) | ||
| 106 | #define XENKBD_OUT_RING_REF(page, idx) \ | ||
| 107 | (XENKBD_OUT_RING((page))[(idx) % XENKBD_OUT_RING_LEN]) | ||
| 108 | |||
| 109 | struct xenkbd_page { | ||
| 110 | uint32_t in_cons, in_prod; | ||
| 111 | uint32_t out_cons, out_prod; | ||
| 112 | }; | ||
| 113 | |||
| 114 | #endif | ||
