--- /dev/null
+diff --git a/drivers/xen/v2v/Kconfig b/drivers/xen/v2v/Kconfig
+index 5966234..b3b6ef4 100644
+--- a/drivers/xen/v2v/Kconfig
++++ b/drivers/xen/v2v/Kconfig
+@@ -22,3 +22,10 @@ config XEN_V2V_DRV
+ default n
+ help
+ Sample for Xen V2V interdomain communication services.
++
++config XEN_V2V_DEV
++ tristate "Xen V2V device"
++ depends on XEN_V2V
++ default m
++ help
++ Xen V2V char device for userland v2v.
+diff --git a/drivers/xen/v2v/Makefile b/drivers/xen/v2v/Makefile
+index f3442d9..5230cd6 100644
+--- a/drivers/xen/v2v/Makefile
++++ b/drivers/xen/v2v/Makefile
+@@ -1,5 +1,6 @@
+
+ obj-$(CONFIG_XEN_V2V) += v2v.o v2vutl.o
+ obj-$(CONFIG_XEN_V2V_DRV) += v2vdrv.o v2vops.o
++obj-$(CONFIG_XEN_V2V_DEV) += v2vdev.o
+
+ ccflags-$(CONFIG_XEN_V2V_DEBUG) += -DDEBUG
+diff --git a/drivers/xen/v2v/v2vdev.c b/drivers/xen/v2v/v2vdev.c
+new file mode 100644
+index 0000000..c7afdf3
+--- /dev/null
++++ b/drivers/xen/v2v/v2vdev.c
+@@ -0,0 +1,400 @@
++/******************************************************************************
++ * drivers/xen/v2v/v2vdev.c
++ *
++ * V2V broker.
++ *
++ * Copyright (c) 2009 Jean Guyader
++ * Copyright (c) 2009 Citrix Systems, Inc.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License version 2
++ * as published by the Free Software Foundation; or, when distributed
++ * separately from the Linux kernel or incorporated into other
++ * software packages, subject to the following license:
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a copy
++ * of this source filp (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy, modify,
++ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
++ * and to permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
++ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
++ * IN THE SOFTWARE.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/wait.h>
++#include <linux/time.h>
++#include <linux/timer.h>
++#include <linux/random.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/major.h>
++#include <linux/proc_fs.h>
++#include <linux/poll.h>
++#include <linux/jiffies.h>
++#include <xen/xenbus.h>
++#include <xen/v2v.h>
++#include <xen/interface/io/vring.h>
++
++#define V2VLISTEN(len) _IOC(_IOC_WRITE, 'V', 0x01, len)
++#define V2VCONNECT(len) _IOC(_IOC_WRITE, 'V', 0x02, len)
++#define V2VTYPE _IOC(_IOC_WRITE, 'V', 0x03, int)
++
++#define MIN(_x_, _y_) ((_x_) > (_y_) ? (_x_) : (_y_))
++
++#define V2VDEV_LOG "V2V-dev"
++
++struct v2vdev_context
++{
++ int connected;
++ void *buff;
++ size_t buff_size;
++ int last_err;
++ int listenner;
++ int type;
++ struct v2v_channel *channel;
++};
++
++
++static void
++hexdump (char *prefix, void *_d, int len)
++{
++ uint8_t *d = (uint8_t *) _d;
++ int i, j, k;
++ int s, e;
++
++ printk (KERN_ERR "%s %d bytes from %p\n", prefix, len, d);
++
++ if (!d || len<0)
++ return;
++
++ e = len + 15;
++ e &= ~15;
++
++ for (i = 0; i < e; i += 16)
++ {
++ printk (KERN_ERR "%s %05x:", prefix, i);
++ for (j = 0; j < 16; ++j)
++ {
++ k = i + j;
++
++ if (k < len)
++ printk (" %02x", d[k]);
++ else
++ printk (" ");
++ if (j == 7)
++ printk (" ");
++ }
++
++ for (j = 0; j < 16; ++j)
++ {
++ k = i + j;
++ if (k < len)
++ {
++ uint8_t c = d[k];
++ if (c < 33)
++ c = '.';
++ if (c > 126)
++ c = '.';
++ printk ("%c", c);
++ }
++ else
++ printk (" ");
++ if (j == 7)
++ printk (" ");
++ }
++ printk ("\n");
++ }
++}
++
++static int v2vdev_wait_connected(struct v2vdev_context *ctx)
++{
++ struct v2v_wait *wait_state;
++ u8 reasons_mask = V2V_WAKE_REASON_CONTROL;
++ int err;
++ enum v2v_endpoint_state state;
++
++ while (!ctx->connected)
++ {
++ wait_state = v2v_get_wait_state(ctx->channel);
++ printk(V2VDEV_LOG" wait event ....\n");
++ wait_event(wait_state->wait_event,
++ atomic_xchg(&wait_state->wait_condition, 0) == 1);
++ v2v_get_wake_reason(ctx->channel, reasons_mask);
++
++ err = v2v_get_remote_state(ctx->channel, &state);
++ if (err)
++ {
++ printk(V2VDEV_LOG" failure in v2v_get_remote_state() - err: %d\n", err);
++ v2v_disconnect(ctx->channel);
++ return err;
++ }
++ printk(V2VDEV_LOG" endpoint state: %s\n", v2v_endpoint_state_name(state));
++ ctx->connected = state == v2v_state_connected;
++ }
++ return 0;
++}
++
++static int v2vdev_listen(struct v2vdev_context *ctx,
++ const char __user *user_path, size_t size)
++{
++ int err;
++ char path[1024];
++
++ if (copy_from_user(path, user_path, size) != 0)
++ return -EINVAL;
++ path[size] = 0;
++
++ err = v2v_listen(path, &ctx->channel, 0, 0, 0);
++ if (err)
++ {
++ printk(V2VDEV_LOG" failure in v2v_listen() - error: %d\n", err);
++ return err;
++ }
++ BUG_ON(ctx->channel == NULL);
++
++ ctx->listenner = 1;
++
++ return 0;
++}
++
++static int v2vdev_connect(struct v2vdev_context *ctx,
++ const char __user *user_path, size_t size)
++{
++ char path[1024];
++ int err;
++
++ if (copy_from_user(path, user_path, size) != 0)
++ return -EINVAL;
++ path[size] = 0;
++
++ printk(V2VDEV_LOG" connect on %s\n", path);
++
++ err = v2v_connect(path, &ctx->channel, 0);
++ if (err)
++ {
++ printk(V2VDEV_LOG" failure in connect() - err: %d\n", err);
++ return err;
++ }
++
++ err = v2vdev_wait_connected(ctx);
++ if (err)
++ {
++ printk(V2VDEV_LOG" failure in wait_connected() - error: %d\n", err);
++ return err;
++ }
++ return 0;
++}
++
++static ssize_t v2vdev_read(struct file *filp, char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ struct v2vdev_context *ctx = filp->private_data;
++ ssize_t n, size;
++ uint16_t tag;
++
++ if (ctx->last_err)
++ return -ctx->last_err;
++
++ size = MIN(count, ctx->buff_size);
++ n = copy_to_user(buf, ctx->buff, size);
++ ctx->buff_size -= size;
++ if (ctx->buff_size == 0)
++ {
++ kfree(ctx->buff);
++ ctx->buff = NULL;
++ }
++ else
++ ctx->buff += size;
++
++ v2v_nc2_finish_message(ctx->channel);
++ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_RECEIVE);
++ return size - n;
++}
++
++static ssize_t v2vdev_write(struct file *filp, const char __user *buf,
++ size_t count, loff_t *ppos)
++{
++ struct v2vdev_context *ctx = filp->private_data;
++ volatile void *msg;
++ int err;
++
++ err = v2v_nc2_prep_message(ctx->channel, count, ctx->type, 0, &msg);
++ if (err)
++ {
++ printk(V2VDEV_LOG" failure in v2v_nc2_prep_message() - error: %d\n", err);
++ return -EAGAIN;
++ }
++
++ if (copy_from_user((void *)msg, buf, count) != 0)
++ {
++ printk(V2VDEV_LOG" failure in copy_from_user()\n");
++ return 0;
++ }
++
++ hexdump(">", msg, count);
++ v2v_nc2_send_messages(ctx->channel);
++ v2v_set_wake_reason(ctx->channel, V2V_WAKE_REASON_SEND);
++ return count;
++}
++
++static long v2vdev_ioctl(struct file *filp,
++ unsigned int cmd, unsigned long arg)
++{
++ int rc;
++ struct v2vdev_context *ctx = filp->private_data;
++
++ rc = -ENOSYS;
++ switch (cmd) {
++ default:
++ if (_IOC_TYPE(cmd) != 'V')
++ return -EINVAL;
++
++ if (_IOC_DIR(cmd) == _IOC_WRITE)
++ {
++ if ((_IOC_NR(cmd) == _IOC_NR(V2VLISTEN(0))))
++ rc = v2vdev_listen(ctx, (void __user *)arg, _IOC_SIZE(cmd));
++ if ((_IOC_NR(cmd) == _IOC_NR(V2VCONNECT(0))))
++ rc = v2vdev_connect(ctx, (void __user *)arg, _IOC_SIZE(cmd));
++ if ((_IOC_NR(cmd) == _IOC_NR(V2VTYPE)))
++ ctx->type = arg;
++ }
++ break;
++ }
++ return rc;
++}
++
++static int v2vdev_open(struct inode *inode, struct file *filp)
++{
++ struct v2vdev_context *ctx;
++
++ ctx = kmalloc(sizeof (struct v2vdev_context), GFP_KERNEL);
++ memset(ctx, 0, sizeof (struct v2vdev_context));
++ /* Set the default type to DATA_STREAM */
++ ctx->type = 1;
++ filp->private_data = ctx;
++ return 0;
++}
++
++static int v2vdev_release(struct inode *inode, struct file *filp)
++{
++ struct v2vdev_context *ctx = filp->private_data;
++
++ if (ctx->connected)
++ v2v_disconnect(ctx->channel);
++ if (ctx->buff)
++ kfree(ctx->buff);
++ kfree(filp->private_data);
++ return 0;
++}
++
++static int v2vdev_message_get(struct v2vdev_context *ctx)
++{
++ volatile void *buff;
++ ssize_t size = 0;
++ unsigned vtype, vflags;
++ uint16_t tag;
++
++ vtype = vflags = 0;
++ ctx->last_err = v2v_nc2_get_message(ctx->channel,
++ (const volatile void **)&buff, &size,
++ &vtype, &vflags);
++ if (ctx->last_err == 0)
++ {
++ hexdump("<", buff, size);
++ ctx->buff = krealloc(ctx->buff, ctx->buff_size + size, GFP_KERNEL);
++ memcpy(ctx->buff + ctx->buff_size, (void *)buff, size);
++ ctx->buff_size += size;
++ }
++ return ctx->last_err;
++}
++
++static unsigned int v2vdev_poll(struct file *filp, poll_table *wait)
++{
++ unsigned int mask = 0;
++ struct v2vdev_context *ctx = filp->private_data;
++ struct v2v_wait *wait_state;
++ u8 reasons_mask = V2V_WAKE_REASON_CONTROL;
++ u8 reason;
++
++ if (!ctx->connected)
++ reasons_mask |= V2V_WAKE_REASON_ANY;
++ else
++ reasons_mask |= V2V_WAKE_REASON_RECEIVE;
++
++ wait_state = v2v_get_wait_state(ctx->channel);
++ poll_wait(filp, &wait_state->wait_event, wait);
++
++ if (!ctx->connected && ctx->listenner)
++ if (v2v_accept(ctx->channel, 1) != EAGAIN)
++ {
++ v2vdev_wait_connected(ctx);
++ printk(V2VDEV_LOG" connected !\n");
++ return 0;
++ }
++
++ reason = v2v_get_wake_reason(ctx->channel, reasons_mask);
++ if (reason & V2V_WAKE_REASON_RECEIVE)
++ if (v2vdev_message_get(ctx) == 0)
++ mask = POLLIN | POLLRDNORM;
++ return mask;
++}
++
++static const struct file_operations v2vdev_fops = {
++ .owner = THIS_MODULE,
++ .write = v2vdev_write,
++ .read = v2vdev_read,
++ .unlocked_ioctl = v2vdev_ioctl,
++ .open = v2vdev_open,
++ .release = v2vdev_release,
++ .poll = v2vdev_poll,
++};
++
++static struct miscdevice v2vdev_miscdev = {
++ .minor = MISC_DYNAMIC_MINOR,
++ .name = "v2v",
++ .fops = &v2vdev_fops,
++};
++
++static int __init v2vdev_init(void)
++{
++ int err = 0;
++
++ if (!is_running_on_xen())
++ return -ENODEV;
++
++ err = misc_register(&v2vdev_miscdev);
++ if (err != 0) {
++ printk(KERN_ALERT "Could not register /dev/v2v\n");
++ return err;
++ }
++
++ printk("Xen V2V device installed.\n");
++
++ return 0;
++}
++
++static void __exit v2vdev_cleanup(void)
++{
++ misc_deregister(&v2vdev_miscdev);
++}
++
++module_init(v2vdev_init);
++module_exit(v2vdev_cleanup);
++
++MODULE_LICENSE("Dual BSD/GPL");