Skip to content

Commit 9e24b2d

Browse files
Volker Rümelinkraxel
authored andcommitted
ps2: use a separate keyboard command reply queue
A PS/2 keyboard has a separate command reply queue that is independent of the key queue. This prevents that command replies and keyboard input mix. Keyboard command replies take precedence over queued keystrokes. A new keyboard command removes any remaining command replies from the command reply queue. Implement a separate keyboard command reply queue and clear the command reply queue before command execution. This brings the PS/2 keyboard emulation much closer to a real PS/2 keyboard. The command reply queue is located in a few free bytes directly in front of the scancode queue. Because the scancode queue has a maximum length of 16 bytes there are 240 bytes available for the command reply queue. At the moment only a maximum of 3 bytes are required. For compatibility reasons rptr, wptr and count kept their function. rptr is the start, wptr is the end and count is the length of the entire keyboard queue. The new variable cwptr is the end of the command reply queue or -1 if the queue is empty. To write to the command reply queue, rptr is moved backward by the number of required bytes and the command replies are written to the buffer starting at the new rptr position. After writing, cwptr is at the old rptr position. Copying cwptr to rptr clears the command reply queue. The command reply queue can't overflow because each new keyboard command clears the command reply queue. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/501 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/502 Signed-off-by: Volker Rümelin <[email protected]> Message-Id: <[email protected]> Signed-off-by: Gerd Hoffmann <[email protected]>
1 parent 47db243 commit 9e24b2d

File tree

1 file changed

+84
-31
lines changed

1 file changed

+84
-31
lines changed

hw/input/ps2.c

Lines changed: 84 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191

9292
typedef struct {
9393
uint8_t data[PS2_BUFFER_SIZE];
94-
int rptr, wptr, count;
94+
int rptr, wptr, cwptr, count;
9595
} PS2Queue;
9696

9797
struct PS2State {
@@ -186,6 +186,7 @@ static void ps2_reset_queue(PS2State *s)
186186

187187
q->rptr = 0;
188188
q->wptr = 0;
189+
q->cwptr = -1;
189190
q->count = 0;
190191
}
191192

@@ -198,7 +199,7 @@ void ps2_queue_noirq(PS2State *s, int b)
198199
{
199200
PS2Queue *q = &s->queue;
200201

201-
if (q->count == PS2_QUEUE_SIZE) {
202+
if (q->count >= PS2_QUEUE_SIZE) {
202203
return;
203204
}
204205

@@ -260,6 +261,63 @@ void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
260261
ps2_raise_irq(s);
261262
}
262263

264+
static void ps2_cqueue_data(PS2Queue *q, int b)
265+
{
266+
q->data[q->cwptr] = b;
267+
if (++q->cwptr >= PS2_BUFFER_SIZE) {
268+
q->cwptr = 0;
269+
}
270+
q->count++;
271+
}
272+
273+
static void ps2_cqueue_1(PS2State *s, int b1)
274+
{
275+
PS2Queue *q = &s->queue;
276+
277+
q->rptr = (q->rptr - 1) & (PS2_BUFFER_SIZE - 1);
278+
q->cwptr = q->rptr;
279+
ps2_cqueue_data(q, b1);
280+
ps2_raise_irq(s);
281+
}
282+
283+
static void ps2_cqueue_2(PS2State *s, int b1, int b2)
284+
{
285+
PS2Queue *q = &s->queue;
286+
287+
q->rptr = (q->rptr - 2) & (PS2_BUFFER_SIZE - 1);
288+
q->cwptr = q->rptr;
289+
ps2_cqueue_data(q, b1);
290+
ps2_cqueue_data(q, b2);
291+
ps2_raise_irq(s);
292+
}
293+
294+
static void ps2_cqueue_3(PS2State *s, int b1, int b2, int b3)
295+
{
296+
PS2Queue *q = &s->queue;
297+
298+
q->rptr = (q->rptr - 3) & (PS2_BUFFER_SIZE - 1);
299+
q->cwptr = q->rptr;
300+
ps2_cqueue_data(q, b1);
301+
ps2_cqueue_data(q, b2);
302+
ps2_cqueue_data(q, b3);
303+
ps2_raise_irq(s);
304+
}
305+
306+
static void ps2_cqueue_reset(PS2State *s)
307+
{
308+
PS2Queue *q = &s->queue;
309+
int ccount;
310+
311+
if (q->cwptr == -1) {
312+
return;
313+
}
314+
315+
ccount = (q->cwptr - q->rptr) & (PS2_BUFFER_SIZE - 1);
316+
q->count -= ccount;
317+
q->rptr = q->cwptr;
318+
q->cwptr = -1;
319+
}
320+
263321
/* keycode is the untranslated scancode in the current scancode set. */
264322
static void ps2_put_keycode(void *opaque, int keycode)
265323
{
@@ -523,6 +581,10 @@ uint32_t ps2_read_data(PS2State *s)
523581
q->rptr = 0;
524582
}
525583
q->count--;
584+
if (q->rptr == q->cwptr) {
585+
/* command reply queue is empty */
586+
q->cwptr = -1;
587+
}
526588
/* reading deasserts IRQ */
527589
s->update_irq(s->update_arg, 0);
528590
/* reassert IRQs if data left */
@@ -554,92 +616,83 @@ void ps2_write_keyboard(void *opaque, int val)
554616
PS2KbdState *s = (PS2KbdState *)opaque;
555617

556618
trace_ps2_write_keyboard(opaque, val);
619+
ps2_cqueue_reset(&s->common);
557620
switch(s->common.write_cmd) {
558621
default:
559622
case -1:
560623
switch(val) {
561624
case 0x00:
562-
ps2_queue(&s->common, KBD_REPLY_ACK);
625+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
563626
break;
564627
case 0x05:
565-
ps2_queue(&s->common, KBD_REPLY_RESEND);
628+
ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
566629
break;
567630
case KBD_CMD_GET_ID:
568631
/* We emulate a MF2 AT keyboard here */
569-
if (s->translate)
570-
ps2_queue_3(&s->common,
571-
KBD_REPLY_ACK,
572-
KBD_REPLY_ID,
573-
0x41);
574-
else
575-
ps2_queue_3(&s->common,
576-
KBD_REPLY_ACK,
577-
KBD_REPLY_ID,
578-
0x83);
632+
ps2_cqueue_3(&s->common, KBD_REPLY_ACK, KBD_REPLY_ID,
633+
s->translate ? 0x41 : 0x83);
579634
break;
580635
case KBD_CMD_ECHO:
581-
ps2_queue(&s->common, KBD_CMD_ECHO);
636+
ps2_cqueue_1(&s->common, KBD_CMD_ECHO);
582637
break;
583638
case KBD_CMD_ENABLE:
584639
s->scan_enabled = 1;
585-
ps2_queue(&s->common, KBD_REPLY_ACK);
640+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
586641
break;
587642
case KBD_CMD_SCANCODE:
588643
case KBD_CMD_SET_LEDS:
589644
case KBD_CMD_SET_RATE:
590645
case KBD_CMD_SET_MAKE_BREAK:
591646
s->common.write_cmd = val;
592-
ps2_queue(&s->common, KBD_REPLY_ACK);
647+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
593648
break;
594649
case KBD_CMD_RESET_DISABLE:
595650
ps2_reset_keyboard(s);
596651
s->scan_enabled = 0;
597-
ps2_queue(&s->common, KBD_REPLY_ACK);
652+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
598653
break;
599654
case KBD_CMD_RESET_ENABLE:
600655
ps2_reset_keyboard(s);
601656
s->scan_enabled = 1;
602-
ps2_queue(&s->common, KBD_REPLY_ACK);
657+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
603658
break;
604659
case KBD_CMD_RESET:
605660
ps2_reset_keyboard(s);
606-
ps2_queue_2(&s->common,
661+
ps2_cqueue_2(&s->common,
607662
KBD_REPLY_ACK,
608663
KBD_REPLY_POR);
609664
break;
610665
case KBD_CMD_SET_TYPEMATIC:
611-
ps2_queue(&s->common, KBD_REPLY_ACK);
666+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
612667
break;
613668
default:
614-
ps2_queue(&s->common, KBD_REPLY_RESEND);
669+
ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
615670
break;
616671
}
617672
break;
618673
case KBD_CMD_SET_MAKE_BREAK:
619-
ps2_queue(&s->common, KBD_REPLY_ACK);
674+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
620675
s->common.write_cmd = -1;
621676
break;
622677
case KBD_CMD_SCANCODE:
623678
if (val == 0) {
624-
if (s->common.queue.count <= PS2_QUEUE_SIZE - 2) {
625-
ps2_queue(&s->common, KBD_REPLY_ACK);
626-
ps2_put_keycode(s, s->scancode_set);
627-
}
679+
ps2_cqueue_2(&s->common, KBD_REPLY_ACK, s->translate ?
680+
translate_table[s->scancode_set] : s->scancode_set);
628681
} else if (val >= 1 && val <= 3) {
629682
s->scancode_set = val;
630-
ps2_queue(&s->common, KBD_REPLY_ACK);
683+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
631684
} else {
632-
ps2_queue(&s->common, KBD_REPLY_RESEND);
685+
ps2_cqueue_1(&s->common, KBD_REPLY_RESEND);
633686
}
634687
s->common.write_cmd = -1;
635688
break;
636689
case KBD_CMD_SET_LEDS:
637690
ps2_set_ledstate(s, val);
638-
ps2_queue(&s->common, KBD_REPLY_ACK);
691+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
639692
s->common.write_cmd = -1;
640693
break;
641694
case KBD_CMD_SET_RATE:
642-
ps2_queue(&s->common, KBD_REPLY_ACK);
695+
ps2_cqueue_1(&s->common, KBD_REPLY_ACK);
643696
s->common.write_cmd = -1;
644697
break;
645698
}

0 commit comments

Comments
 (0)