mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-08-23 14:36:11 +00:00
a4d8c55eb2
Give threads a meaningful name. This makes debugging much easier, and everything else much prettier. Suggested-by: Ingo Molnar <mingo@kernel.org> Signed-off-by: Sasha Levin <sasha.levin@oracle.com> [ penberg@kernel.org: specify vcpu names ] Signed-off-by: Pekka Enberg <penberg@kernel.org>
251 lines
6.4 KiB
C
251 lines
6.4 KiB
C
#include "kvm/vnc.h"
|
|
|
|
#include "kvm/framebuffer.h"
|
|
#include "kvm/i8042.h"
|
|
#include "kvm/vesa.h"
|
|
|
|
#include <linux/types.h>
|
|
#include <rfb/keysym.h>
|
|
#include <rfb/rfb.h>
|
|
#include <pthread.h>
|
|
#include <linux/err.h>
|
|
|
|
#define VESA_QUEUE_SIZE 128
|
|
#define VESA_IRQ 14
|
|
|
|
/*
|
|
* This "6000" value is pretty much the result of experimentation
|
|
* It seems that around this value, things update pretty smoothly
|
|
*/
|
|
#define VESA_UPDATE_TIME 6000
|
|
|
|
/*
|
|
* We can map the letters and numbers without a fuss,
|
|
* but the other characters not so much.
|
|
*/
|
|
static char letters[26] = {
|
|
0x1c, 0x32, 0x21, 0x23, 0x24, /* a-e */
|
|
0x2b, 0x34, 0x33, 0x43, 0x3b, /* f-j */
|
|
0x42, 0x4b, 0x3a, 0x31, 0x44, /* k-o */
|
|
0x4d, 0x15, 0x2d, 0x1b, 0x2c, /* p-t */
|
|
0x3c, 0x2a, 0x1d, 0x22, 0x35, /* u-y */
|
|
0x1a,
|
|
};
|
|
|
|
static rfbScreenInfoPtr server;
|
|
static char num[10] = {
|
|
0x45, 0x16, 0x1e, 0x26, 0x2e, 0x23, 0x36, 0x3d, 0x3e, 0x46,
|
|
};
|
|
|
|
/*
|
|
* This is called when the VNC server receives a key event
|
|
* The reason this function is such a beast is that we have
|
|
* to convert from ASCII characters (which is what VNC gets)
|
|
* to PC keyboard scancodes, which is what Linux expects to
|
|
* get from its keyboard. ASCII and the scancode set don't
|
|
* really seem to mesh in any good way beyond some basics with
|
|
* the letters and numbers.
|
|
*/
|
|
static void kbd_handle_key(rfbBool down, rfbKeySym key, rfbClientPtr cl)
|
|
{
|
|
char tosend = 0;
|
|
|
|
if (key >= 0x41 && key <= 0x5a)
|
|
key += 0x20; /* convert to lowercase */
|
|
|
|
if (key >= 0x61 && key <= 0x7a) /* a-z */
|
|
tosend = letters[key - 0x61];
|
|
|
|
if (key >= 0x30 && key <= 0x39)
|
|
tosend = num[key - 0x30];
|
|
|
|
switch (key) {
|
|
case XK_Insert: kbd_queue(0xe0); tosend = 0x70; break;
|
|
case XK_Delete: kbd_queue(0xe0); tosend = 0x71; break;
|
|
case XK_Up: kbd_queue(0xe0); tosend = 0x75; break;
|
|
case XK_Down: kbd_queue(0xe0); tosend = 0x72; break;
|
|
case XK_Left: kbd_queue(0xe0); tosend = 0x6b; break;
|
|
case XK_Right: kbd_queue(0xe0); tosend = 0x74; break;
|
|
case XK_Page_Up: kbd_queue(0xe0); tosend = 0x7d; break;
|
|
case XK_Page_Down: kbd_queue(0xe0); tosend = 0x7a; break;
|
|
case XK_Home: kbd_queue(0xe0); tosend = 0x6c; break;
|
|
case XK_BackSpace: tosend = 0x66; break;
|
|
case XK_Tab: tosend = 0x0d; break;
|
|
case XK_Return: tosend = 0x5a; break;
|
|
case XK_Escape: tosend = 0x76; break;
|
|
case XK_End: tosend = 0x69; break;
|
|
case XK_Shift_L: tosend = 0x12; break;
|
|
case XK_Shift_R: tosend = 0x59; break;
|
|
case XK_Control_R: kbd_queue(0xe0);
|
|
case XK_Control_L: tosend = 0x14; break;
|
|
case XK_Alt_R: kbd_queue(0xe0);
|
|
case XK_Alt_L: tosend = 0x11; break;
|
|
case XK_quoteleft: tosend = 0x0e; break;
|
|
case XK_minus: tosend = 0x4e; break;
|
|
case XK_equal: tosend = 0x55; break;
|
|
case XK_bracketleft: tosend = 0x54; break;
|
|
case XK_bracketright: tosend = 0x5b; break;
|
|
case XK_backslash: tosend = 0x5d; break;
|
|
case XK_Caps_Lock: tosend = 0x58; break;
|
|
case XK_semicolon: tosend = 0x4c; break;
|
|
case XK_quoteright: tosend = 0x52; break;
|
|
case XK_comma: tosend = 0x41; break;
|
|
case XK_period: tosend = 0x49; break;
|
|
case XK_slash: tosend = 0x4a; break;
|
|
case XK_space: tosend = 0x29; break;
|
|
|
|
/*
|
|
* This is where I handle the shifted characters.
|
|
* They don't really map nicely the way A-Z maps to a-z,
|
|
* so I'm doing it manually
|
|
*/
|
|
case XK_exclam: tosend = 0x16; break;
|
|
case XK_quotedbl: tosend = 0x52; break;
|
|
case XK_numbersign: tosend = 0x26; break;
|
|
case XK_dollar: tosend = 0x25; break;
|
|
case XK_percent: tosend = 0x2e; break;
|
|
case XK_ampersand: tosend = 0x3d; break;
|
|
case XK_parenleft: tosend = 0x46; break;
|
|
case XK_parenright: tosend = 0x45; break;
|
|
case XK_asterisk: tosend = 0x3e; break;
|
|
case XK_plus: tosend = 0x55; break;
|
|
case XK_colon: tosend = 0x4c; break;
|
|
case XK_less: tosend = 0x41; break;
|
|
case XK_greater: tosend = 0x49; break;
|
|
case XK_question: tosend = 0x4a; break;
|
|
case XK_at: tosend = 0x1e; break;
|
|
case XK_asciicircum: tosend = 0x36; break;
|
|
case XK_underscore: tosend = 0x4e; break;
|
|
case XK_braceleft: tosend = 0x54; break;
|
|
case XK_braceright: tosend = 0x5b; break;
|
|
case XK_bar: tosend = 0x5d; break;
|
|
case XK_asciitilde: tosend = 0x0e; break;
|
|
default: break;
|
|
}
|
|
|
|
/*
|
|
* If this is a "key up" event (the user has released the key, we
|
|
* need to send 0xf0 first.
|
|
*/
|
|
if (!down && tosend != 0x0)
|
|
kbd_queue(0xf0);
|
|
|
|
if (tosend)
|
|
kbd_queue(tosend);
|
|
}
|
|
|
|
/* The previous X and Y coordinates of the mouse */
|
|
static int xlast, ylast = -1;
|
|
|
|
/*
|
|
* This function is called by the VNC server whenever a mouse event occurs.
|
|
*/
|
|
static void kbd_handle_ptr(int buttonMask, int x, int y, rfbClientPtr cl)
|
|
{
|
|
int dx, dy;
|
|
char b1 = 0x8;
|
|
|
|
/* The VNC mask and the PS/2 button encoding are the same */
|
|
b1 |= buttonMask;
|
|
|
|
if (xlast >= 0 && ylast >= 0) {
|
|
/* The PS/2 mouse sends deltas, not absolutes */
|
|
dx = x - xlast;
|
|
dy = ylast - y;
|
|
|
|
/* Set overflow bits if needed */
|
|
if (dy > 255)
|
|
b1 |= 0x80;
|
|
if (dx > 255)
|
|
b1 |= 0x40;
|
|
|
|
/* Set negative bits if needed */
|
|
if (dy < 0)
|
|
b1 |= 0x20;
|
|
if (dx < 0)
|
|
b1 |= 0x10;
|
|
|
|
mouse_queue(b1);
|
|
mouse_queue(dx);
|
|
mouse_queue(dy);
|
|
}
|
|
|
|
xlast = x;
|
|
ylast = y;
|
|
rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
|
|
}
|
|
|
|
static void *vnc__thread(void *p)
|
|
{
|
|
struct framebuffer *fb = p;
|
|
/*
|
|
* Make a fake argc and argv because the getscreen function
|
|
* seems to want it.
|
|
*/
|
|
char argv[1][1] = {{0}};
|
|
int argc = 1;
|
|
|
|
kvm__set_thread_name("kvm-vnc-worker");
|
|
|
|
server = rfbGetScreen(&argc, (char **) argv, fb->width, fb->height, 8, 3, 4);
|
|
server->frameBuffer = fb->mem;
|
|
server->alwaysShared = TRUE;
|
|
server->kbdAddEvent = kbd_handle_key;
|
|
server->ptrAddEvent = kbd_handle_ptr;
|
|
rfbInitServer(server);
|
|
|
|
while (rfbIsActive(server)) {
|
|
rfbMarkRectAsModified(server, 0, 0, fb->width, fb->height);
|
|
rfbProcessEvents(server, server->deferUpdateTime * VESA_UPDATE_TIME);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int vnc__start(struct framebuffer *fb)
|
|
{
|
|
pthread_t thread;
|
|
|
|
if (pthread_create(&thread, NULL, vnc__thread, fb) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int vnc__stop(struct framebuffer *fb)
|
|
{
|
|
rfbShutdownServer(server, TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct fb_target_operations vnc_ops = {
|
|
.start = vnc__start,
|
|
.stop = vnc__stop,
|
|
};
|
|
|
|
int vnc__init(struct kvm *kvm)
|
|
{
|
|
struct framebuffer *fb;
|
|
|
|
if (!kvm->cfg.vnc)
|
|
return 0;
|
|
|
|
fb = vesa__init(kvm);
|
|
if (IS_ERR(fb)) {
|
|
pr_err("vesa__init() failed with error %ld\n", PTR_ERR(fb));
|
|
return PTR_ERR(fb);
|
|
}
|
|
|
|
return fb__attach(fb, &vnc_ops);
|
|
}
|
|
dev_init(vnc__init);
|
|
|
|
int vnc__exit(struct kvm *kvm)
|
|
{
|
|
if (kvm->cfg.vnc)
|
|
return vnc__stop(NULL);
|
|
|
|
return 0;
|
|
}
|
|
dev_exit(vnc__exit);
|