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