📄 xenfb.c
字号:
static void xenfb_detach_dom(struct xenfb *xenfb){ xenfb_unbind(&xenfb->fb); xenfb_unbind(&xenfb->kbd); if (xenfb->pixels) { munmap(xenfb->pixels, xenfb->fb_len); xenfb->pixels = NULL; }}/* Remove the backend area in xenbus since the framebuffer really is going away. */void xenfb_shutdown(struct xenfb *xenfb){ fprintf(stderr, "FB: Shutting down backend\n"); xs_rm(xenfb->xsh, XBT_NULL, xenfb->fb.nodename); xs_rm(xenfb->xsh, XBT_NULL, xenfb->kbd.nodename); xenfb_detach_dom(xenfb); if (xenfb->xc >= 0) xc_interface_close(xenfb->xc); if (xenfb->evt_xch >= 0) xc_evtchn_close(xenfb->evt_xch); if (xenfb->xsh) xs_daemon_close(xenfb->xsh); free(xenfb);}static int xenfb_configure_fb(struct xenfb *xenfb, size_t fb_len_lim, int width, int height, int depth, size_t fb_len, int offset, int row_stride){ size_t mfn_sz = sizeof(*((struct xenfb_page *)0)->pd); size_t pd_len = sizeof(((struct xenfb_page *)0)->pd) / mfn_sz; size_t fb_pages = pd_len * XC_PAGE_SIZE / mfn_sz; size_t fb_len_max = fb_pages * XC_PAGE_SIZE; int max_width, max_height; if (fb_len_lim > fb_len_max) { fprintf(stderr, "FB: fb size limit %zu exceeds %zu, corrected\n", fb_len_lim, fb_len_max); fb_len_lim = fb_len_max; } if (fb_len_lim && fb_len > fb_len_lim) { fprintf(stderr, "FB: frontend fb size %zu limited to %zu\n", fb_len, fb_len_lim); fb_len = fb_len_lim; } if (depth != 8 && depth != 16 && depth != 24 && depth != 32) { fprintf(stderr, "FB: can't handle frontend fb depth %d\n", depth); return -1; } if (row_stride < 0 || row_stride > fb_len) { fprintf(stderr, "FB: invalid frontend stride %d\n", row_stride); return -1; } max_width = row_stride / (depth / 8); if (width < 0 || width > max_width) { fprintf(stderr, "FB: invalid frontend width %d limited to %d\n", width, max_width); width = max_width; } if (offset < 0 || offset >= fb_len) { fprintf(stderr, "FB: invalid frontend offset %d (max %zu)\n", offset, fb_len - 1); return -1; } max_height = (fb_len - offset) / row_stride; if (height < 0 || height > max_height) { fprintf(stderr, "FB: invalid frontend height %d limited to %d\n", height, max_height); height = max_height; } xenfb->fb_len = fb_len; xenfb->row_stride = row_stride; xenfb->depth = depth; xenfb->width = width; xenfb->height = height; xenfb->offset = offset; fprintf(stderr, "Framebuffer %dx%dx%d offset %d stride %d\n", width, height, depth, offset, row_stride); return 0;}static void xenfb_on_fb_event(struct xenfb *xenfb){ uint32_t prod, cons; struct xenfb_page *page = xenfb->fb.page; prod = page->out_prod; if (prod == page->out_cons) return; xen_rmb(); /* ensure we see ring contents up to prod */ for (cons = page->out_cons; cons != prod; cons++) { union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons); int x, y, w, h; switch (event->type) { case XENFB_TYPE_UPDATE: x = MAX(event->update.x, 0); y = MAX(event->update.y, 0); w = MIN(event->update.width, xenfb->width - x); h = MIN(event->update.height, xenfb->height - y); if (w < 0 || h < 0) { fprintf(stderr, "%s bogus update ignored\n", xenfb->fb.nodename); break; } if (x != event->update.x || y != event->update.y || w != event->update.width || h != event->update.height) { fprintf(stderr, "%s bogus update clipped\n", xenfb->fb.nodename); } xenfb_guest_copy(xenfb, x, y, w, h); break; case XENFB_TYPE_RESIZE: if (xenfb_configure_fb(xenfb, xenfb->fb_len, event->resize.width, event->resize.height, event->resize.depth, xenfb->fb_len, event->resize.offset, event->resize.stride) < 0) break; if (xenfb->ds->dpy_resize_shared) dpy_resize_shared(xenfb->ds, xenfb->width, xenfb->height, xenfb->depth, xenfb->row_stride, xenfb->pixels + xenfb->offset); else dpy_resize(xenfb->ds, xenfb->width, xenfb->height); xenfb_invalidate(xenfb); break; } } xen_mb(); /* ensure we're done with ring contents */ page->out_cons = cons; xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);}static int xenfb_queue_full(struct xenfb *xenfb){ struct xenfb_page *page = xenfb->fb.page; uint32_t cons, prod; prod = page->in_prod; cons = page->in_cons; return prod - cons == XENFB_IN_RING_LEN;}static void xenfb_send_event(struct xenfb *xenfb, union xenfb_in_event *event){ uint32_t prod; struct xenfb_page *page = xenfb->fb.page; prod = page->in_prod; /* caller ensures !xenfb_queue_full() */ xen_mb(); /* ensure ring space available */ XENFB_IN_RING_REF(page, prod) = *event; xen_wmb(); /* ensure ring contents visible */ page->in_prod = prod + 1; xc_evtchn_notify(xenfb->evt_xch, xenfb->fb.port);}static void xenfb_send_refresh_period(struct xenfb *xenfb, int period){ union xenfb_in_event event; memset(&event, 0, sizeof(event)); event.type = XENFB_TYPE_REFRESH_PERIOD; event.refresh_period.period = period; xenfb_send_event(xenfb, &event);}static void xenfb_on_kbd_event(struct xenfb *xenfb){ struct xenkbd_page *page = xenfb->kbd.page; /* We don't understand any keyboard events, so just ignore them. */ if (page->out_prod == page->out_cons) return; page->out_cons = page->out_prod; xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);}static int xenfb_on_state_change(struct xenfb_device *dev){ enum xenbus_state state; state = xenfb_read_state(dev->xenfb->xsh, dev->otherend); switch (state) { case XenbusStateUnknown: /* There was an error reading the frontend state. The domain has probably gone away; in any case, there's not much point in us continuing. */ return -1; case XenbusStateInitialising: case XenbusStateInitWait: case XenbusStateInitialised: case XenbusStateConnected: break; case XenbusStateClosing: xenfb_unbind(dev); xenfb_switch_state(dev, state); break; case XenbusStateClosed: xenfb_switch_state(dev, state); } return 0;}/* Send an event to the keyboard frontend driver */static int xenfb_kbd_event(struct xenfb *xenfb, union xenkbd_in_event *event){ uint32_t prod; struct xenkbd_page *page = xenfb->kbd.page; if (xenfb->kbd.state != XenbusStateConnected) return 0; prod = page->in_prod; if (prod - page->in_cons == XENKBD_IN_RING_LEN) { errno = EAGAIN; return -1; } xen_mb(); /* ensure ring space available */ XENKBD_IN_RING_REF(page, prod) = *event; xen_wmb(); /* ensure ring contents visible */ page->in_prod = prod + 1; return xc_evtchn_notify(xenfb->evt_xch, xenfb->kbd.port);}/* Send a keyboard (or mouse button) event */static int xenfb_send_key(struct xenfb *xenfb, bool down, int keycode){ union xenkbd_in_event event; memset(&event, 0, XENKBD_IN_EVENT_SIZE); event.type = XENKBD_TYPE_KEY; event.key.pressed = down ? 1 : 0; event.key.keycode = keycode; return xenfb_kbd_event(xenfb, &event);}/* Send a relative mouse movement event */static int xenfb_send_motion(struct xenfb *xenfb, int rel_x, int rel_y, int rel_z){ union xenkbd_in_event event; memset(&event, 0, XENKBD_IN_EVENT_SIZE); event.type = XENKBD_TYPE_MOTION; event.motion.rel_x = rel_x; event.motion.rel_y = rel_y; event.motion.rel_z = rel_z; return xenfb_kbd_event(xenfb, &event);}/* Send an absolute mouse movement event */static int xenfb_send_position(struct xenfb *xenfb, int abs_x, int abs_y, int rel_z){ union xenkbd_in_event event; memset(&event, 0, XENKBD_IN_EVENT_SIZE); event.type = XENKBD_TYPE_POS; event.pos.abs_x = abs_x; event.pos.abs_y = abs_y; event.pos.rel_z = rel_z; return xenfb_kbd_event(xenfb, &event);}/* Process events from the frontend event channel */static void xenfb_dispatch_channel(void *opaque){ struct xenfb *xenfb = (struct xenfb *)opaque; evtchn_port_t port; port = xc_evtchn_pending(xenfb->evt_xch); if (port == -1) { xenfb_shutdown(xenfb); exit(1); } if (port == xenfb->fb.port) xenfb_on_fb_event(xenfb); else if (port == xenfb->kbd.port) xenfb_on_kbd_event(xenfb); if (xc_evtchn_unmask(xenfb->evt_xch, port) == -1) { xenfb_shutdown(xenfb); exit(1); }}/* Process ongoing events from the frontend devices */static void xenfb_dispatch_store(void *opaque){ struct xenfb *xenfb = (struct xenfb *)opaque; unsigned dummy; char **vec; int r; vec = xs_read_watch(xenfb->xsh, &dummy); free(vec); r = xenfb_on_state_change(&xenfb->fb); if (r == 0) r = xenfb_on_state_change(&xenfb->kbd); if (r < 0) { xenfb_shutdown(xenfb); exit(1); }}/**************************************************************** * * Functions for processing frontend config * ****************************************************************//* Process the frontend framebuffer config */static int xenfb_read_frontend_fb_config(struct xenfb *xenfb) { struct xenfb_page *fb_page; int val; int videoram; if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "feature-update", "%d", &val) < 0) val = 0; if (!val) { fprintf(stderr, "feature-update not supported\n"); errno = ENOTSUP; return -1; } if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.otherend, "protocol", "%63s", xenfb->protocol) < 0) xenfb->protocol[0] = '\0'; xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "request-update", "1"); xenfb->refresh_period = -1; if (xenfb_xs_scanf1(xenfb->xsh, xenfb->fb.nodename, "videoram", "%d", &videoram) < 0) videoram = 0; fb_page = xenfb->fb.page; if (xenfb_configure_fb(xenfb, videoram * 1024 * 1024U, fb_page->width, fb_page->height, fb_page->depth, fb_page->mem_length, 0, fb_page->line_length) < 0) { errno = EINVAL; return -1; } if (xenfb_map_fb(xenfb, xenfb->fb.otherend_id) < 0) return -1; /* Indicate we have the frame buffer resize feature */ xenfb_xs_printf(xenfb->xsh, xenfb->fb.nodename, "feature-resize", "1"); /* Tell kbd pointer the screen geometry */ xenfb_xs_printf(xenfb->xsh, xenfb->kbd.nodename, "width", "%d", xenfb->width); xenfb_xs_printf(xenfb->xsh, xenfb->kbd.nodename, "height", "%d", xenfb->height); if (xenfb_switch_state(&xenfb->fb, XenbusStateConnected)) return -1; if (xenfb_switch_state(&xenfb->kbd, XenbusStateConnected)) return -1; return 0;}/* Process the frontend keyboard config */static int xenfb_read_frontend_kbd_config(struct xenfb *xenfb){ int val; if (xenfb_xs_scanf1(xenfb->xsh, xenfb->kbd.otherend, "request-abs-pointer", "%d", &val) < 0) val = 0; xenfb->abs_pointer_wanted = val; return 0;}/**************************************************************** * * Functions for frontend/backend state machine * ****************************************************************//* Register a watch against a frontend device, and setup * QEMU event loop to poll the xenstore FD for notification */static int xenfb_wait_for_frontend(struct xenfb_device *dev, IOHandler *handler){ fprintf(stderr, "Doing frontend watch on %s\n", dev->otherend); if (!xs_watch(dev->xenfb->xsh, dev->otherend, "")) { fprintf(stderr, "Watch for dev failed\n"); return -1; } if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0) return -1; return 0;}/* Register a watch against a backend device, and setup * QEMU event loop to poll the xenstore FD for notification */static int xenfb_wait_for_backend(struct xenfb_device *dev, IOHandler *handler){ fprintf(stderr, "Doing backend watch on %s\n", dev->nodename); if (!xs_watch(dev->xenfb->xsh, dev->nodename, "")) { fprintf(stderr, "Watch for dev failed\n"); return -1; } if (qemu_set_fd_handler2(xs_fileno(dev->xenfb->xsh), NULL, handler, NULL, dev) < 0) return -1; return 0;}/* Callback invoked while waiting for KBD backend to change * to the created state */static void xenfb_backend_created_kbd(void *opaque){ struct xenfb_device *dev = (struct xenfb_device *)opaque; int ret = xenfb_backend_created(dev); if (ret < 0) { xenfb_shutdown(dev->xenfb); exit(1); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -