aboutsummaryrefslogtreecommitdiff
path: root/drivers/input/keyboard/atkbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard/atkbd.c')
-rw-r--r--drivers/input/keyboard/atkbd.c159
1 files changed, 123 insertions, 36 deletions
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index d358ef8623f..2dd1d0dd4f7 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -63,6 +63,10 @@ static bool atkbd_extra;
module_param_named(extra, atkbd_extra, bool, 0);
MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");
+static bool atkbd_terminal;
+module_param_named(terminal, atkbd_terminal, bool, 0);
+MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
+
/*
* Scancode to keycode tables. These are just the default setting, and
* are loadable via a userland utility.
@@ -136,7 +140,8 @@ static const unsigned short atkbd_unxlate_table[128] = {
#define ATKBD_CMD_ENABLE 0x00f4
#define ATKBD_CMD_RESET_DIS 0x00f5 /* Reset to defaults and disable */
#define ATKBD_CMD_RESET_DEF 0x00f6 /* Reset to defaults */
-#define ATKBD_CMD_SETALL_MBR 0x00fa
+#define ATKBD_CMD_SETALL_MB 0x00f8 /* Set all keys to give break codes */
+#define ATKBD_CMD_SETALL_MBR 0x00fa /* ... and repeat */
#define ATKBD_CMD_RESET_BAT 0x02ff
#define ATKBD_CMD_RESEND 0x00fe
#define ATKBD_CMD_EX_ENABLE 0x10ea
@@ -238,6 +243,12 @@ static void (*atkbd_platform_fixup)(struct atkbd *, const void *data);
static void *atkbd_platform_fixup_data;
static unsigned int (*atkbd_platform_scancode_fixup)(struct atkbd *, unsigned int);
+/*
+ * Certain keyboards to not like ATKBD_CMD_RESET_DIS and stop responding
+ * to many commands until full reset (ATKBD_CMD_RESET_BAT) is performed.
+ */
+static bool atkbd_skip_deactivate;
+
static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
ssize_t (*handler)(struct atkbd *, char *));
static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
@@ -428,7 +439,7 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
if (printk_ratelimit())
dev_warn(&serio->dev,
"Spurious %s on %s. "
- "Some program might be trying access hardware directly.\n",
+ "Some program might be trying to access hardware directly.\n",
data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
goto out;
case ATKBD_RET_ERR:
@@ -671,6 +682,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.
*/
@@ -721,11 +765,18 @@ static int atkbd_probe(struct atkbd *atkbd)
if (atkbd->id == 0xaca1 && atkbd->translated) {
dev_err(&ps2dev->serio->dev,
- "NCD terminal keyboards are only supported on non-translating controlelrs. "
+ "NCD terminal keyboards are only supported on non-translating controllers. "
"Use i8042.direct=1 to disable translation.\n");
return -1;
}
+/*
+ * Make sure nothing is coming from the keyboard and disturbs our
+ * internal state.
+ */
+ if (!atkbd_skip_deactivate)
+ atkbd_deactivate(atkbd);
+
return 0;
}
@@ -764,6 +815,11 @@ static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra
}
}
+ if (atkbd_terminal) {
+ ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MB);
+ return 3;
+ }
+
if (target_set != 3)
return 2;
@@ -815,24 +871,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.
@@ -1140,7 +1178,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;
@@ -1155,6 +1192,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)
@@ -1198,8 +1237,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
@@ -1213,7 +1250,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:
@@ -1295,7 +1342,7 @@ static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *old_dev, *new_dev;
- unsigned long value;
+ unsigned int value;
int err;
bool old_extra;
unsigned char old_set;
@@ -1303,7 +1350,11 @@ static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t coun
if (!atkbd->write)
return -EIO;
- if (strict_strtoul(buf, 10, &value) || value > 1)
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value > 1)
return -EINVAL;
if (atkbd->extra != value) {
@@ -1379,11 +1430,15 @@ static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *old_dev, *new_dev;
- unsigned long value;
+ unsigned int value;
int err;
bool old_scroll;
- if (strict_strtoul(buf, 10, &value) || value > 1)
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value > 1)
return -EINVAL;
if (atkbd->scroll != value) {
@@ -1423,7 +1478,7 @@ static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *old_dev, *new_dev;
- unsigned long value;
+ unsigned int value;
int err;
unsigned char old_set;
bool old_extra;
@@ -1431,7 +1486,11 @@ static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
if (!atkbd->write)
return -EIO;
- if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3))
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value != 2 && value != 3)
return -EINVAL;
if (atkbd->set != value) {
@@ -1474,14 +1533,18 @@ static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *old_dev, *new_dev;
- unsigned long value;
+ unsigned int value;
int err;
bool old_softrepeat, old_softraw;
if (!atkbd->write)
return -EIO;
- if (strict_strtoul(buf, 10, &value) || value > 1)
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value > 1)
return -EINVAL;
if (atkbd->softrepeat != value) {
@@ -1524,11 +1587,15 @@ static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
{
struct input_dev *old_dev, *new_dev;
- unsigned long value;
+ unsigned int value;
int err;
bool old_softraw;
- if (strict_strtoul(buf, 10, &value) || value > 1)
+ err = kstrtouint(buf, 10, &value);
+ if (err)
+ return err;
+
+ if (value > 1)
return -EINVAL;
if (atkbd->softraw != value) {
@@ -1568,14 +1635,20 @@ static int __init atkbd_setup_forced_release(const struct dmi_system_id *id)
atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
atkbd_platform_fixup_data = id->driver_data;
- return 0;
+ return 1;
}
static int __init atkbd_setup_scancode_fixup(const struct dmi_system_id *id)
{
atkbd_platform_scancode_fixup = id->driver_data;
- return 0;
+ return 1;
+}
+
+static int __init atkbd_deactivate_fixup(const struct dmi_system_id *id)
+{
+ atkbd_skip_deactivate = true;
+ return 1;
}
static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
@@ -1715,6 +1788,20 @@ static const struct dmi_system_id atkbd_dmi_quirk_table[] __initconst = {
.callback = atkbd_setup_scancode_fixup,
.driver_data = atkbd_oqo_01plus_scancode_fixup,
},
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "LW25-B7HV"),
+ },
+ .callback = atkbd_deactivate_fixup,
+ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P1-J273B"),
+ },
+ .callback = atkbd_deactivate_fixup,
+ },
{ }
};