This commit was manufactured by cvs2svn to create branch 'vserver'.
[linux-2.6.git] / drivers / xen / xenkbd / xenkbd.c
1 /*
2  * linux/drivers/input/keyboard/xenkbd.c -- Xen para-virtual input device
3  *
4  * Copyright (C) 2005
5  *
6  *      Anthony Liguori <aliguori@us.ibm.com>
7  *
8  *  Based on linux/drivers/input/mouse/sermouse.c
9  *
10  *  This file is subject to the terms and conditions of the GNU General Public
11  *  License. See the file COPYING in the main directory of this archive for
12  *  more details.
13  */
14
15 #include <linux/kernel.h>
16 #include <linux/errno.h>
17 #include <linux/module.h>
18 #include <linux/input.h>
19 #include <asm/hypervisor.h>
20 #include <xen/evtchn.h>
21 #include <xen/xenbus.h>
22 #include <linux/xenkbd.h>
23
24 struct xenkbd_device
25 {
26         struct input_dev *dev;
27         struct xenkbd_info *info;
28         unsigned evtchn;
29         int irq;
30 };
31
32 static irqreturn_t input_handler(int rq, void *dev_id, struct pt_regs *regs)
33 {
34         struct xenkbd_device *dev = dev_id;
35         struct xenkbd_info *info = dev ? dev->info : 0;
36         static int button_map[3] = { BTN_RIGHT, BTN_MIDDLE, BTN_LEFT };
37         __u32 cons, prod;
38
39         prod = info->in_prod;
40         rmb();                  /* ensure we see ring contents up to prod */
41         for (cons = info->in_cons; cons != prod; cons++) {
42                 union xenkbd_in_event *event;
43                 event = &XENKBD_IN_RING_REF(info, cons);
44         
45                 switch (event->type) {
46                 case XENKBD_TYPE_MOTION:
47                         input_report_rel(dev->dev, REL_X, event->motion.rel_x);
48                         input_report_rel(dev->dev, REL_Y, event->motion.rel_y);
49                         break;
50                 case XENKBD_TYPE_BUTTON:
51                         if (event->button.button < 3)
52                                 input_report_key(dev->dev,
53                                                  button_map[event->button.button],
54                                                  event->button.pressed);
55                         break;
56                 case XENKBD_TYPE_KEY:
57                         input_report_key(dev->dev, event->key.keycode, event->key.pressed);
58                         break;
59                 }
60         }
61         input_sync(dev->dev);
62         mb();                   /* ensure we got ring contents */
63         info->in_cons = cons;
64         notify_remote_via_evtchn(dev->evtchn);
65
66         return IRQ_HANDLED;
67 }
68
69 static struct xenkbd_device *xenkbd_dev;
70
71 int __init xenkbd_init(void)
72 {
73         int ret = 0;
74         int i;
75         struct xenkbd_device *dev;
76         struct input_dev *input_dev;
77         struct evtchn_alloc_unbound alloc_unbound;
78         struct xenbus_transaction xbt;
79
80         /* Nothing to do if running in dom0. */
81         if (is_initial_xendomain())
82           return -ENODEV;
83 #if 1
84         /* if we're not set up to use graphics mode, then don't initialize */
85         if (xenbus_scanf(XBT_NIL, "console", "use_graphics", "%d", &ret) < 0)
86           return -ENODEV;
87         if (ret == 0)
88           return -ENODEV;
89 #endif
90
91         dev = kmalloc(sizeof(*dev), GFP_KERNEL);
92         input_dev = input_allocate_device();
93         if (!dev || !input_dev)
94                 return -ENOMEM;
95
96         dev->dev = input_dev;
97         dev->info = (void *)__get_free_page(GFP_KERNEL);
98         if (!dev->info) {
99                 ret = -ENOMEM;
100                 goto error;
101         }
102
103         alloc_unbound.dom = DOMID_SELF;
104         alloc_unbound.remote_dom = 0;
105         ret = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound,
106                                           &alloc_unbound);
107         if (ret)
108                 goto error_freep;
109         dev->evtchn = alloc_unbound.port;
110         ret = bind_evtchn_to_irqhandler(dev->evtchn, input_handler, 0,
111                                         "xenkbd", dev);
112         if (ret < 0)
113                 goto error_freep;
114         dev->irq = ret;
115
116         input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
117         input_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT);
118         input_dev->relbit[0] = BIT(REL_X) | BIT(REL_Y);
119
120         /* FIXME not sure this is quite right */
121         for (i = 0; i < 256; i++)
122                 set_bit(i, input_dev->keybit);
123
124         input_dev->name = "Xen Virtual Keyboard/Mouse";
125
126         input_register_device(input_dev);
127
128  again:
129         ret = xenbus_transaction_start(&xbt);
130         if (ret)
131                 goto error_unreg;
132         ret = xenbus_printf(xbt, "vkbd", "page-ref", "%lu",
133                             virt_to_mfn(dev->info));
134         if (ret)
135                 goto error_xenbus;
136         ret = xenbus_printf(xbt, "vkbd", "event-channel", "%u",
137                             dev->evtchn);
138         if (ret)
139                 goto error_xenbus;
140         ret = xenbus_transaction_end(xbt, 0);
141         if (ret) {
142                 if (ret == -EAGAIN)
143                         goto again;
144                 goto error_unreg;
145         }
146
147         dev->info->in_cons = dev->info->in_prod = 0;
148         dev->info->out_cons = dev->info->out_prod = 0;
149         xenkbd_dev = dev;
150
151         return ret;
152
153         
154  error_xenbus:
155         xenbus_transaction_end(xbt, 1);
156  error_unreg:
157         input_unregister_device(input_dev);
158         unbind_from_irqhandler(dev->irq, dev);
159  error_freep:
160         free_page((unsigned long)dev->info);
161  error:
162         kfree(dev);
163         return ret;
164 }
165
166 static void __exit xenkbd_cleanup(void)
167 {
168         input_unregister_device(xenkbd_dev->dev);
169         unbind_from_irqhandler(xenkbd_dev->irq, xenkbd_dev);
170         free_page((unsigned long)xenkbd_dev->info);
171         kfree(xenkbd_dev);
172         xenkbd_dev = NULL;
173 }
174
175 module_init(xenkbd_init);
176 module_exit(xenkbd_cleanup);
177
178 MODULE_LICENSE("GPL");