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