aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard/scan_keyb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard/scan_keyb.c')
-rw-r--r--drivers/input/keyboard/scan_keyb.c148
1 files changed, 148 insertions, 0 deletions
diff --git a/drivers/input/keyboard/scan_keyb.c b/drivers/input/keyboard/scan_keyb.c
new file mode 100644
index 00000000000000..c01aa0a04ce5e7
--- /dev/null
+++ b/drivers/input/keyboard/scan_keyb.c
@@ -0,0 +1,148 @@
+/*
+ * Generic scan keyboard driver
+ *
+ * Copyright (C) 2000 YAEGASHI Takeshi
+ * Copyright (C) 2003 Andriy Skulysh
+ * Copyright (C) 2006 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/kbd_kern.h>
+#include <linux/input.h>
+#include <linux/timer.h>
+#include "scan_keyb.h"
+
+#define SCANHZ (HZ/20)
+
+static int scan_jiffies;
+static struct scan_keyboard *keyboards;
+struct timer_list scan_timer;
+static char *hpkbd_name = "Hitachi scankeyboard";
+static char *hpkbd_phys = "input0";
+
+static void check_kbd(struct scan_keyboard *kbd,
+ unsigned char *new, unsigned char *old)
+{
+ const unsigned char *table = kbd->table;
+ int length = kbd->length;
+ int need_tasklet_schedule = 0;
+ unsigned int xor, bit;
+
+ while (length-- > 0) {
+ if ((xor = *new ^ *old) == 0)
+ table += 8;
+ else {
+ for (bit = 0x01; bit < 0x100; bit <<= 1) {
+ if (xor & bit) {
+ input_report_key(kbd->dev, *table,
+ !(*new & bit));
+ need_tasklet_schedule = 1;
+ }
+
+ table++;
+ }
+ }
+
+ new++;
+ old++;
+ }
+
+ if (need_tasklet_schedule) {
+ input_sync(kbd->dev);
+ tasklet_schedule(&keyboard_tasklet);
+ }
+}
+
+static void scan_kbd(unsigned long dummy)
+{
+ struct scan_keyboard *kbd;
+
+ scan_jiffies++;
+
+ for (kbd = keyboards; kbd != NULL; kbd = kbd->next) {
+ if (scan_jiffies & 1) {
+ if (!kbd->scan(kbd->s0))
+ check_kbd(kbd, kbd->s0, kbd->s1);
+ else
+ memcpy(kbd->s0, kbd->s1, kbd->length);
+ } else {
+ if (!kbd->scan(kbd->s1))
+ check_kbd(kbd, kbd->s1, kbd->s0);
+ else
+ memcpy(kbd->s1, kbd->s0, kbd->length);
+ }
+ }
+
+ mod_timer(&scan_timer, jiffies + SCANHZ);
+}
+
+int register_scan_keyboard(struct scan_keyboard *kbd)
+{
+ int i;
+
+ kbd->s0 = kmalloc(kbd->length, GFP_KERNEL);
+ if (kbd->s0 == NULL)
+ goto error;
+
+ kbd->s1 = kmalloc(kbd->length, GFP_KERNEL);
+ if (kbd->s1 == NULL)
+ goto error;
+
+ memset(kbd->s0, -1, kbd->length);
+ memset(kbd->s1, -1, kbd->length);
+
+ kbd->dev = input_allocate_device();
+ if (!kbd->dev)
+ goto error;
+
+ kbd->dev->name = hpkbd_name;
+ kbd->dev->phys = hpkbd_phys;
+ kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ init_input_dev(kbd->dev);
+ kbd->dev->keycode = (unsigned char *)kbd->table;
+ kbd->dev->keycodesize = sizeof(unsigned char);
+ kbd->dev->keycodemax = ARRAY_SIZE(kbd->table);
+
+ for (i = 0; i < 128; i++)
+ if (kbd->table[i])
+ set_bit(kbd->table[i], kbd->dev->keybit);
+
+ clear_bit(0, kbd->dev->keybit);
+ input_register_device(kbd->dev);
+
+ kbd->next = keyboards;
+ keyboards = kbd;
+
+ init_timer(&scan_timer);
+ scan_timer.expires = jiffies + SCANHZ;
+ scan_timer.data = 0;
+ scan_timer.function = scan_kbd;
+ add_timer(&scan_timer);
+
+ return 0;
+
+error:
+ kfree(kbd->s1);
+ kfree(kbd->s0);
+
+ return -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(register_scan_keyboard);
+
+void unregister_scan_keyboard(struct scan_keyboard *kbd)
+{
+ del_timer_sync(&scan_timer);
+ keyboards = kbd->next;
+ input_unregister_device(kbd->dev);
+ input_free_device(kbd->dev);
+}
+EXPORT_SYMBOL_GPL(unregister_scan_keyboard);