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
= xenforeignmemory_map(xen_fmem
, c
->xendev
.dom
,
102 PROT_READ
| PROT_WRITE
, 1, &mfn
, NULL
);
106 xen_be_bind_evtchn(&c
->xendev
);
107 xen_pv_printf(&c
->xendev
, 1,
108 "ring mfn %"PRI_xen_pfn
", remote-port %d, local-port %d\n",
109 mfn
, c
->xendev
.remote_port
, c
->xendev
.local_port
);
114 static void common_unbind(struct common
*c
)
116 xen_pv_unbind_evtchn(&c
->xendev
);
118 xenforeignmemory_unmap(xen_fmem
, c
->page
, 1);
123 /* -------------------------------------------------------------------- */
124 /* Send an event to the keyboard frontend driver */
125 static int xenfb_kbd_event(struct XenInput
*xenfb
,
126 union xenkbd_in_event
*event
)
128 struct xenkbd_page
*page
= xenfb
->c
.page
;
131 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
136 prod
= page
->in_prod
;
137 if (prod
- page
->in_cons
== XENKBD_IN_RING_LEN
) {
142 xen_mb(); /* ensure ring space available */
143 XENKBD_IN_RING_REF(page
, prod
) = *event
;
144 xen_wmb(); /* ensure ring contents visible */
145 page
->in_prod
= prod
+ 1;
146 return xen_pv_send_notify(&xenfb
->c
.xendev
);
149 /* Send a keyboard (or mouse button) event */
150 static int xenfb_send_key(struct XenInput
*xenfb
, bool down
, int keycode
)
152 union xenkbd_in_event event
;
154 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
155 event
.type
= XENKBD_TYPE_KEY
;
156 event
.key
.pressed
= down
? 1 : 0;
157 event
.key
.keycode
= keycode
;
159 return xenfb_kbd_event(xenfb
, &event
);
162 /* Send a relative mouse movement event */
163 static int xenfb_send_motion(struct XenInput
*xenfb
,
164 int rel_x
, int rel_y
, int rel_z
)
166 union xenkbd_in_event event
;
168 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
169 event
.type
= XENKBD_TYPE_MOTION
;
170 event
.motion
.rel_x
= rel_x
;
171 event
.motion
.rel_y
= rel_y
;
172 event
.motion
.rel_z
= rel_z
;
174 return xenfb_kbd_event(xenfb
, &event
);
177 /* Send an absolute mouse movement event */
178 static int xenfb_send_position(struct XenInput
*xenfb
,
179 int abs_x
, int abs_y
, int z
)
181 union xenkbd_in_event event
;
183 memset(&event
, 0, XENKBD_IN_EVENT_SIZE
);
184 event
.type
= XENKBD_TYPE_POS
;
185 event
.pos
.abs_x
= abs_x
;
186 event
.pos
.abs_y
= abs_y
;
189 return xenfb_kbd_event(xenfb
, &event
);
193 * Send a key event from the client to the guest OS
194 * QEMU gives us a QCode.
195 * We have to turn this into a Linux Input layer keycode.
197 * Wish we could just send scancodes straight to the guest which
198 * already has code for dealing with this...
200 static void xenfb_key_event(DeviceState
*dev
, QemuConsole
*src
,
203 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
204 InputKeyEvent
*key
= evt
->u
.key
.data
;
205 int qcode
= qemu_input_key_value_to_qcode(key
->key
);
208 if (qcode
< qemu_input_map_qcode_to_linux_len
) {
209 lnx
= qemu_input_map_qcode_to_linux
[qcode
];
212 trace_xenfb_key_event(xenfb
, lnx
, key
->down
);
213 xenfb_send_key(xenfb
, key
->down
, lnx
);
219 * Send a mouse event from the client to the guest OS
221 * The QEMU mouse can be in either relative, or absolute mode.
222 * Movement is sent separately from button state, which has to
223 * be encoded as virtual key events. We also don't actually get
224 * given any button up/down events, so have to track changes in
227 static void xenfb_mouse_event(DeviceState
*dev
, QemuConsole
*src
,
230 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
232 InputMoveEvent
*move
;
234 DisplaySurface
*surface
;
238 case INPUT_EVENT_KIND_BTN
:
239 btn
= evt
->u
.btn
.data
;
240 switch (btn
->button
) {
241 case INPUT_BUTTON_LEFT
:
242 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
);
244 case INPUT_BUTTON_RIGHT
:
245 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 1);
247 case INPUT_BUTTON_MIDDLE
:
248 xenfb_send_key(xenfb
, btn
->down
, BTN_LEFT
+ 2);
250 case INPUT_BUTTON_WHEEL_UP
:
255 case INPUT_BUTTON_WHEEL_DOWN
:
265 case INPUT_EVENT_KIND_ABS
:
266 move
= evt
->u
.abs
.data
;
267 if (xenfb
->raw_pointer_wanted
) {
268 xenfb
->axis
[move
->axis
] = move
->value
;
270 con
= qemu_console_lookup_by_index(0);
272 xen_pv_printf(&xenfb
->c
.xendev
, 0, "No QEMU console available");
275 surface
= qemu_console_surface(con
);
276 switch (move
->axis
) {
278 scale
= surface_width(surface
) - 1;
281 scale
= surface_height(surface
) - 1;
287 xenfb
->axis
[move
->axis
] = move
->value
* scale
/ 0x7fff;
291 case INPUT_EVENT_KIND_REL
:
292 move
= evt
->u
.rel
.data
;
293 xenfb
->axis
[move
->axis
] += move
->value
;
301 static void xenfb_mouse_sync(DeviceState
*dev
)
303 struct XenInput
*xenfb
= (struct XenInput
*)dev
;
305 trace_xenfb_mouse_event(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
306 xenfb
->axis
[INPUT_AXIS_Y
],
308 xenfb
->abs_pointer_wanted
);
309 if (xenfb
->abs_pointer_wanted
) {
310 xenfb_send_position(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
311 xenfb
->axis
[INPUT_AXIS_Y
],
314 xenfb_send_motion(xenfb
, xenfb
->axis
[INPUT_AXIS_X
],
315 xenfb
->axis
[INPUT_AXIS_Y
],
317 xenfb
->axis
[INPUT_AXIS_X
] = 0;
318 xenfb
->axis
[INPUT_AXIS_Y
] = 0;
323 static QemuInputHandler xenfb_keyboard
= {
324 .name
= "Xen PV Keyboard",
325 .mask
= INPUT_EVENT_MASK_KEY
,
326 .event
= xenfb_key_event
,
329 static QemuInputHandler xenfb_abs_mouse
= {
330 .name
= "Xen PV Mouse",
331 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_ABS
,
332 .event
= xenfb_mouse_event
,
333 .sync
= xenfb_mouse_sync
,
336 static QemuInputHandler xenfb_rel_mouse
= {
337 .name
= "Xen PV Mouse",
338 .mask
= INPUT_EVENT_MASK_BTN
| INPUT_EVENT_MASK_REL
,
339 .event
= xenfb_mouse_event
,
340 .sync
= xenfb_mouse_sync
,
343 static int input_init(struct XenLegacyDevice
*xendev
)
345 xenstore_write_be_int(xendev
, "feature-abs-pointer", 1);
346 xenstore_write_be_int(xendev
, "feature-raw-pointer", 1);
350 static int input_initialise(struct XenLegacyDevice
*xendev
)
352 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
355 rc
= common_bind(&in
->c
);
362 static void input_connected(struct XenLegacyDevice
*xendev
)
364 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
366 if (xenstore_read_fe_int(xendev
, "request-abs-pointer",
367 &in
->abs_pointer_wanted
) == -1) {
368 in
->abs_pointer_wanted
= 0;
370 if (xenstore_read_fe_int(xendev
, "request-raw-pointer",
371 &in
->raw_pointer_wanted
) == -1) {
372 in
->raw_pointer_wanted
= 0;
374 if (in
->raw_pointer_wanted
&& in
->abs_pointer_wanted
== 0) {
375 xen_pv_printf(xendev
, 0, "raw pointer set without abs pointer");
379 qemu_input_handler_unregister(in
->qkbd
);
382 qemu_input_handler_unregister(in
->qmou
);
384 trace_xenfb_input_connected(xendev
, in
->abs_pointer_wanted
);
386 in
->qkbd
= qemu_input_handler_register((DeviceState
*)in
, &xenfb_keyboard
);
387 in
->qmou
= qemu_input_handler_register((DeviceState
*)in
,
388 in
->abs_pointer_wanted
? &xenfb_abs_mouse
: &xenfb_rel_mouse
);
390 if (in
->raw_pointer_wanted
) {
391 qemu_input_handler_activate(in
->qkbd
);
392 qemu_input_handler_activate(in
->qmou
);
396 static void input_disconnect(struct XenLegacyDevice
*xendev
)
398 struct XenInput
*in
= container_of(xendev
, struct XenInput
, c
.xendev
);
401 qemu_input_handler_unregister(in
->qkbd
);
405 qemu_input_handler_unregister(in
->qmou
);
408 common_unbind(&in
->c
);
411 static void input_event(struct XenLegacyDevice
*xendev
)
413 struct XenInput
*xenfb
= container_of(xendev
, struct XenInput
, c
.xendev
);
414 struct xenkbd_page
*page
= xenfb
->c
.page
;
416 /* We don't understand any keyboard events, so just ignore them. */
417 if (page
->out_prod
== page
->out_cons
)
419 page
->out_cons
= page
->out_prod
;
420 xen_pv_send_notify(&xenfb
->c
.xendev
);
423 /* -------------------------------------------------------------------- */
425 static void xenfb_copy_mfns(int mode
, int count
, xen_pfn_t
*dst
, void *src
)
427 uint32_t *src32
= src
;
428 uint64_t *src64
= src
;
431 for (i
= 0; i
< count
; i
++)
432 dst
[i
] = (mode
== 32) ? src32
[i
] : src64
[i
];
435 static int xenfb_map_fb(struct XenFB
*xenfb
)
437 struct xenfb_page
*page
= xenfb
->c
.page
;
438 char *protocol
= xenfb
->c
.xendev
.protocol
;
440 xen_pfn_t
*pgmfns
= NULL
;
441 xen_pfn_t
*fbmfns
= NULL
;
445 /* default to native */
447 mode
= sizeof(unsigned long) * 8;
451 * Undefined protocol, some guesswork needed.
453 * Old frontends which don't set the protocol use
454 * one page directory only, thus pd[1] must be zero.
455 * pd[1] of the 32bit struct layout and the lower
456 * 32 bits of pd[0] of the 64bit struct layout have
457 * the same location, so we can check that ...
459 uint32_t *ptr32
= NULL
;
460 uint32_t *ptr64
= NULL
;
461 #if defined(__i386__)
462 ptr32
= (void*)page
->pd
;
463 ptr64
= ((void*)page
->pd
) + 4;
464 #elif defined(__x86_64__)
465 ptr32
= ((void*)page
->pd
) - 4;
466 ptr64
= (void*)page
->pd
;
477 #if defined(__x86_64__)
478 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_32
) == 0) {
479 /* 64bit dom0, 32bit domU */
481 pd
= ((void*)page
->pd
) - 4;
482 #elif defined(__i386__)
483 } else if (strcmp(protocol
, XEN_IO_PROTO_ABI_X86_64
) == 0) {
484 /* 32bit dom0, 64bit domU */
486 pd
= ((void*)page
->pd
) + 4;
491 munmap(xenfb
->pixels
, xenfb
->fbpages
* XC_PAGE_SIZE
);
492 xenfb
->pixels
= NULL
;
495 xenfb
->fbpages
= DIV_ROUND_UP(xenfb
->fb_len
, XC_PAGE_SIZE
);
496 n_fbdirs
= xenfb
->fbpages
* mode
/ 8;
497 n_fbdirs
= DIV_ROUND_UP(n_fbdirs
, XC_PAGE_SIZE
);
499 pgmfns
= g_new0(xen_pfn_t
, n_fbdirs
);
500 fbmfns
= g_new0(xen_pfn_t
, xenfb
->fbpages
);
502 xenfb_copy_mfns(mode
, n_fbdirs
, pgmfns
, pd
);
503 map
= xenforeignmemory_map(xen_fmem
, xenfb
->c
.xendev
.dom
,
504 PROT_READ
, n_fbdirs
, pgmfns
, NULL
);
507 xenfb_copy_mfns(mode
, xenfb
->fbpages
, fbmfns
, map
);
508 xenforeignmemory_unmap(xen_fmem
, map
, n_fbdirs
);
510 xenfb
->pixels
= xenforeignmemory_map(xen_fmem
, xenfb
->c
.xendev
.dom
,
511 PROT_READ
, xenfb
->fbpages
, fbmfns
, NULL
);
512 if (xenfb
->pixels
== NULL
)
515 ret
= 0; /* all is fine */
523 static int xenfb_configure_fb(struct XenFB
*xenfb
, size_t fb_len_lim
,
524 int width
, int height
, int depth
,
525 size_t fb_len
, int offset
, int row_stride
)
527 size_t mfn_sz
= sizeof_field(struct xenfb_page
, pd
[0]);
528 size_t pd_len
= sizeof_field(struct xenfb_page
, pd
) / mfn_sz
;
529 size_t fb_pages
= pd_len
* XC_PAGE_SIZE
/ mfn_sz
;
530 size_t fb_len_max
= fb_pages
* XC_PAGE_SIZE
;
531 int max_width
, max_height
;
533 if (fb_len_lim
> fb_len_max
) {
534 xen_pv_printf(&xenfb
->c
.xendev
, 0,
535 "fb size limit %zu exceeds %zu, corrected\n",
536 fb_len_lim
, fb_len_max
);
537 fb_len_lim
= fb_len_max
;
539 if (fb_len_lim
&& fb_len
> fb_len_lim
) {
540 xen_pv_printf(&xenfb
->c
.xendev
, 0,
541 "frontend fb size %zu limited to %zu\n",
545 if (depth
!= 8 && depth
!= 16 && depth
!= 24 && depth
!= 32) {
546 xen_pv_printf(&xenfb
->c
.xendev
, 0,
547 "can't handle frontend fb depth %d\n",
551 if (row_stride
<= 0 || row_stride
> fb_len
) {
552 xen_pv_printf(&xenfb
->c
.xendev
, 0, "invalid frontend stride %d\n",
556 max_width
= row_stride
/ (depth
/ 8);
557 if (width
< 0 || width
> max_width
) {
558 xen_pv_printf(&xenfb
->c
.xendev
, 0,
559 "invalid frontend width %d limited to %d\n",
563 if (offset
< 0 || offset
>= fb_len
) {
564 xen_pv_printf(&xenfb
->c
.xendev
, 0,
565 "invalid frontend offset %d (max %zu)\n",
569 max_height
= (fb_len
- offset
) / row_stride
;
570 if (height
< 0 || height
> max_height
) {
571 xen_pv_printf(&xenfb
->c
.xendev
, 0,
572 "invalid frontend height %d limited to %d\n",
576 xenfb
->fb_len
= fb_len
;
577 xenfb
->row_stride
= row_stride
;
578 xenfb
->depth
= depth
;
579 xenfb
->width
= width
;
580 xenfb
->height
= height
;
581 xenfb
->offset
= offset
;
582 xenfb
->up_fullscreen
= 1;
583 xenfb
->do_resize
= 1;
584 xen_pv_printf(&xenfb
->c
.xendev
, 1,
585 "framebuffer %dx%dx%d offset %d stride %d\n",
586 width
, height
, depth
, offset
, row_stride
);
590 /* A convenient function for munging pixels between different depths */
591 #define BLT(SRC_T,DST_T,RSB,GSB,BSB,RDB,GDB,BDB) \
592 for (line = y ; line < (y+h) ; line++) { \
593 SRC_T *src = (SRC_T *)(xenfb->pixels \
595 + (line * xenfb->row_stride) \
596 + (x * xenfb->depth / 8)); \
597 DST_T *dst = (DST_T *)(data \
598 + (line * linesize) \
601 const int RSS = 32 - (RSB + GSB + BSB); \
602 const int GSS = 32 - (GSB + BSB); \
603 const int BSS = 32 - (BSB); \
604 const uint32_t RSM = (~0U) << (32 - RSB); \
605 const uint32_t GSM = (~0U) << (32 - GSB); \
606 const uint32_t BSM = (~0U) << (32 - BSB); \
607 const int RDS = 32 - (RDB + GDB + BDB); \
608 const int GDS = 32 - (GDB + BDB); \
609 const int BDS = 32 - (BDB); \
610 const uint32_t RDM = (~0U) << (32 - RDB); \
611 const uint32_t GDM = (~0U) << (32 - GDB); \
612 const uint32_t BDM = (~0U) << (32 - BDB); \
613 for (col = x ; col < (x+w) ; col++) { \
614 uint32_t spix = *src; \
615 *dst = (((spix << RSS) & RSM & RDM) >> RDS) | \
616 (((spix << GSS) & GSM & GDM) >> GDS) | \
617 (((spix << BSS) & BSM & BDM) >> BDS); \
618 src = (SRC_T *) ((unsigned long) src + xenfb->depth / 8); \
619 dst = (DST_T *) ((unsigned long) dst + bpp / 8); \
625 * This copies data from the guest framebuffer region, into QEMU's
626 * displaysurface. qemu uses 16 or 32 bpp. In case the pv framebuffer
627 * uses something else we must convert and copy, otherwise we can
628 * supply the buffer directly and no thing here.
630 static void xenfb_guest_copy(struct XenFB
*xenfb
, int x
, int y
, int w
, int h
)
632 DisplaySurface
*surface
= qemu_console_surface(xenfb
->con
);
634 int bpp
= surface_bits_per_pixel(surface
);
635 int linesize
= surface_stride(surface
);
636 uint8_t *data
= surface_data(surface
);
638 if (!is_buffer_shared(surface
)) {
639 switch (xenfb
->depth
) {
642 BLT(uint8_t, uint16_t, 3, 3, 2, 5, 6, 5);
643 } else if (bpp
== 32) {
644 BLT(uint8_t, uint32_t, 3, 3, 2, 8, 8, 8);
651 BLT(uint32_t, uint16_t, 8, 8, 8, 5, 6, 5);
652 } else if (bpp
== 32) {
653 BLT(uint32_t, uint32_t, 8, 8, 8, 8, 8, 8);
662 if (oops
) /* should not happen */
663 xen_pv_printf(&xenfb
->c
.xendev
, 0, "%s: oops: convert %d -> %d bpp?\n",
664 __func__
, xenfb
->depth
, bpp
);
666 dpy_gfx_update(xenfb
->con
, x
, y
, w
, h
);
669 #ifdef XENFB_TYPE_REFRESH_PERIOD
670 static int xenfb_queue_full(struct XenFB
*xenfb
)
672 struct xenfb_page
*page
= xenfb
->c
.page
;
678 prod
= page
->in_prod
;
679 cons
= page
->in_cons
;
680 return prod
- cons
== XENFB_IN_RING_LEN
;
683 static void xenfb_send_event(struct XenFB
*xenfb
, union xenfb_in_event
*event
)
686 struct xenfb_page
*page
= xenfb
->c
.page
;
688 prod
= page
->in_prod
;
689 /* caller ensures !xenfb_queue_full() */
690 xen_mb(); /* ensure ring space available */
691 XENFB_IN_RING_REF(page
, prod
) = *event
;
692 xen_wmb(); /* ensure ring contents visible */
693 page
->in_prod
= prod
+ 1;
695 xen_pv_send_notify(&xenfb
->c
.xendev
);
698 static void xenfb_send_refresh_period(struct XenFB
*xenfb
, int period
)
700 union xenfb_in_event event
;
702 memset(&event
, 0, sizeof(event
));
703 event
.type
= XENFB_TYPE_REFRESH_PERIOD
;
704 event
.refresh_period
.period
= period
;
705 xenfb_send_event(xenfb
, &event
);
710 * Periodic update of display.
711 * Also transmit the refresh interval to the frontend.
713 * Never ever do any qemu display operations
714 * (resize, screen update) outside this function.
715 * Our screen might be inactive. When asked for
716 * an update we know it is active.
718 static void xenfb_update(void *opaque
)
720 struct XenFB
*xenfb
= opaque
;
721 DisplaySurface
*surface
;
724 if (xenfb
->c
.xendev
.be_state
!= XenbusStateConnected
)
727 if (!xenfb
->feature_update
) {
728 /* we don't get update notifications, thus use the
729 * sledge hammer approach ... */
730 xenfb
->up_fullscreen
= 1;
733 /* resize if needed */
734 if (xenfb
->do_resize
) {
735 pixman_format_code_t format
;
737 xenfb
->do_resize
= 0;
738 switch (xenfb
->depth
) {
741 /* console.c supported depth -> buffer can be used directly */
742 format
= qemu_default_pixman_format(xenfb
->depth
, true);
743 surface
= qemu_create_displaysurface_from
744 (xenfb
->width
, xenfb
->height
, format
,
745 xenfb
->row_stride
, xenfb
->pixels
+ xenfb
->offset
);
748 /* we must convert stuff */
749 surface
= qemu_create_displaysurface(xenfb
->width
, xenfb
->height
);
752 dpy_gfx_replace_surface(xenfb
->con
, surface
);
753 xen_pv_printf(&xenfb
->c
.xendev
, 1,
754 "update: resizing: %dx%d @ %d bpp%s\n",
755 xenfb
->width
, xenfb
->height
, xenfb
->depth
,
756 is_buffer_shared(surface
) ? " (shared)" : "");
757 xenfb
->up_fullscreen
= 1;
760 /* run queued updates */
761 if (xenfb
->up_fullscreen
) {
762 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: fullscreen\n");
763 xenfb_guest_copy(xenfb
, 0, 0, xenfb
->width
, xenfb
->height
);
764 } else if (xenfb
->up_count
) {
765 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: %d rects\n",
767 for (i
= 0; i
< xenfb
->up_count
; i
++)
768 xenfb_guest_copy(xenfb
,
769 xenfb
->up_rects
[i
].x
,
770 xenfb
->up_rects
[i
].y
,
771 xenfb
->up_rects
[i
].w
,
772 xenfb
->up_rects
[i
].h
);
774 xen_pv_printf(&xenfb
->c
.xendev
, 3, "update: nothing\n");
777 xenfb
->up_fullscreen
= 0;
780 static void xenfb_ui_info(void *opaque
, uint32_t idx
, QemuUIInfo
*info
)
782 struct XenFB
*xenfb
= opaque
;
783 uint32_t refresh_rate
;
785 if (xenfb
->feature_update
) {
786 #ifdef XENFB_TYPE_REFRESH_PERIOD
787 if (xenfb_queue_full(xenfb
)) {
791 refresh_rate
= info
->refresh_rate
;
796 /* T = 1 / f = 1 [s*Hz] / f = 1000*1000 [ms*mHz] / f */
797 xenfb_send_refresh_period(xenfb
, 1000 * 1000 / refresh_rate
);
802 /* QEMU display state changed, so refresh the framebuffer copy */
803 static void xenfb_invalidate(void *opaque
)
805 struct XenFB
*xenfb
= opaque
;
806 xenfb
->up_fullscreen
= 1;
809 static void xenfb_handle_events(struct XenFB
*xenfb
)
811 uint32_t prod
, cons
, out_cons
;
812 struct xenfb_page
*page
= xenfb
->c
.page
;
814 prod
= page
->out_prod
;
815 out_cons
= page
->out_cons
;
816 if (prod
- out_cons
> XENFB_OUT_RING_LEN
) {
819 xen_rmb(); /* ensure we see ring contents up to prod */
820 for (cons
= out_cons
; cons
!= prod
; cons
++) {
821 union xenfb_out_event
*event
= &XENFB_OUT_RING_REF(page
, cons
);
822 uint8_t type
= event
->type
;
826 case XENFB_TYPE_UPDATE
:
827 if (xenfb
->up_count
== UP_QUEUE
)
828 xenfb
->up_fullscreen
= 1;
829 if (xenfb
->up_fullscreen
)
831 x
= MAX(event
->update
.x
, 0);
832 y
= MAX(event
->update
.y
, 0);
833 w
= MIN(event
->update
.width
, xenfb
->width
- x
);
834 h
= MIN(event
->update
.height
, xenfb
->height
- y
);
835 if (w
< 0 || h
< 0) {
836 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update ignored\n");
839 if (x
!= event
->update
.x
||
840 y
!= event
->update
.y
||
841 w
!= event
->update
.width
||
842 h
!= event
->update
.height
) {
843 xen_pv_printf(&xenfb
->c
.xendev
, 1, "bogus update clipped\n");
845 if (w
== xenfb
->width
&& h
> xenfb
->height
/ 2) {
846 /* scroll detector: updated more than 50% of the lines,
847 * don't bother keeping track of the rectangles then */
848 xenfb
->up_fullscreen
= 1;
850 xenfb
->up_rects
[xenfb
->up_count
].x
= x
;
851 xenfb
->up_rects
[xenfb
->up_count
].y
= y
;
852 xenfb
->up_rects
[xenfb
->up_count
].w
= w
;
853 xenfb
->up_rects
[xenfb
->up_count
].h
= h
;
857 #ifdef XENFB_TYPE_RESIZE
858 case XENFB_TYPE_RESIZE
:
859 if (xenfb_configure_fb(xenfb
, xenfb
->fb_len
,
861 event
->resize
.height
,
864 event
->resize
.offset
,
865 event
->resize
.stride
) < 0)
867 xenfb_invalidate(xenfb
);
872 xen_mb(); /* ensure we're done with ring contents */
873 page
->out_cons
= cons
;
876 static int fb_init(struct XenLegacyDevice
*xendev
)
878 #ifdef XENFB_TYPE_RESIZE
879 xenstore_write_be_int(xendev
, "feature-resize", 1);
884 static int fb_initialise(struct XenLegacyDevice
*xendev
)
886 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
887 struct xenfb_page
*fb_page
;
891 if (xenstore_read_fe_int(xendev
, "videoram", &videoram
) == -1)
894 rc
= common_bind(&fb
->c
);
898 fb_page
= fb
->c
.page
;
899 rc
= xenfb_configure_fb(fb
, videoram
* MiB
,
900 fb_page
->width
, fb_page
->height
, fb_page
->depth
,
901 fb_page
->mem_length
, 0, fb_page
->line_length
);
905 rc
= xenfb_map_fb(fb
);
909 fb
->con
= graphic_console_init(NULL
, 0, &xenfb_ops
, fb
);
911 if (xenstore_read_fe_int(xendev
, "feature-update", &fb
->feature_update
) == -1)
912 fb
->feature_update
= 0;
913 if (fb
->feature_update
)
914 xenstore_write_be_int(xendev
, "request-update", 1);
916 xen_pv_printf(xendev
, 1, "feature-update=%d, videoram=%d\n",
917 fb
->feature_update
, videoram
);
921 static void fb_disconnect(struct XenLegacyDevice
*xendev
)
923 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
926 * FIXME: qemu can't un-init gfx display (yet?).
927 * Replacing the framebuffer with anonymous shared memory
928 * instead. This releases the guest pages and keeps qemu happy.
930 xenforeignmemory_unmap(xen_fmem
, fb
->pixels
, fb
->fbpages
);
931 fb
->pixels
= mmap(fb
->pixels
, fb
->fbpages
* XC_PAGE_SIZE
,
932 PROT_READ
| PROT_WRITE
, MAP_SHARED
| MAP_ANON
,
934 if (fb
->pixels
== MAP_FAILED
) {
935 xen_pv_printf(xendev
, 0,
936 "Couldn't replace the framebuffer with anonymous memory errno=%d\n",
939 common_unbind(&fb
->c
);
940 fb
->feature_update
= 0;
944 static void fb_frontend_changed(struct XenLegacyDevice
*xendev
,
947 struct XenFB
*fb
= container_of(xendev
, struct XenFB
, c
.xendev
);
950 * Set state to Connected *again* once the frontend switched
951 * to connected. We must trigger the watch a second time to
952 * workaround a frontend bug.
954 if (fb
->bug_trigger
== 0 && strcmp(node
, "state") == 0 &&
955 xendev
->fe_state
== XenbusStateConnected
&&
956 xendev
->be_state
== XenbusStateConnected
) {
957 xen_pv_printf(xendev
, 2, "re-trigger connected (frontend bug)\n");
958 xen_be_set_state(xendev
, XenbusStateConnected
);
959 fb
->bug_trigger
= 1; /* only once */
963 static void fb_event(struct XenLegacyDevice
*xendev
)
965 struct XenFB
*xenfb
= container_of(xendev
, struct XenFB
, c
.xendev
);
967 xenfb_handle_events(xenfb
);
968 xen_pv_send_notify(&xenfb
->c
.xendev
);
971 /* -------------------------------------------------------------------- */
973 struct XenDevOps xen_kbdmouse_ops
= {
974 .size
= sizeof(struct XenInput
),
976 .initialise
= input_initialise
,
977 .connected
= input_connected
,
978 .disconnect
= input_disconnect
,
979 .event
= input_event
,
982 struct XenDevOps xen_framebuffer_ops
= {
983 .size
= sizeof(struct XenFB
),
985 .initialise
= fb_initialise
,
986 .disconnect
= fb_disconnect
,
988 .frontend_changed
= fb_frontend_changed
,
991 static const GraphicHwOps xenfb_ops
= {
992 .invalidate
= xenfb_invalidate
,
993 .gfx_update
= xenfb_update
,
994 .ui_info
= xenfb_ui_info
,