Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.3 708 lines 18 kB view raw
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 39struct 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 60enum { KPARAM_MEM, KPARAM_WIDTH, KPARAM_HEIGHT, KPARAM_CNT }; 61static int video[KPARAM_CNT] = { 2, XENFB_WIDTH, XENFB_HEIGHT }; 62module_param_array(video, int, NULL, 0); 63MODULE_PARM_DESC(video, 64 "Video memory size in MB, width, height in pixels (default 2,800,600)"); 65 66static void xenfb_make_preferred_console(void); 67static int xenfb_remove(struct xenbus_device *); 68static void xenfb_init_shared_page(struct xenfb_info *, struct fb_info *); 69static int xenfb_connect_backend(struct xenbus_device *, struct xenfb_info *); 70static void xenfb_disconnect_backend(struct xenfb_info *); 71 72static 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 87static 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 103static 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 114static 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 123static 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 137static 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 181static 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 206static struct fb_deferred_io xenfb_defio = { 207 .delay = HZ / 20, 208 .deferred_io = xenfb_deferred_io, 209}; 210 211static 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 242static 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 250static 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 258static 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 266static 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 277static int 278xenfb_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 309static 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 328static 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 340static 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 361static 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 = vzalloc(fb_size); 399 if (info->fb == NULL) 400 goto error_nomem; 401 402 info->nr_pages = (fb_size + PAGE_SIZE - 1) >> PAGE_SHIFT; 403 404 info->mfns = vmalloc(sizeof(unsigned long) * info->nr_pages); 405 if (!info->mfns) 406 goto error_nomem; 407 408 /* set up shared page */ 409 info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); 410 if (!info->page) 411 goto error_nomem; 412 413 /* abusing framebuffer_alloc() to allocate pseudo_palette */ 414 fb_info = framebuffer_alloc(sizeof(u32) * 256, NULL); 415 if (fb_info == NULL) 416 goto error_nomem; 417 418 /* complete the abuse: */ 419 fb_info->pseudo_palette = fb_info->par; 420 fb_info->par = info; 421 422 fb_info->screen_base = info->fb; 423 424 fb_info->fbops = &xenfb_fb_ops; 425 fb_info->var.xres_virtual = fb_info->var.xres = video[KPARAM_WIDTH]; 426 fb_info->var.yres_virtual = fb_info->var.yres = video[KPARAM_HEIGHT]; 427 fb_info->var.bits_per_pixel = XENFB_DEPTH; 428 429 fb_info->var.red = (struct fb_bitfield){16, 8, 0}; 430 fb_info->var.green = (struct fb_bitfield){8, 8, 0}; 431 fb_info->var.blue = (struct fb_bitfield){0, 8, 0}; 432 433 fb_info->var.activate = FB_ACTIVATE_NOW; 434 fb_info->var.height = -1; 435 fb_info->var.width = -1; 436 fb_info->var.vmode = FB_VMODE_NONINTERLACED; 437 438 fb_info->fix.visual = FB_VISUAL_TRUECOLOR; 439 fb_info->fix.line_length = fb_info->var.xres * XENFB_DEPTH / 8; 440 fb_info->fix.smem_start = 0; 441 fb_info->fix.smem_len = fb_size; 442 strcpy(fb_info->fix.id, "xen"); 443 fb_info->fix.type = FB_TYPE_PACKED_PIXELS; 444 fb_info->fix.accel = FB_ACCEL_NONE; 445 446 fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; 447 448 ret = fb_alloc_cmap(&fb_info->cmap, 256, 0); 449 if (ret < 0) { 450 framebuffer_release(fb_info); 451 xenbus_dev_fatal(dev, ret, "fb_alloc_cmap"); 452 goto error; 453 } 454 455 fb_info->fbdefio = &xenfb_defio; 456 fb_deferred_io_init(fb_info); 457 458 xenfb_init_shared_page(info, fb_info); 459 460 ret = xenfb_connect_backend(dev, info); 461 if (ret < 0) 462 goto error; 463 464 ret = register_framebuffer(fb_info); 465 if (ret) { 466 fb_deferred_io_cleanup(fb_info); 467 fb_dealloc_cmap(&fb_info->cmap); 468 framebuffer_release(fb_info); 469 xenbus_dev_fatal(dev, ret, "register_framebuffer"); 470 goto error; 471 } 472 info->fb_info = fb_info; 473 474 xenfb_make_preferred_console(); 475 return 0; 476 477 error_nomem: 478 ret = -ENOMEM; 479 xenbus_dev_fatal(dev, ret, "allocating device memory"); 480 error: 481 xenfb_remove(dev); 482 return ret; 483} 484 485static __devinit void 486xenfb_make_preferred_console(void) 487{ 488 struct console *c; 489 490 if (console_set_on_cmdline) 491 return; 492 493 console_lock(); 494 for_each_console(c) { 495 if (!strcmp(c->name, "tty") && c->index == 0) 496 break; 497 } 498 console_unlock(); 499 if (c) { 500 unregister_console(c); 501 c->flags |= CON_CONSDEV; 502 c->flags &= ~CON_PRINTBUFFER; /* don't print again */ 503 register_console(c); 504 } 505} 506 507static int xenfb_resume(struct xenbus_device *dev) 508{ 509 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 510 511 xenfb_disconnect_backend(info); 512 xenfb_init_shared_page(info, info->fb_info); 513 return xenfb_connect_backend(dev, info); 514} 515 516static int xenfb_remove(struct xenbus_device *dev) 517{ 518 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 519 520 xenfb_disconnect_backend(info); 521 if (info->fb_info) { 522 fb_deferred_io_cleanup(info->fb_info); 523 unregister_framebuffer(info->fb_info); 524 fb_dealloc_cmap(&info->fb_info->cmap); 525 framebuffer_release(info->fb_info); 526 } 527 free_page((unsigned long)info->page); 528 vfree(info->mfns); 529 vfree(info->fb); 530 kfree(info); 531 532 return 0; 533} 534 535static unsigned long vmalloc_to_mfn(void *address) 536{ 537 return pfn_to_mfn(vmalloc_to_pfn(address)); 538} 539 540static void xenfb_init_shared_page(struct xenfb_info *info, 541 struct fb_info *fb_info) 542{ 543 int i; 544 int epd = PAGE_SIZE / sizeof(info->mfns[0]); 545 546 for (i = 0; i < info->nr_pages; i++) 547 info->mfns[i] = vmalloc_to_mfn(info->fb + i * PAGE_SIZE); 548 549 for (i = 0; i * epd < info->nr_pages; i++) 550 info->page->pd[i] = vmalloc_to_mfn(&info->mfns[i * epd]); 551 552 info->page->width = fb_info->var.xres; 553 info->page->height = fb_info->var.yres; 554 info->page->depth = fb_info->var.bits_per_pixel; 555 info->page->line_length = fb_info->fix.line_length; 556 info->page->mem_length = fb_info->fix.smem_len; 557 info->page->in_cons = info->page->in_prod = 0; 558 info->page->out_cons = info->page->out_prod = 0; 559} 560 561static int xenfb_connect_backend(struct xenbus_device *dev, 562 struct xenfb_info *info) 563{ 564 int ret, evtchn, irq; 565 struct xenbus_transaction xbt; 566 567 ret = xenbus_alloc_evtchn(dev, &evtchn); 568 if (ret) 569 return ret; 570 irq = bind_evtchn_to_irqhandler(evtchn, xenfb_event_handler, 571 0, dev->devicetype, info); 572 if (irq < 0) { 573 xenbus_free_evtchn(dev, evtchn); 574 xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler"); 575 return irq; 576 } 577 again: 578 ret = xenbus_transaction_start(&xbt); 579 if (ret) { 580 xenbus_dev_fatal(dev, ret, "starting transaction"); 581 goto unbind_irq; 582 } 583 ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu", 584 virt_to_mfn(info->page)); 585 if (ret) 586 goto error_xenbus; 587 ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u", 588 evtchn); 589 if (ret) 590 goto error_xenbus; 591 ret = xenbus_printf(xbt, dev->nodename, "protocol", "%s", 592 XEN_IO_PROTO_ABI_NATIVE); 593 if (ret) 594 goto error_xenbus; 595 ret = xenbus_printf(xbt, dev->nodename, "feature-update", "1"); 596 if (ret) 597 goto error_xenbus; 598 ret = xenbus_transaction_end(xbt, 0); 599 if (ret) { 600 if (ret == -EAGAIN) 601 goto again; 602 xenbus_dev_fatal(dev, ret, "completing transaction"); 603 goto unbind_irq; 604 } 605 606 xenbus_switch_state(dev, XenbusStateInitialised); 607 info->irq = irq; 608 return 0; 609 610 error_xenbus: 611 xenbus_transaction_end(xbt, 1); 612 xenbus_dev_fatal(dev, ret, "writing xenstore"); 613 unbind_irq: 614 unbind_from_irqhandler(irq, info); 615 return ret; 616} 617 618static void xenfb_disconnect_backend(struct xenfb_info *info) 619{ 620 /* Prevent xenfb refresh */ 621 info->update_wanted = 0; 622 if (info->irq >= 0) 623 unbind_from_irqhandler(info->irq, info); 624 info->irq = -1; 625} 626 627static void xenfb_backend_changed(struct xenbus_device *dev, 628 enum xenbus_state backend_state) 629{ 630 struct xenfb_info *info = dev_get_drvdata(&dev->dev); 631 int val; 632 633 switch (backend_state) { 634 case XenbusStateInitialising: 635 case XenbusStateInitialised: 636 case XenbusStateReconfiguring: 637 case XenbusStateReconfigured: 638 case XenbusStateUnknown: 639 case XenbusStateClosed: 640 break; 641 642 case XenbusStateInitWait: 643InitWait: 644 xenbus_switch_state(dev, XenbusStateConnected); 645 break; 646 647 case XenbusStateConnected: 648 /* 649 * Work around xenbus race condition: If backend goes 650 * through InitWait to Connected fast enough, we can 651 * get Connected twice here. 652 */ 653 if (dev->state != XenbusStateConnected) 654 goto InitWait; /* no InitWait seen yet, fudge it */ 655 656 if (xenbus_scanf(XBT_NIL, info->xbdev->otherend, 657 "request-update", "%d", &val) < 0) 658 val = 0; 659 if (val) 660 info->update_wanted = 1; 661 662 if (xenbus_scanf(XBT_NIL, dev->otherend, 663 "feature-resize", "%d", &val) < 0) 664 val = 0; 665 info->feature_resize = val; 666 break; 667 668 case XenbusStateClosing: 669 xenbus_frontend_closed(dev); 670 break; 671 } 672} 673 674static const struct xenbus_device_id xenfb_ids[] = { 675 { "vfb" }, 676 { "" } 677}; 678 679static DEFINE_XENBUS_DRIVER(xenfb, , 680 .probe = xenfb_probe, 681 .remove = xenfb_remove, 682 .resume = xenfb_resume, 683 .otherend_changed = xenfb_backend_changed, 684); 685 686static int __init xenfb_init(void) 687{ 688 if (!xen_pv_domain()) 689 return -ENODEV; 690 691 /* Nothing to do if running in dom0. */ 692 if (xen_initial_domain()) 693 return -ENODEV; 694 695 return xenbus_register_frontend(&xenfb_driver); 696} 697 698static void __exit xenfb_cleanup(void) 699{ 700 xenbus_unregister_driver(&xenfb_driver); 701} 702 703module_init(xenfb_init); 704module_exit(xenfb_cleanup); 705 706MODULE_DESCRIPTION("Xen virtual framebuffer device frontend"); 707MODULE_LICENSE("GPL"); 708MODULE_ALIAS("xen:vfb");