ps2 keyboard:fix can't use ps2 keyboard if typing many times After leaving grub and before finishing linux kernel ps2 driver initialization
diff mbox

Message ID 20150426022627.DB9A647813F@webmail.sinamail.sina.com.cn
State New
Headers show

Commit Message

penghao122@sina.com April 26, 2015, 2:26 a.m. UTC
Starting  a linux guest with ps2 keyboard ,if you type many times during leaving grub and into linux kernel ,then you can't use keyboard after linux initialization finished.
during grub,the work method of ps2 keyboard is like this:
first ,ps2 keyboard driver send command KBD_CCMD_KBD_ENABLE,
second,if there is a keyboard input,then  ps2 keyboard driver read data.
third ,ps2 keyboard driver send command KBD_CCMD_KBD_ENABLE,
...
this is diffrent method of linux kernel .
After leaving  grub and before finishing linux kernel ps2 driver initialization,if you type many times,the input data keep saving the ps2 queue in qemu .Before linux kernel initialize ps2 keyboard,linux call i8042_init->i8042_controller_check,if i8042_controller_check return fail,then linux kernel ps2 keyboard driver will never initialize. 
(linux kernel 2.6.32 i8042.c)
static int i8042_controller_check(void)
{
 if (i8042_flush() == I8042_BUFFER_SIZE)
  return -ENODEV;
 return 0;
}
static int i8042_flush(void)
{...
while (((str = i8042_read_status()) & I8042_STR_OBF) && (i < I8042_BUFFER_SIZE)) {
  udelay(50);
  data = i8042_read_data();
  i++;
  }
return i;
}
 
during calling i8042_flush it is full in qemu queue .
ps_read_data:
                       s->update_irq(s->update_arg, 0);
                       s->update_irq(s->update_arg, q->count != 0);
because q->count!=0, kbd_update_irq can set I8042_STR_OBF.Then i8042_flush()  will return I8042_BUFFER_SIZE.

Patch
diff mbox

--- ps2.c.orig 2015-04-25 09:44:38.865777168 -0400
+++ ps2.c 2015-04-25 09:48:46.385121172 -0400
@@ -150,7 +150,12 @@ 
     q->count++;
     s->update_irq(s->update_arg, 1);
 }
-
+void ps2_clear_queue(void *opaque)
+{
+    PS2State *s = (PS2State *)opaque;
+    PS2Queue *q = &s->queue;
+    q->wptr = q->rptr = q->count = 0;
+}
 /*
    keycode is expressed as follow:
    bit 7    - 0 key pressed, 1 = key released
 
--- pckbd.c.orig 2015-04-25 09:36:59.960640241 -0400
+++ pckbd.c 2015-04-25 09:44:27.298562406 -0400
@@ -207,6 +207,8 @@ 
     KBDState *s = opaque;
     int val;
     val = s->status;
+    if(s->write-cmd == KBD_CCMD_KBD_ENABLE)
+        val &= ~KBD_STAT_OBF;    
     DPRINTF("kbd: read status=0x%02x\n", val);
     return val;
 }
@@ -251,9 +253,10 @@ 
         else
             val = KBD_CCMD_NO_OP;
     }
-
+    s->write_cmd = 0;
     switch(val) {
     case KBD_CCMD_READ_MODE:
+        ps2_clear_queue(s->kbd);
         kbd_queue(s, s->mode, 0);
         break;
     case KBD_CCMD_WRITE_MODE:
@@ -284,6 +287,7 @@ 
         kbd_update_irq(s);
         break;
     case KBD_CCMD_KBD_ENABLE:
+        s->write_cmd = KBD_CCMD_KBD_ENABLE;
         s->mode &= ~KBD_MODE_DISABLE_KBD;
         kbd_update_irq(s);
         break;