2 * xen paravirt framebuffer backend
4 * Copyright IBM, Corp. 2005-2006
5 * Copyright Red Hat, Inc. 2006-2008
8 * Anthony Liguori <aliguori@us.ibm.com>,
9 * Markus Armbruster <armbru@redhat.com>,
10 * Daniel P. Berrange <berrange@redhat.com>,
11 * Pat Campbell <plc@novell.com>,
12 * Gerd Hoffmann <kraxel@redhat.com>
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; under version 2 of the License.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include "qemu/osdep.h"
31 #include "ui/console.h"
32 #include "hw/xen/xen_backend.h"
34 #include <xen/event_channel.h>
35 #include <xen/io/fbif.h>
36 #include <xen/io/kbdif.h>
37 #include <xen/io/protocols.h>
42 #define BTN_LEFT 0x110 /* from <linux/input.h> */
45 /* -------------------------------------------------------------------- */
48 struct XenDevice xendev
; /* must be first */
54 int abs_pointer_wanted
; /* Whether guest supports absolute pointer */
55 int raw_pointer_wanted
; /* Whether guest supports raw (unscaled) pointer */
56 QemuInputHandlerState
*qkbd
;
57 QemuInputHandlerState
*qmou
;
58 int axis
[INPUT_AXIS__MAX
];
85 static const GraphicHwOps xenfb_ops
;
87 /* -------------------------------------------------------------------- */
89 static int common_bind(struct common
*c
)
94 if (xenstore_read_fe_uint64(&c
->xendev
, "page-ref", &val
) == -1)
99 if (xenstore_read_fe_int(&c
->xendev
, "event-channel", &c
->xendev
.remote_port
) == -1)
102 c
->page
= xenforeignmemory_map(xen_fmem
, c
->xendev
.dom
,
103 PROT_READ
| PROT_WRITE
, 1, &mfn
, NULL
);
107 xen_be_bind_evtchn(&c
->xendev
);
108 xen_pv_printf(&c
->xendev
, 1,
109 "ring mfn %"PRI_xen_pfn
", remote-port %d, local-port %d\n",
110 mfn
, c
->xendev
.remote_port
, c
->xendev
.local_port
);
115 static void common_unbind(struct common
*c
)
117 xen_pv_unbind_evtchn(&c
->xendev
);
119 xenforeignmemory_unmap(xen_fmem
, c
->page
, 1);
124 /* -------------------------------------------------------------------- */
125 /* Send an event to the keyboard frontend driver */
126 static int xenfb_kbd_event(struct XenInput
*xenfb
,
127 union xenkbd_in_event
*event
)
129 struct xenkbd_page
*page
= xenfb
->c
.page
;
132 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
137 prod
= page
->in_prod
;
138 if (prod
- page
->in_cons
== XENKBD_IN_RING_LEN
) {
143 xen_mb(); /* ensure ring space available */
144 XENKBD_IN_RING_REF(page
, prod
) = *event
;
145 xen_wmb(); /* ensure ring contents visible */
146 page
->in_prod
= prod
+ 1;
147 return xen_pv_send_notify(&xenfb
->c
.xendev
);
150 /* Send a keyboard (or mouse button) event */
151 static int xenfb_send_key(struct XenInput
*xenfb
, bool down
, int keycode
)
153 union xenkbd_in_event event
;
155 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
156 event
.type
= XENKBD_TYPE_KEY
;
157 event
.key
.pressed
= down
? 1 : 0;
158 event
.key
.keycode
= keycode
;
160 return xenfb_kbd_event(xenfb
, &event
);
163 /* Send a relative mouse movement event */
164 static int xenfb_send_motion(struct XenInput
*xenfb
,
165 int rel_x
, int rel_y
, int rel_z
)
167 union xenkbd_in_event event
;
169 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
170 event
.type
= XENKBD_TYPE_MOTION
;
171 event
.motion
.rel_x
= rel_x
;
172 event
.motion
.rel_y
= rel_y
;
173 event
.motion
.rel_z
= rel_z
;
175 return xenfb_kbd_event(xenfb
, &event
);
178 /* Send an absolute mouse movement event */
179 static int xenfb_send_position(struct XenInput
*xenfb
,
180 int abs_x
, int abs_y
, int z
)
182 union xenkbd_in_event event
;
184 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
185 event
.type
= XENKBD_TYPE_POS
;
186 event
.pos
.abs_x
= abs_x
;
187 event
.pos
.abs_y
= abs_y
;
190 return xenfb_kbd_event(xenfb
, &event
);
194 * Send a key event from the client to the guest OS
195 * QEMU gives us a QCode.
196 * We have to turn this into a Linux Input layer keycode.
198 * Wish we could just send scancodes straight to the guest which
199 * already has code for dealing with this...
201 static void xenfb_key_event(DeviceState
*dev
, QemuConsole
*src
,
204 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
205 InputKeyEvent
*key
= evt
->u
.key
.data
;
206 int qcode
= qemu_input_key_value_to_qcode(key
->key
);
209 if (qcode
< qemu_input_map_qcode_to_linux_len
) {
210 lnx
= qemu_input_map_qcode_to_linux
[qcode
];
213 trace_xenfb_key_event(xenfb
, lnx
, key
->down
);
214 xenfb_send_key(xenfb
, key
->down
, lnx
);
220 * Send a mouse event from the client to the guest OS
222 * The QEMU mouse can be in either relative, or absolute mode.
223 * Movement is sent separately from button state, which has to
224 * be encoded as virtual key events. We also don't actually get
225 * given any button up/down events, so have to track changes in
228 static void xenfb_mouse_event(DeviceState
*dev
, QemuConsole
*src
,
231 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
233 InputMoveEvent
*move
;
235 DisplaySurface
*surface
;
239 case INPUT_EVENT_KIND_BTN
:
240 btn
= evt
->u
.btn
.data
;
241 switch (btn
->button
) {
242 case INPUT_BUTTON_LEFT
:
243 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
);
245 case INPUT_BUTTON_RIGHT
:
246 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 1);
248 case INPUT_BUTTON_MIDDLE
:
249 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 2);
251 case INPUT_BUTTON_WHEEL_UP
:
256 case INPUT_BUTTON_WHEEL_DOWN
:
266 case INPUT_EVENT_KIND_ABS
:
267 move
= evt
->u
.abs
.data
;
268 if (xenfb
->raw_pointer_wanted
) {
269 xenfb
->axis
[move
->axis
] = move
->value
;
271 con
= qemu_console_lookup_by_index(0);
273 xen_pv_printf(&xenfb
->c
.xendev
, 0, "No QEMU console available");
276 surface
= qemu_console_surface(con
);
277 switch (move
->axis
) {
279 scale
= surface_width(surface
) - 1;
282 scale
= surface_height(surface
) - 1;
288 xenfb
->axis
[move
->axis
] = move
->value
* scale
/ 0x7fff;
292 case INPUT_EVENT_KIND_REL
:
293 move
= evt
->u
.rel
.data
;
294 xenfb
->axis
[move
->axis
] += move
->value
;
302 static void xenfb_mouse_sync(DeviceState
*dev
)
304 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
306 trace_xenfb_mouse_event(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
307 xenfb
->axis
[INPUT_AXIS_Y
],
309 xenfb
->abs_pointer_wanted
);
310 if (xenfb
->abs_pointer_wanted
) {
311 xenfb_send_position(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
312 xenfb
->axis
[INPUT_AXIS_Y
],
315 xenfb_send_motion(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
316 xenfb
->axis
[INPUT_AXIS_Y
],
318 xenfb
->axis
[INPUT_AXIS_X
] = 0;
319 xenfb
->axis
[INPUT_AXIS_Y
] = 0;
324 static QemuInputHandler xenfb_keyboard
= {
325 .name
= "Xen PV Keyboard",
326 .mask
= INPUT_EVENT_MASK_KEY
,
327 .event
= xenfb_key_event
,
330 static QemuInputHandler xenfb_abs_mouse
= {
331 .name
= "Xen PV Mouse",
332 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_ABS
,
333 .event
= xenfb_mouse_event
,
334 .sync
= xenfb_mouse_sync
,
337 static QemuInputHandler xenfb_rel_mouse
= {
338 .name
= "Xen PV Mouse",
339 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_REL
,
340 .event
= xenfb_mouse_event
,
341 .sync
= xenfb_mouse_sync
,
344 static int input_init(struct XenDevice
*xendev
)
346 xenstore_write_be_int(xendev
, "feature-abs-pointer", 1);
347 xenstore_write_be_int(xendev
, "feature-raw-pointer", 1);
351 static int input_initialise(struct XenDevice
*xendev
)
353 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
356 rc
= common_bind(&in
->c
);
363 static void input_connected(struct XenDevice
*xendev
)
365 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
367 if (xenstore_read_fe_int(xendev
, "request-abs-pointer",
368 &in
->abs_pointer_wanted
) == -1) {
369 in
->abs_pointer_wanted
= 0;
371 if (xenstore_read_fe_int(xendev
, "request-raw-pointer",
372 &in
->raw_pointer_wanted
) == -1) {
373 in
->raw_pointer_wanted
= 0;
375 if (in
->raw_pointer_wanted
&& in
->abs_pointer_wanted
== 0) {
376 xen_pv_printf(xendev
, 0, "raw pointer set without abs pointer");
380 qemu_input_handler_unregister(in
->qkbd
);
383 qemu_input_handler_unregister(in
->qmou
);
385 trace_xenfb_input_connected(xendev
, in
->abs_pointer_wanted
);
387 in
->qkbd
= qemu_input_handler_register((DeviceState
*)in
, &xenfb_keyboard
);
388 in
->qmou
= qemu_input_handler_register((DeviceState
*)in
,
389 in
->abs_pointer_wanted
? &xenfb_abs_mouse
: &xenfb_rel_mouse
);
391 if (in
->raw_pointer_wanted
) {
392 qemu_input_handler_activate(in
->qkbd
);
393 qemu_input_handler_activate(in
->qmou
);
397 static void input_disconnect(struct XenDevice
*xendev
)
399 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
402 qemu_input_handler_unregister(in
->qkbd
);
406 qemu_input_handler_unregister(in
->qmou
);
409 common_unbind(&in
->c
);
412 static void input_event(struct XenDevice
*xendev
)
414 struct XenInput
*xenfb
= container_of(xendev
, struct XenInput
, c
.xendev
);
415 struct xenkbd_page
*page
= xenfb
->c
.page
;
417 /* We don't understand any keyboard events, so just ignore them. */
418 if (page
->out_prod
== page
->out_cons
)
420 page
->out_cons
= page
->out_prod
;
421 xen_pv_send_notify(&xenfb
->c
.xendev
);
424 /* -------------------------------------------------------------------- */
426 static void xenfb_copy_mfns(int mode
, int count
, xen_pfn_t
*dst
, void *src
)
428 uint32_t *src32
= src
;
429 uint64_t *src64
= src
;
432 for (i
= 0; i
< count
; i
++)
433 dst
[i
] = (mode
== 32) ? src32
[i
] : src64
[i
];
436 static int xenfb_map_fb(struct XenFB
*xenfb
)
438 struct xenfb_page
*page
= xenfb
->c
.page
;
439 char *protocol
= xenfb
->c
.xendev
.protocol
;
441 xen_pfn_t
*pgmfns
= NULL
;
442 xen_pfn_t
*fbmfns
= NULL
;
446 /* default to native */
448 mode
= sizeof(unsigned long) * 8;
452 * Undefined protocol, some guesswork needed.
454 * Old frontends which don't set the protocol use
455 * one page directory only, thus pd[1] must be zero.
456 * pd[1] of the 32bit struct layout and the lower
457 * 32 bits of pd[0] of the 64bit struct layout have
458 * the same location, so we can check that ...
460 uint32_t *ptr32
= NULL
;
461 uint32_t *ptr64
= NULL
;
462 #if defined(__i386__)
463 ptr32
= (void*)page
->pd
;
464 ptr64
= ((void*)page
->pd
) + 4;
465 #elif defined(__x86_64__)
466 ptr32
= ((void*)page
->pd
) - 4;
467 ptr64
= (void*)page
->pd
;
478 #if defined(__x86_64__)
479 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_32
) == 0) {
480 /* 64bit dom0, 32bit domU */
482 pd
= ((void*)page
->pd
) - 4;
483 #elif defined(__i386__)
484 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_64
) == 0) {
485 /* 32bit dom0, 64bit domU */
487 pd
= ((void*)page
->pd
) + 4;
492 munmap(xenfb
->pixels
, xenfb
->fbpages
* XC_PAGE_SIZE
);
493 xenfb
->pixels
= NULL
;
496 xenfb
->fbpages
= DIV_ROUND_UP(xenfb
->fb_len
, XC_PAGE_SIZE
);
497 n_fbdirs
= xenfb
->fbpages
* mode
/ 8;
498 n_fbdirs
= DIV_ROUND_UP(n_fbdirs
, XC_PAGE_SIZE
);
500 pgmfns
= g_malloc0(sizeof(xen_pfn_t
) * n_fbdirs
);
501 fbmfns
= g_malloc0(sizeof(xen_pfn_t
) * xenfb
->fbpages
);
503 xenfb_copy_mfns(mode
, n_fbdirs
, pgmfns
, pd
);
504 map
= xenforeignmemory_map(xen_fmem
, xenfb
->c
.xendev
.dom
,
505 PROT_READ
, n_fbdirs
, pgmfns
, NULL
);
508 xenfb_copy_mfns(mode
, xenfb
->fbpages
, fbmfns
, map
);
509 xenforeignmemory_unmap(xen_fmem
, map
, n_fbdirs
);
511 xenfb
->pixels
= xenforeignmemory_map(xen_fmem
, xenfb
->c
.xendev
.dom
,
512 PROT_READ
, xenfb
->fbpages
, fbmfns
, NULL
);
513 if (xenfb
->pixels
== NULL
)
516 ret
= 0; /* all is fine */
524 static int xenfb_configure_fb(struct XenFB
*xenfb
, size_t fb_len_lim
,
525 int width
, int height
, int depth
,
526 size_t fb_len
, int offset
, int row_stride
)
528 size_t mfn_sz
= sizeof(*((struct xenfb_page
*)0)->pd
);
529 size_t pd_len
= sizeof(((struct xenfb_page
*)0)->pd
) / mfn_sz
;
530 size_t fb_pages
= pd_len
* XC_PAGE_SIZE
/ mfn_sz
;
531 size_t fb_len_max
= fb_pages
* XC_PAGE_SIZE
;
532 int max_width
, max_height
;
534 if (fb_len_lim
> fb_len_max
) {
535 xen_pv_printf(&xenfb
->c
.xendev
, 0,
536 "fb size limit %zu exceeds %zu, corrected\n",
537 fb_len_lim
, fb_len_max
);
538 fb_len_lim
= fb_len_max
;
540 if (fb_len_lim
&& fb_len
> fb_len_lim
) {
541 xen_pv_printf(&xenfb
->c
.xendev
, 0,
542 "frontend fb size %zu limited to %zu\n",
546 if (depth
!= 8 && depth
!= 16 && depth
!= 24 && depth
!= 32) {
547 xen_pv_printf(&xenfb
->c
.xendev
, 0,
548 "can't handle frontend fb depth %d\n",
552 if (row_stride
<= 0 || row_stride
> fb_len
) {
553 xen_pv_printf(&xenfb
->c
.xendev
, 0, "invalid frontend stride %d\n",
557 max_width
= row_stride
/ (depth
/ 8);
558 if (width
< 0 || width
> max_width
) {
559 xen_pv_printf(&xenfb
->c
.xendev
, 0,
560 "invalid frontend width %d limited to %d\n",
564 if (offset
< 0 || offset
>= fb_len
) {
565 xen_pv_printf(&xenfb
->c
.xendev
, 0,
566 "invalid frontend offset %d (max %zu)\n",
570 max_height
= (fb_len
- offset
) / row_stride
;
571 if (height
< 0 || height
> max_height
) {
572 xen_pv_printf(&xenfb
->c
.xendev
, 0,
573 "invalid frontend height %d limited to %d\n",
577 xenfb
->fb_len
= fb_len
;
578 xenfb
->row_stride
= row_stride
;
579 xenfb
->depth
= depth
;
580 xenfb
->width
= width
;
581 xenfb
->height
= height
;
582 xenfb
->offset
= offset
;
583 xenfb
->up_fullscreen
= 1;
584 xenfb
->do_resize
= 1;
585 xen_pv_printf(&xenfb
->c
.xendev
, 1,
586 "framebuffer %dx%dx%d offset %d stride %d\n",
587 width
, height
, depth
, offset
, row_stride
);
591 /* A convenient function for munging pixels between different depths */
592 #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
593 for (line = y ; line < (y+h) ; line++) { \
594 SRC_T *src = (SRC_T *)(xenfb->pixels \
596 + (line * xenfb->row_stride) \
597 + (x * xenfb->depth / 8)); \
598 DST_T *dst = (DST_T *)(data \
599 + (line * linesize) \
602 const int RSS = 32 - (RSB + GSB + BSB); \
603 const int GSS = 32 - (GSB + BSB); \
604 const int BSS = 32 - (BSB); \
605 const uint32_t RSM = (~0U) << (32 - RSB); \
606 const uint32_t GSM = (~0U) << (32 - GSB); \
607 const uint32_t BSM = (~0U) << (32 - BSB); \
608 const int RDS = 32 - (RDB + GDB + BDB); \
609 const int GDS = 32 - (GDB + BDB); \
610 const int BDS = 32 - (BDB); \
611 const uint32_t RDM = (~0U) << (32 - RDB); \
612 const uint32_t GDM = (~0U) << (32 - GDB); \
613 const uint32_t BDM = (~0U) << (32 - BDB); \
614 for (col = x ; col < (x+w) ; col++) { \
615 uint32_t spix = *src; \
616 *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
617 (((spix << GSS) & GSM & GDM) >> GDS) | \
618 (((spix << BSS) & BSM & BDM) >> BDS); \
619 src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
620 dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
626 * This copies data from the guest framebuffer region, into QEMU's
627 * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
628 * uses something else we must convert and copy, otherwise we can
629 * supply the buffer directly and no thing here.
631 static void xenfb_guest_copy(struct XenFB
*xenfb
, int x
, int y
, int w
, int h
)
633 DisplaySurface
*surface
= qemu_console_surface(xenfb
->con
);
635 int bpp
= surface_bits_per_pixel(surface
);
636 int linesize
= surface_stride(surface
);
637 uint8_t *data
= surface_data(surface
);
639 if (!is_buffer_shared(surface
)) {
640 switch (xenfb
->depth
) {
643 BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
644 } else if (bpp
== 32) {
645 BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
652 BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
653 } else if (bpp
== 32) {
654 BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
663 if (oops
) /* should not happen */
664 xen_pv_printf(&xenfb
->c
.xendev
, 0, "%s: oops: convert %d -> %d bpp?\n",
665 __func__
, xenfb
->depth
, bpp
);
667 dpy_gfx_update(xenfb
->con
, x
, y
, w
, h
);
670 #ifdef XENFB_TYPE_REFRESH_PERIOD
671 static int xenfb_queue_full(struct XenFB
*xenfb
)
673 struct xenfb_page
*page
= xenfb
->c
.page
;
679 prod
= page
->in_prod
;
680 cons
= page
->in_cons
;
681 return prod
- cons
== XENFB_IN_RING_LEN
;
684 static void xenfb_send_event(struct XenFB
*xenfb
, union xenfb_in_event
*event
)
687 struct xenfb_page
*page
= xenfb
->c
.page
;
689 prod
= page
->in_prod
;
690 /* caller ensures !xenfb_queue_full() */
691 xen_mb(); /* ensure ring space available */
692 XENFB_IN_RING_REF(page
, prod
) = *event
;
693 xen_wmb(); /* ensure ring contents visible */
694 page
->in_prod
= prod
+ 1;
696 xen_pv_send_notify(&xenfb
->c
.xendev
);
699 static void xenfb_send_refresh_period(struct XenFB
*xenfb
, int period
)
701 union xenfb_in_event event
;
703 memset(&event
, 0, sizeof(event
));
704 event
.type
= XENFB_TYPE_REFRESH_PERIOD
;
705 event
.refresh_period
.period
= period
;
706 xenfb_send_event(xenfb
, &event
);
711 * Periodic update of display.
712 * Also transmit the refresh interval to the frontend.
714 * Never ever do any qemu display operations
715 * (resize, screen update) outside this function.
716 * Our screen might be inactive. When asked for
717 * an update we know it is active.
719 static void xenfb_update(void *opaque
)
721 struct XenFB
*xenfb
= opaque
;
722 DisplaySurface
*surface
;
725 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
728 if (!xenfb
->feature_update
) {
729 /* we don't get update notifications, thus use the
730 * sledge hammer approach ... */
731 xenfb
->up_fullscreen
= 1;
734 /* resize if needed */
735 if (xenfb
->do_resize
) {
736 pixman_format_code_t format
;
738 xenfb
->do_resize
= 0;
739 switch (xenfb
->depth
) {
742 /* console.c supported depth -> buffer can be used directly */
743 format
= qemu_default_pixman_format(xenfb
->depth
, true);
744 surface
= qemu_create_displaysurface_from
745 (xenfb
->width
, xenfb
->height
, format
,
746 xenfb
->row_stride
, xenfb
->pixels
+ xenfb
->offset
);
749 /* we must convert stuff */
750 surface
= qemu_create_displaysurface(xenfb
->width
, xenfb
->height
);
753 dpy_gfx_replace_surface(xenfb
->con
, surface
);
754 xen_pv_printf(&xenfb
->c
.xendev
, 1,
755 "update: resizing: %dx%d @ %d bpp%s\n",
756 xenfb
->width
, xenfb
->height
, xenfb
->depth
,
757 is_buffer_shared(surface
) ? " (shared)" : "");
758 xenfb
->up_fullscreen
= 1;
761 /* run queued updates */
762 if (xenfb
->up_fullscreen
) {
763 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: fullscreen\n");
764 xenfb_guest_copy(xenfb
, 0, 0, xenfb
->width
, xenfb
->height
);
765 } else if (xenfb
->up_count
) {
766 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: %d rects\n",
768 for (i
= 0; i
< xenfb
->up_count
; i
++)
769 xenfb_guest_copy(xenfb
,
770 xenfb
->up_rects
[i
].x
,
771 xenfb
->up_rects
[i
].y
,
772 xenfb
->up_rects
[i
].w
,
773 xenfb
->up_rects
[i
].h
);
775 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: nothing\n");
778 xenfb
->up_fullscreen
= 0;
781 static void xenfb_update_interval(void *opaque
, uint64_t interval
)
783 struct XenFB
*xenfb
= opaque
;
785 if (xenfb
->feature_update
) {
786 #ifdef XENFB_TYPE_REFRESH_PERIOD
787 if (xenfb_queue_full(xenfb
)) {
790 xenfb_send_refresh_period(xenfb
, interval
);
795 /* QEMU display state changed, so refresh the framebuffer copy */
796 static void xenfb_invalidate(void *opaque
)
798 struct XenFB
*xenfb
= opaque
;
799 xenfb
->up_fullscreen
= 1;
802 static void xenfb_handle_events(struct XenFB
*xenfb
)
804 uint32_t prod
, cons
, out_cons
;
805 struct xenfb_page
*page
= xenfb
->c
.page
;
807 prod
= page
->out_prod
;
808 out_cons
= page
->out_cons
;
809 if (prod
- out_cons
> XENFB_OUT_RING_LEN
) {
812 xen_rmb(); /* ensure we see ring contents up to prod */
813 for (cons
= out_cons
; cons
!= prod
; cons
++) {
814 union xenfb_out_event
*event
= &XENFB_OUT_RING_REF(page
, cons
);
815 uint8_t type
= event
->type
;
819 case XENFB_TYPE_UPDATE
:
820 if (xenfb
->up_count
== UP_QUEUE
)
821 xenfb
->up_fullscreen
= 1;
822 if (xenfb
->up_fullscreen
)
824 x
= MAX(event
->update
.x
, 0);
825 y
= MAX(event
->update
.y
, 0);
826 w
= MIN(event
->update
.width
, xenfb
->width
- x
);
827 h
= MIN(event
->update
.height
, xenfb
->height
- y
);
828 if (w
< 0 || h
< 0) {
829 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update ignored\n");
832 if (x
!= event
->update
.x
||
833 y
!= event
->update
.y
||
834 w
!= event
->update
.width
||
835 h
!= event
->update
.height
) {
836 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update clipped\n");
838 if (w
== xenfb
->width
&& h
> xenfb
->height
/ 2) {
839 /* scroll detector: updated more than 50% of the lines,
840 * don't bother keeping track of the rectangles then */
841 xenfb
->up_fullscreen
= 1;
843 xenfb
->up_rects
[xenfb
->up_count
].x
= x
;
844 xenfb
->up_rects
[xenfb
->up_count
].y
= y
;
845 xenfb
->up_rects
[xenfb
->up_count
].w
= w
;
846 xenfb
->up_rects
[xenfb
->up_count
].h
= h
;
850 #ifdef XENFB_TYPE_RESIZE
851 case XENFB_TYPE_RESIZE
:
852 if (xenfb_configure_fb(xenfb
, xenfb
->fb_len
,
854 event
->resize
.height
,
857 event
->resize
.offset
,
858 event
->resize
.stride
) < 0)
860 xenfb_invalidate(xenfb
);
865 xen_mb(); /* ensure we're done with ring contents */
866 page
->out_cons
= cons
;
869 static int fb_init(struct XenDevice
*xendev
)
871 #ifdef XENFB_TYPE_RESIZE
872 xenstore_write_be_int(xendev
, "feature-resize", 1);
877 static int fb_initialise(struct XenDevice
*xendev
)
879 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
880 struct xenfb_page
*fb_page
;
884 if (xenstore_read_fe_int(xendev
, "videoram", &videoram
) == -1)
887 rc
= common_bind(&fb
->c
);
891 fb_page
= fb
->c
.page
;
892 rc
= xenfb_configure_fb(fb
, videoram
* 1024 * 1024U,
893 fb_page
->width
, fb_page
->height
, fb_page
->depth
,
894 fb_page
->mem_length
, 0, fb_page
->line_length
);
898 rc
= xenfb_map_fb(fb
);
902 fb
->con
= graphic_console_init(NULL
, 0, &xenfb_ops
, fb
);
904 if (xenstore_read_fe_int(xendev
, "feature-update", &fb
->feature_update
) == -1)
905 fb
->feature_update
= 0;
906 if (fb
->feature_update
)
907 xenstore_write_be_int(xendev
, "request-update", 1);
909 xen_pv_printf(xendev
, 1, "feature-update=%d, videoram=%d\n",
910 fb
->feature_update
, videoram
);
914 static void fb_disconnect(struct XenDevice
*xendev
)
916 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
919 * FIXME: qemu can't un-init gfx display (yet?).
920 * Replacing the framebuffer with anonymous shared memory
921 * instead. This releases the guest pages and keeps qemu happy.
923 xenforeignmemory_unmap(xen_fmem
, fb
->pixels
, fb
->fbpages
);
924 fb
->pixels
= mmap(fb
->pixels
, fb
->fbpages
* XC_PAGE_SIZE
,
925 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANON
,
927 if (fb
->pixels
== MAP_FAILED
) {
928 xen_pv_printf(xendev
, 0,
929 "Couldn't replace the framebuffer with anonymous memory errno=%d\n",
932 common_unbind(&fb
->c
);
933 fb
->feature_update
= 0;
937 static void fb_frontend_changed(struct XenDevice
*xendev
, const char *node
)
939 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
942 * Set state to Connected *again* once the frontend switched
943 * to connected. We must trigger the watch a second time to
944 * workaround a frontend bug.
946 if (fb
->bug_trigger
== 0 && strcmp(node
, "state") == 0 &&
947 xendev
->fe_state
== XenbusStateConnected
&&
948 xendev
->be_state
== XenbusStateConnected
) {
949 xen_pv_printf(xendev
, 2, "re-trigger connected (frontend bug)\n");
950 xen_be_set_state(xendev
, XenbusStateConnected
);
951 fb
->bug_trigger
= 1; /* only once */
955 static void fb_event(struct XenDevice
*xendev
)
957 struct XenFB
*xenfb
= container_of(xendev
, struct XenFB
, c
.xendev
);
959 xenfb_handle_events(xenfb
);
960 xen_pv_send_notify(&xenfb
->c
.xendev
);
963 /* -------------------------------------------------------------------- */
965 struct XenDevOps xen_kbdmouse_ops
= {
966 .size
= sizeof(struct XenInput
),
968 .initialise
= input_initialise
,
969 .connected
= input_connected
,
970 .disconnect
= input_disconnect
,
971 .event
= input_event
,
974 struct XenDevOps xen_framebuffer_ops
= {
975 .size
= sizeof(struct XenFB
),
977 .initialise
= fb_initialise
,
978 .disconnect
= fb_disconnect
,
980 .frontend_changed
= fb_frontend_changed
,
983 static const GraphicHwOps xenfb_ops
= {
984 .invalidate
= xenfb_invalidate
,
985 .gfx_update
= xenfb_update
,
986 .update_interval
= xenfb_update_interval
,