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"
28 #include "qemu/units.h"
31 #include "ui/console.h"
32 #include "hw/xen/xen-legacy-backend.h"
34 #include "hw/xen/interface/io/fbif.h"
35 #include "hw/xen/interface/io/kbdif.h"
36 #include "hw/xen/interface/io/protocols.h"
41 #define BTN_LEFT 0x110 /* from <linux/input.h> */
44 /* -------------------------------------------------------------------- */
47 struct XenLegacyDevice xendev
; /* must be first */
53 int abs_pointer_wanted
; /* Whether guest supports absolute pointer */
54 int raw_pointer_wanted
; /* Whether guest supports raw (unscaled) pointer */
55 QemuInputHandlerState
*qkbd
;
56 QemuInputHandlerState
*qmou
;
57 int axis
[INPUT_AXIS__MAX
];
84 static const GraphicHwOps xenfb_ops
;
86 /* -------------------------------------------------------------------- */
88 static int common_bind(struct common
*c
)
93 if (xenstore_read_fe_uint64(&c
->xendev
, "page-ref", &val
) == -1)
98 if (xenstore_read_fe_int(&c
->xendev
, "event-channel", &c
->xendev
.remote_port
) == -1)
101 c
->page
= qemu_xen_foreignmem_map(c
->xendev
.dom
, NULL
,
102 PROT_READ
| PROT_WRITE
, 1, &mfn
,
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 qemu_xen_foreignmem_unmap(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 const QemuInputHandler xenfb_keyboard
= {
325 .name
= "Xen PV Keyboard",
326 .mask
= INPUT_EVENT_MASK_KEY
,
327 .event
= xenfb_key_event
,
330 static const 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 const 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 XenLegacyDevice
*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 XenLegacyDevice
*xendev
)
353 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
356 rc
= common_bind(&in
->c
);
363 static void input_connected(struct XenLegacyDevice
*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 XenLegacyDevice
*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 XenLegacyDevice
*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
* XEN_PAGE_SIZE
);
493 xenfb
->pixels
= NULL
;
496 xenfb
->fbpages
= DIV_ROUND_UP(xenfb
->fb_len
, XEN_PAGE_SIZE
);
497 n_fbdirs
= xenfb
->fbpages
* mode
/ 8;
498 n_fbdirs
= DIV_ROUND_UP(n_fbdirs
, XEN_PAGE_SIZE
);
500 pgmfns
= g_new0(xen_pfn_t
, n_fbdirs
);
501 fbmfns
= g_new0(xen_pfn_t
, xenfb
->fbpages
);
503 xenfb_copy_mfns(mode
, n_fbdirs
, pgmfns
, pd
);
504 map
= qemu_xen_foreignmem_map(xenfb
->c
.xendev
.dom
, NULL
, PROT_READ
,
505 n_fbdirs
, pgmfns
, NULL
);
508 xenfb_copy_mfns(mode
, xenfb
->fbpages
, fbmfns
, map
);
509 qemu_xen_foreignmem_unmap(map
, n_fbdirs
);
511 xenfb
->pixels
= qemu_xen_foreignmem_map(xenfb
->c
.xendev
.dom
, NULL
,
512 PROT_READ
, xenfb
->fbpages
,
514 if (xenfb
->pixels
== NULL
)
517 ret
= 0; /* all is fine */
525 static int xenfb_configure_fb(struct XenFB
*xenfb
, size_t fb_len_lim
,
526 int width
, int height
, int depth
,
527 size_t fb_len
, int offset
, int row_stride
)
529 size_t mfn_sz
= sizeof_field(struct xenfb_page
, pd
[0]);
530 size_t pd_len
= sizeof_field(struct xenfb_page
, pd
) / mfn_sz
;
531 size_t fb_pages
= pd_len
* XEN_PAGE_SIZE
/ mfn_sz
;
532 size_t fb_len_max
= fb_pages
* XEN_PAGE_SIZE
;
533 int max_width
, max_height
;
535 if (fb_len_lim
> fb_len_max
) {
536 xen_pv_printf(&xenfb
->c
.xendev
, 0,
537 "fb size limit %zu exceeds %zu, corrected\n",
538 fb_len_lim
, fb_len_max
);
539 fb_len_lim
= fb_len_max
;
541 if (fb_len_lim
&& fb_len
> fb_len_lim
) {
542 xen_pv_printf(&xenfb
->c
.xendev
, 0,
543 "frontend fb size %zu limited to %zu\n",
547 if (depth
!= 8 && depth
!= 16 && depth
!= 24 && depth
!= 32) {
548 xen_pv_printf(&xenfb
->c
.xendev
, 0,
549 "can't handle frontend fb depth %d\n",
553 if (row_stride
<= 0 || row_stride
> fb_len
) {
554 xen_pv_printf(&xenfb
->c
.xendev
, 0, "invalid frontend stride %d\n",
558 max_width
= row_stride
/ (depth
/ 8);
559 if (width
< 0 || width
> max_width
) {
560 xen_pv_printf(&xenfb
->c
.xendev
, 0,
561 "invalid frontend width %d limited to %d\n",
565 if (offset
< 0 || offset
>= fb_len
) {
566 xen_pv_printf(&xenfb
->c
.xendev
, 0,
567 "invalid frontend offset %d (max %zu)\n",
571 max_height
= (fb_len
- offset
) / row_stride
;
572 if (height
< 0 || height
> max_height
) {
573 xen_pv_printf(&xenfb
->c
.xendev
, 0,
574 "invalid frontend height %d limited to %d\n",
578 xenfb
->fb_len
= fb_len
;
579 xenfb
->row_stride
= row_stride
;
580 xenfb
->depth
= depth
;
581 xenfb
->width
= width
;
582 xenfb
->height
= height
;
583 xenfb
->offset
= offset
;
584 xenfb
->up_fullscreen
= 1;
585 xenfb
->do_resize
= 1;
586 xen_pv_printf(&xenfb
->c
.xendev
, 1,
587 "framebuffer %dx%dx%d offset %d stride %d\n",
588 width
, height
, depth
, offset
, row_stride
);
592 /* A convenient function for munging pixels between different depths */
593 #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
594 for (line = y ; line < (y+h) ; line++) { \
595 SRC_T *src = (SRC_T *)(xenfb->pixels \
597 + (line * xenfb->row_stride) \
598 + (x * xenfb->depth / 8)); \
599 DST_T *dst = (DST_T *)(data \
600 + (line * linesize) \
603 const int RSS = 32 - (RSB + GSB + BSB); \
604 const int GSS = 32 - (GSB + BSB); \
605 const int BSS = 32 - (BSB); \
606 const uint32_t RSM = (~0U) << (32 - RSB); \
607 const uint32_t GSM = (~0U) << (32 - GSB); \
608 const uint32_t BSM = (~0U) << (32 - BSB); \
609 const int RDS = 32 - (RDB + GDB + BDB); \
610 const int GDS = 32 - (GDB + BDB); \
611 const int BDS = 32 - (BDB); \
612 const uint32_t RDM = (~0U) << (32 - RDB); \
613 const uint32_t GDM = (~0U) << (32 - GDB); \
614 const uint32_t BDM = (~0U) << (32 - BDB); \
615 for (col = x ; col < (x+w) ; col++) { \
616 uint32_t spix = *src; \
617 *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
618 (((spix << GSS) & GSM & GDM) >> GDS) | \
619 (((spix << BSS) & BSM & BDM) >> BDS); \
620 src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
621 dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
627 * This copies data from the guest framebuffer region, into QEMU's
628 * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
629 * uses something else we must convert and copy, otherwise we can
630 * supply the buffer directly and no thing here.
632 static void xenfb_guest_copy(struct XenFB
*xenfb
, int x
, int y
, int w
, int h
)
634 DisplaySurface
*surface
= qemu_console_surface(xenfb
->con
);
636 int bpp
= surface_bits_per_pixel(surface
);
637 int linesize
= surface_stride(surface
);
638 uint8_t *data
= surface_data(surface
);
640 if (!is_buffer_shared(surface
)) {
641 switch (xenfb
->depth
) {
644 BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
645 } else if (bpp
== 32) {
646 BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
653 BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
654 } else if (bpp
== 32) {
655 BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
664 if (oops
) /* should not happen */
665 xen_pv_printf(&xenfb
->c
.xendev
, 0, "%s: oops: convert %d -> %d bpp?\n",
666 __func__
, xenfb
->depth
, bpp
);
668 dpy_gfx_update(xenfb
->con
, x
, y
, w
, h
);
671 #ifdef XENFB_TYPE_REFRESH_PERIOD
672 static int xenfb_queue_full(struct XenFB
*xenfb
)
674 struct xenfb_page
*page
= xenfb
->c
.page
;
680 prod
= page
->in_prod
;
681 cons
= page
->in_cons
;
682 return prod
- cons
== XENFB_IN_RING_LEN
;
685 static void xenfb_send_event(struct XenFB
*xenfb
, union xenfb_in_event
*event
)
688 struct xenfb_page
*page
= xenfb
->c
.page
;
690 prod
= page
->in_prod
;
691 /* caller ensures !xenfb_queue_full() */
692 xen_mb(); /* ensure ring space available */
693 XENFB_IN_RING_REF(page
, prod
) = *event
;
694 xen_wmb(); /* ensure ring contents visible */
695 page
->in_prod
= prod
+ 1;
697 xen_pv_send_notify(&xenfb
->c
.xendev
);
700 static void xenfb_send_refresh_period(struct XenFB
*xenfb
, int period
)
702 union xenfb_in_event event
;
704 memset(&event
, 0, sizeof(event
));
705 event
.type
= XENFB_TYPE_REFRESH_PERIOD
;
706 event
.refresh_period
.period
= period
;
707 xenfb_send_event(xenfb
, &event
);
712 * Periodic update of display.
713 * Also transmit the refresh interval to the frontend.
715 * Never ever do any qemu display operations
716 * (resize, screen update) outside this function.
717 * Our screen might be inactive. When asked for
718 * an update we know it is active.
720 static void xenfb_update(void *opaque
)
722 struct XenFB
*xenfb
= opaque
;
723 DisplaySurface
*surface
;
726 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
729 if (!xenfb
->feature_update
) {
730 /* we don't get update notifications, thus use the
731 * sledge hammer approach ... */
732 xenfb
->up_fullscreen
= 1;
735 /* resize if needed */
736 if (xenfb
->do_resize
) {
737 pixman_format_code_t format
;
739 xenfb
->do_resize
= 0;
740 switch (xenfb
->depth
) {
743 /* console.c supported depth -> buffer can be used directly */
744 format
= qemu_default_pixman_format(xenfb
->depth
, true);
745 surface
= qemu_create_displaysurface_from
746 (xenfb
->width
, xenfb
->height
, format
,
747 xenfb
->row_stride
, xenfb
->pixels
+ xenfb
->offset
);
750 /* we must convert stuff */
751 surface
= qemu_create_displaysurface(xenfb
->width
, xenfb
->height
);
754 dpy_gfx_replace_surface(xenfb
->con
, surface
);
755 xen_pv_printf(&xenfb
->c
.xendev
, 1,
756 "update: resizing: %dx%d @ %d bpp%s\n",
757 xenfb
->width
, xenfb
->height
, xenfb
->depth
,
758 is_buffer_shared(surface
) ? " (shared)" : "");
759 xenfb
->up_fullscreen
= 1;
762 /* run queued updates */
763 if (xenfb
->up_fullscreen
) {
764 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: fullscreen\n");
765 xenfb_guest_copy(xenfb
, 0, 0, xenfb
->width
, xenfb
->height
);
766 } else if (xenfb
->up_count
) {
767 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: %d rects\n",
769 for (i
= 0; i
< xenfb
->up_count
; i
++)
770 xenfb_guest_copy(xenfb
,
771 xenfb
->up_rects
[i
].x
,
772 xenfb
->up_rects
[i
].y
,
773 xenfb
->up_rects
[i
].w
,
774 xenfb
->up_rects
[i
].h
);
776 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: nothing\n");
779 xenfb
->up_fullscreen
= 0;
782 static void xenfb_ui_info(void *opaque
, uint32_t idx
, QemuUIInfo
*info
)
784 struct XenFB
*xenfb
= opaque
;
785 uint32_t refresh_rate
;
787 if (xenfb
->feature_update
) {
788 #ifdef XENFB_TYPE_REFRESH_PERIOD
789 if (xenfb_queue_full(xenfb
)) {
793 refresh_rate
= info
->refresh_rate
;
798 /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
799 xenfb_send_refresh_period(xenfb
, 1000 * 1000 / refresh_rate
);
804 /* QEMU display state changed, so refresh the framebuffer copy */
805 static void xenfb_invalidate(void *opaque
)
807 struct XenFB
*xenfb
= opaque
;
808 xenfb
->up_fullscreen
= 1;
811 static void xenfb_handle_events(struct XenFB
*xenfb
)
813 uint32_t prod
, cons
, out_cons
;
814 struct xenfb_page
*page
= xenfb
->c
.page
;
816 prod
= page
->out_prod
;
817 out_cons
= page
->out_cons
;
818 if (prod
- out_cons
> XENFB_OUT_RING_LEN
) {
821 xen_rmb(); /* ensure we see ring contents up to prod */
822 for (cons
= out_cons
; cons
!= prod
; cons
++) {
823 union xenfb_out_event
*event
= &XENFB_OUT_RING_REF(page
, cons
);
824 uint8_t type
= event
->type
;
828 case XENFB_TYPE_UPDATE
:
829 if (xenfb
->up_count
== UP_QUEUE
)
830 xenfb
->up_fullscreen
= 1;
831 if (xenfb
->up_fullscreen
)
833 x
= MAX(event
->update
.x
, 0);
834 y
= MAX(event
->update
.y
, 0);
835 w
= MIN(event
->update
.width
, xenfb
->width
- x
);
836 h
= MIN(event
->update
.height
, xenfb
->height
- y
);
837 if (w
< 0 || h
< 0) {
838 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update ignored\n");
841 if (x
!= event
->update
.x
||
842 y
!= event
->update
.y
||
843 w
!= event
->update
.width
||
844 h
!= event
->update
.height
) {
845 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update clipped\n");
847 if (w
== xenfb
->width
&& h
> xenfb
->height
/ 2) {
848 /* scroll detector: updated more than 50% of the lines,
849 * don't bother keeping track of the rectangles then */
850 xenfb
->up_fullscreen
= 1;
852 xenfb
->up_rects
[xenfb
->up_count
].x
= x
;
853 xenfb
->up_rects
[xenfb
->up_count
].y
= y
;
854 xenfb
->up_rects
[xenfb
->up_count
].w
= w
;
855 xenfb
->up_rects
[xenfb
->up_count
].h
= h
;
859 #ifdef XENFB_TYPE_RESIZE
860 case XENFB_TYPE_RESIZE
:
861 if (xenfb_configure_fb(xenfb
, xenfb
->fb_len
,
863 event
->resize
.height
,
866 event
->resize
.offset
,
867 event
->resize
.stride
) < 0)
869 xenfb_invalidate(xenfb
);
874 xen_mb(); /* ensure we're done with ring contents */
875 page
->out_cons
= cons
;
878 static int fb_init(struct XenLegacyDevice
*xendev
)
880 #ifdef XENFB_TYPE_RESIZE
881 xenstore_write_be_int(xendev
, "feature-resize", 1);
886 static int fb_initialise(struct XenLegacyDevice
*xendev
)
888 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
889 struct xenfb_page
*fb_page
;
893 if (xenstore_read_fe_int(xendev
, "videoram", &videoram
) == -1)
896 rc
= common_bind(&fb
->c
);
900 fb_page
= fb
->c
.page
;
901 rc
= xenfb_configure_fb(fb
, videoram
* MiB
,
902 fb_page
->width
, fb_page
->height
, fb_page
->depth
,
903 fb_page
->mem_length
, 0, fb_page
->line_length
);
907 rc
= xenfb_map_fb(fb
);
911 fb
->con
= graphic_console_init(NULL
, 0, &xenfb_ops
, fb
);
913 if (xenstore_read_fe_int(xendev
, "feature-update", &fb
->feature_update
) == -1)
914 fb
->feature_update
= 0;
915 if (fb
->feature_update
)
916 xenstore_write_be_int(xendev
, "request-update", 1);
918 xen_pv_printf(xendev
, 1, "feature-update=%d, videoram=%d\n",
919 fb
->feature_update
, videoram
);
923 static void fb_disconnect(struct XenLegacyDevice
*xendev
)
925 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
928 * FIXME: qemu can't un-init gfx display (yet?).
929 * Replacing the framebuffer with anonymous shared memory
930 * instead. This releases the guest pages and keeps qemu happy.
932 qemu_xen_foreignmem_unmap(fb
->pixels
, fb
->fbpages
);
933 fb
->pixels
= mmap(fb
->pixels
, fb
->fbpages
* XEN_PAGE_SIZE
,
934 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANON
,
936 if (fb
->pixels
== MAP_FAILED
) {
937 xen_pv_printf(xendev
, 0,
938 "Couldn't replace the framebuffer with anonymous memory errno=%d\n",
941 common_unbind(&fb
->c
);
942 fb
->feature_update
= 0;
946 static void fb_frontend_changed(struct XenLegacyDevice
*xendev
,
949 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
952 * Set state to Connected *again* once the frontend switched
953 * to connected. We must trigger the watch a second time to
954 * workaround a frontend bug.
956 if (fb
->bug_trigger
== 0 && strcmp(node
, "state") == 0 &&
957 xendev
->fe_state
== XenbusStateConnected
&&
958 xendev
->be_state
== XenbusStateConnected
) {
959 xen_pv_printf(xendev
, 2, "re-trigger connected (frontend bug)\n");
960 xen_be_set_state(xendev
, XenbusStateConnected
);
961 fb
->bug_trigger
= 1; /* only once */
965 static void fb_event(struct XenLegacyDevice
*xendev
)
967 struct XenFB
*xenfb
= container_of(xendev
, struct XenFB
, c
.xendev
);
969 xenfb_handle_events(xenfb
);
970 xen_pv_send_notify(&xenfb
->c
.xendev
);
973 /* -------------------------------------------------------------------- */
975 struct XenDevOps xen_kbdmouse_ops
= {
976 .size
= sizeof(struct XenInput
),
978 .initialise
= input_initialise
,
979 .connected
= input_connected
,
980 .disconnect
= input_disconnect
,
981 .event
= input_event
,
984 struct XenDevOps xen_framebuffer_ops
= {
985 .size
= sizeof(struct XenFB
),
987 .initialise
= fb_initialise
,
988 .disconnect
= fb_disconnect
,
990 .frontend_changed
= fb_frontend_changed
,
993 static const GraphicHwOps xenfb_ops
= {
994 .invalidate
= xenfb_invalidate
,
995 .gfx_update
= xenfb_update
,
996 .ui_info
= xenfb_ui_info
,