1aba1efa5SPekka Enberg #include "kvm/vnc.h"
2aba1efa5SPekka Enberg
3aba1efa5SPekka Enberg #include "kvm/framebuffer.h"
4aba1efa5SPekka Enberg #include "kvm/i8042.h"
507d52d77SSasha 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>
1107d52d77SSasha 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 */
kbd_handle_key(rfbBool down,rfbKeySym key,rfbClientPtr cl)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 */
kbd_handle_ptr(int buttonMask,int x,int y,rfbClientPtr cl)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
vnc__thread(void * p)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
188*a4d8c55eSSasha Levin kvm__set_thread_name("kvm-vnc-worker");
189*a4d8c55eSSasha Levin
190aba1efa5SPekka Enberg server = rfbGetScreen(&argc, (char **) argv, fb->width, fb->height, 8, 3, 4);
191aba1efa5SPekka Enberg server->frameBuffer = fb->mem;
192aba1efa5SPekka Enberg server->alwaysShared = TRUE;
193aba1efa5SPekka Enberg server->kbdAddEvent = kbd_handle_key;
194aba1efa5SPekka Enberg server->ptrAddEvent = kbd_handle_ptr;
195aba1efa5SPekka Enberg rfbInitServer(server);
196aba1efa5SPekka Enberg
197aba1efa5SPekka Enberg while (rfbIsActive(server)) {
198aba1efa5SPekka Enberg rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height);
199aba1efa5SPekka Enberg rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
200aba1efa5SPekka Enberg }
201aba1efa5SPekka Enberg return NULL;
202aba1efa5SPekka Enberg }
203aba1efa5SPekka Enberg
vnc__start(struct framebuffer * fb)204aba1efa5SPekka Enberg static int vnc__start(struct framebuffer *fb)
205aba1efa5SPekka Enberg {
206aba1efa5SPekka Enberg pthread_t thread;
207aba1efa5SPekka Enberg
208aba1efa5SPekka Enberg if (pthread_create(&thread, NULL, vnc__thread, fb) != 0)
209aba1efa5SPekka Enberg return -1;
210aba1efa5SPekka Enberg
211aba1efa5SPekka Enberg return 0;
212aba1efa5SPekka Enberg }
213aba1efa5SPekka Enberg
vnc__stop(struct framebuffer * fb)21448d9e01aSSasha Levin static int vnc__stop(struct framebuffer *fb)
21548d9e01aSSasha Levin {
21648d9e01aSSasha Levin rfbShutdownServer(server, TRUE);
21748d9e01aSSasha Levin
21848d9e01aSSasha Levin return 0;
21948d9e01aSSasha Levin }
22048d9e01aSSasha Levin
221aba1efa5SPekka Enberg static struct fb_target_operations vnc_ops = {
222aba1efa5SPekka Enberg .start = vnc__start,
22348d9e01aSSasha Levin .stop = vnc__stop,
224aba1efa5SPekka Enberg };
225aba1efa5SPekka Enberg
vnc__init(struct kvm * kvm)22607d52d77SSasha Levin int vnc__init(struct kvm *kvm)
227aba1efa5SPekka Enberg {
22807d52d77SSasha Levin struct framebuffer *fb;
22907d52d77SSasha Levin
23007d52d77SSasha Levin if (!kvm->cfg.vnc)
23107d52d77SSasha Levin return 0;
23207d52d77SSasha Levin
23307d52d77SSasha Levin fb = vesa__init(kvm);
23407d52d77SSasha Levin if (IS_ERR(fb)) {
23507d52d77SSasha Levin pr_err("vesa__init() failed with error %ld\n", PTR_ERR(fb));
23607d52d77SSasha Levin return PTR_ERR(fb);
23707d52d77SSasha Levin }
23807d52d77SSasha Levin
23948d9e01aSSasha Levin return fb__attach(fb, &vnc_ops);
24048d9e01aSSasha Levin }
24149a8afd1SSasha Levin dev_init(vnc__init);
24248d9e01aSSasha Levin
vnc__exit(struct kvm * kvm)24307d52d77SSasha Levin int vnc__exit(struct kvm *kvm)
24448d9e01aSSasha Levin {
24549a8afd1SSasha Levin if (kvm->cfg.vnc)
24607d52d77SSasha Levin return vnc__stop(NULL);
24749a8afd1SSasha Levin
24849a8afd1SSasha Levin return 0;
249aba1efa5SPekka Enberg }
25049a8afd1SSasha Levin dev_exit(vnc__exit);
251