Hi Dmitry,

On Sun, Jan 6, 2013 at 1:10 AM, Dmitry Torokhov
<dmitry.torok...@gmail.com> wrote:
> Hi Shawn,
>
> On Thu, Dec 20, 2012 at 06:33:11PM -0800, Shawn Nematbakhsh wrote:
>> On resume from suspend there is a possibility for multi-byte scancodes
>> to be handled incorrectly. atkbd_reconnect disables the processing of
>> scancodes in software by calling atkbd_disable, but the keyboard may
>> still be active because no disconnect command was sent. Later, software
>> handling is re-enabled. If a multi-byte scancode sent from the keyboard
>> straddles the re-enable, only the latter byte(s) will be handled.
>>
>> In practice, this leads to cases where multi-byte break codes (ex. "e0
>> 4d" - break code for right-arrow) are misread as make codes ("4d" - make
>> code for numeric 6), leading to one or more unwanted, untyped characters
>> being interpreted.
>>
>> The solution implemented here involves sending command f5 (reset
>> disable) to the keyboard prior to disabling software handling of codes.
>> Later, the command to re-enable the keyboard is sent only after we are
>> prepared to handle scancodes.
>
> The core tries to avoid disturbing devices that are not keyboards, so I
> believe we should check the device ID first and if it is keyboard, then
> do the reset. We should also reset the internal state (emul and xl_bit)
> when re-enabling the device.
>
> Does the version of the patch below work for you?
>
> Thanks.
>
> --
> Dmitry

Thanks for the revision. This version of the patch tests good. I agree
with your changes, it makes sense to deactivate the keyboard from both
connect and reconnect, just in case the keyboard is able to send
scancodes at this point.

--
Shawn

>
> Input: atkbd - fix multi-byte scancode handling on reconnect
>
> From: Shawn Nematbakhsh <sha...@chromium.org>
>
> On resume from suspend there is a possibility for multi-byte scancodes
> to be handled incorrectly. atkbd_reconnect disables the processing of
> scancodes in software by calling atkbd_disable, but the keyboard may
> still be active because no disconnect command was sent. Later, software
> handling is re-enabled. If a multi-byte scancode sent from the keyboard
> straddles the re-enable, only the latter byte(s) will be handled.
>
> In practice, this leads to cases where multi-byte break codes (ex. "e0
> 4d" - break code for right-arrow) are misread as make codes ("4d" - make
> code for numeric 6), leading to one or more unwanted, untyped characters
> being interpreted.
>
> The solution implemented here involves sending command f5 (reset
> disable) to the keyboard prior to disabling software handling of codes.
> Later, the command to re-enable the keyboard is sent only after we are
> prepared to handle scancodes.
>
> Signed-off-by: Shawn Nematbakhsh <sha...@chromium.org>
> Signed-off-by: Dmitry Torokhov <dmitry.torok...@gmail.com>
> ---
>  drivers/input/keyboard/atkbd.c |   72 
> ++++++++++++++++++++++++++++------------
>  1 file changed, 51 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
> index 33d0fcd..2626773 100644
> --- a/drivers/input/keyboard/atkbd.c
> +++ b/drivers/input/keyboard/atkbd.c
> @@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd)
>         serio_continue_rx(atkbd->ps2dev.serio);
>  }
>
> +static int atkbd_activate(struct atkbd *atkbd)
> +{
> +       struct ps2dev *ps2dev = &atkbd->ps2dev;
> +
> +/*
> + * Enable the keyboard to receive keystrokes.
> + */
> +
> +       if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
> +               dev_err(&ps2dev->serio->dev,
> +                       "Failed to enable keyboard on %s\n",
> +                       ps2dev->serio->phys);
> +               return -1;
> +       }
> +
> +       return 0;
> +}
> +
> +/*
> + * atkbd_deactivate() resets and disables the keyboard from sending
> + * keystrokes.
> + */
> +
> +static void atkbd_deactivate(struct atkbd *atkbd)
> +{
> +       struct ps2dev *ps2dev = &atkbd->ps2dev;
> +
> +       if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS))
> +               dev_err(&ps2dev->serio->dev,
> +                       "Failed to deactivate keyboard on %s\n",
> +                       ps2dev->serio->phys);
> +}
> +
>  /*
>   * atkbd_probe() probes for an AT keyboard on a serio port.
>   */
> @@ -731,6 +764,12 @@ static int atkbd_probe(struct atkbd *atkbd)
>                 return -1;
>         }
>
> +/*
> + * Make sure nothing is coming from the keyboard and disturbs our
> + * internal state.
> + */
> +       atkbd_deactivate(atkbd);
> +
>         return 0;
>  }
>
> @@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd)
>         return 0;
>  }
>
> -static int atkbd_activate(struct atkbd *atkbd)
> -{
> -       struct ps2dev *ps2dev = &atkbd->ps2dev;
> -
> -/*
> - * Enable the keyboard to receive keystrokes.
> - */
> -
> -       if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
> -               dev_err(&ps2dev->serio->dev,
> -                       "Failed to enable keyboard on %s\n",
> -                       ps2dev->serio->phys);
> -               return -1;
> -       }
> -
> -       return 0;
> -}
> -
>  /*
>   * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
>   * reboot.
> @@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct 
> serio_driver *drv)
>
>                 atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
>                 atkbd_reset_state(atkbd);
> -               atkbd_activate(atkbd);
>
>         } else {
>                 atkbd->set = 2;
> @@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct 
> serio_driver *drv)
>                 goto fail3;
>
>         atkbd_enable(atkbd);
> +       if (serio->write)
> +               atkbd_activate(atkbd);
>
>         err = input_register_device(atkbd->dev);
>         if (err)
> @@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio)
>                 if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, 
> atkbd->extra))
>                         goto out;
>
> -               atkbd_activate(atkbd);
> -
>                 /*
>                  * Restore LED state and repeat rate. While input core
>                  * will do this for us at resume time reconnect may happen
> @@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio)
>
>         }
>
> +       /*
> +        * Reset our state machine in case reconnect happened in the middle
> +        * of multi-byte scancode.
> +        */
> +       atkbd->xl_bit = 0;
> +       atkbd->emul = 0;
> +
>         atkbd_enable(atkbd);
> +       if (atkbd->write)
> +               atkbd_activate(atkbd);
> +
>         retval = 0;
>
>   out:
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to