input: ps2 fixes.

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJa+qu4AAoJEEy22O7T6HE47msQAI0SIQEPgHYmDmr8iNSggLBK
 cInvCdOw1QzGiyViIKAgj+wUqDjnRv2eM9bmRTzhM5BdESxw4BLmK5zC/kq8bmd6
 QhQZRBpGqk96evD2PbgShv4Vt732+gWIPJqnh4ZuxjDtLmHlCYdZfOC/fCeSNySr
 MbLMR2dqQzJNCll3W8seCDQveaYja9m+l9TSvoJAaZwh6f6PhAOl0linihniDX4a
 aCaa23jWs+M64+3Boo4wLSlhXVRz5vKvaGyiTBdvUhy0Pxe3PCKKNxtzUujg/5t7
 E7xaCEmGFR/WnzMmTYrDMEBqsp0F8sMaNDDQVWbFeWdzZVRXG9+aHJsOIATzSsrf
 KuVwJRtle55G8rvc21L8PELqHy3fGI0CkipjQpEtDiQZV481dkHV/+ytWDUQW6t5
 HgTIhCo4xI7GyHfwo8DjfqejSZKlRBb104SeonlE0oiDvjgEbBnJmnKW2ryNUOSD
 JkZYRxddrHKkgbspYlLHW2/EBJfc4xScbjfvaAjeafaZXKMXI37CRxUxdsXBMoUK
 4IZ2UmIMpWM1P+5L9f5OYJcboGeeV0lbUi22tc9RWGGUG5nCAXZ6r10PZRlI6PHv
 36NUBNLRL4LeC24XKpgWRekMX052lDgwPT0oXGjWfrqc+T1QfK8wafr7KjOnAXRE
 huKY5PiPrGKbzyfBzbXo
 =qt2Q
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kraxel/tags/input-20180515-pull-request' into staging

input: ps2 fixes.

# gpg: Signature made Tue 15 May 2018 10:43:20 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>"
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>"
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>"
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/input-20180515-pull-request:
  ps2: Fix mouse stream corruption due to lost data
  ps2: Clear the PS/2 queue and obey disable

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2018-05-15 12:50:06 +01:00
commit ad1b4ec39c
2 changed files with 111 additions and 29 deletions

View File

@ -188,16 +188,64 @@ static void ps2_reset_queue(PS2State *s)
q->count = 0; q->count = 0;
} }
void ps2_queue(PS2State *s, int b) void ps2_queue_noirq(PS2State *s, int b)
{ {
PS2Queue *q = &s->queue; PS2Queue *q = &s->queue;
if (q->count >= PS2_QUEUE_SIZE - 1) if (q->count == PS2_QUEUE_SIZE) {
return; return;
}
q->data[q->wptr] = b; q->data[q->wptr] = b;
if (++q->wptr == PS2_QUEUE_SIZE) if (++q->wptr == PS2_QUEUE_SIZE)
q->wptr = 0; q->wptr = 0;
q->count++; q->count++;
}
void ps2_raise_irq(PS2State *s)
{
s->update_irq(s->update_arg, 1);
}
void ps2_queue(PS2State *s, int b)
{
ps2_queue_noirq(s, b);
s->update_irq(s->update_arg, 1);
}
void ps2_queue_2(PS2State *s, int b1, int b2)
{
if (PS2_QUEUE_SIZE - s->queue.count < 2) {
return;
}
ps2_queue_noirq(s, b1);
ps2_queue_noirq(s, b2);
s->update_irq(s->update_arg, 1);
}
void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
{
if (PS2_QUEUE_SIZE - s->queue.count < 3) {
return;
}
ps2_queue_noirq(s, b1);
ps2_queue_noirq(s, b2);
ps2_queue_noirq(s, b3);
s->update_irq(s->update_arg, 1);
}
void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
{
if (PS2_QUEUE_SIZE - s->queue.count < 4) {
return;
}
ps2_queue_noirq(s, b1);
ps2_queue_noirq(s, b2);
ps2_queue_noirq(s, b3);
ps2_queue_noirq(s, b4);
s->update_irq(s->update_arg, 1); s->update_irq(s->update_arg, 1);
} }
@ -232,6 +280,11 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
uint16_t keycode = 0; uint16_t keycode = 0;
int mod; int mod;
/* do not process events while disabled to prevent stream corruption */
if (!s->scan_enabled) {
return;
}
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
assert(evt->type == INPUT_EVENT_KIND_KEY); assert(evt->type == INPUT_EVENT_KIND_KEY);
qcode = qemu_input_key_value_to_qcode(key->key); qcode = qemu_input_key_value_to_qcode(key->key);
@ -496,13 +549,17 @@ void ps2_write_keyboard(void *opaque, int val)
ps2_queue(&s->common, KBD_REPLY_RESEND); ps2_queue(&s->common, KBD_REPLY_RESEND);
break; break;
case KBD_CMD_GET_ID: case KBD_CMD_GET_ID:
ps2_queue(&s->common, KBD_REPLY_ACK);
/* We emulate a MF2 AT keyboard here */ /* We emulate a MF2 AT keyboard here */
ps2_queue(&s->common, KBD_REPLY_ID);
if (s->translate) if (s->translate)
ps2_queue(&s->common, 0x41); ps2_queue_3(&s->common,
KBD_REPLY_ACK,
KBD_REPLY_ID,
0x41);
else else
ps2_queue(&s->common, 0x83); ps2_queue_3(&s->common,
KBD_REPLY_ACK,
KBD_REPLY_ID,
0x83);
break; break;
case KBD_CMD_ECHO: case KBD_CMD_ECHO:
ps2_queue(&s->common, KBD_CMD_ECHO); ps2_queue(&s->common, KBD_CMD_ECHO);
@ -529,8 +586,9 @@ void ps2_write_keyboard(void *opaque, int val)
break; break;
case KBD_CMD_RESET: case KBD_CMD_RESET:
ps2_reset_keyboard(s); ps2_reset_keyboard(s);
ps2_queue(&s->common, KBD_REPLY_ACK); ps2_queue_2(&s->common,
ps2_queue(&s->common, KBD_REPLY_POR); KBD_REPLY_ACK,
KBD_REPLY_POR);
break; break;
default: default:
ps2_queue(&s->common, KBD_REPLY_RESEND); ps2_queue(&s->common, KBD_REPLY_RESEND);
@ -539,8 +597,10 @@ void ps2_write_keyboard(void *opaque, int val)
break; break;
case KBD_CMD_SCANCODE: case KBD_CMD_SCANCODE:
if (val == 0) { if (val == 0) {
if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
ps2_queue(&s->common, KBD_REPLY_ACK); ps2_queue(&s->common, KBD_REPLY_ACK);
ps2_put_keycode(s, s->scancode_set); ps2_put_keycode(s, s->scancode_set);
}
} else if (val >= 1 && val <= 3) { } else if (val >= 1 && val <= 3) {
s->scancode_set = val; s->scancode_set = val;
ps2_queue(&s->common, KBD_REPLY_ACK); ps2_queue(&s->common, KBD_REPLY_ACK);
@ -572,11 +632,16 @@ void ps2_keyboard_set_translation(void *opaque, int mode)
s->translate = mode; s->translate = mode;
} }
static void ps2_mouse_send_packet(PS2MouseState *s) static int ps2_mouse_send_packet(PS2MouseState *s)
{ {
const int needed = 3 + (s->mouse_type - 2);
unsigned int b; unsigned int b;
int dx1, dy1, dz1; int dx1, dy1, dz1;
if (PS2_QUEUE_SIZE - s->common.queue.count < needed) {
return 0;
}
dx1 = s->mouse_dx; dx1 = s->mouse_dx;
dy1 = s->mouse_dy; dy1 = s->mouse_dy;
dz1 = s->mouse_dz; dz1 = s->mouse_dz;
@ -590,9 +655,9 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
else if (dy1 < -127) else if (dy1 < -127)
dy1 = -127; dy1 = -127;
b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07); b = 0x08 | ((dx1 < 0) << 4) | ((dy1 < 0) << 5) | (s->mouse_buttons & 0x07);
ps2_queue(&s->common, b); ps2_queue_noirq(&s->common, b);
ps2_queue(&s->common, dx1 & 0xff); ps2_queue_noirq(&s->common, dx1 & 0xff);
ps2_queue(&s->common, dy1 & 0xff); ps2_queue_noirq(&s->common, dy1 & 0xff);
/* extra byte for IMPS/2 or IMEX */ /* extra byte for IMPS/2 or IMEX */
switch(s->mouse_type) { switch(s->mouse_type) {
default: default:
@ -602,7 +667,7 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
dz1 = 127; dz1 = 127;
else if (dz1 < -127) else if (dz1 < -127)
dz1 = -127; dz1 = -127;
ps2_queue(&s->common, dz1 & 0xff); ps2_queue_noirq(&s->common, dz1 & 0xff);
break; break;
case 4: case 4:
if (dz1 > 7) if (dz1 > 7)
@ -610,15 +675,19 @@ static void ps2_mouse_send_packet(PS2MouseState *s)
else if (dz1 < -7) else if (dz1 < -7)
dz1 = -7; dz1 = -7;
b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1); b = (dz1 & 0x0f) | ((s->mouse_buttons & 0x18) << 1);
ps2_queue(&s->common, b); ps2_queue_noirq(&s->common, b);
break; break;
} }
ps2_raise_irq(&s->common);
trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b); trace_ps2_mouse_send_packet(s, dx1, dy1, dz1, b);
/* update deltas */ /* update deltas */
s->mouse_dx -= dx1; s->mouse_dx -= dx1;
s->mouse_dy -= dy1; s->mouse_dy -= dy1;
s->mouse_dz -= dz1; s->mouse_dz -= dz1;
return 1;
} }
static void ps2_mouse_event(DeviceState *dev, QemuConsole *src, static void ps2_mouse_event(DeviceState *dev, QemuConsole *src,
@ -673,14 +742,18 @@ static void ps2_mouse_sync(DeviceState *dev)
{ {
PS2MouseState *s = (PS2MouseState *)dev; PS2MouseState *s = (PS2MouseState *)dev;
/* do not sync while disabled to prevent stream corruption */
if (!(s->mouse_status & MOUSE_STATUS_ENABLED)) {
return;
}
if (s->mouse_buttons) { if (s->mouse_buttons) {
qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER);
} }
if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) {
while (s->common.queue.count < PS2_QUEUE_SIZE - 4) {
/* if not remote, send event. Multiple events are sent if /* if not remote, send event. Multiple events are sent if
too big deltas */ too big deltas */
ps2_mouse_send_packet(s); while (ps2_mouse_send_packet(s)) {
if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0) if (s->mouse_dx == 0 && s->mouse_dy == 0 && s->mouse_dz == 0)
break; break;
} }
@ -739,8 +812,9 @@ void ps2_write_mouse(void *opaque, int val)
ps2_queue(&s->common, AUX_ACK); ps2_queue(&s->common, AUX_ACK);
break; break;
case AUX_GET_TYPE: case AUX_GET_TYPE:
ps2_queue(&s->common, AUX_ACK); ps2_queue_2(&s->common,
ps2_queue(&s->common, s->mouse_type); AUX_ACK,
s->mouse_type);
break; break;
case AUX_SET_RES: case AUX_SET_RES:
case AUX_SET_SAMPLE: case AUX_SET_SAMPLE:
@ -748,10 +822,11 @@ void ps2_write_mouse(void *opaque, int val)
ps2_queue(&s->common, AUX_ACK); ps2_queue(&s->common, AUX_ACK);
break; break;
case AUX_GET_SCALE: case AUX_GET_SCALE:
ps2_queue(&s->common, AUX_ACK); ps2_queue_4(&s->common,
ps2_queue(&s->common, s->mouse_status); AUX_ACK,
ps2_queue(&s->common, s->mouse_resolution); s->mouse_status,
ps2_queue(&s->common, s->mouse_sample_rate); s->mouse_resolution,
s->mouse_sample_rate);
break; break;
case AUX_POLL: case AUX_POLL:
ps2_queue(&s->common, AUX_ACK); ps2_queue(&s->common, AUX_ACK);
@ -776,9 +851,11 @@ void ps2_write_mouse(void *opaque, int val)
s->mouse_resolution = 2; s->mouse_resolution = 2;
s->mouse_status = 0; s->mouse_status = 0;
s->mouse_type = 0; s->mouse_type = 0;
ps2_queue(&s->common, AUX_ACK); ps2_reset_queue(&s->common);
ps2_queue(&s->common, 0xaa); ps2_queue_3(&s->common,
ps2_queue(&s->common, s->mouse_type); AUX_ACK,
0xaa,
s->mouse_type);
break; break;
default: default:
break; break;

View File

@ -37,7 +37,12 @@ void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg);
void ps2_write_mouse(void *, int val); void ps2_write_mouse(void *, int val);
void ps2_write_keyboard(void *, int val); void ps2_write_keyboard(void *, int val);
uint32_t ps2_read_data(PS2State *s); uint32_t ps2_read_data(PS2State *s);
void ps2_queue_noirq(PS2State *s, int b);
void ps2_raise_irq(PS2State *s);
void ps2_queue(PS2State *s, int b); void ps2_queue(PS2State *s, int b);
void ps2_queue_2(PS2State *s, int b1, int b2);
void ps2_queue_3(PS2State *s, int b1, int b2, int b3);
void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4);
void ps2_keyboard_set_translation(void *opaque, int mode); void ps2_keyboard_set_translation(void *opaque, int mode);
void ps2_mouse_fake_event(void *opaque); void ps2_mouse_fake_event(void *opaque);