minios: add PVFB support Signed-off-by: Samuel Thibault <samuel.thibault@eu.citrix.com> diff -r cb3e47897b85 extras/mini-os/fbfront.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extras/mini-os/fbfront.c Tue Feb 26 13:13:00 2008 +0000 @@ -0,0 +1,468 @@ +/* + * Frame Buffer + Keyboard driver for Mini-OS. + * Samuel Thibault <samuel.thibault@eu.citrix.com>, 2008 + * Based on blkfront.c. + */ + +#include <os.h> +#include <xenbus.h> +#include <events.h> +#include <xen/io/kbdif.h> +#include <xen/io/fbif.h> +#include <xen/io/protocols.h> +#include <gnttab.h> +#include <xmalloc.h> +#include <fbfront.h> +#include <lib.h> + +DECLARE_WAIT_QUEUE_HEAD(kbdfront_queue); + + + + + + +struct kbdfront_dev { + domid_t dom; + + struct xenkbd_page *page; + evtchn_port_t evtchn, local_port; + + char *nodename; + char *backend; + + char *data; + int width; + int height; + int depth; + int line_length; + int mem_length; + +#ifdef HAVE_LIBC + int fd; +#endif +}; + +void kbdfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ +#ifdef HAVE_LIBC + struct kbdfront_dev *dev = data; + int fd = dev->fd; + + files[fd].read = 1; +#endif + wake_up(&kbdfront_queue); +} + +struct kbdfront_dev *init_kbdfront(char *nodename, int abs_pointer) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct xenkbd_page *s; + int retry=0; + char* msg; + + struct kbdfront_dev *dev; + + if (!nodename) + nodename = "device/vkbd/0"; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* KBDFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + dev->nodename = strdup(nodename); + + evtchn_alloc_unbound_t op; + op.dom = DOMID_SELF; + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = op.remote_dom = xenbus_read_integer(path); + HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); + clear_evtchn(op.port); /* Without, handler gets invoked now! */ + dev->local_port = bind_evtchn(op.port, kbdfront_handler, dev); + dev->evtchn=op.port; + + dev->page = s = (struct xenkbd_page*) alloc_page(); + memset(s,0,PAGE_SIZE); + + s->in_cons = s->in_prod = 0; + s->out_cons = s->out_prod = 0; + + // FIXME: proper frees on failures +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "page-ref","%u", virt_to_mfn(s)); + if (err) { + message = "writing page-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + if (abs_pointer) { + err = xenbus_printf(xbt, nodename, "request-abs-pointer", "1"); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + } + + err = xenbus_printf(xbt, nodename, "state", "%u", 3); /* initialized */ + if (err) + printk("error writing initialized: %s\n", err); + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + return NULL; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + return NULL; + } + + printk("backend at %s\n", dev->backend); + + { + char path[strlen(dev->backend) + 1 + 6 + 1]; + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path(XBT_NIL, path); + + xenbus_wait_for_value(path,"4"); + + xenbus_unwatch_path(XBT_NIL, path); + + printk("%s connected\n", dev->backend); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 4); /* connected */ + } + + printk("************************** KBDFRONT\n"); + + return dev; +} + +int kbdfront_receive(struct kbdfront_dev *dev, union xenkbd_in_event *buf, int n) +{ + struct xenkbd_page *page = dev->page; + uint32_t prod, cons; + int i; + +#ifdef HAVE_LIBC + files[dev->fd].read = 0; + mb(); /* Make sure to let the handler set read to 1 before we start looking at the ring */ +#endif + + prod = page->in_prod; + + if (prod == page->in_cons) + return 0; + + rmb(); /* ensure we see ring contents up to prod */ + + for (i = 0, cons = page->in_cons; i < n && cons != prod; i++, cons++) + memcpy(buf + i, &XENKBD_IN_RING_REF(page, cons), sizeof(*buf)); + + mb(); /* ensure we got ring contents */ + page->in_cons = cons; + notify_remote_via_evtchn(dev->evtchn); + +#ifdef HAVE_LIBC + if (cons != prod) + /* still some events to read */ + files[dev->fd].read = 1; +#endif + + return i; +} + + +void shutdown_kbdfront(struct kbdfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close kbd: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path,"5"); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path,"6"); + + unbind_evtchn(dev->local_port); + + free_pages(dev->page,0); + free(nodename); + free(dev->backend); + free(dev); +} + +#ifdef HAVE_LIBC +int kbdfront_open(struct kbdfront_dev *dev) +{ + dev->fd = alloc_fd(FTYPE_KBD); + printk("kbd_open(%s) -> %d\n", dev->nodename, dev->fd); + files[dev->fd].kbd.dev = dev; + return dev->fd; +} +#endif + + + + + +DECLARE_WAIT_QUEUE_HEAD(fbfront_queue); + + + + + + +struct fbfront_dev { + domid_t dom; + + struct xenfb_page *page; + evtchn_port_t evtchn, local_port; + + char *nodename; + char *backend; + + char *data; + int width; + int height; + int depth; + int line_length; + int mem_length; +}; + +void fbfront_handler(evtchn_port_t port, struct pt_regs *regs, void *data) +{ + wake_up(&fbfront_queue); +} + +struct fbfront_dev *init_fbfront(char *nodename, void *data, int width, int height, int depth, int line_length, int mem_length) +{ + xenbus_transaction_t xbt; + char* err; + char* message=NULL; + struct xenfb_page *s; + int retry=0; + char* msg; + int i, j; + struct fbfront_dev *dev; + + if (!nodename) + nodename = "device/vfb/0"; + + char path[strlen(nodename) + 1 + 10 + 1]; + + printk("******************* FBFRONT for %s **********\n\n\n", nodename); + + dev = malloc(sizeof(*dev)); + dev->nodename = strdup(nodename); + + evtchn_alloc_unbound_t op; + op.dom = DOMID_SELF; + snprintf(path, sizeof(path), "%s/backend-id", nodename); + dev->dom = op.remote_dom = xenbus_read_integer(path); + HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, &op); + clear_evtchn(op.port); /* Without, handler gets invoked now! */ + dev->local_port = bind_evtchn(op.port, fbfront_handler, dev); + dev->evtchn=op.port; + + dev->page = s = (struct xenfb_page*) alloc_page(); + memset(s,0,PAGE_SIZE); + + s->in_cons = s->in_prod = 0; + s->out_cons = s->out_prod = 0; + dev->width = s->width = width; + dev->height = s->height = height; + dev->depth = s->depth = depth; + dev->line_length = s->line_length = line_length; + dev->mem_length = s->mem_length = mem_length; + + ASSERT(!((unsigned long)data & ~PAGE_MASK)); + dev->data = data; + + const int max_pd = sizeof(s->pd) / sizeof(s->pd[0]); + unsigned long mapped = 0; + + for (i = 0; mapped < mem_length && i < max_pd; i++) { + unsigned long *pd = (unsigned long *) alloc_page(); + for (j = 0; mapped < mem_length && j < PAGE_SIZE / sizeof(unsigned long); j++) { + pd[j] = virt_to_mfn((unsigned long) data + mapped); + mapped += PAGE_SIZE; + } + for ( ; j < PAGE_SIZE / sizeof(unsigned long); j++) + pd[j] = 0; + s->pd[i] = virt_to_mfn(pd); + } + for ( ; i < max_pd; i++) + s->pd[i] = 0; + + + // FIXME: proper frees on failures +again: + err = xenbus_transaction_start(&xbt); + if (err) { + printk("starting transaction\n"); + } + + err = xenbus_printf(xbt, nodename, "page-ref","%u", virt_to_mfn(s)); + if (err) { + message = "writing page-ref"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "event-channel", "%u", dev->evtchn); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "protocol", "%s", + XEN_IO_PROTO_ABI_NATIVE); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + err = xenbus_printf(xbt, nodename, "feature-update", "1"); + if (err) { + message = "writing event-channel"; + goto abort_transaction; + } + + err = xenbus_printf(xbt, nodename, "state", "%u", 3); /* initialized */ + + + err = xenbus_transaction_end(xbt, 0, &retry); + if (retry) { + goto again; + printk("completing transaction\n"); + } + + goto done; + +abort_transaction: + xenbus_transaction_end(xbt, 1, &retry); + return NULL; + +done: + + snprintf(path, sizeof(path), "%s/backend", nodename); + msg = xenbus_read(XBT_NIL, path, &dev->backend); + if (msg) { + printk("Error %s when reading the backend path %s\n", msg, path); + return NULL; + } + + printk("backend at %s\n", dev->backend); + + { + char path[strlen(dev->backend) + 1 + 6 + 1]; + + snprintf(path, sizeof(path), "%s/state", dev->backend); + + xenbus_watch_path(XBT_NIL, path); + + xenbus_wait_for_value(path,"4"); + + printk("%s connected\n", dev->backend); + + xenbus_unwatch_path(XBT_NIL, path); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 4); /* connected */ + } + + printk("************************** FBFRONT\n"); + + return dev; +} + +void fbfront_update(struct fbfront_dev *dev, int x, int y, int width, int height) +{ + struct xenfb_page *page = dev->page; + uint32_t prod; + DEFINE_WAIT(w); + + if (x < 0) { + width += x; + x = 0; + } + if (x + width > dev->width) + width = dev->width - x; + + if (y < 0) { + height += y; + y = 0; + } + if (y + height > dev->height) + height = dev->height - y; + + if (width <= 0 || height <= 0) + return; + + add_waiter(w, fbfront_queue); + while (page->out_prod - page->out_cons == XENFB_OUT_RING_LEN) + schedule(); + remove_waiter(w); + + prod = page->out_prod; + mb(); /* ensure ring space available */ + XENFB_OUT_RING_REF(page, prod).type = XENFB_TYPE_UPDATE; + XENFB_OUT_RING_REF(page, prod).update.x = x; + XENFB_OUT_RING_REF(page, prod).update.y = y; + XENFB_OUT_RING_REF(page, prod).update.width = width; + XENFB_OUT_RING_REF(page, prod).update.height = height; + wmb(); /* ensure ring contents visible */ + page->out_prod = prod + 1; + notify_remote_via_evtchn(dev->evtchn); +} + +void shutdown_fbfront(struct fbfront_dev *dev) +{ + char* err; + char *nodename = dev->nodename; + + char path[strlen(dev->backend) + 1 + 5 + 1]; + + printk("close fb: backend at %s\n",dev->backend); + + snprintf(path, sizeof(path), "%s/state", dev->backend); + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 5); /* closing */ + xenbus_wait_for_value(path,"5"); + + err = xenbus_printf(XBT_NIL, nodename, "state", "%u", 6); + xenbus_wait_for_value(path,"6"); + + unbind_evtchn(dev->local_port); + + free_pages(dev->page,0); + free(nodename); + free(dev->backend); + free(dev); +} --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/extras/mini-os/include/fbfront.h Tue Feb 26 13:13:00 2008 +0000 @@ -0,0 +1,38 @@ +#include <xen/io/kbdif.h> +#include <wait.h> + +/* from <linux/input.h> */ +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 +#endif +#ifndef BTN_RIGHT +#define BTN_RIGHT 0x111 +#endif +#ifndef BTN_MIDDLE +#define BTN_MIDDLE 0x112 +#endif +#ifndef KEY_Q +#define KEY_Q 16 +#endif + + +struct kbdfront_dev; +struct kbdfront_dev *init_kbdfront(char *nodename, int abs_pointer); +#ifdef HAVE_LIBC +int kbdfront_open(struct kbdfront_dev *dev); +#endif + +int kbdfront_receive(struct kbdfront_dev *dev, union xenkbd_in_event *buf, int n); +extern struct wait_queue_head kbdfront_queue; + +void shutdown_kbdfront(struct kbdfront_dev *dev); + + +struct fbfront_dev *init_fbfront(char *nodename, void *data, int width, int height, int depth, int line_length, int mem_length); +#ifdef HAVE_LIBC +int fbfront_open(struct fbfront_dev *dev); +#endif + +void fbfront_update(struct fbfront_dev *dev, int x, int y, int width, int height); + +void shutdown_fbfront(struct fbfront_dev *dev); --- a/extras/mini-os/include/lib.h Mon Feb 25 11:24:06 2008 +0000 +++ b/extras/mini-os/include/lib.h Tue Feb 26 13:13:00 2008 +0000 @@ -140,6 +140,7 @@ FTYPE_SOCKET, FTYPE_TAP, FTYPE_BLK, + FTYPE_KBD, }; #define MAX_EVTCHN_PORTS 16 @@ -171,6 +172,9 @@ struct { struct blkfront_dev *dev; } blk; + struct { + struct kbdfront_dev *dev; + } kbd; struct { /* To each xenbus FD is associated a queue of watch events for this * FD. */ --- a/extras/mini-os/kernel.c Mon Feb 25 11:24:06 2008 +0000 +++ b/extras/mini-os/kernel.c Tue Feb 26 13:13:00 2008 +0000 @@ -39,6 +39,7 @@ #include <gnttab.h> #include <netfront.h> #include <blkfront.h> +#include <fbfront.h> #include <fs.h> #include <xmalloc.h> #include <fcntl.h> @@ -248,6 +249,152 @@ } } +#define WIDTH 800 +#define HEIGHT 600 +#define DEPTH 32 + +static uint32_t *fb; +static struct fbfront_dev *fb_dev; +static struct semaphore fbfront_sem = __SEMAPHORE_INITIALIZER(fbfront_sem, 0); + +static void fbfront_drawvert(int x, int y1, int y2, uint32_t color) +{ + int y; + if (x < 0) + return; + if (x >= WIDTH) + return; + if (y1 < 0) + y1 = 0; + if (y2 >= HEIGHT) + y2 = HEIGHT-1; + for (y = y1; y <= y2; y++) + fb[x + y*WIDTH] ^= color; +} + +static void fbfront_drawhoriz(int x1, int x2, int y, uint32_t color) +{ + int x; + if (y < 0) + return; + if (y >= HEIGHT) + return; + if (x1 < 0) + x1 = 0; + if (x2 >= WIDTH) + x2 = WIDTH-1; + for (x = x1; x <= x2; x++) + fb[x + y*WIDTH] ^= color; +} + +static void fbfront_thread(void *p) +{ + size_t line_length = WIDTH * (DEPTH / 8); + size_t memsize = HEIGHT * line_length; + + fb = _xmalloc(memsize, PAGE_SIZE); + fb_dev = init_fbfront(NULL, fb, WIDTH, HEIGHT, DEPTH, line_length, memsize); + if (!fb_dev) { + xfree(fb); + return; + } + up(&fbfront_sem); +} + +static void clip_cursor(int *x, int *y) +{ + if (*x < 0) + *x = 0; + if (*x >= WIDTH) + *x = WIDTH - 1; + if (*y < 0) + *y = 0; + if (*y >= HEIGHT) + *y = HEIGHT - 1; +} + +static void refresh_cursor(int new_x, int new_y) +{ + static int old_x = -1, old_y = -1; + if (old_x != -1 && old_y != -1) { + fbfront_drawvert(old_x, old_y + 1, old_y + 8, 0xffffffff); + fbfront_drawhoriz(old_x + 1, old_x + 8, old_y, 0xffffffff); + fbfront_update(fb_dev, old_x, old_y, 9, 9); + } + old_x = new_x; + old_y = new_y; + fbfront_drawvert(new_x, new_y + 1, new_y + 8, 0xffffffff); + fbfront_drawhoriz(new_x + 1, new_x + 8, new_y, 0xffffffff); + fbfront_update(fb_dev, new_x, new_y, 9, 9); +} + +static void kbdfront_thread(void *p) +{ + struct kbdfront_dev *kbd_dev; + DEFINE_WAIT(w); + int x = WIDTH / 2, y = HEIGHT / 2, z; + + kbd_dev = init_kbdfront(NULL, 1); + if (!kbd_dev) + return; + + down(&fbfront_sem); + refresh_cursor(x, y); + while (1) { + union xenkbd_in_event event; + + add_waiter(w, kbdfront_queue); + + if (kbdfront_receive(kbd_dev, &event, 1) == 0) + schedule(); + else switch(event.type) { + case XENKBD_TYPE_MOTION: + printk("motion x:%d y:%d z:%d\n", + event.motion.rel_x, + event.motion.rel_y, + event.motion.rel_z); + x += event.motion.rel_x; + y += event.motion.rel_y; + z += event.motion.rel_z; + clip_cursor(&x, &y); + refresh_cursor(x, y); + break; + case XENKBD_TYPE_POS: + printk("pos x:%d y:%d z:%d\n", + event.pos.abs_x, + event.pos.abs_y, + event.pos.abs_z); + x = event.pos.abs_x; + y = event.pos.abs_y; + z = event.pos.abs_z; + clip_cursor(&x, &y); + refresh_cursor(x, y); + break; + case XENKBD_TYPE_KEY: + printk("key %d %s\n", + event.key.keycode, + event.key.pressed ? "pressed" : "released"); + if (event.key.keycode == BTN_LEFT) { + printk("mouse %s at (%d,%d,%d)\n", + event.key.pressed ? "clic" : "release", x, y, z); + if (event.key.pressed) { + uint32_t color = rand(); + fbfront_drawvert(x - 16, y - 16, y + 15, color); + fbfront_drawhoriz(x - 16, x + 15, y + 16, color); + fbfront_drawvert(x + 16, y - 15, y + 16, color); + fbfront_drawhoriz(x - 15, x + 16, y - 16, color); + fbfront_update(fb_dev, x - 16, y - 16, 33, 33); + } + } else if (event.key.keycode == KEY_Q) { + struct sched_shutdown sched_shutdown = { .reason = SHUTDOWN_poweroff }; + HYPERVISOR_sched_op(SCHEDOP_shutdown, &sched_shutdown); + do_exit(); + } + break; + } + } +} + static void fs_thread(void *p) { init_fs_frontend(); @@ -261,6 +408,8 @@ create_thread("periodic_thread", periodic_thread, si); create_thread("netfront", netfront_thread, si); create_thread("blkfront", blkfront_thread, si); + create_thread("fbfront", fbfront_thread, si); + create_thread("kbdfront", kbdfront_thread, si); create_thread("fs-frontend", fs_thread, si); return 0; } --- a/extras/mini-os/lib/sys.c Mon Feb 25 11:24:06 2008 +0000 +++ b/extras/mini-os/lib/sys.c Tue Feb 26 13:13:00 2008 +0000 @@ -26,6 +26,7 @@ #include <wait.h> #include <netfront.h> #include <blkfront.h> +#include <fbfront.h> #include <xenbus.h> #include <xs.h> @@ -221,6 +222,16 @@ } return ret; } + case FTYPE_KBD: { + int ret, n; + n = nbytes / sizeof(union xenkbd_in_event); + ret = kbdfront_receive(files[fd].kbd.dev, buf, n); + if (ret <= 0) { + errno = EAGAIN; + return -1; + } + return ret * sizeof(union xenkbd_in_event); + } case FTYPE_NONE: case FTYPE_XENBUS: case FTYPE_EVTCHN: @@ -261,6 +272,7 @@ case FTYPE_XENBUS: case FTYPE_EVTCHN: case FTYPE_BLK: + case FTYPE_KBD: break; } printk("write(%d): Bad descriptor\n", fd); @@ -318,6 +330,7 @@ case FTYPE_EVTCHN: case FTYPE_TAP: case FTYPE_BLK: + case FTYPE_KBD: break; } printk("fsync(%d): Bad descriptor\n", fd); @@ -360,6 +373,10 @@ shutdown_blkfront(files[fd].blk.dev); files[fd].type = FTYPE_NONE; return 0; + case FTYPE_KBD: + shutdown_kbdfront(files[fd].kbd.dev); + files[fd].type = FTYPE_NONE; + return 0; case FTYPE_NONE: break; } @@ -450,6 +467,7 @@ case FTYPE_EVTCHN: case FTYPE_TAP: case FTYPE_BLK: + case FTYPE_KBD: break; } @@ -477,6 +495,7 @@ case FTYPE_EVTCHN: case FTYPE_TAP: case FTYPE_BLK: + case FTYPE_KBD: break; } @@ -587,6 +606,7 @@ [FTYPE_SOCKET] = ''S'', [FTYPE_TAP] = ''T'', [FTYPE_BLK] = ''B'', + [FTYPE_KBD] = ''K'', }; #ifdef LIBC_DEBUG static void dump_set(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) @@ -694,6 +714,7 @@ case FTYPE_EVTCHN: case FTYPE_TAP: case FTYPE_BLK: + case FTYPE_KBD: if (FD_ISSET(i, readfds)) { if (files[i].read) n++; @@ -775,6 +796,7 @@ DEFINE_WAIT(w2); DEFINE_WAIT(w3); DEFINE_WAIT(w4); + DEFINE_WAIT(w5); assert(thread == main_thread); @@ -795,6 +817,7 @@ add_waiter(w2, event_queue); add_waiter(w3, blkfront_queue); add_waiter(w4, xenbus_watch_queue); + add_waiter(w5, kbdfront_queue); myread = *readfds; mywrite = *writefds; @@ -860,6 +883,7 @@ remove_waiter(w2); remove_waiter(w3); remove_waiter(w4); + remove_waiter(w5); return ret; } _______________________________________________ Xen-devel mailing list Xen-devel@lists.xensource.com http://lists.xensource.com/xen-devel