Root/
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/console.h> |
22 | #include <linux/kernel.h> |
23 | #include <linux/errno.h> |
24 | #include <linux/fb.h> |
25 | #include <linux/module.h> |
26 | #include <linux/slab.h> |
27 | #include <linux/vmalloc.h> |
28 | #include <linux/mm.h> |
29 | |
30 | #include <asm/xen/hypervisor.h> |
31 | |
32 | #include <xen/xen.h> |
33 | #include <xen/events.h> |
34 | #include <xen/page.h> |
35 | #include <xen/interface/io/fbif.h> |
36 | #include <xen/interface/io/protocols.h> |
37 | #include <xen/xenbus.h> |
38 | |
39 | struct xenfb_info { |
40 | unsigned char *fb; |
41 | struct fb_info *fb_info; |
42 | int x1, y1, x2, y2; /* dirty rectangle, |
43 | protected by dirty_lock */ |
44 | spinlock_t dirty_lock; |
45 | int nr_pages; |
46 | int irq; |
47 | struct xenfb_page *page; |
48 | unsigned long *mfns; |
49 | int update_wanted; /* XENFB_TYPE_UPDATE wanted */ |
50 | int feature_resize; /* XENFB_TYPE_RESIZE ok */ |
51 | struct xenfb_resize resize; /* protected by resize_lock */ |
52 | int resize_dpy; /* ditto */ |
53 | spinlock_t resize_lock; |
54 | |
55 | struct xenbus_device *xbdev; |
56 | }; |
57 | |
58 | #define XENFB_DEFAULT_FB_LEN (XENFB_WIDTH * XENFB_HEIGHT * XENFB_DEPTH / 8) |
59 | |
60 | enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT }; |
61 | static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT }; |
62 | module_param_array(video, int, NULL, 0); |
63 | MODULE_PARM_DESC(video, |
64 | "Video memory size in MB, width, height in pixels (default 2,800,600)"); |
65 | |
66 | static void xenfb_make_preferred_console(void); |
67 | static int xenfb_remove(struct xenbus_device *); |
68 | static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); |
69 | static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); |
70 | static void xenfb_disconnect_backend(struct xenfb_info *); |
71 | |
72 | static void xenfb_send_event(struct xenfb_info *info, |
73 | union xenfb_out_event *event) |
74 | { |
75 | u32 prod; |
76 | |
77 | prod = info->page->out_prod; |
78 | /* caller ensures !xenfb_queue_full() */ |
79 | mb(); /* ensure ring space available */ |
80 | XENFB_OUT_RING_REF(info->page, prod) = *event; |
81 | wmb(); /* ensure ring contents visible */ |
82 | info->page->out_prod = prod + 1; |
83 | |
84 | notify_remote_via_irq(info->irq); |
85 | } |
86 | |
87 | static void xenfb_do_update(struct xenfb_info *info, |
88 | int x, int y, int w, int h) |
89 | { |
90 | union xenfb_out_event event; |
91 | |
92 | memset(&event, 0, sizeof(event)); |
93 | event.type = XENFB_TYPE_UPDATE; |
94 | event.update.x = x; |
95 | event.update.y = y; |
96 | event.update.width = w; |
97 | event.update.height = h; |
98 | |
99 | /* caller ensures !xenfb_queue_full() */ |
100 | xenfb_send_event(info, &event); |
101 | } |
102 | |
103 | static void xenfb_do_resize(struct xenfb_info *info) |
104 | { |
105 | union xenfb_out_event event; |
106 | |
107 | memset(&event, 0, sizeof(event)); |
108 | event.resize = info->resize; |
109 | |
110 | /* caller ensures !xenfb_queue_full() */ |
111 | xenfb_send_event(info, &event); |
112 | } |
113 | |
114 | static int xenfb_queue_full(struct xenfb_info *info) |
115 | { |
116 | u32 cons, prod; |
117 | |
118 | prod = info->page->out_prod; |
119 | cons = info->page->out_cons; |
120 | return prod - cons == XENFB_OUT_RING_LEN; |
121 | } |
122 | |
123 | static void xenfb_handle_resize_dpy(struct xenfb_info *info) |
124 | { |
125 | unsigned long flags; |
126 | |
127 | spin_lock_irqsave(&info->resize_lock, flags); |
128 | if (info->resize_dpy) { |
129 | if (!xenfb_queue_full(info)) { |
130 | info->resize_dpy = 0; |
131 | xenfb_do_resize(info); |
132 | } |
133 | } |
134 | spin_unlock_irqrestore(&info->resize_lock, flags); |
135 | } |
136 | |
137 | static void xenfb_refresh(struct xenfb_info *info, |
138 | int x1, int y1, int w, int h) |
139 | { |
140 | unsigned long flags; |
141 | int x2 = x1 + w - 1; |
142 | int y2 = y1 + h - 1; |
143 | |
144 | xenfb_handle_resize_dpy(info); |
145 | |
146 | if (!info->update_wanted) |
147 | return; |
148 | |
149 | spin_lock_irqsave(&info->dirty_lock, flags); |
150 | |
151 | /* Combine with dirty rectangle: */ |
152 | if (info->y1 < y1) |
153 | y1 = info->y1; |
154 | if (info->y2 > y2) |
155 | y2 = info->y2; |
156 | if (info->x1 < x1) |
157 | x1 = info->x1; |
158 | if (info->x2 > x2) |
159 | x2 = info->x2; |
160 | |
161 | if (xenfb_queue_full(info)) { |
162 | /* Can't send right now, stash it in the dirty rectangle */ |
163 | info->x1 = x1; |
164 | info->x2 = x2; |
165 | info->y1 = y1; |
166 | info->y2 = y2; |
167 | spin_unlock_irqrestore(&info->dirty_lock, flags); |
168 | return; |
169 | } |
170 | |
171 | /* Clear dirty rectangle: */ |
172 | info->x1 = info->y1 = INT_MAX; |
173 | info->x2 = info->y2 = 0; |
174 | |
175 | spin_unlock_irqrestore(&info->dirty_lock, flags); |
176 | |
177 | if (x1 <= x2 && y1 <= y2) |
178 | xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1); |
179 | } |
180 | |
181 | static void xenfb_deferred_io(struct fb_info *fb_info, |
182 | struct list_head *pagelist) |
183 | { |
184 | struct xenfb_info *info = fb_info->par; |
185 | struct page *page; |
186 | unsigned long beg, end; |
187 | int y1, y2, miny, maxy; |
188 | |
189 | miny = INT_MAX; |
190 | maxy = 0; |
191 | list_for_each_entry(page, pagelist, lru) { |
192 | beg = page->index << PAGE_SHIFT; |
193 | end = beg + PAGE_SIZE - 1; |
194 | y1 = beg / fb_info->fix.line_length; |
195 | y2 = end / fb_info->fix.line_length; |
196 | if (y2 >= fb_info->var.yres) |
197 | y2 = fb_info->var.yres - 1; |
198 | if (miny > y1) |
199 | miny = y1; |
200 | if (maxy < y2) |
201 | maxy = y2; |
202 | } |
203 | xenfb_refresh(info, 0, miny, fb_info->var.xres, maxy - miny + 1); |
204 | } |
205 | |
206 | static struct fb_deferred_io xenfb_defio = { |
207 | .delay = HZ / 20, |
208 | .deferred_io = xenfb_deferred_io, |
209 | }; |
210 | |
211 | static int xenfb_setcolreg(unsigned regno, unsigned red, unsigned green, |
212 | unsigned blue, unsigned transp, |
213 | struct fb_info *info) |
214 | { |
215 | u32 v; |
216 | |
217 | if (regno > info->cmap.len) |
218 | return 1; |
219 | |
220 | #define CNVT_TOHW(val, width) ((((val)<<(width))+0x7FFF-(val))>>16) |
221 | red = CNVT_TOHW(red, info->var.red.length); |
222 | green = CNVT_TOHW(green, info->var.green.length); |
223 | blue = CNVT_TOHW(blue, info->var.blue.length); |
224 | transp = CNVT_TOHW(transp, info->var.transp.length); |
225 | #undef CNVT_TOHW |
226 | |
227 | v = (red << info->var.red.offset) | |
228 | (green << info->var.green.offset) | |
229 | (blue << info->var.blue.offset); |
230 | |
231 | switch (info->var.bits_per_pixel) { |
232 | case 16: |
233 | case 24: |
234 | case 32: |
235 | ((u32 *)info->pseudo_palette)[regno] = v; |
236 | break; |
237 | } |
238 | |
239 | return 0; |
240 | } |
241 | |
242 | static void xenfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect) |
243 | { |
244 | struct xenfb_info *info = p->par; |
245 | |
246 | sys_fillrect(p, rect); |
247 | xenfb_refresh(info, rect->dx, rect->dy, rect->width, rect->height); |
248 | } |
249 | |
250 | static void xenfb_imageblit(struct fb_info *p, const struct fb_image *image) |
251 | { |
252 | struct xenfb_info *info = p->par; |
253 | |
254 | sys_imageblit(p, image); |
255 | xenfb_refresh(info, image->dx, image->dy, image->width, image->height); |
256 | } |
257 | |
258 | static void xenfb_copyarea(struct fb_info *p, const struct fb_copyarea *area) |
259 | { |
260 | struct xenfb_info *info = p->par; |
261 | |
262 | sys_copyarea(p, area); |
263 | xenfb_refresh(info, area->dx, area->dy, area->width, area->height); |
264 | } |
265 | |
266 | static ssize_t xenfb_write(struct fb_info *p, const char __user *buf, |
267 | size_t count, loff_t *ppos) |
268 | { |
269 | struct xenfb_info *info = p->par; |
270 | ssize_t res; |
271 | |
272 | res = fb_sys_write(p, buf, count, ppos); |
273 | xenfb_refresh(info, 0, 0, info->page->width, info->page->height); |
274 | return res; |
275 | } |
276 | |
277 | static int |
278 | xenfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) |
279 | { |
280 | struct xenfb_info *xenfb_info; |
281 | int required_mem_len; |
282 | |
283 | xenfb_info = info->par; |
284 | |
285 | if (!xenfb_info->feature_resize) { |
286 | if (var->xres == video[KPARAM_WIDTH] && |
287 | var->yres == video[KPARAM_HEIGHT] && |
288 | var->bits_per_pixel == xenfb_info->page->depth) { |
289 | return 0; |
290 | } |
291 | return -EINVAL; |
292 | } |
293 | |
294 | /* Can't resize past initial width and height */ |
295 | if (var->xres > video[KPARAM_WIDTH] || var->yres > video[KPARAM_HEIGHT]) |
296 | return -EINVAL; |
297 | |
298 | required_mem_len = var->xres * var->yres * xenfb_info->page->depth / 8; |
299 | if (var->bits_per_pixel == xenfb_info->page->depth && |
300 | var->xres <= info->fix.line_length / (XENFB_DEPTH / 8) && |
301 | required_mem_len <= info->fix.smem_len) { |
302 | var->xres_virtual = var->xres; |
303 | var->yres_virtual = var->yres; |
304 | return 0; |
305 | } |
306 | return -EINVAL; |
307 | } |
308 | |
309 | static int xenfb_set_par(struct fb_info *info) |
310 | { |
311 | struct xenfb_info *xenfb_info; |
312 | unsigned long flags; |
313 | |
314 | xenfb_info = info->par; |
315 | |
316 | spin_lock_irqsave(&xenfb_info->resize_lock, flags); |
317 | xenfb_info->resize.type = XENFB_TYPE_RESIZE; |
318 | xenfb_info->resize.width = info->var.xres; |
319 | xenfb_info->resize.height = info->var.yres; |
320 | xenfb_info->resize.stride = info->fix.line_length; |
321 | xenfb_info->resize.depth = info->var.bits_per_pixel; |
322 | xenfb_info->resize.offset = 0; |
323 | xenfb_info->resize_dpy = 1; |
324 | spin_unlock_irqrestore(&xenfb_info->resize_lock, flags); |
325 | return 0; |
326 | } |
327 | |
328 | static struct fb_ops xenfb_fb_ops = { |
329 | .owner = THIS_MODULE, |
330 | .fb_read = fb_sys_read, |
331 | .fb_write = xenfb_write, |
332 | .fb_setcolreg = xenfb_setcolreg, |
333 | .fb_fillrect = xenfb_fillrect, |
334 | .fb_copyarea = xenfb_copyarea, |
335 | .fb_imageblit = xenfb_imageblit, |
336 | .fb_check_var = xenfb_check_var, |
337 | .fb_set_par = xenfb_set_par, |
338 | }; |
339 | |
340 | static irqreturn_t xenfb_event_handler(int rq, void *dev_id) |
341 | { |
342 | /* |
343 | * No in events recognized, simply ignore them all. |
344 | * If you need to recognize some, see xen-kbdfront's |
345 | * input_handler() for how to do that. |
346 | */ |
347 | struct xenfb_info *info = dev_id; |
348 | struct xenfb_page *page = info->page; |
349 | |
350 | if (page->in_cons != page->in_prod) { |
351 | info->page->in_cons = info->page->in_prod; |
352 | notify_remote_via_irq(info->irq); |
353 | } |
354 | |
355 | /* Flush dirty rectangle: */ |
356 | xenfb_refresh(info, INT_MAX, INT_MAX, -INT_MAX, -INT_MAX); |
357 | |
358 | return IRQ_HANDLED; |
359 | } |
360 | |
361 | static int __devinit xenfb_probe(struct xenbus_device *dev, |
362 | const struct xenbus_device_id *id) |
363 | { |
364 | struct xenfb_info *info; |
365 | struct fb_info *fb_info; |
366 | int fb_size; |
367 | int val; |
368 | int ret; |
369 | |
370 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
371 | if (info == NULL) { |
372 | xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure"); |
373 | return -ENOMEM; |
374 | } |
375 | |
376 | /* Limit kernel param videoram amount to what is in xenstore */ |
377 | if (xenbus_scanf(XBT_NIL, dev->otherend, "videoram", "%d", &val) == 1) { |
378 | if (val < video[KPARAM_MEM]) |
379 | video[KPARAM_MEM] = val; |
380 | } |
381 | |
382 | /* If requested res does not fit in available memory, use default */ |
383 | fb_size = video[KPARAM_MEM] * 1024 * 1024; |
384 | if (video[KPARAM_WIDTH] * video[KPARAM_HEIGHT] * XENFB_DEPTH / 8 |
385 | > fb_size) { |
386 | video[KPARAM_WIDTH] = XENFB_WIDTH; |
387 | video[KPARAM_HEIGHT] = XENFB_HEIGHT; |
388 | fb_size = XENFB_DEFAULT_FB_LEN; |
389 | } |
390 | |
391 | dev_set_drvdata(&dev->dev, info); |
392 | info->xbdev = dev; |
393 | info->irq = -1; |
394 | info->x1 = info->y1 = INT_MAX; |
395 | spin_lock_init(&info->dirty_lock); |
396 | spin_lock_init(&info->resize_lock); |
397 | |
398 | info->fb = vmalloc(fb_size); |
399 | if (info->fb == NULL) |
400 | goto error_nomem; |
401 | memset(info->fb, 0, fb_size); |
402 | |
403 | info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; |
404 | |
405 | info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); |
406 | if (!info->mfns) |
407 | goto error_nomem; |
408 | |
409 | /* set up shared page */ |
410 | info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); |
411 | if (!info->page) |
412 | goto error_nomem; |
413 | |
414 | /* abusing framebuffer_alloc() to allocate pseudo_palette */ |
415 | fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); |
416 | if (fb_info == NULL) |
417 | goto error_nomem; |
418 | |
419 | /* complete the abuse: */ |
420 | fb_info->pseudo_palette = fb_info->par; |
421 | fb_info->par = info; |
422 | |
423 | fb_info->screen_base = info->fb; |
424 | |
425 | fb_info->fbops = &xenfb_fb_ops; |
426 | fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; |
427 | fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; |
428 | fb_info->var.bits_per_pixel = XENFB_DEPTH; |
429 | |
430 | fb_info->var.red = (struct fb_bitfield){16, 8, 0}; |
431 | fb_info->var.green = (struct fb_bitfield){8, 8, 0}; |
432 | fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; |
433 | |
434 | fb_info->var.activate = FB_ACTIVATE_NOW; |
435 | fb_info->var.height = -1; |
436 | fb_info->var.width = -1; |
437 | fb_info->var.vmode = FB_VMODE_NONINTERLACED; |
438 | |
439 | fb_info->fix.visual = FB_VISUAL_TRUECOLOR; |
440 | fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; |
441 | fb_info->fix.smem_start = 0; |
442 | fb_info->fix.smem_len = fb_size; |
443 | strcpy(fb_info->fix.id, "xen"); |
444 | fb_info->fix.type = FB_TYPE_PACKED_PIXELS; |
445 | fb_info->fix.accel = FB_ACCEL_NONE; |
446 | |
447 | fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; |
448 | |
449 | ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); |
450 | if (ret < 0) { |
451 | framebuffer_release(fb_info); |
452 | xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); |
453 | goto error; |
454 | } |
455 | |
456 | fb_info->fbdefio = &xenfb_defio; |
457 | fb_deferred_io_init(fb_info); |
458 | |
459 | xenfb_init_shared_page(info, fb_info); |
460 | |
461 | ret = xenfb_connect_backend(dev, info); |
462 | if (ret < 0) |
463 | goto error; |
464 | |
465 | ret = register_framebuffer(fb_info); |
466 | if (ret) { |
467 | fb_deferred_io_cleanup(fb_info); |
468 | fb_dealloc_cmap(&fb_info->cmap); |
469 | framebuffer_release(fb_info); |
470 | xenbus_dev_fatal(dev, ret, "register_framebuffer"); |
471 | goto error; |
472 | } |
473 | info->fb_info = fb_info; |
474 | |
475 | xenfb_make_preferred_console(); |
476 | return 0; |
477 | |
478 | error_nomem: |
479 | ret = -ENOMEM; |
480 | xenbus_dev_fatal(dev, ret, "allocating device memory"); |
481 | error: |
482 | xenfb_remove(dev); |
483 | return ret; |
484 | } |
485 | |
486 | static __devinit void |
487 | xenfb_make_preferred_console(void) |
488 | { |
489 | struct console *c; |
490 | |
491 | if (console_set_on_cmdline) |
492 | return; |
493 | |
494 | acquire_console_sem(); |
495 | for (c = console_drivers; c; c = c->next) { |
496 | if (!strcmp(c->name, "tty") && c->index == 0) |
497 | break; |
498 | } |
499 | release_console_sem(); |
500 | if (c) { |
501 | unregister_console(c); |
502 | c->flags |= CON_CONSDEV; |
503 | c->flags &= ~CON_PRINTBUFFER; /* don't print again */ |
504 | register_console(c); |
505 | } |
506 | } |
507 | |
508 | static int xenfb_resume(struct xenbus_device *dev) |
509 | { |
510 | struct xenfb_info *info = dev_get_drvdata(&dev->dev); |
511 | |
512 | xenfb_disconnect_backend(info); |
513 | xenfb_init_shared_page(info, info->fb_info); |
514 | return xenfb_connect_backend(dev, info); |
515 | } |
516 | |
517 | static int xenfb_remove(struct xenbus_device *dev) |
518 | { |
519 | struct xenfb_info *info = dev_get_drvdata(&dev->dev); |
520 | |
521 | xenfb_disconnect_backend(info); |
522 | if (info->fb_info) { |
523 | fb_deferred_io_cleanup(info->fb_info); |
524 | unregister_framebuffer(info->fb_info); |
525 | fb_dealloc_cmap(&info->fb_info->cmap); |
526 | framebuffer_release(info->fb_info); |
527 | } |
528 | free_page((unsigned long)info->page); |
529 | vfree(info->mfns); |
530 | vfree(info->fb); |
531 | kfree(info); |
532 | |
533 | return 0; |
534 | } |
535 | |
536 | static unsigned long vmalloc_to_mfn(void *address) |
537 | { |
538 | return pfn_to_mfn(vmalloc_to_pfn(address)); |
539 | } |
540 | |
541 | static void xenfb_init_shared_page(struct xenfb_info *info, |
542 | struct fb_info *fb_info) |
543 | { |
544 | int i; |
545 | int epd = PAGE_SIZE / sizeof(info->mfns[0]); |
546 | |
547 | for (i = 0; i < info->nr_pages; i++) |
548 | info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); |
549 | |
550 | for (i = 0; i * epd < info->nr_pages; i++) |
551 | info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); |
552 | |
553 | info->page->width = fb_info->var.xres; |
554 | info->page->height = fb_info->var.yres; |
555 | info->page->depth = fb_info->var.bits_per_pixel; |
556 | info->page->line_length = fb_info->fix.line_length; |
557 | info->page->mem_length = fb_info->fix.smem_len; |
558 | info->page->in_cons = info->page->in_prod = 0; |
559 | info->page->out_cons = info->page->out_prod = 0; |
560 | } |
561 | |
562 | static int xenfb_connect_backend(struct xenbus_device *dev, |
563 | struct xenfb_info *info) |
564 | { |
565 | int ret, evtchn; |
566 | struct xenbus_transaction xbt; |
567 | |
568 | ret = xenbus_alloc_evtchn(dev, &evtchn); |
569 | if (ret) |
570 | return ret; |
571 | ret = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, |
572 | 0, dev->devicetype, info); |
573 | if (ret < 0) { |
574 | xenbus_free_evtchn(dev, evtchn); |
575 | xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); |
576 | return ret; |
577 | } |
578 | info->irq = ret; |
579 | |
580 | again: |
581 | ret = xenbus_transaction_start(&xbt); |
582 | if (ret) { |
583 | xenbus_dev_fatal(dev, ret, "starting transaction"); |
584 | return ret; |
585 | } |
586 | ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", |
587 | virt_to_mfn(info->page)); |
588 | if (ret) |
589 | goto error_xenbus; |
590 | ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", |
591 | evtchn); |
592 | if (ret) |
593 | goto error_xenbus; |
594 | ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", |
595 | XEN_IO_PROTO_ABI_NATIVE); |
596 | if (ret) |
597 | goto error_xenbus; |
598 | ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); |
599 | if (ret) |
600 | goto error_xenbus; |
601 | ret = xenbus_transaction_end(xbt, 0); |
602 | if (ret) { |
603 | if (ret == -EAGAIN) |
604 | goto again; |
605 | xenbus_dev_fatal(dev, ret, "completing transaction"); |
606 | return ret; |
607 | } |
608 | |
609 | xenbus_switch_state(dev, XenbusStateInitialised); |
610 | return 0; |
611 | |
612 | error_xenbus: |
613 | xenbus_transaction_end(xbt, 1); |
614 | xenbus_dev_fatal(dev, ret, "writing xenstore"); |
615 | return ret; |
616 | } |
617 | |
618 | static void xenfb_disconnect_backend(struct xenfb_info *info) |
619 | { |
620 | if (info->irq >= 0) |
621 | unbind_from_irqhandler(info->irq, info); |
622 | info->irq = -1; |
623 | } |
624 | |
625 | static void xenfb_backend_changed(struct xenbus_device *dev, |
626 | enum xenbus_state backend_state) |
627 | { |
628 | struct xenfb_info *info = dev_get_drvdata(&dev->dev); |
629 | int val; |
630 | |
631 | switch (backend_state) { |
632 | case XenbusStateInitialising: |
633 | case XenbusStateInitialised: |
634 | case XenbusStateUnknown: |
635 | case XenbusStateClosed: |
636 | break; |
637 | |
638 | case XenbusStateInitWait: |
639 | InitWait: |
640 | xenbus_switch_state(dev, XenbusStateConnected); |
641 | break; |
642 | |
643 | case XenbusStateConnected: |
644 | /* |
645 | * Work around xenbus race condition: If backend goes |
646 | * through InitWait to Connected fast enough, we can |
647 | * get Connected twice here. |
648 | */ |
649 | if (dev->state != XenbusStateConnected) |
650 | goto InitWait; /* no InitWait seen yet, fudge it */ |
651 | |
652 | if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, |
653 | "request-update", "%d", &val) < 0) |
654 | val = 0; |
655 | if (val) |
656 | info->update_wanted = 1; |
657 | |
658 | if (xenbus_scanf(XBT_NIL, dev->otherend, |
659 | "feature-resize", "%d", &val) < 0) |
660 | val = 0; |
661 | info->feature_resize = val; |
662 | break; |
663 | |
664 | case XenbusStateClosing: |
665 | xenbus_frontend_closed(dev); |
666 | break; |
667 | } |
668 | } |
669 | |
670 | static struct xenbus_device_id xenfb_ids[] = { |
671 | { "vfb" }, |
672 | { "" } |
673 | }; |
674 | |
675 | static struct xenbus_driver xenfb_driver = { |
676 | .name = "vfb", |
677 | .owner = THIS_MODULE, |
678 | .ids = xenfb_ids, |
679 | .probe = xenfb_probe, |
680 | .remove = xenfb_remove, |
681 | .resume = xenfb_resume, |
682 | .otherend_changed = xenfb_backend_changed, |
683 | }; |
684 | |
685 | static int __init xenfb_init(void) |
686 | { |
687 | if (!xen_domain()) |
688 | return -ENODEV; |
689 | |
690 | /* Nothing to do if running in dom0. */ |
691 | if (xen_initial_domain()) |
692 | return -ENODEV; |
693 | |
694 | return xenbus_register_frontend(&xenfb_driver); |
695 | } |
696 | |
697 | static void __exit xenfb_cleanup(void) |
698 | { |
699 | xenbus_unregister_driver(&xenfb_driver); |
700 | } |
701 | |
702 | module_init(xenfb_init); |
703 | module_exit(xenfb_cleanup); |
704 | |
705 | MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); |
706 | MODULE_LICENSE("GPL"); |
707 | MODULE_ALIAS("xen:vfb"); |
708 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9