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