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