Blame view

drivers/input/misc/xen-kbdfront.c 9.49 KB
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * Xen para-virtual input device
   *
   * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
   * Copyright (C) 2006-2008 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.
   */
da0c49011   Joe Perches   Input: use pr_fmt...
13
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
14
15
16
17
  #include <linux/kernel.h>
  #include <linux/errno.h>
  #include <linux/module.h>
  #include <linux/input.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
18
  #include <linux/slab.h>
1ccbf5344   Jeremy Fitzhardinge   xen: move Xen-tes...
19

4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
20
  #include <asm/xen/hypervisor.h>
1ccbf5344   Jeremy Fitzhardinge   xen: move Xen-tes...
21
22
  
  #include <xen/xen.h>
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
23
24
  #include <xen/events.h>
  #include <xen/page.h>
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
25
26
  #include <xen/grant_table.h>
  #include <xen/interface/grant_table.h>
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
27
28
29
30
31
32
33
34
  #include <xen/interface/io/fbif.h>
  #include <xen/interface/io/kbdif.h>
  #include <xen/xenbus.h>
  
  struct xenkbd_info {
  	struct input_dev *kbd;
  	struct input_dev *ptr;
  	struct xenkbd_page *page;
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
35
  	int gref;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
  	int irq;
  	struct xenbus_device *xbdev;
  	char phys[32];
  };
  
  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 xenkbd_info *info = dev_id;
  	struct xenkbd_page *page = info->page;
  	__u32 cons, prod;
  
  	prod = page->in_prod;
  	if (prod == page->in_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;
  		struct input_dev *dev;
  		event = &XENKBD_IN_RING_REF(page, cons);
  
  		dev = info->ptr;
  		switch (event->type) {
  		case XENKBD_TYPE_MOTION:
  			input_report_rel(dev, REL_X, event->motion.rel_x);
  			input_report_rel(dev, REL_Y, event->motion.rel_y);
6ba0e7b36   Markus Armbruster   xen pvfb: Pointer...
70
71
72
  			if (event->motion.rel_z)
  				input_report_rel(dev, REL_WHEEL,
  						 -event->motion.rel_z);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
73
74
75
76
77
78
79
80
81
82
83
  			break;
  		case XENKBD_TYPE_KEY:
  			dev = NULL;
  			if (test_bit(event->key.keycode, info->kbd->keybit))
  				dev = info->kbd;
  			if (test_bit(event->key.keycode, info->ptr->keybit))
  				dev = info->ptr;
  			if (dev)
  				input_report_key(dev, event->key.keycode,
  						 event->key.pressed);
  			else
da0c49011   Joe Perches   Input: use pr_fmt...
84
85
86
  				pr_warning("unhandled keycode 0x%x
  ",
  					   event->key.keycode);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
87
88
89
90
  			break;
  		case XENKBD_TYPE_POS:
  			input_report_abs(dev, ABS_X, event->pos.abs_x);
  			input_report_abs(dev, ABS_Y, event->pos.abs_y);
6ba0e7b36   Markus Armbruster   xen pvfb: Pointer...
91
92
93
  			if (event->pos.rel_z)
  				input_report_rel(dev, REL_WHEEL,
  						 -event->pos.rel_z);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
  			break;
  		}
  		if (dev)
  			input_sync(dev);
  	}
  	mb();			/* ensure we got ring contents */
  	page->in_cons = cons;
  	notify_remote_via_irq(info->irq);
  
  	return IRQ_HANDLED;
  }
  
  static int __devinit xenkbd_probe(struct xenbus_device *dev,
  				  const struct xenbus_device_id *id)
  {
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
109
  	int ret, i, abs;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
110
111
112
113
114
115
116
117
  	struct xenkbd_info *info;
  	struct input_dev *kbd, *ptr;
  
  	info = kzalloc(sizeof(*info), GFP_KERNEL);
  	if (!info) {
  		xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
  		return -ENOMEM;
  	}
1b713e005   Greg Kroah-Hartman   xen: remove drive...
118
  	dev_set_drvdata(&dev->dev, info);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
119
120
  	info->xbdev = dev;
  	info->irq = -1;
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
121
  	info->gref = -1;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
122
123
124
125
126
  	snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
  
  	info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
  	if (!info->page)
  		goto error_nomem;
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
127
128
129
130
  	if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
  		abs = 0;
  	if (abs)
  		xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1");
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
131
132
133
134
135
136
137
138
139
  	/* keyboard */
  	kbd = input_allocate_device();
  	if (!kbd)
  		goto error_nomem;
  	kbd->name = "Xen Virtual Keyboard";
  	kbd->phys = info->phys;
  	kbd->id.bustype = BUS_PCI;
  	kbd->id.vendor = 0x5853;
  	kbd->id.product = 0xffff;
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
140
141
  
  	__set_bit(EV_KEY, kbd->evbit);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
142
  	for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
143
  		__set_bit(i, kbd->keybit);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
144
  	for (i = KEY_OK; i < KEY_MAX; i++)
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
145
  		__set_bit(i, kbd->keybit);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  
  	ret = input_register_device(kbd);
  	if (ret) {
  		input_free_device(kbd);
  		xenbus_dev_fatal(dev, ret, "input_register_device(kbd)");
  		goto error;
  	}
  	info->kbd = kbd;
  
  	/* pointing device */
  	ptr = input_allocate_device();
  	if (!ptr)
  		goto error_nomem;
  	ptr->name = "Xen Virtual Pointer";
  	ptr->phys = info->phys;
  	ptr->id.bustype = BUS_PCI;
  	ptr->id.vendor = 0x5853;
  	ptr->id.product = 0xfffe;
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
164
165
166
167
168
169
170
171
172
173
174
175
  
  	if (abs) {
  		__set_bit(EV_ABS, ptr->evbit);
  		input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
  		input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
  	} else {
  		input_set_capability(ptr, EV_REL, REL_X);
  		input_set_capability(ptr, EV_REL, REL_Y);
  	}
  	input_set_capability(ptr, EV_REL, REL_WHEEL);
  
  	__set_bit(EV_KEY, ptr->evbit);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
176
  	for (i = BTN_LEFT; i <= BTN_TASK; i++)
8c3c283e6   Olaf Hering   Input: xen-kbdfro...
177
  		__set_bit(i, ptr->keybit);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  
  	ret = input_register_device(ptr);
  	if (ret) {
  		input_free_device(ptr);
  		xenbus_dev_fatal(dev, ret, "input_register_device(ptr)");
  		goto error;
  	}
  	info->ptr = ptr;
  
  	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)
  {
1b713e005   Greg Kroah-Hartman   xen: remove drive...
203
  	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
204
205
206
207
208
209
210
211
  
  	xenkbd_disconnect_backend(info);
  	memset(info->page, 0, PAGE_SIZE);
  	return xenkbd_connect_backend(dev, info);
  }
  
  static int xenkbd_remove(struct xenbus_device *dev)
  {
1b713e005   Greg Kroah-Hartman   xen: remove drive...
212
  	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  
  	xenkbd_disconnect_backend(info);
  	if (info->kbd)
  		input_unregister_device(info->kbd);
  	if (info->ptr)
  		input_unregister_device(info->ptr);
  	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, evtchn;
  	struct xenbus_transaction xbt;
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
229
230
231
232
233
  	ret = gnttab_grant_foreign_access(dev->otherend_id,
  	                                  virt_to_mfn(info->page), 0);
  	if (ret < 0)
  		return ret;
  	info->gref = ret;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
234
235
  	ret = xenbus_alloc_evtchn(dev, &evtchn);
  	if (ret)
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
236
  		goto error_grant;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
237
238
239
  	ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
  					0, dev->devicetype, info);
  	if (ret < 0) {
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
240
  		xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
241
  		goto error_evtchan;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
242
243
244
245
246
247
248
  	}
  	info->irq = ret;
  
   again:
  	ret = xenbus_transaction_start(&xbt);
  	if (ret) {
  		xenbus_dev_fatal(dev, ret, "starting transaction");
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
249
  		goto error_irqh;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
250
251
252
253
254
  	}
  	ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
  			    virt_to_mfn(info->page));
  	if (ret)
  		goto error_xenbus;
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
255
256
257
  	ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
  	if (ret)
  		goto error_xenbus;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
258
259
260
261
262
263
264
265
266
  	ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
  			    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");
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
267
  		goto error_irqh;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
268
269
270
271
272
273
274
275
  	}
  
  	xenbus_switch_state(dev, XenbusStateInitialised);
  	return 0;
  
   error_xenbus:
  	xenbus_transaction_end(xbt, 1);
  	xenbus_dev_fatal(dev, ret, "writing xenstore");
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
276
277
278
279
280
281
282
283
   error_irqh:
  	unbind_from_irqhandler(info->irq, info);
  	info->irq = -1;
   error_evtchan:
  	xenbus_free_evtchn(dev, evtchn);
   error_grant:
  	gnttab_end_foreign_access_ref(info->gref, 0);
  	info->gref = -1;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
284
285
286
287
288
289
290
291
  	return ret;
  }
  
  static void xenkbd_disconnect_backend(struct xenkbd_info *info)
  {
  	if (info->irq >= 0)
  		unbind_from_irqhandler(info->irq, info);
  	info->irq = -1;
0a4dfa5dd   Daniel De Graaf   Input: xen-kbdfro...
292
293
294
  	if (info->gref >= 0)
  		gnttab_end_foreign_access_ref(info->gref, 0);
  	info->gref = -1;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
295
296
297
298
299
  }
  
  static void xenkbd_backend_changed(struct xenbus_device *dev,
  				   enum xenbus_state backend_state)
  {
1b713e005   Greg Kroah-Hartman   xen: remove drive...
300
  	struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
c36b58e8a   Igor Mammedov   Input: xen-kbdfro...
301
  	int ret, val;
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
302
303
304
305
  
  	switch (backend_state) {
  	case XenbusStateInitialising:
  	case XenbusStateInitialised:
b78c95125   Noboru Iwamatsu   xenbus: prevent w...
306
307
  	case XenbusStateReconfiguring:
  	case XenbusStateReconfigured:
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
308
309
310
311
312
313
  	case XenbusStateUnknown:
  	case XenbusStateClosed:
  		break;
  
  	case XenbusStateInitWait:
  InitWait:
c36b58e8a   Igor Mammedov   Input: xen-kbdfro...
314
315
316
317
318
319
320
321
322
323
  		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)
  				pr_warning("xenkbd: can't request abs-pointer");
  		}
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
324
325
326
327
328
329
330
331
332
333
334
  		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 */
e4dcff1f6   Markus Armbruster   xen pvfb: Dynamic...
335
336
337
338
339
340
341
342
343
  
  		/* Set input abs params to match backend screen res */
  		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
  				 "width", "%d", &val) > 0)
  			input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
  
  		if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
  				 "height", "%d", &val) > 0)
  			input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
344
345
346
347
348
349
350
  		break;
  
  	case XenbusStateClosing:
  		xenbus_frontend_closed(dev);
  		break;
  	}
  }
c6d570938   Márton Németh   Input: xen-kbdfro...
351
  static const struct xenbus_device_id xenkbd_ids[] = {
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
352
353
354
  	{ "vkbd" },
  	{ "" }
  };
73db144b5   Jan Beulich   Xen: consolidate ...
355
  static DEFINE_XENBUS_DRIVER(xenkbd, ,
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
356
357
358
359
  	.probe = xenkbd_probe,
  	.remove = xenkbd_remove,
  	.resume = xenkbd_resume,
  	.otherend_changed = xenkbd_backend_changed,
73db144b5   Jan Beulich   Xen: consolidate ...
360
  );
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
361
362
363
  
  static int __init xenkbd_init(void)
  {
5f098ecd4   Stefano Stabellini   Input: xen-kbdfro...
364
  	if (!xen_domain())
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
365
366
367
  		return -ENODEV;
  
  	/* Nothing to do if running in dom0. */
6e833587e   Jeremy Fitzhardinge   xen: clean up dom...
368
  	if (xen_initial_domain())
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
369
  		return -ENODEV;
ffb78a261   Al Viro   get xenbus_driver...
370
  	return xenbus_register_frontend(&xenkbd_driver);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
371
372
373
374
  }
  
  static void __exit xenkbd_cleanup(void)
  {
ffb78a261   Al Viro   get xenbus_driver...
375
  	xenbus_unregister_driver(&xenkbd_driver);
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
376
377
378
379
  }
  
  module_init(xenkbd_init);
  module_exit(xenkbd_cleanup);
1e892c959   Markus Armbruster   xen pvfb: Module ...
380
  MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
4ee36dc08   Markus Armbruster   xen pvfb: Para-vi...
381
  MODULE_LICENSE("GPL");
1e892c959   Markus Armbruster   xen pvfb: Module ...
382
  MODULE_ALIAS("xen:vkbd");