]> git.karo-electronics.de Git - karo-tx-linux.git/blob - tools/kvm/ui/vnc.c
Merge branch 'linus'
[karo-tx-linux.git] / tools / kvm / ui / vnc.c
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  */
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  */
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
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         server = rfbGetScreen(&argc, (char **) argv, fb->width, fb->height, 8, 3, 4);
189         server->frameBuffer             = fb->mem;
190         server->alwaysShared            = TRUE;
191         server->kbdAddEvent             = kbd_handle_key;
192         server->ptrAddEvent             = kbd_handle_ptr;
193         rfbInitServer(server);
194
195         while (rfbIsActive(server)) {
196                 rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height);
197                 rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
198         }
199         return NULL;
200 }
201
202 static int vnc__start(struct framebuffer *fb)
203 {
204         pthread_t thread;
205
206         if (pthread_create(&thread, NULL, vnc__thread, fb) != 0)
207                 return -1;
208
209         return 0;
210 }
211
212 static int vnc__stop(struct framebuffer *fb)
213 {
214         rfbShutdownServer(server, TRUE);
215
216         return 0;
217 }
218
219 static struct fb_target_operations vnc_ops = {
220         .start  = vnc__start,
221         .stop   = vnc__stop,
222 };
223
224 int vnc__init(struct kvm *kvm)
225 {
226         struct framebuffer *fb;
227
228         if (!kvm->cfg.vnc)
229                 return 0;
230
231         fb = vesa__init(kvm);
232         if (IS_ERR(fb)) {
233                 pr_err("vesa__init() failed with error %ld\n", PTR_ERR(fb));
234                 return PTR_ERR(fb);
235         }
236
237         return fb__attach(fb, &vnc_ops);
238 }
239 dev_init(vnc__init);
240
241 int vnc__exit(struct kvm *kvm)
242 {
243         if (kvm->cfg.vnc)
244                 return vnc__stop(NULL);
245
246         return 0;
247 }
248 dev_exit(vnc__exit);