1aba1efa5SPekka Enberg #include "kvm/vnc.h" 2aba1efa5SPekka Enberg 3aba1efa5SPekka Enberg #include "kvm/framebuffer.h" 4aba1efa5SPekka Enberg #include "kvm/i8042.h" 5*07d52d77SSasha Levin #include "kvm/vesa.h" 6aba1efa5SPekka Enberg 7aba1efa5SPekka Enberg #include <linux/types.h> 8b6f60a85SPekka Enberg #include <rfb/keysym.h> 9aba1efa5SPekka Enberg #include <rfb/rfb.h> 10aba1efa5SPekka Enberg #include <pthread.h> 11*07d52d77SSasha Levin #include <linux/err.h> 12aba1efa5SPekka Enberg 13aba1efa5SPekka Enberg #define VESA_QUEUE_SIZE 128 14aba1efa5SPekka Enberg #define VESA_IRQ 14 15aba1efa5SPekka Enberg 16aba1efa5SPekka Enberg /* 17aba1efa5SPekka Enberg * This "6000" value is pretty much the result of experimentation 18aba1efa5SPekka Enberg * It seems that around this value, things update pretty smoothly 19aba1efa5SPekka Enberg */ 20aba1efa5SPekka Enberg #define VESA_UPDATE_TIME 6000 21aba1efa5SPekka Enberg 22b6f60a85SPekka Enberg /* 23b6f60a85SPekka Enberg * We can map the letters and numbers without a fuss, 24b6f60a85SPekka Enberg * but the other characters not so much. 25b6f60a85SPekka Enberg */ 26b6f60a85SPekka Enberg static char letters[26] = { 27b6f60a85SPekka Enberg 0x1c, 0x32, 0x21, 0x23, 0x24, /* a-e */ 28b6f60a85SPekka Enberg 0x2b, 0x34, 0x33, 0x43, 0x3b, /* f-j */ 29b6f60a85SPekka Enberg 0x42, 0x4b, 0x3a, 0x31, 0x44, /* k-o */ 30b6f60a85SPekka Enberg 0x4d, 0x15, 0x2d, 0x1b, 0x2c, /* p-t */ 31b6f60a85SPekka Enberg 0x3c, 0x2a, 0x1d, 0x22, 0x35, /* u-y */ 32b6f60a85SPekka Enberg 0x1a, 33b6f60a85SPekka Enberg }; 34b6f60a85SPekka Enberg 3548d9e01aSSasha Levin static rfbScreenInfoPtr server; 36b6f60a85SPekka Enberg static char num[10] = { 37b6f60a85SPekka Enberg 0x45, 0x16, 0x1e, 0x26, 0x2e, 0x23, 0x36, 0x3d, 0x3e, 0x46, 38b6f60a85SPekka Enberg }; 39b6f60a85SPekka Enberg 40b6f60a85SPekka Enberg /* 41b6f60a85SPekka Enberg * This is called when the VNC server receives a key event 42b6f60a85SPekka Enberg * The reason this function is such a beast is that we have 43b6f60a85SPekka Enberg * to convert from ASCII characters (which is what VNC gets) 44b6f60a85SPekka Enberg * to PC keyboard scancodes, which is what Linux expects to 45b6f60a85SPekka Enberg * get from its keyboard. ASCII and the scancode set don't 46b6f60a85SPekka Enberg * really seem to mesh in any good way beyond some basics with 47b6f60a85SPekka Enberg * the letters and numbers. 48b6f60a85SPekka Enberg */ 49b6f60a85SPekka Enberg static void kbd_handle_key(rfbBool down, rfbKeySym key, rfbClientPtr cl) 50b6f60a85SPekka Enberg { 51b6f60a85SPekka Enberg char tosend = 0; 52b6f60a85SPekka Enberg 53b6f60a85SPekka Enberg if (key >= 0x41 && key <= 0x5a) 54b6f60a85SPekka Enberg key += 0x20; /* convert to lowercase */ 55b6f60a85SPekka Enberg 56b6f60a85SPekka Enberg if (key >= 0x61 && key <= 0x7a) /* a-z */ 57b6f60a85SPekka Enberg tosend = letters[key - 0x61]; 58b6f60a85SPekka Enberg 59b6f60a85SPekka Enberg if (key >= 0x30 && key <= 0x39) 60b6f60a85SPekka Enberg tosend = num[key - 0x30]; 61b6f60a85SPekka Enberg 62b6f60a85SPekka Enberg switch (key) { 63b6f60a85SPekka Enberg case XK_Insert: kbd_queue(0xe0); tosend = 0x70; break; 64b6f60a85SPekka Enberg case XK_Delete: kbd_queue(0xe0); tosend = 0x71; break; 65b6f60a85SPekka Enberg case XK_Up: kbd_queue(0xe0); tosend = 0x75; break; 66b6f60a85SPekka Enberg case XK_Down: kbd_queue(0xe0); tosend = 0x72; break; 67b6f60a85SPekka Enberg case XK_Left: kbd_queue(0xe0); tosend = 0x6b; break; 68b6f60a85SPekka Enberg case XK_Right: kbd_queue(0xe0); tosend = 0x74; break; 69b6f60a85SPekka Enberg case XK_Page_Up: kbd_queue(0xe0); tosend = 0x7d; break; 70b6f60a85SPekka Enberg case XK_Page_Down: kbd_queue(0xe0); tosend = 0x7a; break; 71b6f60a85SPekka Enberg case XK_Home: kbd_queue(0xe0); tosend = 0x6c; break; 72b6f60a85SPekka Enberg case XK_BackSpace: tosend = 0x66; break; 73b6f60a85SPekka Enberg case XK_Tab: tosend = 0x0d; break; 74b6f60a85SPekka Enberg case XK_Return: tosend = 0x5a; break; 75b6f60a85SPekka Enberg case XK_Escape: tosend = 0x76; break; 76b6f60a85SPekka Enberg case XK_End: tosend = 0x69; break; 77b6f60a85SPekka Enberg case XK_Shift_L: tosend = 0x12; break; 78b6f60a85SPekka Enberg case XK_Shift_R: tosend = 0x59; break; 79b6f60a85SPekka Enberg case XK_Control_R: kbd_queue(0xe0); 80b6f60a85SPekka Enberg case XK_Control_L: tosend = 0x14; break; 81b6f60a85SPekka Enberg case XK_Alt_R: kbd_queue(0xe0); 82b6f60a85SPekka Enberg case XK_Alt_L: tosend = 0x11; break; 83b6f60a85SPekka Enberg case XK_quoteleft: tosend = 0x0e; break; 84b6f60a85SPekka Enberg case XK_minus: tosend = 0x4e; break; 85b6f60a85SPekka Enberg case XK_equal: tosend = 0x55; break; 86b6f60a85SPekka Enberg case XK_bracketleft: tosend = 0x54; break; 87b6f60a85SPekka Enberg case XK_bracketright: tosend = 0x5b; break; 88b6f60a85SPekka Enberg case XK_backslash: tosend = 0x5d; break; 89b6f60a85SPekka Enberg case XK_Caps_Lock: tosend = 0x58; break; 90b6f60a85SPekka Enberg case XK_semicolon: tosend = 0x4c; break; 91b6f60a85SPekka Enberg case XK_quoteright: tosend = 0x52; break; 92b6f60a85SPekka Enberg case XK_comma: tosend = 0x41; break; 93b6f60a85SPekka Enberg case XK_period: tosend = 0x49; break; 94b6f60a85SPekka Enberg case XK_slash: tosend = 0x4a; break; 95b6f60a85SPekka Enberg case XK_space: tosend = 0x29; break; 96b6f60a85SPekka Enberg 97b6f60a85SPekka Enberg /* 98b6f60a85SPekka Enberg * This is where I handle the shifted characters. 99b6f60a85SPekka Enberg * They don't really map nicely the way A-Z maps to a-z, 100b6f60a85SPekka Enberg * so I'm doing it manually 101b6f60a85SPekka Enberg */ 102b6f60a85SPekka Enberg case XK_exclam: tosend = 0x16; break; 103b6f60a85SPekka Enberg case XK_quotedbl: tosend = 0x52; break; 104b6f60a85SPekka Enberg case XK_numbersign: tosend = 0x26; break; 105b6f60a85SPekka Enberg case XK_dollar: tosend = 0x25; break; 106b6f60a85SPekka Enberg case XK_percent: tosend = 0x2e; break; 107b6f60a85SPekka Enberg case XK_ampersand: tosend = 0x3d; break; 108b6f60a85SPekka Enberg case XK_parenleft: tosend = 0x46; break; 109b6f60a85SPekka Enberg case XK_parenright: tosend = 0x45; break; 110b6f60a85SPekka Enberg case XK_asterisk: tosend = 0x3e; break; 111b6f60a85SPekka Enberg case XK_plus: tosend = 0x55; break; 112b6f60a85SPekka Enberg case XK_colon: tosend = 0x4c; break; 113b6f60a85SPekka Enberg case XK_less: tosend = 0x41; break; 114b6f60a85SPekka Enberg case XK_greater: tosend = 0x49; break; 115b6f60a85SPekka Enberg case XK_question: tosend = 0x4a; break; 116b6f60a85SPekka Enberg case XK_at: tosend = 0x1e; break; 117b6f60a85SPekka Enberg case XK_asciicircum: tosend = 0x36; break; 118b6f60a85SPekka Enberg case XK_underscore: tosend = 0x4e; break; 119b6f60a85SPekka Enberg case XK_braceleft: tosend = 0x54; break; 120b6f60a85SPekka Enberg case XK_braceright: tosend = 0x5b; break; 121b6f60a85SPekka Enberg case XK_bar: tosend = 0x5d; break; 122b6f60a85SPekka Enberg case XK_asciitilde: tosend = 0x0e; break; 123b6f60a85SPekka Enberg default: break; 124b6f60a85SPekka Enberg } 125b6f60a85SPekka Enberg 126b6f60a85SPekka Enberg /* 127b6f60a85SPekka Enberg * If this is a "key up" event (the user has released the key, we 128b6f60a85SPekka Enberg * need to send 0xf0 first. 129b6f60a85SPekka Enberg */ 130b6f60a85SPekka Enberg if (!down && tosend != 0x0) 131b6f60a85SPekka Enberg kbd_queue(0xf0); 132b6f60a85SPekka Enberg 133b6f60a85SPekka Enberg if (tosend) 134b6f60a85SPekka Enberg kbd_queue(tosend); 135b6f60a85SPekka Enberg } 136b6f60a85SPekka Enberg 137b6f60a85SPekka Enberg /* The previous X and Y coordinates of the mouse */ 138b6f60a85SPekka Enberg static int xlast, ylast = -1; 139b6f60a85SPekka Enberg 140b6f60a85SPekka Enberg /* 141b6f60a85SPekka Enberg * This function is called by the VNC server whenever a mouse event occurs. 142b6f60a85SPekka Enberg */ 143b6f60a85SPekka Enberg static void kbd_handle_ptr(int buttonMask, int x, int y, rfbClientPtr cl) 144b6f60a85SPekka Enberg { 145b6f60a85SPekka Enberg int dx, dy; 146b6f60a85SPekka Enberg char b1 = 0x8; 147b6f60a85SPekka Enberg 148b6f60a85SPekka Enberg /* The VNC mask and the PS/2 button encoding are the same */ 149b6f60a85SPekka Enberg b1 |= buttonMask; 150b6f60a85SPekka Enberg 151b6f60a85SPekka Enberg if (xlast >= 0 && ylast >= 0) { 152b6f60a85SPekka Enberg /* The PS/2 mouse sends deltas, not absolutes */ 153b6f60a85SPekka Enberg dx = x - xlast; 154b6f60a85SPekka Enberg dy = ylast - y; 155b6f60a85SPekka Enberg 156b6f60a85SPekka Enberg /* Set overflow bits if needed */ 157b6f60a85SPekka Enberg if (dy > 255) 158b6f60a85SPekka Enberg b1 |= 0x80; 159b6f60a85SPekka Enberg if (dx > 255) 160b6f60a85SPekka Enberg b1 |= 0x40; 161b6f60a85SPekka Enberg 162b6f60a85SPekka Enberg /* Set negative bits if needed */ 163b6f60a85SPekka Enberg if (dy < 0) 164b6f60a85SPekka Enberg b1 |= 0x20; 165b6f60a85SPekka Enberg if (dx < 0) 166b6f60a85SPekka Enberg b1 |= 0x10; 167b6f60a85SPekka Enberg 168b6f60a85SPekka Enberg mouse_queue(b1); 169b6f60a85SPekka Enberg mouse_queue(dx); 170b6f60a85SPekka Enberg mouse_queue(dy); 171b6f60a85SPekka Enberg } 172b6f60a85SPekka Enberg 173b6f60a85SPekka Enberg xlast = x; 174b6f60a85SPekka Enberg ylast = y; 175b6f60a85SPekka Enberg rfbDefaultPtrAddEvent(buttonMask, x, y, cl); 176b6f60a85SPekka Enberg } 177b6f60a85SPekka Enberg 178aba1efa5SPekka Enberg static void *vnc__thread(void *p) 179aba1efa5SPekka Enberg { 180aba1efa5SPekka Enberg struct framebuffer *fb = p; 181aba1efa5SPekka Enberg /* 182aba1efa5SPekka Enberg * Make a fake argc and argv because the getscreen function 183aba1efa5SPekka Enberg * seems to want it. 184aba1efa5SPekka Enberg */ 185aba1efa5SPekka Enberg char argv[1][1] = {{0}}; 186aba1efa5SPekka Enberg int argc = 1; 187aba1efa5SPekka Enberg 188aba1efa5SPekka Enberg server = rfbGetScreen(&argc, (char **) argv, fb->width, fb->height, 8, 3, 4); 189aba1efa5SPekka Enberg server->frameBuffer = fb->mem; 190aba1efa5SPekka Enberg server->alwaysShared = TRUE; 191aba1efa5SPekka Enberg server->kbdAddEvent = kbd_handle_key; 192aba1efa5SPekka Enberg server->ptrAddEvent = kbd_handle_ptr; 193aba1efa5SPekka Enberg rfbInitServer(server); 194aba1efa5SPekka Enberg 195aba1efa5SPekka Enberg while (rfbIsActive(server)) { 196aba1efa5SPekka Enberg rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height); 197aba1efa5SPekka Enberg rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME); 198aba1efa5SPekka Enberg } 199aba1efa5SPekka Enberg return NULL; 200aba1efa5SPekka Enberg } 201aba1efa5SPekka Enberg 202aba1efa5SPekka Enberg static int vnc__start(struct framebuffer *fb) 203aba1efa5SPekka Enberg { 204aba1efa5SPekka Enberg pthread_t thread; 205aba1efa5SPekka Enberg 206aba1efa5SPekka Enberg if (pthread_create(&thread, NULL, vnc__thread, fb) != 0) 207aba1efa5SPekka Enberg return -1; 208aba1efa5SPekka Enberg 209aba1efa5SPekka Enberg return 0; 210aba1efa5SPekka Enberg } 211aba1efa5SPekka Enberg 21248d9e01aSSasha Levin static int vnc__stop(struct framebuffer *fb) 21348d9e01aSSasha Levin { 21448d9e01aSSasha Levin rfbShutdownServer(server, TRUE); 21548d9e01aSSasha Levin 21648d9e01aSSasha Levin return 0; 21748d9e01aSSasha Levin } 21848d9e01aSSasha Levin 219aba1efa5SPekka Enberg static struct fb_target_operations vnc_ops = { 220aba1efa5SPekka Enberg .start = vnc__start, 22148d9e01aSSasha Levin .stop = vnc__stop, 222aba1efa5SPekka Enberg }; 223aba1efa5SPekka Enberg 224*07d52d77SSasha Levin int vnc__init(struct kvm *kvm) 225aba1efa5SPekka Enberg { 226*07d52d77SSasha Levin struct framebuffer *fb; 227*07d52d77SSasha Levin 228*07d52d77SSasha Levin if (!kvm->cfg.vnc) 229*07d52d77SSasha Levin return 0; 230*07d52d77SSasha Levin 231*07d52d77SSasha Levin fb = vesa__init(kvm); 232*07d52d77SSasha Levin if (IS_ERR(fb)) { 233*07d52d77SSasha Levin pr_err("vesa__init() failed with error %ld\n", PTR_ERR(fb)); 234*07d52d77SSasha Levin return PTR_ERR(fb); 235*07d52d77SSasha Levin } 236*07d52d77SSasha Levin 23748d9e01aSSasha Levin return fb__attach(fb, &vnc_ops); 23848d9e01aSSasha Levin } 23948d9e01aSSasha Levin 240*07d52d77SSasha Levin int vnc__exit(struct kvm *kvm) 24148d9e01aSSasha Levin { 242*07d52d77SSasha Levin return vnc__stop(NULL); 243aba1efa5SPekka Enberg } 244