Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
251
drivers/input/keyboard/Kconfig
Normal file
251
drivers/input/keyboard/Kconfig
Normal file
@@ -0,0 +1,251 @@
|
||||
#
|
||||
# Input core configuration
|
||||
#
|
||||
menuconfig INPUT_KEYBOARD
|
||||
bool "Keyboards" if EMBEDDED || !X86
|
||||
default y
|
||||
help
|
||||
Say Y here, and a list of supported keyboards will be displayed.
|
||||
This option doesn't affect the kernel.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
if INPUT_KEYBOARD
|
||||
|
||||
config KEYBOARD_ATKBD
|
||||
tristate "AT keyboard" if EMBEDDED || !X86_PC
|
||||
default y
|
||||
select SERIO
|
||||
select SERIO_LIBPS2
|
||||
select SERIO_I8042 if X86_PC
|
||||
select SERIO_GSCPS2 if GSC
|
||||
help
|
||||
Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
|
||||
you'll need this, unless you have a different type keyboard (USB, ADB
|
||||
or other). This also works for AT and PS/2 keyboards connected over a
|
||||
PS/2 to serial converter.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called atkbd.
|
||||
|
||||
config KEYBOARD_ATKBD_HP_KEYCODES
|
||||
bool "Use HP keyboard scancodes"
|
||||
depends on PARISC && KEYBOARD_ATKBD
|
||||
default y
|
||||
help
|
||||
Say Y here if you have a PA-RISC machine and want to use an AT or
|
||||
PS/2 keyboard, and your keyboard uses keycodes that are specific to
|
||||
PA-RISC keyboards.
|
||||
|
||||
Say N if you use a standard keyboard.
|
||||
|
||||
config KEYBOARD_ATKBD_RDI_KEYCODES
|
||||
bool "Use PrecisionBook keyboard scancodes"
|
||||
depends on KEYBOARD_ATKBD_HP_KEYCODES
|
||||
default n
|
||||
help
|
||||
If you have an RDI PrecisionBook, say Y here if you want to use its
|
||||
built-in keyboard (as opposed to an external keyboard).
|
||||
|
||||
The PrecisionBook has five keys that conflict with those used by most
|
||||
AT and PS/2 keyboards. These are as follows:
|
||||
|
||||
PrecisionBook Standard AT or PS/2
|
||||
|
||||
F1 F12
|
||||
Left Ctrl Left Alt
|
||||
Caps Lock Left Ctrl
|
||||
Right Ctrl Caps Lock
|
||||
Left 102nd key (the key to the right of Left Shift)
|
||||
|
||||
If you say N here, and use the PrecisionBook keyboard, then each key
|
||||
in the left-hand column will be interpreted as the corresponding key
|
||||
in the right-hand column.
|
||||
|
||||
If you say Y here, and use an external keyboard, then each key in the
|
||||
right-hand column will be interpreted as the key shown in the
|
||||
left-hand column.
|
||||
|
||||
config KEYBOARD_SUNKBD
|
||||
tristate "Sun Type 4 and Type 5 keyboard"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
|
||||
connected either to the Sun keyboard connector or to an serial
|
||||
(RS-232) port via a simple adapter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called sunkbd.
|
||||
|
||||
config KEYBOARD_LKKBD
|
||||
tristate "DECstation/VAXstation LK201/LK401 keyboard"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you want to use a LK201 or LK401 style serial
|
||||
keyboard. This keyboard is also useable on PCs if you attach
|
||||
it with the inputattach program. The connector pinout is
|
||||
described within lkkbd.c.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called lkkbd.
|
||||
|
||||
config KEYBOARD_LOCOMO
|
||||
tristate "LoCoMo Keyboard Support"
|
||||
depends on SHARP_LOCOMO && INPUT_KEYBOARD
|
||||
help
|
||||
Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called locomokbd.
|
||||
|
||||
config KEYBOARD_XTKBD
|
||||
tristate "XT keyboard"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you want to use the old IBM PC/XT keyboard (or
|
||||
compatible) on your system. This is only possible with a
|
||||
parallel port keyboard adapter, you cannot connect it to the
|
||||
keyboard port on a PC that runs Linux.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called xtkbd.
|
||||
|
||||
config KEYBOARD_NEWTON
|
||||
tristate "Newton keyboard"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a Newton keyboard on a serial port.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called newtonkbd.
|
||||
|
||||
config KEYBOARD_STOWAWAY
|
||||
tristate "Stowaway keyboard"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you have a Stowaway keyboard on a serial port.
|
||||
Stowaway compatible keyboards like Dicota Input-PDA keyboard
|
||||
are also supported by this driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called stowaway.
|
||||
|
||||
config KEYBOARD_CORGI
|
||||
tristate "Corgi keyboard"
|
||||
depends on PXA_SHARPSL
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx
|
||||
series of PDAs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called corgikbd.
|
||||
|
||||
config KEYBOARD_SPITZ
|
||||
tristate "Spitz keyboard"
|
||||
depends on PXA_SHARPSL
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
|
||||
SL-C3000 and Sl-C3100 series of PDAs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called spitzkbd.
|
||||
|
||||
config KEYBOARD_AMIGA
|
||||
tristate "Amiga keyboard"
|
||||
depends on AMIGA
|
||||
help
|
||||
Say Y here if you are running Linux on any AMIGA and have a keyboard
|
||||
attached.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called amikbd.
|
||||
|
||||
config KEYBOARD_HIL_OLD
|
||||
tristate "HP HIL keyboard support (simple driver)"
|
||||
depends on GSC || HP300
|
||||
default y
|
||||
help
|
||||
The "Human Interface Loop" is a older, 8-channel USB-like
|
||||
controller used in several Hewlett Packard models. This driver
|
||||
was adapted from the one written for m68k/hp300, and implements
|
||||
support for a keyboard attached to the HIL port, but not for
|
||||
any other types of HIL input devices like mice or tablets.
|
||||
However, it has been thoroughly tested and is stable.
|
||||
|
||||
If you want full HIL support including support for multiple
|
||||
keyboards, mice, and tablets, you have to enable the
|
||||
"HP System Device Controller i8042 Support" in the input/serio
|
||||
submenu.
|
||||
|
||||
config KEYBOARD_HIL
|
||||
tristate "HP HIL keyboard support"
|
||||
depends on GSC || HP300
|
||||
default y
|
||||
select HP_SDC
|
||||
select HIL_MLC
|
||||
select SERIO
|
||||
help
|
||||
The "Human Interface Loop" is a older, 8-channel USB-like
|
||||
controller used in several Hewlett Packard models.
|
||||
This driver implements support for HIL-keyboards attached
|
||||
to your machine, so normally you should say Y here.
|
||||
|
||||
config KEYBOARD_OMAP
|
||||
tristate "TI OMAP keypad support"
|
||||
depends on (ARCH_OMAP1 || ARCH_OMAP2)
|
||||
help
|
||||
Say Y here if you want to use the OMAP keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called omap-keypad.
|
||||
|
||||
config KEYBOARD_AAED2000
|
||||
tristate "AAED-2000 keyboard"
|
||||
depends on MACH_AAED2000
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the keyboard on the Agilent AAED-2000
|
||||
development board.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called aaed2000_kbd.
|
||||
|
||||
config KEYBOARD_GPIO
|
||||
tristate "GPIO Buttons"
|
||||
depends on GENERIC_GPIO
|
||||
help
|
||||
This driver implements support for buttons connected
|
||||
to GPIO pins of various CPUs (and some other chips).
|
||||
|
||||
Say Y here if your device has buttons connected
|
||||
directly to such GPIO pins. Your board-specific
|
||||
setup logic must also provide a platform device,
|
||||
with configuration data saying which GPIOs are used.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called gpio-keys.
|
||||
|
||||
config KEYPAD_S3C
|
||||
tristate "S3C keypad support"
|
||||
depends on (CPU_S3C6400 || CPU_S3C6410)
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to use the S3C SMDK keypad.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called s3c-keypad.
|
||||
|
||||
#Qisda Tony 090324, add keypad [
|
||||
config KEYPAD_S3C_QISDA
|
||||
tristate "S3C keypad support for Qisda use"
|
||||
default n
|
||||
help
|
||||
Say Y here if you want to use the menu key for Qisda use
|
||||
tony.yc.huang@qisda.com
|
||||
#Qisda Tony 090324, add keypad ]
|
||||
|
||||
endif
|
||||
25
drivers/input/keyboard/Makefile
Normal file
25
drivers/input/keyboard/Makefile
Normal file
@@ -0,0 +1,25 @@
|
||||
#
|
||||
# Makefile for the input core drivers.
|
||||
#
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
|
||||
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
|
||||
obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
|
||||
obj-$(CONFIG_KEYPAD_S3C) += s3c-keypad.o
|
||||
#Qisda Tony 090324, add keypad [
|
||||
obj-$(CONFIG_KEYPAD_S3C_QISDA) += s3c-keypad-qisda.o
|
||||
#Qisda Tony 090324, add keypad ]
|
||||
203
drivers/input/keyboard/aaed2000_kbd.c
Normal file
203
drivers/input/keyboard/aaed2000_kbd.c
Normal file
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
* Keyboard driver for the AAED-2000 dev board
|
||||
*
|
||||
* Copyright (c) 2006 Nicolas Bellido Y Ortega
|
||||
*
|
||||
* Based on corgikbd.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/aaed2000.h>
|
||||
|
||||
#define KB_ROWS 12
|
||||
#define KB_COLS 8
|
||||
#define KB_ROWMASK(r) (1 << (r))
|
||||
#define SCANCODE(r,c) (((c) * KB_ROWS) + (r))
|
||||
#define NR_SCANCODES (KB_COLS * KB_ROWS)
|
||||
|
||||
#define SCAN_INTERVAL (50) /* ms */
|
||||
#define KB_ACTIVATE_DELAY (20) /* us */
|
||||
|
||||
static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
|
||||
KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
|
||||
KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
|
||||
KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
|
||||
KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
|
||||
KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
|
||||
KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
|
||||
KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
|
||||
0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
|
||||
};
|
||||
|
||||
struct aaedkbd {
|
||||
unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
|
||||
struct input_dev *input;
|
||||
struct work_struct workq;
|
||||
int kbdscan_state[KB_COLS];
|
||||
int kbdscan_count[KB_COLS];
|
||||
};
|
||||
|
||||
#define KBDSCAN_STABLE_COUNT 2
|
||||
|
||||
static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
|
||||
unsigned int col, unsigned int rowd)
|
||||
{
|
||||
unsigned int scancode, pressed;
|
||||
unsigned int row;
|
||||
|
||||
for (row = 0; row < KB_ROWS; row++) {
|
||||
scancode = SCANCODE(row, col);
|
||||
pressed = rowd & KB_ROWMASK(row);
|
||||
|
||||
input_report_key(aaedkbd->input, aaedkbd->keycode[scancode], pressed);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan the hardware keyboard and push any changes up through the input layer */
|
||||
static void aaedkbd_work(void *data)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = data;
|
||||
unsigned int col, rowd;
|
||||
|
||||
col = 0;
|
||||
do {
|
||||
AAEC_GPIO_KSCAN = col + 8;
|
||||
udelay(KB_ACTIVATE_DELAY);
|
||||
rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
|
||||
|
||||
if (rowd != aaedkbd->kbdscan_state[col]) {
|
||||
aaedkbd->kbdscan_count[col] = 0;
|
||||
aaedkbd->kbdscan_state[col] = rowd;
|
||||
} else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
|
||||
aaedkbd_report_col(aaedkbd, col, rowd);
|
||||
col++;
|
||||
}
|
||||
} while (col < KB_COLS);
|
||||
|
||||
AAEC_GPIO_KSCAN = 0x07;
|
||||
input_sync(aaedkbd->input);
|
||||
|
||||
schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
|
||||
}
|
||||
|
||||
static int aaedkbd_open(struct input_dev *indev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = indev->private;
|
||||
|
||||
schedule_delayed_work(&aaedkbd->workq, msecs_to_jiffies(SCAN_INTERVAL));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aaedkbd_close(struct input_dev *indev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = indev->private;
|
||||
|
||||
cancel_delayed_work(&aaedkbd->workq);
|
||||
flush_scheduled_work();
|
||||
}
|
||||
|
||||
static int __devinit aaedkbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd;
|
||||
struct input_dev *input_dev;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!aaedkbd || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, aaedkbd);
|
||||
|
||||
aaedkbd->input = input_dev;
|
||||
|
||||
/* Init keyboard rescan workqueue */
|
||||
INIT_WORK(&aaedkbd->workq, aaedkbd_work, aaedkbd);
|
||||
|
||||
memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
|
||||
|
||||
input_dev->name = "AAED-2000 Keyboard";
|
||||
input_dev->phys = "aaedkbd/input0";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &pdev->dev;
|
||||
input_dev->private = aaedkbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
input_dev->keycode = aaedkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
|
||||
set_bit(aaedkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
input_dev->open = aaedkbd_open;
|
||||
input_dev->close = aaedkbd_close;
|
||||
|
||||
error = input_register_device(aaedkbd->input);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail: kfree(aaedkbd);
|
||||
input_free_device(input_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit aaedkbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(aaedkbd->input);
|
||||
kfree(aaedkbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver aaedkbd_driver = {
|
||||
.probe = aaedkbd_probe,
|
||||
.remove = __devexit_p(aaedkbd_remove),
|
||||
.driver = {
|
||||
.name = "aaed2000-keyboard",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aaedkbd_init(void)
|
||||
{
|
||||
return platform_driver_register(&aaedkbd_driver);
|
||||
}
|
||||
|
||||
static void __exit aaedkbd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aaedkbd_driver);
|
||||
}
|
||||
|
||||
module_init(aaedkbd_init);
|
||||
module_exit(aaedkbd_exit);
|
||||
|
||||
MODULE_AUTHOR("Nicolas Bellido Y Ortega");
|
||||
MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
|
||||
MODULE_LICENSE("GPLv2");
|
||||
260
drivers/input/keyboard/amikbd.c
Normal file
260
drivers/input/keyboard/amikbd.c
Normal file
@@ -0,0 +1,260 @@
|
||||
/*
|
||||
* $Id: amikbd.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 2000-2001 Vojtech Pavlik
|
||||
*
|
||||
* Based on the work of:
|
||||
* Hamish Macdonald
|
||||
*/
|
||||
|
||||
/*
|
||||
* Amiga keyboard driver for Linux/m68k
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/keyboard.h>
|
||||
|
||||
#include <asm/amigaints.h>
|
||||
#include <asm/amigahw.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION("Amiga keyboard driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned char amikbd_keycode[0x78] __initdata = {
|
||||
[0] = KEY_GRAVE,
|
||||
[1] = KEY_1,
|
||||
[2] = KEY_2,
|
||||
[3] = KEY_3,
|
||||
[4] = KEY_4,
|
||||
[5] = KEY_5,
|
||||
[6] = KEY_6,
|
||||
[7] = KEY_7,
|
||||
[8] = KEY_8,
|
||||
[9] = KEY_9,
|
||||
[10] = KEY_0,
|
||||
[11] = KEY_MINUS,
|
||||
[12] = KEY_EQUAL,
|
||||
[13] = KEY_BACKSLASH,
|
||||
[15] = KEY_KP0,
|
||||
[16] = KEY_Q,
|
||||
[17] = KEY_W,
|
||||
[18] = KEY_E,
|
||||
[19] = KEY_R,
|
||||
[20] = KEY_T,
|
||||
[21] = KEY_Y,
|
||||
[22] = KEY_U,
|
||||
[23] = KEY_I,
|
||||
[24] = KEY_O,
|
||||
[25] = KEY_P,
|
||||
[26] = KEY_LEFTBRACE,
|
||||
[27] = KEY_RIGHTBRACE,
|
||||
[29] = KEY_KP1,
|
||||
[30] = KEY_KP2,
|
||||
[31] = KEY_KP3,
|
||||
[32] = KEY_A,
|
||||
[33] = KEY_S,
|
||||
[34] = KEY_D,
|
||||
[35] = KEY_F,
|
||||
[36] = KEY_G,
|
||||
[37] = KEY_H,
|
||||
[38] = KEY_J,
|
||||
[39] = KEY_K,
|
||||
[40] = KEY_L,
|
||||
[41] = KEY_SEMICOLON,
|
||||
[42] = KEY_APOSTROPHE,
|
||||
[43] = KEY_BACKSLASH,
|
||||
[45] = KEY_KP4,
|
||||
[46] = KEY_KP5,
|
||||
[47] = KEY_KP6,
|
||||
[48] = KEY_102ND,
|
||||
[49] = KEY_Z,
|
||||
[50] = KEY_X,
|
||||
[51] = KEY_C,
|
||||
[52] = KEY_V,
|
||||
[53] = KEY_B,
|
||||
[54] = KEY_N,
|
||||
[55] = KEY_M,
|
||||
[56] = KEY_COMMA,
|
||||
[57] = KEY_DOT,
|
||||
[58] = KEY_SLASH,
|
||||
[60] = KEY_KPDOT,
|
||||
[61] = KEY_KP7,
|
||||
[62] = KEY_KP8,
|
||||
[63] = KEY_KP9,
|
||||
[64] = KEY_SPACE,
|
||||
[65] = KEY_BACKSPACE,
|
||||
[66] = KEY_TAB,
|
||||
[67] = KEY_KPENTER,
|
||||
[68] = KEY_ENTER,
|
||||
[69] = KEY_ESC,
|
||||
[70] = KEY_DELETE,
|
||||
[74] = KEY_KPMINUS,
|
||||
[76] = KEY_UP,
|
||||
[77] = KEY_DOWN,
|
||||
[78] = KEY_RIGHT,
|
||||
[79] = KEY_LEFT,
|
||||
[80] = KEY_F1,
|
||||
[81] = KEY_F2,
|
||||
[82] = KEY_F3,
|
||||
[83] = KEY_F4,
|
||||
[84] = KEY_F5,
|
||||
[85] = KEY_F6,
|
||||
[86] = KEY_F7,
|
||||
[87] = KEY_F8,
|
||||
[88] = KEY_F9,
|
||||
[89] = KEY_F10,
|
||||
[90] = KEY_KPLEFTPAREN,
|
||||
[91] = KEY_KPRIGHTPAREN,
|
||||
[92] = KEY_KPSLASH,
|
||||
[93] = KEY_KPASTERISK,
|
||||
[94] = KEY_KPPLUS,
|
||||
[95] = KEY_HELP,
|
||||
[96] = KEY_LEFTSHIFT,
|
||||
[97] = KEY_RIGHTSHIFT,
|
||||
[98] = KEY_CAPSLOCK,
|
||||
[99] = KEY_LEFTCTRL,
|
||||
[100] = KEY_LEFTALT,
|
||||
[101] = KEY_RIGHTALT,
|
||||
[102] = KEY_LEFTMETA,
|
||||
[103] = KEY_RIGHTMETA
|
||||
};
|
||||
|
||||
static const char *amikbd_messages[8] = {
|
||||
[0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
|
||||
[1] = KERN_WARNING "amikbd: keyboard lost sync\n",
|
||||
[2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
|
||||
[3] = KERN_WARNING "amikbd: keyboard controller failure\n",
|
||||
[4] = KERN_ERR "amikbd: keyboard selftest failure\n",
|
||||
[5] = KERN_INFO "amikbd: initiate power-up key stream\n",
|
||||
[6] = KERN_INFO "amikbd: terminate power-up key stream\n",
|
||||
[7] = KERN_WARNING "amikbd: keyboard interrupt\n"
|
||||
};
|
||||
|
||||
static struct input_dev *amikbd_dev;
|
||||
|
||||
static irqreturn_t amikbd_interrupt(int irq, void *dummy)
|
||||
{
|
||||
unsigned char scancode, down;
|
||||
|
||||
scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
|
||||
ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
|
||||
udelay(85); /* wait until 85 us have expired */
|
||||
ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
|
||||
|
||||
down = !(scancode & 1); /* lowest bit is release bit */
|
||||
scancode >>= 1;
|
||||
|
||||
if (scancode < 0x78) { /* scancodes < 0x78 are keys */
|
||||
if (scancode == 98) { /* CapsLock is a toggle switch key on Amiga */
|
||||
input_report_key(amikbd_dev, scancode, 1);
|
||||
input_report_key(amikbd_dev, scancode, 0);
|
||||
} else {
|
||||
input_report_key(amikbd_dev, scancode, down);
|
||||
}
|
||||
|
||||
input_sync(amikbd_dev);
|
||||
} else /* scancodes >= 0x78 are error codes */
|
||||
printk(amikbd_messages[scancode - 0x78]);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init amikbd_init(void)
|
||||
{
|
||||
int i, j, err;
|
||||
|
||||
if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
|
||||
return -ENODEV;
|
||||
|
||||
if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
|
||||
return -EBUSY;
|
||||
|
||||
amikbd_dev = input_allocate_device();
|
||||
if (!amikbd_dev) {
|
||||
printk(KERN_ERR "amikbd: not enough memory for input device\n");
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
amikbd_dev->name = "Amiga Keyboard";
|
||||
amikbd_dev->phys = "amikbd/input0";
|
||||
amikbd_dev->id.bustype = BUS_AMIGA;
|
||||
amikbd_dev->id.vendor = 0x0001;
|
||||
amikbd_dev->id.product = 0x0001;
|
||||
amikbd_dev->id.version = 0x0100;
|
||||
|
||||
amikbd_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
|
||||
for (i = 0; i < 0x78; i++)
|
||||
set_bit(i, amikbd_dev->keybit);
|
||||
|
||||
for (i = 0; i < MAX_NR_KEYMAPS; i++) {
|
||||
static u_short temp_map[NR_KEYS] __initdata;
|
||||
if (!key_maps[i])
|
||||
continue;
|
||||
memset(temp_map, 0, sizeof(temp_map));
|
||||
for (j = 0; j < 0x78; j++) {
|
||||
if (!amikbd_keycode[j])
|
||||
continue;
|
||||
temp_map[j] = key_maps[i][amikbd_keycode[j]];
|
||||
}
|
||||
for (j = 0; j < NR_KEYS; j++) {
|
||||
if (!temp_map[j])
|
||||
temp_map[j] = 0xf200;
|
||||
}
|
||||
memcpy(key_maps[i], temp_map, sizeof(temp_map));
|
||||
}
|
||||
ciaa.cra &= ~0x41; /* serial data in, turn off TA */
|
||||
if (request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
|
||||
amikbd_interrupt)) {
|
||||
err = -EBUSY;
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
err = input_register_device(amikbd_dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
|
||||
fail2: input_free_device(amikbd_dev);
|
||||
fail1: release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit amikbd_exit(void)
|
||||
{
|
||||
free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
|
||||
input_unregister_device(amikbd_dev);
|
||||
release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
|
||||
}
|
||||
|
||||
module_init(amikbd_init);
|
||||
module_exit(amikbd_exit);
|
||||
1397
drivers/input/keyboard/atkbd.c
Normal file
1397
drivers/input/keyboard/atkbd.c
Normal file
File diff suppressed because it is too large
Load Diff
413
drivers/input/keyboard/corgikbd.c
Normal file
413
drivers/input/keyboard/corgikbd.c
Normal file
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Keyboard driver for Sharp Corgi models (SL-C7xx)
|
||||
*
|
||||
* Copyright (c) 2004-2005 Richard Purdie
|
||||
*
|
||||
* Based on xtkbd.c/locomkbd.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/arch/corgi.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
#include <asm/hardware/scoop.h>
|
||||
|
||||
#define KB_ROWS 8
|
||||
#define KB_COLS 12
|
||||
#define KB_ROWMASK(r) (1 << (r))
|
||||
#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
|
||||
/* zero code, 124 scancodes */
|
||||
#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 )
|
||||
|
||||
#define SCAN_INTERVAL (50) /* ms */
|
||||
#define HINGE_SCAN_INTERVAL (250) /* ms */
|
||||
|
||||
#define CORGI_KEY_CALENDER KEY_F1
|
||||
#define CORGI_KEY_ADDRESS KEY_F2
|
||||
#define CORGI_KEY_FN KEY_F3
|
||||
#define CORGI_KEY_CANCEL KEY_F4
|
||||
#define CORGI_KEY_OFF KEY_SUSPEND
|
||||
#define CORGI_KEY_EXOK KEY_F5
|
||||
#define CORGI_KEY_EXCANCEL KEY_F6
|
||||
#define CORGI_KEY_EXJOGDOWN KEY_F7
|
||||
#define CORGI_KEY_EXJOGUP KEY_F8
|
||||
#define CORGI_KEY_JAP1 KEY_LEFTCTRL
|
||||
#define CORGI_KEY_JAP2 KEY_LEFTALT
|
||||
#define CORGI_KEY_MAIL KEY_F10
|
||||
#define CORGI_KEY_OK KEY_F11
|
||||
#define CORGI_KEY_MENU KEY_F12
|
||||
|
||||
static unsigned char corgikbd_keycode[NR_SCANCODES] = {
|
||||
0, /* 0 */
|
||||
0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */
|
||||
0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */
|
||||
KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
|
||||
CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
|
||||
CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */
|
||||
CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */
|
||||
KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */
|
||||
CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */
|
||||
};
|
||||
|
||||
|
||||
struct corgikbd {
|
||||
unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
|
||||
struct input_dev *input;
|
||||
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
struct timer_list htimer;
|
||||
|
||||
unsigned int suspended;
|
||||
unsigned long suspend_jiffies;
|
||||
};
|
||||
|
||||
#define KB_DISCHARGE_DELAY 10
|
||||
#define KB_ACTIVATE_DELAY 10
|
||||
|
||||
/* Helper functions for reading the keyboard matrix
|
||||
* Note: We should really be using pxa_gpio_mode to alter GPDR but it
|
||||
* requires a function call per GPIO bit which is excessive
|
||||
* when we need to access 12 bits at once multiple times.
|
||||
* These functions must be called within local_irq_save()/local_irq_restore()
|
||||
* or similar.
|
||||
*/
|
||||
static inline void corgikbd_discharge_all(void)
|
||||
{
|
||||
/* STROBE All HiZ */
|
||||
GPCR2 = CORGI_GPIO_ALL_STROBE_BIT;
|
||||
GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
|
||||
}
|
||||
|
||||
static inline void corgikbd_activate_all(void)
|
||||
{
|
||||
/* STROBE ALL -> High */
|
||||
GPSR2 = CORGI_GPIO_ALL_STROBE_BIT;
|
||||
GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
|
||||
|
||||
udelay(KB_DISCHARGE_DELAY);
|
||||
|
||||
/* Clear any interrupts we may have triggered when altering the GPIO lines */
|
||||
GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
|
||||
GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
|
||||
}
|
||||
|
||||
static inline void corgikbd_activate_col(int col)
|
||||
{
|
||||
/* STROBE col -> High, not col -> HiZ */
|
||||
GPSR2 = CORGI_GPIO_STROBE_BIT(col);
|
||||
GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
|
||||
}
|
||||
|
||||
static inline void corgikbd_reset_col(int col)
|
||||
{
|
||||
/* STROBE col -> Low */
|
||||
GPCR2 = CORGI_GPIO_STROBE_BIT(col);
|
||||
/* STROBE col -> out, not col -> HiZ */
|
||||
GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
|
||||
}
|
||||
|
||||
#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
|
||||
|
||||
/*
|
||||
* The corgi keyboard only generates interrupts when a key is pressed.
|
||||
* When a key is pressed, we enable a timer which then scans the
|
||||
* keyboard to detect when the key is released.
|
||||
*/
|
||||
|
||||
/* Scan the hardware keyboard and push any changes up through the input layer */
|
||||
static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
|
||||
{
|
||||
unsigned int row, col, rowd;
|
||||
unsigned long flags;
|
||||
unsigned int num_pressed;
|
||||
|
||||
if (corgikbd_data->suspended)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&corgikbd_data->lock, flags);
|
||||
|
||||
num_pressed = 0;
|
||||
for (col = 0; col < KB_COLS; col++) {
|
||||
/*
|
||||
* Discharge the output driver capacitatance
|
||||
* in the keyboard matrix. (Yes it is significant..)
|
||||
*/
|
||||
|
||||
corgikbd_discharge_all();
|
||||
udelay(KB_DISCHARGE_DELAY);
|
||||
|
||||
corgikbd_activate_col(col);
|
||||
udelay(KB_ACTIVATE_DELAY);
|
||||
|
||||
rowd = GET_ROWS_STATUS(col);
|
||||
for (row = 0; row < KB_ROWS; row++) {
|
||||
unsigned int scancode, pressed;
|
||||
|
||||
scancode = SCANCODE(row, col);
|
||||
pressed = rowd & KB_ROWMASK(row);
|
||||
|
||||
input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);
|
||||
|
||||
if (pressed)
|
||||
num_pressed++;
|
||||
|
||||
if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
|
||||
&& time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) {
|
||||
input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
|
||||
corgikbd_data->suspend_jiffies=jiffies;
|
||||
}
|
||||
}
|
||||
corgikbd_reset_col(col);
|
||||
}
|
||||
|
||||
corgikbd_activate_all();
|
||||
|
||||
input_sync(corgikbd_data->input);
|
||||
|
||||
/* if any keys are pressed, enable the timer */
|
||||
if (num_pressed)
|
||||
mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
|
||||
|
||||
spin_unlock_irqrestore(&corgikbd_data->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* corgi keyboard interrupt handler.
|
||||
*/
|
||||
static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct corgikbd *corgikbd_data = dev_id;
|
||||
|
||||
if (!timer_pending(&corgikbd_data->timer)) {
|
||||
/** wait chattering delay **/
|
||||
udelay(20);
|
||||
corgikbd_scankeyboard(corgikbd_data);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* corgi timer checking for released keys
|
||||
*/
|
||||
static void corgikbd_timer_callback(unsigned long data)
|
||||
{
|
||||
struct corgikbd *corgikbd_data = (struct corgikbd *) data;
|
||||
corgikbd_scankeyboard(corgikbd_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* The hinge switches generate no interrupt so they need to be
|
||||
* monitored by a timer.
|
||||
*
|
||||
* We debounce the switches and pass them to the input system.
|
||||
*
|
||||
* gprr == 0x00 - Keyboard with Landscape Screen
|
||||
* 0x08 - No Keyboard with Portrait Screen
|
||||
* 0x0c - Keyboard and Screen Closed
|
||||
*/
|
||||
|
||||
#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x))
|
||||
#define HINGE_STABLE_COUNT 2
|
||||
static int sharpsl_hinge_state;
|
||||
static int hinge_count;
|
||||
|
||||
static void corgikbd_hinge_timer(unsigned long data)
|
||||
{
|
||||
struct corgikbd *corgikbd_data = (struct corgikbd *) data;
|
||||
unsigned long gprr;
|
||||
unsigned long flags;
|
||||
|
||||
gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
|
||||
gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0);
|
||||
if (gprr != sharpsl_hinge_state) {
|
||||
hinge_count = 0;
|
||||
sharpsl_hinge_state = gprr;
|
||||
} else if (hinge_count < HINGE_STABLE_COUNT) {
|
||||
hinge_count++;
|
||||
if (hinge_count >= HINGE_STABLE_COUNT) {
|
||||
spin_lock_irqsave(&corgikbd_data->lock, flags);
|
||||
|
||||
input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0));
|
||||
input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0));
|
||||
input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0));
|
||||
input_sync(corgikbd_data->input);
|
||||
|
||||
spin_unlock_irqrestore(&corgikbd_data->lock, flags);
|
||||
}
|
||||
}
|
||||
mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int corgikbd_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
int i;
|
||||
struct corgikbd *corgikbd = platform_get_drvdata(dev);
|
||||
|
||||
corgikbd->suspended = 1;
|
||||
/* strobe 0 is the power key so this can't be made an input for
|
||||
powersaving therefore i = 1 */
|
||||
for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int corgikbd_resume(struct platform_device *dev)
|
||||
{
|
||||
int i;
|
||||
struct corgikbd *corgikbd = platform_get_drvdata(dev);
|
||||
|
||||
for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
|
||||
|
||||
/* Upon resume, ignore the suspend key for a short while */
|
||||
corgikbd->suspend_jiffies=jiffies;
|
||||
corgikbd->suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define corgikbd_suspend NULL
|
||||
#define corgikbd_resume NULL
|
||||
#endif
|
||||
|
||||
static int __init corgikbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct corgikbd *corgikbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, err = -ENOMEM;
|
||||
|
||||
corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!corgikbd || !input_dev)
|
||||
goto fail;
|
||||
|
||||
platform_set_drvdata(pdev, corgikbd);
|
||||
|
||||
corgikbd->input = input_dev;
|
||||
spin_lock_init(&corgikbd->lock);
|
||||
|
||||
/* Init Keyboard rescan timer */
|
||||
init_timer(&corgikbd->timer);
|
||||
corgikbd->timer.function = corgikbd_timer_callback;
|
||||
corgikbd->timer.data = (unsigned long) corgikbd;
|
||||
|
||||
/* Init Hinge Timer */
|
||||
init_timer(&corgikbd->htimer);
|
||||
corgikbd->htimer.function = corgikbd_hinge_timer;
|
||||
corgikbd->htimer.data = (unsigned long) corgikbd;
|
||||
|
||||
corgikbd->suspend_jiffies=jiffies;
|
||||
|
||||
memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
|
||||
|
||||
input_dev->name = "Corgi Keyboard";
|
||||
input_dev->phys = "corgikbd/input0";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &pdev->dev;
|
||||
input_dev->private = corgikbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
|
||||
input_dev->keycode = corgikbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
|
||||
set_bit(corgikbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
set_bit(SW_LID, input_dev->swbit);
|
||||
set_bit(SW_TABLET_MODE, input_dev->swbit);
|
||||
set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
|
||||
|
||||
err = input_register_device(corgikbd->input);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
|
||||
|
||||
/* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
|
||||
for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
|
||||
pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
|
||||
if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING,
|
||||
"corgikbd", corgikbd))
|
||||
printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
|
||||
}
|
||||
|
||||
/* Set Strobe lines as outputs - set high */
|
||||
for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
|
||||
|
||||
/* Setup the headphone jack as an input */
|
||||
pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
|
||||
|
||||
return 0;
|
||||
|
||||
fail: input_free_device(input_dev);
|
||||
kfree(corgikbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int corgikbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct corgikbd *corgikbd = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
|
||||
free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
|
||||
|
||||
del_timer_sync(&corgikbd->htimer);
|
||||
del_timer_sync(&corgikbd->timer);
|
||||
|
||||
input_unregister_device(corgikbd->input);
|
||||
|
||||
kfree(corgikbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver corgikbd_driver = {
|
||||
.probe = corgikbd_probe,
|
||||
.remove = corgikbd_remove,
|
||||
.suspend = corgikbd_suspend,
|
||||
.resume = corgikbd_resume,
|
||||
.driver = {
|
||||
.name = "corgi-keyboard",
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit corgikbd_init(void)
|
||||
{
|
||||
return platform_driver_register(&corgikbd_driver);
|
||||
}
|
||||
|
||||
static void __exit corgikbd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&corgikbd_driver);
|
||||
}
|
||||
|
||||
module_init(corgikbd_init);
|
||||
module_exit(corgikbd_exit);
|
||||
|
||||
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
|
||||
MODULE_DESCRIPTION("Corgi Keyboard Driver");
|
||||
MODULE_LICENSE("GPLv2");
|
||||
146
drivers/input/keyboard/gpio_keys.c
Normal file
146
drivers/input/keyboard/gpio_keys.c
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Driver for keys on GPIO lines capable of generating interrupts.
|
||||
*
|
||||
* Copyright 2005 Phil Blundell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
|
||||
#include <asm/gpio.h>
|
||||
|
||||
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
|
||||
{
|
||||
int i;
|
||||
struct platform_device *pdev = dev_id;
|
||||
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct input_dev *input = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
int gpio = pdata->buttons[i].gpio;
|
||||
if (irq == gpio_to_irq(gpio)) {
|
||||
int state = (gpio_get_value(gpio) ? 1 : 0) ^ (pdata->buttons[i].active_low);
|
||||
|
||||
input_report_key(input, pdata->buttons[i].keycode, state);
|
||||
input_sync(input);
|
||||
}
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct input_dev *input;
|
||||
int i, error;
|
||||
|
||||
input = input_allocate_device();
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, input);
|
||||
|
||||
input->evbit[0] = BIT(EV_KEY);
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "gpio-keys/input0";
|
||||
input->cdev.dev = &pdev->dev;
|
||||
input->private = pdata;
|
||||
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->id.vendor = 0x0001;
|
||||
input->id.product = 0x0001;
|
||||
input->id.version = 0x0100;
|
||||
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
int code = pdata->buttons[i].keycode;
|
||||
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
||||
|
||||
set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);
|
||||
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
|
||||
pdata->buttons[i].desc ? pdata->buttons[i].desc : "gpio_keys",
|
||||
pdev);
|
||||
if (error) {
|
||||
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
|
||||
irq, error);
|
||||
goto fail;
|
||||
}
|
||||
set_bit(code, input->keybit);
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
printk(KERN_ERR "Unable to register gpio-keys input device\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i = i - 1; i >= 0; i--)
|
||||
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
|
||||
|
||||
input_free_device(input);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit gpio_keys_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct input_dev *input = platform_get_drvdata(pdev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
||||
free_irq(irq, pdev);
|
||||
}
|
||||
|
||||
input_unregister_device(input);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct platform_driver gpio_keys_device_driver = {
|
||||
.probe = gpio_keys_probe,
|
||||
.remove = __devexit_p(gpio_keys_remove),
|
||||
.driver = {
|
||||
.name = "gpio-keys",
|
||||
}
|
||||
};
|
||||
|
||||
static int __init gpio_keys_init(void)
|
||||
{
|
||||
return platform_driver_register(&gpio_keys_device_driver);
|
||||
}
|
||||
|
||||
static void __exit gpio_keys_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&gpio_keys_device_driver);
|
||||
}
|
||||
|
||||
module_init(gpio_keys_init);
|
||||
module_exit(gpio_keys_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
|
||||
MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
|
||||
393
drivers/input/keyboard/hil_kbd.c
Normal file
393
drivers/input/keyboard/hil_kbd.c
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Generic linux-input device driver for keyboard devices
|
||||
*
|
||||
* Copyright (c) 2001 Brian S. Julin
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* Alternatively, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL").
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
*
|
||||
* References:
|
||||
* HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/hil.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pci_ids.h>
|
||||
|
||||
#define PREFIX "HIL KEYB: "
|
||||
#define HIL_GENERIC_NAME "HIL keyboard"
|
||||
|
||||
MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
|
||||
MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
|
||||
#define HIL_KBD_MAX_LENGTH 16
|
||||
|
||||
#define HIL_KBD_SET1_UPBIT 0x01
|
||||
#define HIL_KBD_SET1_SHIFT 1
|
||||
static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] =
|
||||
{ HIL_KEYCODES_SET1 };
|
||||
|
||||
#define HIL_KBD_SET2_UPBIT 0x01
|
||||
#define HIL_KBD_SET2_SHIFT 1
|
||||
/* Set2 is user defined */
|
||||
|
||||
#define HIL_KBD_SET3_UPBIT 0x80
|
||||
#define HIL_KBD_SET3_SHIFT 0
|
||||
static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
|
||||
{ HIL_KEYCODES_SET3 };
|
||||
|
||||
static char hil_language[][16] = { HIL_LOCALE_MAP };
|
||||
|
||||
struct hil_kbd {
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
|
||||
/* Input buffer and index for packets from HIL bus. */
|
||||
hil_packet data[HIL_KBD_MAX_LENGTH];
|
||||
int idx4; /* four counts per packet */
|
||||
|
||||
/* Raw device info records from HIL bus, see hil.h for fields. */
|
||||
char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */
|
||||
char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */
|
||||
char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */
|
||||
char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */
|
||||
|
||||
/* Something to sleep around with. */
|
||||
struct semaphore sem;
|
||||
};
|
||||
|
||||
/* Process a complete packet after transfer from the HIL */
|
||||
static void hil_kbd_process_record(struct hil_kbd *kbd)
|
||||
{
|
||||
struct input_dev *dev = kbd->dev;
|
||||
hil_packet *data = kbd->data;
|
||||
hil_packet p;
|
||||
int idx, i, cnt;
|
||||
|
||||
idx = kbd->idx4/4;
|
||||
p = data[idx - 1];
|
||||
|
||||
if ((p & ~HIL_CMDCT_POL) ==
|
||||
(HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL)) goto report;
|
||||
if ((p & ~HIL_CMDCT_RPL) ==
|
||||
(HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL)) goto report;
|
||||
|
||||
/* Not a poll response. See if we are loading config records. */
|
||||
switch (p & HIL_PKT_DATA_MASK) {
|
||||
case HIL_CMD_IDD:
|
||||
for (i = 0; i < idx; i++)
|
||||
kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_KBD_MAX_LENGTH; i++)
|
||||
kbd->idd[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_RSC:
|
||||
for (i = 0; i < idx; i++)
|
||||
kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_KBD_MAX_LENGTH; i++)
|
||||
kbd->rsc[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_EXD:
|
||||
for (i = 0; i < idx; i++)
|
||||
kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_KBD_MAX_LENGTH; i++)
|
||||
kbd->exd[i] = 0;
|
||||
break;
|
||||
case HIL_CMD_RNM:
|
||||
for (i = 0; i < idx; i++)
|
||||
kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
|
||||
for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
|
||||
kbd->rnm[i] = '\0';
|
||||
break;
|
||||
default:
|
||||
/* These occur when device isn't present */
|
||||
if (p == (HIL_ERR_INT | HIL_PKT_CMD)) break;
|
||||
/* Anything else we'd like to know about. */
|
||||
printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
|
||||
break;
|
||||
}
|
||||
goto out;
|
||||
|
||||
report:
|
||||
cnt = 1;
|
||||
switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
|
||||
case HIL_POL_CHARTYPE_NONE:
|
||||
break;
|
||||
case HIL_POL_CHARTYPE_ASCII:
|
||||
while (cnt < idx - 1)
|
||||
input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
|
||||
break;
|
||||
case HIL_POL_CHARTYPE_RSVD1:
|
||||
case HIL_POL_CHARTYPE_RSVD2:
|
||||
case HIL_POL_CHARTYPE_BINARY:
|
||||
while (cnt < idx - 1)
|
||||
input_report_key(dev, kbd->data[cnt++], 1);
|
||||
break;
|
||||
case HIL_POL_CHARTYPE_SET1:
|
||||
while (cnt < idx - 1) {
|
||||
unsigned int key;
|
||||
int up;
|
||||
key = kbd->data[cnt++];
|
||||
up = key & HIL_KBD_SET1_UPBIT;
|
||||
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
|
||||
key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
|
||||
if (key != KEY_RESERVED)
|
||||
input_report_key(dev, key, !up);
|
||||
}
|
||||
break;
|
||||
case HIL_POL_CHARTYPE_SET2:
|
||||
while (cnt < idx - 1) {
|
||||
unsigned int key;
|
||||
int up;
|
||||
key = kbd->data[cnt++];
|
||||
up = key & HIL_KBD_SET2_UPBIT;
|
||||
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
|
||||
key = key >> HIL_KBD_SET2_SHIFT;
|
||||
if (key != KEY_RESERVED)
|
||||
input_report_key(dev, key, !up);
|
||||
}
|
||||
break;
|
||||
case HIL_POL_CHARTYPE_SET3:
|
||||
while (cnt < idx - 1) {
|
||||
unsigned int key;
|
||||
int up;
|
||||
key = kbd->data[cnt++];
|
||||
up = key & HIL_KBD_SET3_UPBIT;
|
||||
key &= (~HIL_KBD_SET1_UPBIT & 0xff);
|
||||
key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
|
||||
if (key != KEY_RESERVED)
|
||||
input_report_key(dev, key, !up);
|
||||
}
|
||||
break;
|
||||
}
|
||||
out:
|
||||
kbd->idx4 = 0;
|
||||
up(&kbd->sem);
|
||||
}
|
||||
|
||||
static void hil_kbd_process_err(struct hil_kbd *kbd) {
|
||||
printk(KERN_WARNING PREFIX "errored HIL packet\n");
|
||||
kbd->idx4 = 0;
|
||||
up(&kbd->sem);
|
||||
}
|
||||
|
||||
static irqreturn_t hil_kbd_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct hil_kbd *kbd;
|
||||
hil_packet packet;
|
||||
int idx;
|
||||
|
||||
kbd = serio_get_drvdata(serio);
|
||||
if (kbd == NULL) {
|
||||
BUG();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
|
||||
hil_kbd_process_err(kbd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
idx = kbd->idx4/4;
|
||||
if (!(kbd->idx4 % 4)) kbd->data[idx] = 0;
|
||||
packet = kbd->data[idx];
|
||||
packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
|
||||
kbd->data[idx] = packet;
|
||||
|
||||
/* Records of N 4-byte hil_packets must terminate with a command. */
|
||||
if ((++(kbd->idx4)) % 4) return IRQ_HANDLED;
|
||||
if ((packet & 0xffff0000) != HIL_ERR_INT) {
|
||||
hil_kbd_process_err(kbd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (packet & HIL_PKT_CMD) hil_kbd_process_record(kbd);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void hil_kbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct hil_kbd *kbd;
|
||||
|
||||
kbd = serio_get_drvdata(serio);
|
||||
if (kbd == NULL) {
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
serio_close(serio);
|
||||
input_unregister_device(kbd->dev);
|
||||
kfree(kbd);
|
||||
}
|
||||
|
||||
static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct hil_kbd *kbd;
|
||||
uint8_t did, *idd;
|
||||
int i;
|
||||
|
||||
kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
|
||||
if (!kbd)
|
||||
return -ENOMEM;
|
||||
|
||||
kbd->dev = input_allocate_device();
|
||||
if (!kbd->dev)
|
||||
goto bail0;
|
||||
|
||||
kbd->dev->private = kbd;
|
||||
|
||||
if (serio_open(serio, drv))
|
||||
goto bail1;
|
||||
|
||||
serio_set_drvdata(serio, kbd);
|
||||
kbd->serio = serio;
|
||||
|
||||
init_MUTEX_LOCKED(&(kbd->sem));
|
||||
|
||||
/* Get device info. MLC driver supplies devid/status/etc. */
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_IDD);
|
||||
down(&(kbd->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_RSC);
|
||||
down(&(kbd->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_RNM);
|
||||
down(&(kbd->sem));
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_EXD);
|
||||
down(&(kbd->sem));
|
||||
|
||||
up(&(kbd->sem));
|
||||
|
||||
did = kbd->idd[0];
|
||||
idd = kbd->idd + 1;
|
||||
switch (did & HIL_IDD_DID_TYPE_MASK) {
|
||||
case HIL_IDD_DID_TYPE_KB_INTEGRAL:
|
||||
case HIL_IDD_DID_TYPE_KB_ITF:
|
||||
case HIL_IDD_DID_TYPE_KB_RSVD:
|
||||
case HIL_IDD_DID_TYPE_CHAR:
|
||||
printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
|
||||
did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
|
||||
break;
|
||||
default:
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
|
||||
printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
|
||||
goto bail2;
|
||||
}
|
||||
|
||||
|
||||
kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
kbd->dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
|
||||
kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
|
||||
kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]);
|
||||
kbd->dev->keycode = hil_kbd_set1;
|
||||
kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
|
||||
kbd->dev->phys = "hpkbd/input0"; /* XXX */
|
||||
|
||||
kbd->dev->id.bustype = BUS_HIL;
|
||||
kbd->dev->id.vendor = PCI_VENDOR_ID_HP;
|
||||
kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
|
||||
kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
|
||||
kbd->dev->cdev.dev = &serio->dev;
|
||||
|
||||
for (i = 0; i < 128; i++) {
|
||||
set_bit(hil_kbd_set1[i], kbd->dev->keybit);
|
||||
set_bit(hil_kbd_set3[i], kbd->dev->keybit);
|
||||
}
|
||||
clear_bit(0, kbd->dev->keybit);
|
||||
|
||||
input_register_device(kbd->dev);
|
||||
printk(KERN_INFO "input: %s, ID: %d\n",
|
||||
kbd->dev->name, did);
|
||||
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, 0);
|
||||
serio->write(serio, HIL_PKT_CMD >> 8);
|
||||
serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
|
||||
down(&(kbd->sem));
|
||||
up(&(kbd->sem));
|
||||
|
||||
return 0;
|
||||
bail2:
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
bail1:
|
||||
input_free_device(kbd->dev);
|
||||
bail0:
|
||||
kfree(kbd);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static struct serio_device_id hil_kbd_ids[] = {
|
||||
{
|
||||
.type = SERIO_HIL_MLC,
|
||||
.proto = SERIO_HIL,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
struct serio_driver hil_kbd_serio_drv = {
|
||||
.driver = {
|
||||
.name = "hil_kbd",
|
||||
},
|
||||
.description = "HP HIL keyboard driver",
|
||||
.id_table = hil_kbd_ids,
|
||||
.connect = hil_kbd_connect,
|
||||
.disconnect = hil_kbd_disconnect,
|
||||
.interrupt = hil_kbd_interrupt
|
||||
};
|
||||
|
||||
static int __init hil_kbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&hil_kbd_serio_drv);
|
||||
}
|
||||
|
||||
static void __exit hil_kbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&hil_kbd_serio_drv);
|
||||
}
|
||||
|
||||
module_init(hil_kbd_init);
|
||||
module_exit(hil_kbd_exit);
|
||||
372
drivers/input/keyboard/hilkbd.c
Normal file
372
drivers/input/keyboard/hilkbd.c
Normal file
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* linux/drivers/hil/hilkbd.c
|
||||
*
|
||||
* Copyright (C) 1998 Philip Blundell <philb@gnu.org>
|
||||
* Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
|
||||
* Copyright (C) 1999-2006 Helge Deller <deller@gmx.de>
|
||||
*
|
||||
* Very basic HP Human Interface Loop (HIL) driver.
|
||||
* This driver handles the keyboard on HP300 (m68k) and on some
|
||||
* HP700 (parisc) series machines.
|
||||
*
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License version 2. See the file COPYING in the main directory of this
|
||||
* archive for more details.
|
||||
*/
|
||||
|
||||
#include <linux/pci_ids.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/hil.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <asm/irq.h>
|
||||
#ifdef CONFIG_HP300
|
||||
#include <asm/hwtest.h>
|
||||
#endif
|
||||
|
||||
|
||||
MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
|
||||
MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
|
||||
#if defined(CONFIG_PARISC)
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/parisc-device.h>
|
||||
static unsigned long hil_base; /* HPA for the HIL device */
|
||||
static unsigned int hil_irq;
|
||||
#define HILBASE hil_base /* HPPA (parisc) port address */
|
||||
#define HIL_DATA 0x800
|
||||
#define HIL_CMD 0x801
|
||||
#define HIL_IRQ hil_irq
|
||||
#define hil_readb(p) gsc_readb(p)
|
||||
#define hil_writeb(v,p) gsc_writeb((v),(p))
|
||||
|
||||
#elif defined(CONFIG_HP300)
|
||||
|
||||
#define HILBASE 0xf0428000 /* HP300 (m86k) port address */
|
||||
#define HIL_DATA 0x1
|
||||
#define HIL_CMD 0x3
|
||||
#define HIL_IRQ 2
|
||||
#define hil_readb(p) readb(p)
|
||||
#define hil_writeb(v,p) writeb((v),(p))
|
||||
|
||||
#else
|
||||
#error "HIL is not supported on this platform"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* HIL helper functions */
|
||||
|
||||
#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
|
||||
#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
|
||||
#define hil_status() (hil_readb(HILBASE + HIL_CMD))
|
||||
#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
|
||||
#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
|
||||
#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
|
||||
|
||||
/* HIL constants */
|
||||
|
||||
#define HIL_BUSY 0x02
|
||||
#define HIL_DATA_RDY 0x01
|
||||
|
||||
#define HIL_SETARD 0xA0 /* set auto-repeat delay */
|
||||
#define HIL_SETARR 0xA2 /* set auto-repeat rate */
|
||||
#define HIL_SETTONE 0xA3 /* set tone generator */
|
||||
#define HIL_CNMT 0xB2 /* clear nmi */
|
||||
#define HIL_INTON 0x5C /* Turn on interrupts. */
|
||||
#define HIL_INTOFF 0x5D /* Turn off interrupts. */
|
||||
|
||||
#define HIL_READKBDSADR 0xF9
|
||||
#define HIL_WRITEKBDSADR 0xE9
|
||||
|
||||
static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] =
|
||||
{ HIL_KEYCODES_SET1 };
|
||||
|
||||
/* HIL structure */
|
||||
static struct {
|
||||
struct input_dev *dev;
|
||||
|
||||
unsigned int curdev;
|
||||
|
||||
unsigned char s;
|
||||
unsigned char c;
|
||||
int valid;
|
||||
|
||||
unsigned char data[16];
|
||||
unsigned int ptr;
|
||||
spinlock_t lock;
|
||||
|
||||
void *dev_id; /* native bus device */
|
||||
} hil_dev;
|
||||
|
||||
|
||||
static void poll_finished(void)
|
||||
{
|
||||
int down;
|
||||
int key;
|
||||
unsigned char scode;
|
||||
|
||||
switch (hil_dev.data[0]) {
|
||||
case 0x40:
|
||||
down = (hil_dev.data[1] & 1) == 0;
|
||||
scode = hil_dev.data[1] >> 1;
|
||||
key = hphilkeyb_keycode[scode];
|
||||
input_report_key(hil_dev.dev, key, down);
|
||||
break;
|
||||
}
|
||||
hil_dev.curdev = 0;
|
||||
}
|
||||
|
||||
|
||||
static inline void handle_status(unsigned char s, unsigned char c)
|
||||
{
|
||||
if (c & 0x8) {
|
||||
/* End of block */
|
||||
if (c & 0x10)
|
||||
poll_finished();
|
||||
} else {
|
||||
if (c & 0x10) {
|
||||
if (hil_dev.curdev)
|
||||
poll_finished(); /* just in case */
|
||||
hil_dev.curdev = c & 7;
|
||||
hil_dev.ptr = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void handle_data(unsigned char s, unsigned char c)
|
||||
{
|
||||
if (hil_dev.curdev) {
|
||||
hil_dev.data[hil_dev.ptr++] = c;
|
||||
hil_dev.ptr &= 15;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* handle HIL interrupts */
|
||||
static irqreturn_t hil_interrupt(int irq, void *handle)
|
||||
{
|
||||
unsigned char s, c;
|
||||
|
||||
s = hil_status();
|
||||
c = hil_read_data();
|
||||
|
||||
switch (s >> 4) {
|
||||
case 0x5:
|
||||
handle_status(s, c);
|
||||
break;
|
||||
case 0x6:
|
||||
handle_data(s, c);
|
||||
break;
|
||||
case 0x4:
|
||||
hil_dev.s = s;
|
||||
hil_dev.c = c;
|
||||
mb();
|
||||
hil_dev.valid = 1;
|
||||
break;
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* send a command to the HIL */
|
||||
static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&hil_dev.lock, flags);
|
||||
while (hil_busy())
|
||||
/* wait */;
|
||||
hil_command(cmd);
|
||||
while (len--) {
|
||||
while (hil_busy())
|
||||
/* wait */;
|
||||
hil_write_data(*(data++));
|
||||
}
|
||||
spin_unlock_irqrestore(&hil_dev.lock, flags);
|
||||
}
|
||||
|
||||
|
||||
/* initialise HIL */
|
||||
static int __init
|
||||
hil_keyb_init(void)
|
||||
{
|
||||
unsigned char c;
|
||||
unsigned int i, kbid;
|
||||
wait_queue_head_t hil_wait;
|
||||
int err;
|
||||
|
||||
if (hil_dev.dev) {
|
||||
return -ENODEV; /* already initialized */
|
||||
}
|
||||
|
||||
hil_dev.dev = input_allocate_device();
|
||||
if (!hil_dev.dev)
|
||||
return -ENOMEM;
|
||||
hil_dev.dev->private = &hil_dev;
|
||||
|
||||
#if defined(CONFIG_HP300)
|
||||
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
|
||||
printk(KERN_ERR "HIL: hardware register was not found\n");
|
||||
err = -ENODEV;
|
||||
goto err1;
|
||||
}
|
||||
if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
|
||||
printk(KERN_ERR "HIL: IOPORT region already used\n");
|
||||
err = -EIO;
|
||||
goto err1;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
|
||||
if (err) {
|
||||
printk(KERN_ERR "HIL: Can't get IRQ\n");
|
||||
goto err2;
|
||||
}
|
||||
|
||||
/* Turn on interrupts */
|
||||
hil_do(HIL_INTON, NULL, 0);
|
||||
|
||||
/* Look for keyboards */
|
||||
hil_dev.valid = 0; /* clear any pending data */
|
||||
hil_do(HIL_READKBDSADR, NULL, 0);
|
||||
|
||||
init_waitqueue_head(&hil_wait);
|
||||
wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
|
||||
if (!hil_dev.valid) {
|
||||
printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
|
||||
}
|
||||
|
||||
c = hil_dev.c;
|
||||
hil_dev.valid = 0;
|
||||
if (c == 0) {
|
||||
kbid = -1;
|
||||
printk(KERN_WARNING "HIL: no keyboard present\n");
|
||||
} else {
|
||||
kbid = ffz(~c);
|
||||
printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
|
||||
}
|
||||
|
||||
/* set it to raw mode */
|
||||
c = 0;
|
||||
hil_do(HIL_WRITEKBDSADR, &c, 1);
|
||||
|
||||
for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
|
||||
if (hphilkeyb_keycode[i] != KEY_RESERVED)
|
||||
set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
|
||||
|
||||
hil_dev.dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
hil_dev.dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
|
||||
hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
|
||||
hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
|
||||
hil_dev.dev->keycode = hphilkeyb_keycode;
|
||||
hil_dev.dev->name = "HIL keyboard";
|
||||
hil_dev.dev->phys = "hpkbd/input0";
|
||||
|
||||
hil_dev.dev->id.bustype = BUS_HIL;
|
||||
hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
|
||||
hil_dev.dev->id.product = 0x0001;
|
||||
hil_dev.dev->id.version = 0x0010;
|
||||
|
||||
err = input_register_device(hil_dev.dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "HIL: Can't register device\n");
|
||||
goto err3;
|
||||
}
|
||||
printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
|
||||
hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
disable_irq(HIL_IRQ);
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
err2:
|
||||
#if defined(CONFIG_HP300)
|
||||
release_region(HILBASE + HIL_DATA, 2);
|
||||
err1:
|
||||
#endif
|
||||
input_free_device(hil_dev.dev);
|
||||
hil_dev.dev = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
#if defined(CONFIG_PARISC)
|
||||
static int __init
|
||||
hil_init_chip(struct parisc_device *dev)
|
||||
{
|
||||
if (!dev->irq) {
|
||||
printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hil_base = dev->hpa.start;
|
||||
hil_irq = dev->irq;
|
||||
hil_dev.dev_id = dev;
|
||||
|
||||
printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
|
||||
|
||||
return hil_keyb_init();
|
||||
}
|
||||
|
||||
static struct parisc_device_id hil_tbl[] = {
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(parisc, hil_tbl);
|
||||
|
||||
static struct parisc_driver hil_driver = {
|
||||
.name = "hil",
|
||||
.id_table = hil_tbl,
|
||||
.probe = hil_init_chip,
|
||||
};
|
||||
#endif /* CONFIG_PARISC */
|
||||
|
||||
|
||||
static int __init hil_init(void)
|
||||
{
|
||||
#if defined(CONFIG_PARISC)
|
||||
return register_parisc_driver(&hil_driver);
|
||||
#else
|
||||
return hil_keyb_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void __exit hil_exit(void)
|
||||
{
|
||||
if (HIL_IRQ) {
|
||||
disable_irq(HIL_IRQ);
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
}
|
||||
|
||||
/* Turn off interrupts */
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
|
||||
input_unregister_device(hil_dev.dev);
|
||||
|
||||
hil_dev.dev = NULL;
|
||||
|
||||
#if defined(CONFIG_PARISC)
|
||||
unregister_parisc_driver(&hil_driver);
|
||||
#else
|
||||
release_region(HILBASE+HIL_DATA, 2);
|
||||
#endif
|
||||
}
|
||||
|
||||
module_init(hil_init);
|
||||
module_exit(hil_exit);
|
||||
110
drivers/input/keyboard/hpps2atkbd.h
Normal file
110
drivers/input/keyboard/hpps2atkbd.h
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* drivers/input/keyboard/hpps2atkbd.h
|
||||
*
|
||||
* Copyright (c) 2004 Helge Deller <deller@gmx.de>
|
||||
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
|
||||
* Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
|
||||
* Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
|
||||
*
|
||||
* HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
/* Is the keyboard an RDI PrecisionBook? */
|
||||
#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
|
||||
# define CONFLICT(x,y) x
|
||||
#else
|
||||
# define CONFLICT(x,y) y
|
||||
#endif
|
||||
|
||||
/* sadly RDI (Tadpole) decided to ship a different keyboard layout
|
||||
than HP for their PS/2 laptop keyboard which leads to conflicting
|
||||
keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
|
||||
HP: RDI: */
|
||||
#define C_07 CONFLICT( KEY_F12, KEY_F1 )
|
||||
#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
|
||||
#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
|
||||
#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
|
||||
#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
|
||||
|
||||
/* Raw SET 2 scancode table */
|
||||
|
||||
/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
|
||||
/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
|
||||
/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
|
||||
/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
|
||||
/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
|
||||
/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
|
||||
/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
|
||||
/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
|
||||
/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
|
||||
/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
|
||||
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
|
||||
/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
|
||||
/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
|
||||
/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
|
||||
/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
|
||||
/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND,
|
||||
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
|
||||
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
|
||||
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
|
||||
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
|
||||
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
|
||||
|
||||
/* These are offset for escaped keycodes: */
|
||||
|
||||
/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
|
||||
/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
|
||||
|
||||
#undef CONFLICT
|
||||
#undef C_07
|
||||
#undef C_11
|
||||
#undef C_14
|
||||
#undef C_58
|
||||
#undef C_61
|
||||
|
||||
767
drivers/input/keyboard/lkkbd.c
Normal file
767
drivers/input/keyboard/lkkbd.c
Normal file
@@ -0,0 +1,767 @@
|
||||
/*
|
||||
* Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
|
||||
*/
|
||||
|
||||
/*
|
||||
* LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
|
||||
* and VAXstations, but can also be used on any standard RS232 with an
|
||||
* adaptor).
|
||||
*
|
||||
* DISCLAIMER: This works for _me_. If you break anything by using the
|
||||
* information given below, I will _not_ be liable!
|
||||
*
|
||||
* RJ10 pinout: To DE9: Or DB25:
|
||||
* 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
|
||||
* 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
|
||||
* 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
|
||||
* 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
|
||||
*
|
||||
* Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
|
||||
* RJ10, it's like this:
|
||||
*
|
||||
* __=__ Hold the plug in front of you, cable downwards,
|
||||
* /___/| nose is hidden behind the plug. Now, pin 1 is at
|
||||
* |1234|| the left side, pin 4 at the right and 2 and 3 are
|
||||
* |IIII|| in between, of course:)
|
||||
* | ||
|
||||
* |____|/
|
||||
* || So the adaptor consists of three connected cables
|
||||
* || for data transmission (RxD and TxD) and signal ground.
|
||||
* Additionally, you have to get +12V from somewhere.
|
||||
* Most easily, you'll get that from a floppy or HDD power connector.
|
||||
* It's the yellow cable there (black is ground and red is +5V).
|
||||
*
|
||||
* The keyboard and all the commands it understands are documented in
|
||||
* "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
|
||||
* document is LK201 specific, but LK401 is mostly compatible. It comes
|
||||
* up in LK201 mode and doesn't report any of the additional keys it
|
||||
* has. These need to be switched on with the LK_CMD_ENABLE_LK401
|
||||
* command. You'll find this document (scanned .pdf file) on MANX,
|
||||
* a search engine specific to DEC documentation. Try
|
||||
* http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define DRIVER_DESC "LK keyboard driver"
|
||||
|
||||
MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
|
||||
MODULE_DESCRIPTION (DRIVER_DESC);
|
||||
MODULE_LICENSE ("GPL");
|
||||
|
||||
/*
|
||||
* Known parameters:
|
||||
* bell_volume
|
||||
* keyclick_volume
|
||||
* ctrlclick_volume
|
||||
*
|
||||
* Please notice that there's not yet an API to set these at runtime.
|
||||
*/
|
||||
static int bell_volume = 100; /* % */
|
||||
module_param (bell_volume, int, 0);
|
||||
MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
|
||||
|
||||
static int keyclick_volume = 100; /* % */
|
||||
module_param (keyclick_volume, int, 0);
|
||||
MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
|
||||
|
||||
static int ctrlclick_volume = 100; /* % */
|
||||
module_param (ctrlclick_volume, int, 0);
|
||||
MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
|
||||
|
||||
static int lk201_compose_is_alt;
|
||||
module_param (lk201_compose_is_alt, int, 0);
|
||||
MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
|
||||
"will act as an Alt key");
|
||||
|
||||
|
||||
|
||||
#undef LKKBD_DEBUG
|
||||
#ifdef LKKBD_DEBUG
|
||||
#define DBG(x...) printk (x)
|
||||
#else
|
||||
#define DBG(x...) do {} while (0)
|
||||
#endif
|
||||
|
||||
/* LED control */
|
||||
#define LK_LED_WAIT 0x81
|
||||
#define LK_LED_COMPOSE 0x82
|
||||
#define LK_LED_SHIFTLOCK 0x84
|
||||
#define LK_LED_SCROLLLOCK 0x88
|
||||
#define LK_CMD_LED_ON 0x13
|
||||
#define LK_CMD_LED_OFF 0x11
|
||||
|
||||
/* Mode control */
|
||||
#define LK_MODE_DOWN 0x80
|
||||
#define LK_MODE_AUTODOWN 0x82
|
||||
#define LK_MODE_UPDOWN 0x86
|
||||
#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
|
||||
|
||||
/* Misc commands */
|
||||
#define LK_CMD_ENABLE_KEYCLICK 0x1b
|
||||
#define LK_CMD_DISABLE_KEYCLICK 0x99
|
||||
#define LK_CMD_DISABLE_BELL 0xa1
|
||||
#define LK_CMD_SOUND_BELL 0xa7
|
||||
#define LK_CMD_ENABLE_BELL 0x23
|
||||
#define LK_CMD_DISABLE_CTRCLICK 0xb9
|
||||
#define LK_CMD_ENABLE_CTRCLICK 0xbb
|
||||
#define LK_CMD_SET_DEFAULTS 0xd3
|
||||
#define LK_CMD_POWERCYCLE_RESET 0xfd
|
||||
#define LK_CMD_ENABLE_LK401 0xe9
|
||||
#define LK_CMD_REQUEST_ID 0xab
|
||||
|
||||
/* Misc responses from keyboard */
|
||||
#define LK_STUCK_KEY 0x3d
|
||||
#define LK_SELFTEST_FAILED 0x3e
|
||||
#define LK_ALL_KEYS_UP 0xb3
|
||||
#define LK_METRONOME 0xb4
|
||||
#define LK_OUTPUT_ERROR 0xb5
|
||||
#define LK_INPUT_ERROR 0xb6
|
||||
#define LK_KBD_LOCKED 0xb7
|
||||
#define LK_KBD_TEST_MODE_ACK 0xb8
|
||||
#define LK_PREFIX_KEY_DOWN 0xb9
|
||||
#define LK_MODE_CHANGE_ACK 0xba
|
||||
#define LK_RESPONSE_RESERVED 0xbb
|
||||
|
||||
#define LK_NUM_KEYCODES 256
|
||||
#define LK_NUM_IGNORE_BYTES 6
|
||||
typedef u_int16_t lk_keycode_t;
|
||||
|
||||
|
||||
|
||||
static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
|
||||
[0x56] = KEY_F1,
|
||||
[0x57] = KEY_F2,
|
||||
[0x58] = KEY_F3,
|
||||
[0x59] = KEY_F4,
|
||||
[0x5a] = KEY_F5,
|
||||
[0x64] = KEY_F6,
|
||||
[0x65] = KEY_F7,
|
||||
[0x66] = KEY_F8,
|
||||
[0x67] = KEY_F9,
|
||||
[0x68] = KEY_F10,
|
||||
[0x71] = KEY_F11,
|
||||
[0x72] = KEY_F12,
|
||||
[0x73] = KEY_F13,
|
||||
[0x74] = KEY_F14,
|
||||
[0x7c] = KEY_F15,
|
||||
[0x7d] = KEY_F16,
|
||||
[0x80] = KEY_F17,
|
||||
[0x81] = KEY_F18,
|
||||
[0x82] = KEY_F19,
|
||||
[0x83] = KEY_F20,
|
||||
[0x8a] = KEY_FIND,
|
||||
[0x8b] = KEY_INSERT,
|
||||
[0x8c] = KEY_DELETE,
|
||||
[0x8d] = KEY_SELECT,
|
||||
[0x8e] = KEY_PAGEUP,
|
||||
[0x8f] = KEY_PAGEDOWN,
|
||||
[0x92] = KEY_KP0,
|
||||
[0x94] = KEY_KPDOT,
|
||||
[0x95] = KEY_KPENTER,
|
||||
[0x96] = KEY_KP1,
|
||||
[0x97] = KEY_KP2,
|
||||
[0x98] = KEY_KP3,
|
||||
[0x99] = KEY_KP4,
|
||||
[0x9a] = KEY_KP5,
|
||||
[0x9b] = KEY_KP6,
|
||||
[0x9c] = KEY_KPCOMMA,
|
||||
[0x9d] = KEY_KP7,
|
||||
[0x9e] = KEY_KP8,
|
||||
[0x9f] = KEY_KP9,
|
||||
[0xa0] = KEY_KPMINUS,
|
||||
[0xa1] = KEY_PROG1,
|
||||
[0xa2] = KEY_PROG2,
|
||||
[0xa3] = KEY_PROG3,
|
||||
[0xa4] = KEY_PROG4,
|
||||
[0xa7] = KEY_LEFT,
|
||||
[0xa8] = KEY_RIGHT,
|
||||
[0xa9] = KEY_DOWN,
|
||||
[0xaa] = KEY_UP,
|
||||
[0xab] = KEY_RIGHTSHIFT,
|
||||
[0xac] = KEY_LEFTALT,
|
||||
[0xad] = KEY_COMPOSE, /* Right Compose, that is. */
|
||||
[0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
|
||||
[0xaf] = KEY_LEFTCTRL,
|
||||
[0xb0] = KEY_CAPSLOCK,
|
||||
[0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
|
||||
[0xb2] = KEY_RIGHTALT,
|
||||
[0xbc] = KEY_BACKSPACE,
|
||||
[0xbd] = KEY_ENTER,
|
||||
[0xbe] = KEY_TAB,
|
||||
[0xbf] = KEY_ESC,
|
||||
[0xc0] = KEY_1,
|
||||
[0xc1] = KEY_Q,
|
||||
[0xc2] = KEY_A,
|
||||
[0xc3] = KEY_Z,
|
||||
[0xc5] = KEY_2,
|
||||
[0xc6] = KEY_W,
|
||||
[0xc7] = KEY_S,
|
||||
[0xc8] = KEY_X,
|
||||
[0xc9] = KEY_102ND,
|
||||
[0xcb] = KEY_3,
|
||||
[0xcc] = KEY_E,
|
||||
[0xcd] = KEY_D,
|
||||
[0xce] = KEY_C,
|
||||
[0xd0] = KEY_4,
|
||||
[0xd1] = KEY_R,
|
||||
[0xd2] = KEY_F,
|
||||
[0xd3] = KEY_V,
|
||||
[0xd4] = KEY_SPACE,
|
||||
[0xd6] = KEY_5,
|
||||
[0xd7] = KEY_T,
|
||||
[0xd8] = KEY_G,
|
||||
[0xd9] = KEY_B,
|
||||
[0xdb] = KEY_6,
|
||||
[0xdc] = KEY_Y,
|
||||
[0xdd] = KEY_H,
|
||||
[0xde] = KEY_N,
|
||||
[0xe0] = KEY_7,
|
||||
[0xe1] = KEY_U,
|
||||
[0xe2] = KEY_J,
|
||||
[0xe3] = KEY_M,
|
||||
[0xe5] = KEY_8,
|
||||
[0xe6] = KEY_I,
|
||||
[0xe7] = KEY_K,
|
||||
[0xe8] = KEY_COMMA,
|
||||
[0xea] = KEY_9,
|
||||
[0xeb] = KEY_O,
|
||||
[0xec] = KEY_L,
|
||||
[0xed] = KEY_DOT,
|
||||
[0xef] = KEY_0,
|
||||
[0xf0] = KEY_P,
|
||||
[0xf2] = KEY_SEMICOLON,
|
||||
[0xf3] = KEY_SLASH,
|
||||
[0xf5] = KEY_EQUAL,
|
||||
[0xf6] = KEY_RIGHTBRACE,
|
||||
[0xf7] = KEY_BACKSLASH,
|
||||
[0xf9] = KEY_MINUS,
|
||||
[0xfa] = KEY_LEFTBRACE,
|
||||
[0xfb] = KEY_APOSTROPHE,
|
||||
};
|
||||
|
||||
#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \
|
||||
if (test_bit (LED, (LK)->dev->led)) \
|
||||
VAR_ON |= BITS; \
|
||||
else \
|
||||
VAR_OFF |= BITS; \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Per-keyboard data
|
||||
*/
|
||||
struct lkkbd {
|
||||
lk_keycode_t keycode[LK_NUM_KEYCODES];
|
||||
int ignore_bytes;
|
||||
unsigned char id[LK_NUM_IGNORE_BYTES];
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
struct work_struct tq;
|
||||
char name[64];
|
||||
char phys[32];
|
||||
char type;
|
||||
int bell_volume;
|
||||
int keyclick_volume;
|
||||
int ctrlclick_volume;
|
||||
};
|
||||
|
||||
#ifdef LKKBD_DEBUG
|
||||
/*
|
||||
* Responses from the keyboard and mapping back to their names.
|
||||
*/
|
||||
static struct {
|
||||
unsigned char value;
|
||||
unsigned char *name;
|
||||
} lk_response[] = {
|
||||
#define RESPONSE(x) { .value = (x), .name = #x, }
|
||||
RESPONSE (LK_STUCK_KEY),
|
||||
RESPONSE (LK_SELFTEST_FAILED),
|
||||
RESPONSE (LK_ALL_KEYS_UP),
|
||||
RESPONSE (LK_METRONOME),
|
||||
RESPONSE (LK_OUTPUT_ERROR),
|
||||
RESPONSE (LK_INPUT_ERROR),
|
||||
RESPONSE (LK_KBD_LOCKED),
|
||||
RESPONSE (LK_KBD_TEST_MODE_ACK),
|
||||
RESPONSE (LK_PREFIX_KEY_DOWN),
|
||||
RESPONSE (LK_MODE_CHANGE_ACK),
|
||||
RESPONSE (LK_RESPONSE_RESERVED),
|
||||
#undef RESPONSE
|
||||
};
|
||||
|
||||
static unsigned char *
|
||||
response_name (unsigned char value)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (lk_response); i++)
|
||||
if (lk_response[i].value == value)
|
||||
return lk_response[i].name;
|
||||
|
||||
return "<unknown>";
|
||||
}
|
||||
#endif /* LKKBD_DEBUG */
|
||||
|
||||
/*
|
||||
* Calculate volume parameter byte for a given volume.
|
||||
*/
|
||||
static unsigned char
|
||||
volume_to_hw (int volume_percent)
|
||||
{
|
||||
unsigned char ret = 0;
|
||||
|
||||
if (volume_percent < 0)
|
||||
volume_percent = 0;
|
||||
if (volume_percent > 100)
|
||||
volume_percent = 100;
|
||||
|
||||
if (volume_percent >= 0)
|
||||
ret = 7;
|
||||
if (volume_percent >= 13) /* 12.5 */
|
||||
ret = 6;
|
||||
if (volume_percent >= 25)
|
||||
ret = 5;
|
||||
if (volume_percent >= 38) /* 37.5 */
|
||||
ret = 4;
|
||||
if (volume_percent >= 50)
|
||||
ret = 3;
|
||||
if (volume_percent >= 63) /* 62.5 */
|
||||
ret = 2; /* This is the default volume */
|
||||
if (volume_percent >= 75)
|
||||
ret = 1;
|
||||
if (volume_percent >= 88) /* 87.5 */
|
||||
ret = 0;
|
||||
|
||||
ret |= 0x80;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
lkkbd_detection_done (struct lkkbd *lk)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Reset setting for Compose key. Let Compose be KEY_COMPOSE.
|
||||
*/
|
||||
lk->keycode[0xb1] = KEY_COMPOSE;
|
||||
|
||||
/*
|
||||
* Print keyboard name and modify Compose=Alt on user's request.
|
||||
*/
|
||||
switch (lk->id[4]) {
|
||||
case 1:
|
||||
strlcpy (lk->name, "DEC LK201 keyboard",
|
||||
sizeof (lk->name));
|
||||
|
||||
if (lk201_compose_is_alt)
|
||||
lk->keycode[0xb1] = KEY_LEFTALT;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
strlcpy (lk->name, "DEC LK401 keyboard",
|
||||
sizeof (lk->name));
|
||||
break;
|
||||
|
||||
default:
|
||||
strlcpy (lk->name, "Unknown DEC keyboard",
|
||||
sizeof (lk->name));
|
||||
printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
|
||||
"please report to Jan-Benedict Glaw "
|
||||
"<jbglaw@lug-owl.de>\n", lk->phys);
|
||||
printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
|
||||
for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
|
||||
printk (" 0x%02x", lk->id[i]);
|
||||
printk ("\n");
|
||||
break;
|
||||
}
|
||||
printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
|
||||
lk->phys, lk->name);
|
||||
|
||||
/*
|
||||
* Report errors during keyboard boot-up.
|
||||
*/
|
||||
switch (lk->id[2]) {
|
||||
case 0x00:
|
||||
/* All okay */
|
||||
break;
|
||||
|
||||
case LK_STUCK_KEY:
|
||||
printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
|
||||
"%s\n", lk->phys);
|
||||
break;
|
||||
|
||||
case LK_SELFTEST_FAILED:
|
||||
printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
|
||||
"at %s, keyboard may not work "
|
||||
"properly\n", lk->phys);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk (KERN_ERR "lkkbd: Unknown error %02x on "
|
||||
"keyboard at %s\n", lk->id[2],
|
||||
lk->phys);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to hint user if there's a stuck key.
|
||||
*/
|
||||
if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
|
||||
printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
|
||||
"is 0x%04x\n", lk->id[3],
|
||||
lk->keycode[lk->id[3]]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* lkkbd_interrupt() is called by the low level driver when a character
|
||||
* is received.
|
||||
*/
|
||||
static irqreturn_t
|
||||
lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct lkkbd *lk = serio_get_drvdata (serio);
|
||||
int i;
|
||||
|
||||
DBG (KERN_INFO "Got byte 0x%02x\n", data);
|
||||
|
||||
if (lk->ignore_bytes > 0) {
|
||||
DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name);
|
||||
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
|
||||
|
||||
if (lk->ignore_bytes == 0)
|
||||
lkkbd_detection_done (lk);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
case LK_ALL_KEYS_UP:
|
||||
for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
|
||||
if (lk->keycode[i] != KEY_RESERVED)
|
||||
input_report_key (lk->dev, lk->keycode[i], 0);
|
||||
input_sync (lk->dev);
|
||||
break;
|
||||
|
||||
case 0x01:
|
||||
DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
|
||||
lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
|
||||
lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
|
||||
schedule_work (&lk->tq);
|
||||
break;
|
||||
|
||||
case LK_METRONOME:
|
||||
case LK_OUTPUT_ERROR:
|
||||
case LK_INPUT_ERROR:
|
||||
case LK_KBD_LOCKED:
|
||||
case LK_KBD_TEST_MODE_ACK:
|
||||
case LK_PREFIX_KEY_DOWN:
|
||||
case LK_MODE_CHANGE_ACK:
|
||||
case LK_RESPONSE_RESERVED:
|
||||
DBG (KERN_INFO "Got %s and don't know how to handle...\n",
|
||||
response_name (data));
|
||||
break;
|
||||
|
||||
default:
|
||||
if (lk->keycode[data] != KEY_RESERVED) {
|
||||
if (!test_bit (lk->keycode[data], lk->dev->key))
|
||||
input_report_key (lk->dev, lk->keycode[data], 1);
|
||||
else
|
||||
input_report_key (lk->dev, lk->keycode[data], 0);
|
||||
input_sync (lk->dev);
|
||||
} else
|
||||
printk (KERN_WARNING "%s: Unknown key with "
|
||||
"scancode 0x%02x on %s.\n",
|
||||
__FILE__, data, lk->name);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* lkkbd_event() handles events from the input module.
|
||||
*/
|
||||
static int
|
||||
lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
|
||||
int value)
|
||||
{
|
||||
struct lkkbd *lk = dev->private;
|
||||
unsigned char leds_on = 0;
|
||||
unsigned char leds_off = 0;
|
||||
|
||||
switch (type) {
|
||||
case EV_LED:
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
|
||||
if (leds_on != 0) {
|
||||
lk->serio->write (lk->serio, LK_CMD_LED_ON);
|
||||
lk->serio->write (lk->serio, leds_on);
|
||||
}
|
||||
if (leds_off != 0) {
|
||||
lk->serio->write (lk->serio, LK_CMD_LED_OFF);
|
||||
lk->serio->write (lk->serio, leds_off);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case EV_SND:
|
||||
switch (code) {
|
||||
case SND_CLICK:
|
||||
if (value == 0) {
|
||||
DBG ("%s: Deactivating key clicks\n", __FUNCTION__);
|
||||
lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
|
||||
lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
|
||||
} else {
|
||||
DBG ("%s: Activating key clicks\n", __FUNCTION__);
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
|
||||
lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
|
||||
lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
|
||||
}
|
||||
return 0;
|
||||
|
||||
case SND_BELL:
|
||||
if (value != 0)
|
||||
lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
|
||||
__FUNCTION__, type, code, value);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* lkkbd_reinit() sets leds and beeps to a state the computer remembers they
|
||||
* were in.
|
||||
*/
|
||||
static void
|
||||
lkkbd_reinit (struct work_struct *work)
|
||||
{
|
||||
struct lkkbd *lk = container_of(work, struct lkkbd, tq);
|
||||
int division;
|
||||
unsigned char leds_on = 0;
|
||||
unsigned char leds_off = 0;
|
||||
|
||||
/* Ask for ID */
|
||||
lk->serio->write (lk->serio, LK_CMD_REQUEST_ID);
|
||||
|
||||
/* Reset parameters */
|
||||
lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS);
|
||||
|
||||
/* Set LEDs */
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
|
||||
CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
|
||||
if (leds_on != 0) {
|
||||
lk->serio->write (lk->serio, LK_CMD_LED_ON);
|
||||
lk->serio->write (lk->serio, leds_on);
|
||||
}
|
||||
if (leds_off != 0) {
|
||||
lk->serio->write (lk->serio, LK_CMD_LED_OFF);
|
||||
lk->serio->write (lk->serio, leds_off);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to activate extended LK401 mode. This command will
|
||||
* only work with a LK401 keyboard and grants access to
|
||||
* LAlt, RAlt, RCompose and RShift.
|
||||
*/
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401);
|
||||
|
||||
/* Set all keys to UPDOWN mode */
|
||||
for (division = 1; division <= 14; division++)
|
||||
lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
|
||||
division));
|
||||
|
||||
/* Enable bell and set volume */
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL);
|
||||
lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume));
|
||||
|
||||
/* Enable/disable keyclick (and possibly set volume) */
|
||||
if (test_bit (SND_CLICK, lk->dev->snd)) {
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
|
||||
lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
|
||||
lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
|
||||
lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
|
||||
} else {
|
||||
lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
|
||||
lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
|
||||
}
|
||||
|
||||
/* Sound the bell if needed */
|
||||
if (test_bit (SND_BELL, lk->dev->snd))
|
||||
lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
|
||||
}
|
||||
|
||||
/*
|
||||
* lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
|
||||
*/
|
||||
static int
|
||||
lkkbd_connect (struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct lkkbd *lk;
|
||||
struct input_dev *input_dev;
|
||||
int i;
|
||||
int err;
|
||||
|
||||
lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device ();
|
||||
if (!lk || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
lk->serio = serio;
|
||||
lk->dev = input_dev;
|
||||
INIT_WORK (&lk->tq, lkkbd_reinit);
|
||||
lk->bell_volume = bell_volume;
|
||||
lk->keyclick_volume = keyclick_volume;
|
||||
lk->ctrlclick_volume = ctrlclick_volume;
|
||||
memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
|
||||
|
||||
strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name));
|
||||
snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = lk->name;
|
||||
input_dev->phys = lk->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_LKKBD;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->event = lkkbd_event;
|
||||
input_dev->private = lk;
|
||||
|
||||
set_bit (EV_KEY, input_dev->evbit);
|
||||
set_bit (EV_LED, input_dev->evbit);
|
||||
set_bit (EV_SND, input_dev->evbit);
|
||||
set_bit (EV_REP, input_dev->evbit);
|
||||
set_bit (LED_CAPSL, input_dev->ledbit);
|
||||
set_bit (LED_SLEEP, input_dev->ledbit);
|
||||
set_bit (LED_COMPOSE, input_dev->ledbit);
|
||||
set_bit (LED_SCROLLL, input_dev->ledbit);
|
||||
set_bit (SND_BELL, input_dev->sndbit);
|
||||
set_bit (SND_CLICK, input_dev->sndbit);
|
||||
|
||||
input_dev->keycode = lk->keycode;
|
||||
input_dev->keycodesize = sizeof (lk_keycode_t);
|
||||
input_dev->keycodemax = LK_NUM_KEYCODES;
|
||||
for (i = 0; i < LK_NUM_KEYCODES; i++)
|
||||
set_bit (lk->keycode[i], input_dev->keybit);
|
||||
|
||||
serio_set_drvdata (serio, lk);
|
||||
|
||||
err = serio_open (serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device (lk->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close (serio);
|
||||
fail2: serio_set_drvdata (serio, NULL);
|
||||
fail1: input_free_device (input_dev);
|
||||
kfree (lk);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* lkkbd_disconnect() unregisters and closes behind us.
|
||||
*/
|
||||
static void
|
||||
lkkbd_disconnect (struct serio *serio)
|
||||
{
|
||||
struct lkkbd *lk = serio_get_drvdata (serio);
|
||||
|
||||
input_get_device (lk->dev);
|
||||
input_unregister_device (lk->dev);
|
||||
serio_close (serio);
|
||||
serio_set_drvdata (serio, NULL);
|
||||
input_put_device (lk->dev);
|
||||
kfree (lk);
|
||||
}
|
||||
|
||||
static struct serio_device_id lkkbd_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_LKKBD,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
|
||||
|
||||
static struct serio_driver lkkbd_drv = {
|
||||
.driver = {
|
||||
.name = "lkkbd",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = lkkbd_serio_ids,
|
||||
.connect = lkkbd_connect,
|
||||
.disconnect = lkkbd_disconnect,
|
||||
.interrupt = lkkbd_interrupt,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for insering/removing us as a module.
|
||||
*/
|
||||
static int __init
|
||||
lkkbd_init (void)
|
||||
{
|
||||
return serio_register_driver(&lkkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit
|
||||
lkkbd_exit (void)
|
||||
{
|
||||
serio_unregister_driver(&lkkbd_drv);
|
||||
}
|
||||
|
||||
module_init (lkkbd_init);
|
||||
module_exit (lkkbd_exit);
|
||||
|
||||
309
drivers/input/keyboard/locomokbd.c
Normal file
309
drivers/input/keyboard/locomokbd.c
Normal file
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
* Copyright (c) 2005 John Lenz
|
||||
*
|
||||
* Based on from xtkbd.c
|
||||
*/
|
||||
|
||||
/*
|
||||
* LoCoMo keyboard driver for Linux/ARM
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#include <asm/hardware/locomo.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
|
||||
MODULE_DESCRIPTION("LoCoMo keyboard driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define LOCOMOKBD_NUMKEYS 128
|
||||
|
||||
#define KEY_ACTIVITY KEY_F16
|
||||
#define KEY_CONTACT KEY_F18
|
||||
#define KEY_CENTER KEY_F15
|
||||
|
||||
static unsigned char locomokbd_keycode[LOCOMOKBD_NUMKEYS] = {
|
||||
0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
|
||||
0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
|
||||
0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
|
||||
KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */
|
||||
KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */
|
||||
KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */
|
||||
0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */
|
||||
KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */
|
||||
0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */
|
||||
KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */
|
||||
KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */
|
||||
};
|
||||
|
||||
#define KB_ROWS 16
|
||||
#define KB_COLS 8
|
||||
#define KB_ROWMASK(r) (1 << (r))
|
||||
#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 )
|
||||
#define NR_SCANCODES 128
|
||||
|
||||
#define KB_DELAY 8
|
||||
#define SCAN_INTERVAL (HZ/10)
|
||||
#define LOCOMOKBD_PRESSED 1
|
||||
|
||||
struct locomokbd {
|
||||
unsigned char keycode[LOCOMOKBD_NUMKEYS];
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
|
||||
struct locomo_dev *ldev;
|
||||
unsigned long base;
|
||||
spinlock_t lock;
|
||||
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
||||
/* helper functions for reading the keyboard matrix */
|
||||
static inline void locomokbd_charge_all(unsigned long membase)
|
||||
{
|
||||
locomo_writel(0x00FF, membase + LOCOMO_KSC);
|
||||
}
|
||||
|
||||
static inline void locomokbd_activate_all(unsigned long membase)
|
||||
{
|
||||
unsigned long r;
|
||||
|
||||
locomo_writel(0, membase + LOCOMO_KSC);
|
||||
r = locomo_readl(membase + LOCOMO_KIC);
|
||||
r &= 0xFEFF;
|
||||
locomo_writel(r, membase + LOCOMO_KIC);
|
||||
}
|
||||
|
||||
static inline void locomokbd_activate_col(unsigned long membase, int col)
|
||||
{
|
||||
unsigned short nset;
|
||||
unsigned short nbset;
|
||||
|
||||
nset = 0xFF & ~(1 << col);
|
||||
nbset = (nset << 8) + nset;
|
||||
locomo_writel(nbset, membase + LOCOMO_KSC);
|
||||
}
|
||||
|
||||
static inline void locomokbd_reset_col(unsigned long membase, int col)
|
||||
{
|
||||
unsigned short nbset;
|
||||
|
||||
nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
|
||||
locomo_writel(nbset, membase + LOCOMO_KSC);
|
||||
}
|
||||
|
||||
/*
|
||||
* The LoCoMo keyboard only generates interrupts when a key is pressed.
|
||||
* So when a key is pressed, we enable a timer. This timer scans the
|
||||
* keyboard, and this is how we detect when the key is released.
|
||||
*/
|
||||
|
||||
/* Scan the hardware keyboard and push any changes up through the input layer */
|
||||
static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
|
||||
{
|
||||
unsigned int row, col, rowd, scancode;
|
||||
unsigned long flags;
|
||||
unsigned int num_pressed;
|
||||
unsigned long membase = locomokbd->base;
|
||||
|
||||
spin_lock_irqsave(&locomokbd->lock, flags);
|
||||
|
||||
locomokbd_charge_all(membase);
|
||||
|
||||
num_pressed = 0;
|
||||
for (col = 0; col < KB_COLS; col++) {
|
||||
|
||||
locomokbd_activate_col(membase, col);
|
||||
udelay(KB_DELAY);
|
||||
|
||||
rowd = ~locomo_readl(membase + LOCOMO_KIB);
|
||||
for (row = 0; row < KB_ROWS; row++) {
|
||||
scancode = SCANCODE(col, row);
|
||||
if (rowd & KB_ROWMASK(row)) {
|
||||
num_pressed += 1;
|
||||
input_report_key(locomokbd->input, locomokbd->keycode[scancode], 1);
|
||||
} else {
|
||||
input_report_key(locomokbd->input, locomokbd->keycode[scancode], 0);
|
||||
}
|
||||
}
|
||||
locomokbd_reset_col(membase, col);
|
||||
}
|
||||
locomokbd_activate_all(membase);
|
||||
|
||||
input_sync(locomokbd->input);
|
||||
|
||||
/* if any keys are pressed, enable the timer */
|
||||
if (num_pressed)
|
||||
mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
|
||||
|
||||
spin_unlock_irqrestore(&locomokbd->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* LoCoMo keyboard interrupt handler.
|
||||
*/
|
||||
static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct locomokbd *locomokbd = dev_id;
|
||||
/** wait chattering delay **/
|
||||
udelay(100);
|
||||
|
||||
locomokbd_scankeyboard(locomokbd);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* LoCoMo timer checking for released keys
|
||||
*/
|
||||
static void locomokbd_timer_callback(unsigned long data)
|
||||
{
|
||||
struct locomokbd *locomokbd = (struct locomokbd *) data;
|
||||
locomokbd_scankeyboard(locomokbd);
|
||||
}
|
||||
|
||||
static int locomokbd_probe(struct locomo_dev *dev)
|
||||
{
|
||||
struct locomokbd *locomokbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, err;
|
||||
|
||||
locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!locomokbd || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
/* try and claim memory region */
|
||||
if (!request_mem_region((unsigned long) dev->mapbase,
|
||||
dev->length,
|
||||
LOCOMO_DRIVER_NAME(dev))) {
|
||||
err = -EBUSY;
|
||||
printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
locomokbd->ldev = dev;
|
||||
locomo_set_drvdata(dev, locomokbd);
|
||||
|
||||
locomokbd->base = (unsigned long) dev->mapbase;
|
||||
|
||||
spin_lock_init(&locomokbd->lock);
|
||||
|
||||
init_timer(&locomokbd->timer);
|
||||
locomokbd->timer.function = locomokbd_timer_callback;
|
||||
locomokbd->timer.data = (unsigned long) locomokbd;
|
||||
|
||||
locomokbd->input = input_dev;
|
||||
strcpy(locomokbd->phys, "locomokbd/input0");
|
||||
|
||||
input_dev->name = "LoCoMo keyboard";
|
||||
input_dev->phys = locomokbd->phys;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->private = locomokbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
input_dev->keycode = locomokbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
|
||||
|
||||
memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
|
||||
for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
|
||||
set_bit(locomokbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
/* attempt to get the interrupt */
|
||||
err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
|
||||
if (err) {
|
||||
printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
err = input_register_device(locomokbd->input);
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(dev->irq[0], locomokbd);
|
||||
err_release_region:
|
||||
release_mem_region((unsigned long) dev->mapbase, dev->length);
|
||||
locomo_set_drvdata(dev, NULL);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(locomokbd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int locomokbd_remove(struct locomo_dev *dev)
|
||||
{
|
||||
struct locomokbd *locomokbd = locomo_get_drvdata(dev);
|
||||
|
||||
free_irq(dev->irq[0], locomokbd);
|
||||
|
||||
del_timer_sync(&locomokbd->timer);
|
||||
|
||||
input_unregister_device(locomokbd->input);
|
||||
locomo_set_drvdata(dev, NULL);
|
||||
|
||||
release_mem_region((unsigned long) dev->mapbase, dev->length);
|
||||
|
||||
kfree(locomokbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct locomo_driver keyboard_driver = {
|
||||
.drv = {
|
||||
.name = "locomokbd"
|
||||
},
|
||||
.devid = LOCOMO_DEVID_KEYBOARD,
|
||||
.probe = locomokbd_probe,
|
||||
.remove = locomokbd_remove,
|
||||
};
|
||||
|
||||
static int __init locomokbd_init(void)
|
||||
{
|
||||
return locomo_driver_register(&keyboard_driver);
|
||||
}
|
||||
|
||||
static void __exit locomokbd_exit(void)
|
||||
{
|
||||
locomo_driver_unregister(&keyboard_driver);
|
||||
}
|
||||
|
||||
module_init(locomokbd_init);
|
||||
module_exit(locomokbd_exit);
|
||||
181
drivers/input/keyboard/newtonkbd.c
Normal file
181
drivers/input/keyboard/newtonkbd.c
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (c) 2000 Justin Cormack
|
||||
*/
|
||||
|
||||
/*
|
||||
* Newton keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
|
||||
* Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
#define DRIVER_DESC "Newton keyboard driver"
|
||||
|
||||
MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define NKBD_KEY 0x7f
|
||||
#define NKBD_PRESS 0x80
|
||||
|
||||
static unsigned char nkbd_keycode[128] = {
|
||||
KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
|
||||
KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
|
||||
KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
|
||||
KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
|
||||
KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
|
||||
KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
|
||||
KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
|
||||
KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
|
||||
};
|
||||
|
||||
struct nkbd {
|
||||
unsigned char keycode[128];
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t nkbd_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct nkbd *nkbd = serio_get_drvdata(serio);
|
||||
|
||||
/* invalid scan codes are probably the init sequence, so we ignore them */
|
||||
if (nkbd->keycode[data & NKBD_KEY]) {
|
||||
input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
|
||||
input_sync(nkbd->dev);
|
||||
}
|
||||
|
||||
else if (data == 0xe7) /* end of init sequence */
|
||||
printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys);
|
||||
return IRQ_HANDLED;
|
||||
|
||||
}
|
||||
|
||||
static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct nkbd *nkbd;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!nkbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
nkbd->serio = serio;
|
||||
nkbd->dev = input_dev;
|
||||
snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
|
||||
memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
|
||||
|
||||
input_dev->name = "Newton Keyboard";
|
||||
input_dev->phys = nkbd->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_NEWTON;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->private = nkbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
input_dev->keycode = nkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode);
|
||||
for (i = 0; i < 128; i++)
|
||||
set_bit(nkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, nkbd);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(nkbd->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(nkbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void nkbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct nkbd *nkbd = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(nkbd->dev);
|
||||
kfree(nkbd);
|
||||
}
|
||||
|
||||
static struct serio_device_id nkbd_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_NEWTON,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
|
||||
|
||||
static struct serio_driver nkbd_drv = {
|
||||
.driver = {
|
||||
.name = "newtonkbd",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = nkbd_serio_ids,
|
||||
.interrupt = nkbd_interrupt,
|
||||
.connect = nkbd_connect,
|
||||
.disconnect = nkbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init nkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&nkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit nkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&nkbd_drv);
|
||||
}
|
||||
|
||||
module_init(nkbd_init);
|
||||
module_exit(nkbd_exit);
|
||||
491
drivers/input/keyboard/omap-keypad.c
Normal file
491
drivers/input/keyboard/omap-keypad.c
Normal file
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* linux/drivers/input/keyboard/omap-keypad.c
|
||||
*
|
||||
* OMAP Keypad Driver
|
||||
*
|
||||
* Copyright (C) 2003 Nokia Corporation
|
||||
* Written by Timo Ter<65>s <ext-timo.teras@nokia.com>
|
||||
*
|
||||
* Added support for H2 & H3 Keypad
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/arch/gpio.h>
|
||||
#include <asm/arch/keypad.h>
|
||||
#include <asm/arch/menelaus.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/arch/mux.h>
|
||||
|
||||
#undef NEW_BOARD_LEARNING_MODE
|
||||
|
||||
static void omap_kp_tasklet(unsigned long);
|
||||
static void omap_kp_timer(unsigned long);
|
||||
|
||||
static unsigned char keypad_state[8];
|
||||
static DEFINE_MUTEX(kp_enable_mutex);
|
||||
static int kp_enable = 1;
|
||||
static int kp_cur_group = -1;
|
||||
|
||||
struct omap_kp {
|
||||
struct input_dev *input;
|
||||
struct timer_list timer;
|
||||
int irq;
|
||||
unsigned int rows;
|
||||
unsigned int cols;
|
||||
unsigned long delay;
|
||||
unsigned int debounce;
|
||||
};
|
||||
|
||||
DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
|
||||
|
||||
static int *keymap;
|
||||
static unsigned int *row_gpios;
|
||||
static unsigned int *col_gpios;
|
||||
|
||||
#ifdef CONFIG_ARCH_OMAP2
|
||||
static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
|
||||
{
|
||||
int col;
|
||||
for (col = 0; col < omap_kp->cols; col++) {
|
||||
if (value & (1 << col))
|
||||
omap_set_gpio_dataout(col_gpios[col], 1);
|
||||
else
|
||||
omap_set_gpio_dataout(col_gpios[col], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 get_row_gpio_val(struct omap_kp *omap_kp)
|
||||
{
|
||||
int row;
|
||||
u8 value = 0;
|
||||
|
||||
for (row = 0; row < omap_kp->rows; row++) {
|
||||
if (omap_get_gpio_datain(row_gpios[row]))
|
||||
value |= (1 << row);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
#else
|
||||
#define set_col_gpio_val(x, y) do {} while (0)
|
||||
#define get_row_gpio_val(x) 0
|
||||
#endif
|
||||
|
||||
static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct omap_kp *omap_kp = dev_id;
|
||||
|
||||
/* disable keyboard interrupt and schedule for handling */
|
||||
if (cpu_is_omap24xx()) {
|
||||
int i;
|
||||
for (i = 0; i < omap_kp->rows; i++)
|
||||
disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
|
||||
} else
|
||||
/* disable keyboard interrupt and schedule for handling */
|
||||
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
|
||||
tasklet_schedule(&kp_tasklet);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void omap_kp_timer(unsigned long data)
|
||||
{
|
||||
tasklet_schedule(&kp_tasklet);
|
||||
}
|
||||
|
||||
static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
|
||||
{
|
||||
int col = 0;
|
||||
|
||||
/* read the keypad status */
|
||||
if (cpu_is_omap24xx()) {
|
||||
int i;
|
||||
for (i = 0; i < omap_kp->rows; i++)
|
||||
disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
|
||||
|
||||
/* read the keypad status */
|
||||
for (col = 0; col < omap_kp->cols; col++) {
|
||||
set_col_gpio_val(omap_kp, ~(1 << col));
|
||||
state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f;
|
||||
}
|
||||
set_col_gpio_val(omap_kp, 0);
|
||||
|
||||
} else {
|
||||
/* disable keyboard interrupt and schedule for handling */
|
||||
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
|
||||
/* read the keypad status */
|
||||
omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
|
||||
for (col = 0; col < omap_kp->cols; col++) {
|
||||
omap_writew(~(1 << col) & 0xff,
|
||||
OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
|
||||
|
||||
udelay(omap_kp->delay);
|
||||
|
||||
state[col] = ~omap_readw(OMAP_MPUIO_BASE +
|
||||
OMAP_MPUIO_KBR_LATCH) & 0xff;
|
||||
}
|
||||
omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
|
||||
udelay(2);
|
||||
}
|
||||
}
|
||||
|
||||
static inline int omap_kp_find_key(int col, int row)
|
||||
{
|
||||
int i, key;
|
||||
|
||||
key = KEY(col, row, 0);
|
||||
for (i = 0; keymap[i] != 0; i++)
|
||||
if ((keymap[i] & 0xff000000) == key)
|
||||
return keymap[i] & 0x00ffffff;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void omap_kp_tasklet(unsigned long data)
|
||||
{
|
||||
struct omap_kp *omap_kp_data = (struct omap_kp *) data;
|
||||
unsigned char new_state[8], changed, key_down = 0;
|
||||
int col, row;
|
||||
int spurious = 0;
|
||||
|
||||
/* check for any changes */
|
||||
omap_kp_scan_keypad(omap_kp_data, new_state);
|
||||
|
||||
/* check for changes and print those */
|
||||
for (col = 0; col < omap_kp_data->cols; col++) {
|
||||
changed = new_state[col] ^ keypad_state[col];
|
||||
key_down |= new_state[col];
|
||||
if (changed == 0)
|
||||
continue;
|
||||
|
||||
for (row = 0; row < omap_kp_data->rows; row++) {
|
||||
int key;
|
||||
if (!(changed & (1 << row)))
|
||||
continue;
|
||||
#ifdef NEW_BOARD_LEARNING_MODE
|
||||
printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
|
||||
row, (new_state[col] & (1 << row)) ?
|
||||
"pressed" : "released");
|
||||
#else
|
||||
key = omap_kp_find_key(col, row);
|
||||
if (key < 0) {
|
||||
printk(KERN_WARNING
|
||||
"omap-keypad: Spurious key event %d-%d\n",
|
||||
col, row);
|
||||
/* We scan again after a couple of seconds */
|
||||
spurious = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(kp_cur_group == (key & GROUP_MASK) ||
|
||||
kp_cur_group == -1))
|
||||
continue;
|
||||
|
||||
kp_cur_group = key & GROUP_MASK;
|
||||
input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
|
||||
new_state[col] & (1 << row));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
memcpy(keypad_state, new_state, sizeof(keypad_state));
|
||||
|
||||
if (key_down) {
|
||||
int delay = HZ / 20;
|
||||
/* some key is pressed - keep irq disabled and use timer
|
||||
* to poll the keypad */
|
||||
if (spurious)
|
||||
delay = 2 * HZ;
|
||||
mod_timer(&omap_kp_data->timer, jiffies + delay);
|
||||
} else {
|
||||
/* enable interrupts */
|
||||
if (cpu_is_omap24xx()) {
|
||||
int i;
|
||||
for (i = 0; i < omap_kp_data->rows; i++)
|
||||
enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
|
||||
} else {
|
||||
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
kp_cur_group = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t omap_kp_enable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%u\n", kp_enable);
|
||||
}
|
||||
|
||||
static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int state;
|
||||
|
||||
if (sscanf(buf, "%u", &state) != 1)
|
||||
return -EINVAL;
|
||||
|
||||
if ((state != 1) && (state != 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&kp_enable_mutex);
|
||||
if (state != kp_enable) {
|
||||
if (state)
|
||||
enable_irq(INT_KEYBOARD);
|
||||
else
|
||||
disable_irq(INT_KEYBOARD);
|
||||
kp_enable = state;
|
||||
}
|
||||
mutex_unlock(&kp_enable_mutex);
|
||||
|
||||
return strnlen(buf, count);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
/* Nothing yet */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_kp_resume(struct platform_device *dev)
|
||||
{
|
||||
/* Nothing yet */
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define omap_kp_suspend NULL
|
||||
#define omap_kp_resume NULL
|
||||
#endif
|
||||
|
||||
static int __init omap_kp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_kp *omap_kp;
|
||||
struct input_dev *input_dev;
|
||||
struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
|
||||
int i, col_idx, row_idx, irq_idx, ret;
|
||||
|
||||
if (!pdata->rows || !pdata->cols || !pdata->keymap) {
|
||||
printk(KERN_ERR "No rows, cols or keymap from pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!omap_kp || !input_dev) {
|
||||
kfree(omap_kp);
|
||||
input_free_device(input_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, omap_kp);
|
||||
|
||||
omap_kp->input = input_dev;
|
||||
|
||||
/* Disable the interrupt for the MPUIO keyboard */
|
||||
if (!cpu_is_omap24xx())
|
||||
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
|
||||
keymap = pdata->keymap;
|
||||
|
||||
if (pdata->rep)
|
||||
set_bit(EV_REP, input_dev->evbit);
|
||||
|
||||
if (pdata->delay)
|
||||
omap_kp->delay = pdata->delay;
|
||||
|
||||
if (pdata->row_gpios && pdata->col_gpios) {
|
||||
row_gpios = pdata->row_gpios;
|
||||
col_gpios = pdata->col_gpios;
|
||||
}
|
||||
|
||||
omap_kp->rows = pdata->rows;
|
||||
omap_kp->cols = pdata->cols;
|
||||
|
||||
if (cpu_is_omap24xx()) {
|
||||
/* Cols: outputs */
|
||||
for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) {
|
||||
if (omap_request_gpio(col_gpios[col_idx]) < 0) {
|
||||
printk(KERN_ERR "Failed to request"
|
||||
"GPIO%d for keypad\n",
|
||||
col_gpios[col_idx]);
|
||||
goto err1;
|
||||
}
|
||||
omap_set_gpio_direction(col_gpios[col_idx], 0);
|
||||
}
|
||||
/* Rows: inputs */
|
||||
for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) {
|
||||
if (omap_request_gpio(row_gpios[row_idx]) < 0) {
|
||||
printk(KERN_ERR "Failed to request"
|
||||
"GPIO%d for keypad\n",
|
||||
row_gpios[row_idx]);
|
||||
goto err2;
|
||||
}
|
||||
omap_set_gpio_direction(row_gpios[row_idx], 1);
|
||||
}
|
||||
}
|
||||
|
||||
setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
|
||||
|
||||
/* get the irq and init timer*/
|
||||
tasklet_enable(&kp_tasklet);
|
||||
kp_tasklet.data = (unsigned long) omap_kp;
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_enable);
|
||||
if (ret < 0)
|
||||
goto err2;
|
||||
|
||||
/* setup input device */
|
||||
set_bit(EV_KEY, input_dev->evbit);
|
||||
for (i = 0; keymap[i] != 0; i++)
|
||||
set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
|
||||
input_dev->name = "omap-keypad";
|
||||
input_dev->phys = "omap-keypad/input0";
|
||||
input_dev->cdev.dev = &pdev->dev;
|
||||
input_dev->private = omap_kp;
|
||||
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
|
||||
input_dev->keycode = keymap;
|
||||
input_dev->keycodesize = sizeof(unsigned int);
|
||||
input_dev->keycodemax = pdata->keymapsize;
|
||||
|
||||
ret = input_register_device(omap_kp->input);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR "Unable to register omap-keypad input device\n");
|
||||
goto err3;
|
||||
}
|
||||
|
||||
if (pdata->dbounce)
|
||||
omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
|
||||
|
||||
/* scan current status and enable interrupt */
|
||||
omap_kp_scan_keypad(omap_kp, keypad_state);
|
||||
if (!cpu_is_omap24xx()) {
|
||||
omap_kp->irq = platform_get_irq(pdev, 0);
|
||||
if (omap_kp->irq >= 0) {
|
||||
if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
|
||||
"omap-keypad", omap_kp) < 0)
|
||||
goto err4;
|
||||
}
|
||||
omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
} else {
|
||||
for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
|
||||
if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
|
||||
omap_kp_interrupt,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"omap-keypad", omap_kp) < 0)
|
||||
goto err5;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
err5:
|
||||
for (i = irq_idx-1; i >=0; i--)
|
||||
free_irq(row_gpios[i], 0);
|
||||
err4:
|
||||
input_unregister_device(omap_kp->input);
|
||||
input_dev = NULL;
|
||||
err3:
|
||||
device_remove_file(&pdev->dev, &dev_attr_enable);
|
||||
err2:
|
||||
for (i = row_idx-1; i >=0; i--)
|
||||
omap_free_gpio(row_gpios[i]);
|
||||
err1:
|
||||
for (i = col_idx-1; i >=0; i--)
|
||||
omap_free_gpio(col_gpios[i]);
|
||||
|
||||
kfree(omap_kp);
|
||||
input_free_device(input_dev);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int omap_kp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_kp *omap_kp = platform_get_drvdata(pdev);
|
||||
|
||||
/* disable keypad interrupt handling */
|
||||
tasklet_disable(&kp_tasklet);
|
||||
if (cpu_is_omap24xx()) {
|
||||
int i;
|
||||
for (i = 0; i < omap_kp->cols; i++)
|
||||
omap_free_gpio(col_gpios[i]);
|
||||
for (i = 0; i < omap_kp->rows; i++) {
|
||||
omap_free_gpio(row_gpios[i]);
|
||||
free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
|
||||
}
|
||||
} else {
|
||||
omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
|
||||
free_irq(omap_kp->irq, 0);
|
||||
}
|
||||
|
||||
del_timer_sync(&omap_kp->timer);
|
||||
tasklet_kill(&kp_tasklet);
|
||||
|
||||
/* unregister everything */
|
||||
input_unregister_device(omap_kp->input);
|
||||
|
||||
kfree(omap_kp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver omap_kp_driver = {
|
||||
.probe = omap_kp_probe,
|
||||
.remove = omap_kp_remove,
|
||||
.suspend = omap_kp_suspend,
|
||||
.resume = omap_kp_resume,
|
||||
.driver = {
|
||||
.name = "omap-keypad",
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit omap_kp_init(void)
|
||||
{
|
||||
printk(KERN_INFO "OMAP Keypad Driver\n");
|
||||
return platform_driver_register(&omap_kp_driver);
|
||||
}
|
||||
|
||||
static void __exit omap_kp_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&omap_kp_driver);
|
||||
}
|
||||
|
||||
module_init(omap_kp_init);
|
||||
module_exit(omap_kp_exit);
|
||||
|
||||
MODULE_AUTHOR("Timo Ter<65>s");
|
||||
MODULE_DESCRIPTION("OMAP Keypad Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
1066
drivers/input/keyboard/s3c-keypad-qisda.c
Normal file
1066
drivers/input/keyboard/s3c-keypad-qisda.c
Normal file
File diff suppressed because it is too large
Load Diff
333
drivers/input/keyboard/s3c-keypad.c
Normal file
333
drivers/input/keyboard/s3c-keypad.c
Normal file
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* drivers/input/keyboard/s3c-keypad.c
|
||||
* KeyPad Interface on S3C
|
||||
*
|
||||
* $Id: s3c-keypad.c,v 1.2 2008/02/29 01:57:09 wizardsj Exp $
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include <asm/hardware.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#include <asm/arch/regs-gpio.h>
|
||||
#include <asm/arch/regs-keypad.h>
|
||||
#include "s3c-keypad.h"
|
||||
|
||||
#undef S3C_KEYPAD_DEBUG
|
||||
//#define S3C_KEYPAD_DEBUG
|
||||
|
||||
#ifdef S3C_KEYPAD_DEBUG
|
||||
#define DPRINTK(x...) printk("S3C-Keypad " x)
|
||||
#else
|
||||
#define DPRINTK(x...) /* !!!! */
|
||||
#endif
|
||||
|
||||
#define DEVICE_NAME "s3c-keypad"
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
static struct timer_list keypad_timer;
|
||||
static int is_timer_on = FALSE;
|
||||
|
||||
|
||||
static int keypad_scan(u32 *keymask_low, u32 *keymask_high)
|
||||
{
|
||||
int i,j = 0;
|
||||
u32 cval,rval;
|
||||
|
||||
for (i=0; i<KEYPAD_COLUMNS; i++) {
|
||||
cval = readl(key_base+S3C_KEYIFCOL) | KEYCOL_DMASK;
|
||||
cval &= ~(1 << i);
|
||||
writel(cval, key_base+S3C_KEYIFCOL);
|
||||
|
||||
udelay(KEYPAD_DELAY);
|
||||
|
||||
rval = ~(readl(key_base+S3C_KEYIFROW)) & KEYROW_DMASK;
|
||||
|
||||
if ((i*KEYPAD_ROWS) < MAX_KEYMASK_NR)
|
||||
*keymask_low |= (rval << (i * KEYPAD_ROWS));
|
||||
else {
|
||||
*keymask_high |= (rval << (j * KEYPAD_ROWS));
|
||||
j = j +1;
|
||||
}
|
||||
}
|
||||
|
||||
writel(KEYIFCOL_CLEAR, key_base+S3C_KEYIFCOL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned prevmask_low = 0, prevmask_high = 0;
|
||||
|
||||
static void keypad_timer_handler(unsigned long data)
|
||||
{
|
||||
u32 keymask_low = 0, keymask_high = 0;
|
||||
u32 press_mask_low, press_mask_high;
|
||||
u32 release_mask_low, release_mask_high;
|
||||
int i;
|
||||
struct s3c_keypad *pdata = (struct s3c_keypad *)data;
|
||||
struct input_dev *dev = pdata->dev;
|
||||
|
||||
keypad_scan(&keymask_low, &keymask_high);
|
||||
|
||||
if (keymask_low != prevmask_low) {
|
||||
press_mask_low =
|
||||
((keymask_low ^ prevmask_low) & keymask_low);
|
||||
release_mask_low =
|
||||
((keymask_low ^ prevmask_low) & prevmask_low);
|
||||
|
||||
i = 0;
|
||||
while (press_mask_low) {
|
||||
if (press_mask_low & 1) {
|
||||
input_report_key(dev,pdata->keycodes[i],1);
|
||||
DPRINTK("low Pressed : %d\n",i);
|
||||
}
|
||||
press_mask_low >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (release_mask_low) {
|
||||
if (release_mask_low & 1) {
|
||||
input_report_key(dev,pdata->keycodes[i],0);
|
||||
DPRINTK("low Released : %d\n",i);
|
||||
}
|
||||
release_mask_low >>= 1;
|
||||
i++;
|
||||
}
|
||||
prevmask_low = keymask_low;
|
||||
}
|
||||
|
||||
if (keymask_high != prevmask_high) {
|
||||
press_mask_high =
|
||||
((keymask_high ^ prevmask_high) & keymask_high);
|
||||
release_mask_high =
|
||||
((keymask_high ^ prevmask_high) & prevmask_high);
|
||||
|
||||
i = 0;
|
||||
while (press_mask_high) {
|
||||
if (press_mask_high & 1) {
|
||||
input_report_key(dev,pdata->keycodes[i+MAX_KEYMASK_NR],1);
|
||||
DPRINTK("high Pressed : %d %d\n",pdata->keycodes[i+MAX_KEYMASK_NR],i);
|
||||
}
|
||||
press_mask_high >>= 1;
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (release_mask_high) {
|
||||
if (release_mask_high & 1) {
|
||||
input_report_key(dev,pdata->keycodes[i+MAX_KEYMASK_NR],0);
|
||||
DPRINTK("high Released : %d\n",pdata->keycodes[i+MAX_KEYMASK_NR]);
|
||||
}
|
||||
release_mask_high >>= 1;
|
||||
i++;
|
||||
}
|
||||
prevmask_high = keymask_high;
|
||||
}
|
||||
|
||||
if (keymask_low | keymask_high) {
|
||||
mod_timer(&keypad_timer,jiffies + HZ/10);
|
||||
} else {
|
||||
writel(KEYIFCON_INIT, key_base+S3C_KEYIFCON);
|
||||
is_timer_on = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t s3c_keypad_isr(int irq, void *dev_id)
|
||||
{
|
||||
|
||||
/* disable keypad interrupt and schedule for keypad timer handler */
|
||||
writel(readl(key_base+S3C_KEYIFCON) & ~(INT_F_EN|INT_R_EN), key_base+S3C_KEYIFCON);
|
||||
|
||||
keypad_timer.expires = jiffies + (HZ/100);
|
||||
if ( is_timer_on == FALSE) {
|
||||
add_timer(&keypad_timer);
|
||||
is_timer_on = TRUE;
|
||||
} else {
|
||||
mod_timer(&keypad_timer,keypad_timer.expires);
|
||||
}
|
||||
/*Clear the keypad interrupt status*/
|
||||
writel(KEYIFSTSCLR_CLEAR, key_base+S3C_KEYIFSTSCLR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init s3c_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct input_dev *input_dev;
|
||||
int key, code;
|
||||
struct s3c_keypad *s3c_keypad;
|
||||
|
||||
key_base = ioremap(S3C24XX_PA_KEYPAD, S3C24XX_SZ_KEYPAD);
|
||||
|
||||
if (key_base == NULL) {
|
||||
printk(KERN_ERR "Failed to remap register block\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
s3c_keypad = kzalloc(sizeof(struct s3c_keypad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
|
||||
if (!s3c_keypad || !input_dev) {
|
||||
kfree(s3c_keypad);
|
||||
input_free_device(input_dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, s3c_keypad);
|
||||
s3c_keypad->dev = input_dev;
|
||||
|
||||
writel(KEYIFCON_INIT, key_base+S3C_KEYIFCON);
|
||||
writel(KEYIFFC_DIV, key_base+S3C_KEYIFFC);
|
||||
|
||||
/* Set GPIO Port for keypad mode and pull-up disable*/
|
||||
writel(KEYPAD_ROW_GPIO_SET, KEYPAD_ROW_GPIOCON);
|
||||
writel(KEYPAD_COL_GPIO_SET, KEYPAD_COL_GPIOCON);
|
||||
|
||||
writel(KEYPAD_ROW_GPIOPUD_DIS, KEYPAD_ROW_GPIOPUD);
|
||||
writel(KEYPAD_COL_GPIOPUD_DIS, KEYPAD_COL_GPIOPUD);
|
||||
|
||||
writel(KEYIFCOL_CLEAR, key_base+S3C_KEYIFCOL);
|
||||
|
||||
/* create and register the input driver */
|
||||
set_bit(EV_KEY, input_dev->evbit);
|
||||
set_bit(EV_REP, input_dev->evbit);
|
||||
s3c_keypad->nr_rows = KEYPAD_ROWS;
|
||||
s3c_keypad->no_cols = KEYPAD_COLUMNS;
|
||||
s3c_keypad->total_keys = MAX_KEYPAD_NR;
|
||||
|
||||
for(key = 0; key < s3c_keypad->total_keys; key++){
|
||||
code = s3c_keypad->keycodes[key] = keypad_keycode[key];
|
||||
if(code<=0)
|
||||
continue;
|
||||
set_bit(code & KEY_MAX, input_dev->keybit);
|
||||
}
|
||||
|
||||
input_dev->name = DEVICE_NAME;
|
||||
input_dev->phys = "s3c-keypad/input0";
|
||||
input_dev->cdev.dev = &pdev->dev;
|
||||
input_dev->private = s3c_keypad;
|
||||
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0001;
|
||||
|
||||
input_dev->keycode = keypad_keycode;
|
||||
|
||||
input_register_device(input_dev);
|
||||
|
||||
|
||||
/* Scan timer init */
|
||||
init_timer(&keypad_timer);
|
||||
keypad_timer.function = keypad_timer_handler;
|
||||
keypad_timer.data = (unsigned long)s3c_keypad;
|
||||
|
||||
ret = request_irq(IRQ_KEYPAD, s3c_keypad_isr, SA_SAMPLE_RANDOM,
|
||||
DEVICE_NAME, (void *) pdev);
|
||||
if (ret) {
|
||||
printk("request_irq failed (IRQ_KEYPAD) !!!\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
keypad_timer.expires = jiffies + (HZ/10);
|
||||
|
||||
if (is_timer_on == FALSE) {
|
||||
add_timer(&keypad_timer);
|
||||
is_timer_on = TRUE;
|
||||
} else {
|
||||
mod_timer(&keypad_timer,keypad_timer.expires);
|
||||
}
|
||||
|
||||
printk( DEVICE_NAME " Initialized\n");
|
||||
return 0;
|
||||
|
||||
out:
|
||||
input_unregister_device(input_dev);
|
||||
kfree(s3c_keypad);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int s3c_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *input_dev = platform_get_drvdata(pdev);
|
||||
writel(KEYIFCON_CLEAR, key_base+S3C_KEYIFCON);
|
||||
|
||||
input_unregister_device(input_dev);
|
||||
kfree(pdev->dev.platform_data);
|
||||
free_irq(IRQ_KEYPAD, (void *) pdev);
|
||||
|
||||
del_timer(&keypad_timer);
|
||||
printk(DEVICE_NAME " Removed.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int s3c_keypad_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s3c_keypad_resume(struct platform_device *dev)
|
||||
{
|
||||
/* TODO */
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define s3c_keypad_suspend NULL
|
||||
#define s3c_keypad_resume NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct platform_driver s3c_keypad_driver = {
|
||||
.probe = s3c_keypad_probe,
|
||||
.remove = s3c_keypad_remove,
|
||||
.suspend = s3c_keypad_suspend,
|
||||
.resume = s3c_keypad_resume,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "s3c-keypad",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init s3c_keypad_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&s3c_keypad_driver);
|
||||
|
||||
if(!ret)
|
||||
printk(KERN_INFO "S3C Keypad Driver\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit s3c_keypad_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&s3c_keypad_driver);
|
||||
}
|
||||
|
||||
module_init(s3c_keypad_init);
|
||||
module_exit(s3c_keypad_exit);
|
||||
|
||||
MODULE_AUTHOR("Samsung");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("KeyPad interface for Samsung S3C");
|
||||
61
drivers/input/keyboard/s3c-keypad.h
Normal file
61
drivers/input/keyboard/s3c-keypad.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
*
|
||||
* $Id: s3c-keypad.h,v 1.5 2008/05/23 06:22:53 dark0351 Exp $
|
||||
*/
|
||||
|
||||
#ifndef _S3C_KEYPAD_H_
|
||||
#define _S3C_KEYPAD_H_
|
||||
|
||||
static void __iomem *key_base;
|
||||
|
||||
#if defined(CONFIG_CPU_S3C6400) || defined (CONFIG_CPU_S3C6410)
|
||||
#define KEYPAD_COLUMNS 8
|
||||
#define KEYPAD_ROWS 8
|
||||
#define MAX_KEYPAD_NR 64 /* 8*8 */
|
||||
#define MAX_KEYMASK_NR 32
|
||||
|
||||
int keypad_keycode[] = {
|
||||
1, 2, KEY_1, KEY_Q, KEY_A, 6, 7, KEY_LEFT,
|
||||
9, 10, KEY_2, KEY_W, KEY_S, KEY_Z, KEY_RIGHT, 16,
|
||||
17, 18, KEY_3, KEY_E, KEY_D, KEY_X, 23, KEY_UP,
|
||||
25, 26, KEY_4, KEY_R, KEY_F, KEY_C, 31, 32,
|
||||
33, KEY_O, KEY_5, KEY_T, KEY_G, KEY_V, KEY_DOWN, KEY_BACKSPACE,
|
||||
KEY_P, KEY_0, KEY_6, KEY_Y, KEY_H, KEY_SPACE, 47, 48,
|
||||
KEY_M, KEY_L, KEY_7, KEY_U, KEY_J, KEY_N, 55, KEY_ENTER,
|
||||
KEY_LEFTSHIFT, KEY_9, KEY_8, KEY_I, KEY_K, KEY_B, 63, KEY_COMMA
|
||||
};
|
||||
|
||||
|
||||
#define KEYPAD_DELAY (50)
|
||||
#define KEYPAD_ROW_GPIOCON S3C_GPK1CON
|
||||
#define KEYPAD_ROW_GPIOPUD S3C_GPKPU
|
||||
#define KEYPAD_COL_GPIOCON S3C_GPL0CON
|
||||
#define KEYPAD_COL_GPIOPUD S3C_GPLPU
|
||||
|
||||
#define KEYPAD_ROW_GPIO_SET \
|
||||
((readl(KEYPAD_ROW_GPIOCON) & ~(0xfffffff)) | 0x33333333)
|
||||
|
||||
#define KEYPAD_COL_GPIO_SET \
|
||||
((readl(KEYPAD_COL_GPIOCON) & ~(0xfffffff)) | 0x33333333)
|
||||
|
||||
#define KEYPAD_ROW_GPIOPUD_DIS (readl(KEYPAD_ROW_GPIOPUD) & ~(0xffff<<16))
|
||||
#define KEYPAD_COL_GPIOPUD_DIS (readl(KEYPAD_COL_GPIOPUD) & ~0xffff)
|
||||
|
||||
#define KEYIFCOL_CLEAR (readl(key_base+S3C_KEYIFCOL) & ~0xffff)
|
||||
#define KEYIFCON_CLEAR (readl(key_base+S3C_KEYIFCON) & ~0x1f)
|
||||
#define KEYIFFC_DIV (readl(key_base+S3C_KEYIFFC) | 0x1)
|
||||
|
||||
#else
|
||||
#error "Not supported S3C Configuration!!"
|
||||
#endif
|
||||
|
||||
struct s3c_keypad {
|
||||
struct input_dev *dev;
|
||||
int nr_rows;
|
||||
int no_cols;
|
||||
int total_keys;
|
||||
int keycodes[MAX_KEYPAD_NR];
|
||||
};
|
||||
|
||||
|
||||
#endif /* _S3C_KEYIF_H_ */
|
||||
495
drivers/input/keyboard/spitzkbd.c
Normal file
495
drivers/input/keyboard/spitzkbd.c
Normal file
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* Keyboard driver for Sharp Spitz, Borzoi and Akita (SL-Cxx00 series)
|
||||
*
|
||||
* Copyright (c) 2005 Richard Purdie
|
||||
*
|
||||
* Based on corgikbd.c
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/arch/spitz.h>
|
||||
#include <asm/arch/hardware.h>
|
||||
#include <asm/arch/pxa-regs.h>
|
||||
|
||||
#define KB_ROWS 7
|
||||
#define KB_COLS 11
|
||||
#define KB_ROWMASK(r) (1 << (r))
|
||||
#define SCANCODE(r,c) (((r)<<4) + (c) + 1)
|
||||
#define NR_SCANCODES ((KB_ROWS<<4) + 1)
|
||||
|
||||
#define SCAN_INTERVAL (50) /* ms */
|
||||
#define HINGE_SCAN_INTERVAL (150) /* ms */
|
||||
|
||||
#define SPITZ_KEY_CALENDER KEY_F1
|
||||
#define SPITZ_KEY_ADDRESS KEY_F2
|
||||
#define SPITZ_KEY_FN KEY_F3
|
||||
#define SPITZ_KEY_CANCEL KEY_F4
|
||||
#define SPITZ_KEY_EXOK KEY_F5
|
||||
#define SPITZ_KEY_EXCANCEL KEY_F6
|
||||
#define SPITZ_KEY_EXJOGDOWN KEY_F7
|
||||
#define SPITZ_KEY_EXJOGUP KEY_F8
|
||||
#define SPITZ_KEY_JAP1 KEY_LEFTALT
|
||||
#define SPITZ_KEY_JAP2 KEY_RIGHTCTRL
|
||||
#define SPITZ_KEY_SYNC KEY_F9
|
||||
#define SPITZ_KEY_MAIL KEY_F10
|
||||
#define SPITZ_KEY_OK KEY_F11
|
||||
#define SPITZ_KEY_MENU KEY_F12
|
||||
|
||||
static unsigned char spitzkbd_keycode[NR_SCANCODES] = {
|
||||
0, /* 0 */
|
||||
KEY_LEFTCTRL, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, SPITZ_KEY_EXOK, SPITZ_KEY_EXCANCEL, 0, 0, 0, 0, 0, /* 1-16 */
|
||||
0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, SPITZ_KEY_EXJOGDOWN, SPITZ_KEY_EXJOGUP, 0, 0, 0, 0, 0, /* 17-32 */
|
||||
KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
|
||||
SPITZ_KEY_ADDRESS, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
|
||||
SPITZ_KEY_CALENDER, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */
|
||||
SPITZ_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, SPITZ_KEY_FN, 0, 0, 0, 0, 0, /* 81-96 */
|
||||
KEY_SYSRQ, SPITZ_KEY_JAP1, SPITZ_KEY_JAP2, SPITZ_KEY_CANCEL, SPITZ_KEY_OK, SPITZ_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0 /* 97-112 */
|
||||
};
|
||||
|
||||
static int spitz_strobes[] = {
|
||||
SPITZ_GPIO_KEY_STROBE0,
|
||||
SPITZ_GPIO_KEY_STROBE1,
|
||||
SPITZ_GPIO_KEY_STROBE2,
|
||||
SPITZ_GPIO_KEY_STROBE3,
|
||||
SPITZ_GPIO_KEY_STROBE4,
|
||||
SPITZ_GPIO_KEY_STROBE5,
|
||||
SPITZ_GPIO_KEY_STROBE6,
|
||||
SPITZ_GPIO_KEY_STROBE7,
|
||||
SPITZ_GPIO_KEY_STROBE8,
|
||||
SPITZ_GPIO_KEY_STROBE9,
|
||||
SPITZ_GPIO_KEY_STROBE10,
|
||||
};
|
||||
|
||||
static int spitz_senses[] = {
|
||||
SPITZ_GPIO_KEY_SENSE0,
|
||||
SPITZ_GPIO_KEY_SENSE1,
|
||||
SPITZ_GPIO_KEY_SENSE2,
|
||||
SPITZ_GPIO_KEY_SENSE3,
|
||||
SPITZ_GPIO_KEY_SENSE4,
|
||||
SPITZ_GPIO_KEY_SENSE5,
|
||||
SPITZ_GPIO_KEY_SENSE6,
|
||||
};
|
||||
|
||||
struct spitzkbd {
|
||||
unsigned char keycode[ARRAY_SIZE(spitzkbd_keycode)];
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
|
||||
spinlock_t lock;
|
||||
struct timer_list timer;
|
||||
struct timer_list htimer;
|
||||
|
||||
unsigned int suspended;
|
||||
unsigned long suspend_jiffies;
|
||||
};
|
||||
|
||||
#define KB_DISCHARGE_DELAY 10
|
||||
#define KB_ACTIVATE_DELAY 10
|
||||
|
||||
/* Helper functions for reading the keyboard matrix
|
||||
* Note: We should really be using pxa_gpio_mode to alter GPDR but it
|
||||
* requires a function call per GPIO bit which is excessive
|
||||
* when we need to access 11 bits at once, multiple times.
|
||||
* These functions must be called within local_irq_save()/local_irq_restore()
|
||||
* or similar.
|
||||
*/
|
||||
static inline void spitzkbd_discharge_all(void)
|
||||
{
|
||||
/* STROBE All HiZ */
|
||||
GPCR0 = SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPCR1 = SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPCR2 = SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPCR3 = SPITZ_GPIO_G3_STROBE_BIT;
|
||||
GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
|
||||
}
|
||||
|
||||
static inline void spitzkbd_activate_all(void)
|
||||
{
|
||||
/* STROBE ALL -> High */
|
||||
GPSR0 = SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPDR0 |= SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPSR1 = SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPDR1 |= SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPSR2 = SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPDR2 |= SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPSR3 = SPITZ_GPIO_G3_STROBE_BIT;
|
||||
GPDR3 |= SPITZ_GPIO_G3_STROBE_BIT;
|
||||
|
||||
udelay(KB_DISCHARGE_DELAY);
|
||||
|
||||
/* Clear any interrupts we may have triggered when altering the GPIO lines */
|
||||
GEDR0 = SPITZ_GPIO_G0_SENSE_BIT;
|
||||
GEDR1 = SPITZ_GPIO_G1_SENSE_BIT;
|
||||
GEDR2 = SPITZ_GPIO_G2_SENSE_BIT;
|
||||
GEDR3 = SPITZ_GPIO_G3_SENSE_BIT;
|
||||
}
|
||||
|
||||
static inline void spitzkbd_activate_col(int col)
|
||||
{
|
||||
int gpio = spitz_strobes[col];
|
||||
GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
|
||||
GPSR(gpio) = GPIO_bit(gpio);
|
||||
GPDR(gpio) |= GPIO_bit(gpio);
|
||||
}
|
||||
|
||||
static inline void spitzkbd_reset_col(int col)
|
||||
{
|
||||
int gpio = spitz_strobes[col];
|
||||
GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
|
||||
GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
|
||||
GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
|
||||
GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
|
||||
GPCR(gpio) = GPIO_bit(gpio);
|
||||
GPDR(gpio) |= GPIO_bit(gpio);
|
||||
}
|
||||
|
||||
static inline int spitzkbd_get_row_status(int col)
|
||||
{
|
||||
return ((GPLR0 >> 12) & 0x01) | ((GPLR0 >> 16) & 0x02)
|
||||
| ((GPLR2 >> 25) & 0x04) | ((GPLR1 << 1) & 0x08)
|
||||
| ((GPLR1 >> 0) & 0x10) | ((GPLR1 >> 1) & 0x60);
|
||||
}
|
||||
|
||||
/*
|
||||
* The spitz keyboard only generates interrupts when a key is pressed.
|
||||
* When a key is pressed, we enable a timer which then scans the
|
||||
* keyboard to detect when the key is released.
|
||||
*/
|
||||
|
||||
/* Scan the hardware keyboard and push any changes up through the input layer */
|
||||
static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data)
|
||||
{
|
||||
unsigned int row, col, rowd;
|
||||
unsigned long flags;
|
||||
unsigned int num_pressed, pwrkey = ((GPLR(SPITZ_GPIO_ON_KEY) & GPIO_bit(SPITZ_GPIO_ON_KEY)) != 0);
|
||||
|
||||
if (spitzkbd_data->suspended)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&spitzkbd_data->lock, flags);
|
||||
|
||||
num_pressed = 0;
|
||||
for (col = 0; col < KB_COLS; col++) {
|
||||
/*
|
||||
* Discharge the output driver capacitatance
|
||||
* in the keyboard matrix. (Yes it is significant..)
|
||||
*/
|
||||
|
||||
spitzkbd_discharge_all();
|
||||
udelay(KB_DISCHARGE_DELAY);
|
||||
|
||||
spitzkbd_activate_col(col);
|
||||
udelay(KB_ACTIVATE_DELAY);
|
||||
|
||||
rowd = spitzkbd_get_row_status(col);
|
||||
for (row = 0; row < KB_ROWS; row++) {
|
||||
unsigned int scancode, pressed;
|
||||
|
||||
scancode = SCANCODE(row, col);
|
||||
pressed = rowd & KB_ROWMASK(row);
|
||||
|
||||
input_report_key(spitzkbd_data->input, spitzkbd_data->keycode[scancode], pressed);
|
||||
|
||||
if (pressed)
|
||||
num_pressed++;
|
||||
}
|
||||
spitzkbd_reset_col(col);
|
||||
}
|
||||
|
||||
spitzkbd_activate_all();
|
||||
|
||||
input_report_key(spitzkbd_data->input, SPITZ_KEY_SYNC, (GPLR(SPITZ_GPIO_SYNC) & GPIO_bit(SPITZ_GPIO_SYNC)) != 0 );
|
||||
input_report_key(spitzkbd_data->input, KEY_SUSPEND, pwrkey);
|
||||
|
||||
if (pwrkey && time_after(jiffies, spitzkbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
|
||||
input_event(spitzkbd_data->input, EV_PWR, KEY_SUSPEND, 1);
|
||||
spitzkbd_data->suspend_jiffies = jiffies;
|
||||
}
|
||||
|
||||
input_sync(spitzkbd_data->input);
|
||||
|
||||
/* if any keys are pressed, enable the timer */
|
||||
if (num_pressed)
|
||||
mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
|
||||
|
||||
spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* spitz keyboard interrupt handler.
|
||||
*/
|
||||
static irqreturn_t spitzkbd_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct spitzkbd *spitzkbd_data = dev_id;
|
||||
|
||||
if (!timer_pending(&spitzkbd_data->timer)) {
|
||||
/** wait chattering delay **/
|
||||
udelay(20);
|
||||
spitzkbd_scankeyboard(spitzkbd_data);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* spitz timer checking for released keys
|
||||
*/
|
||||
static void spitzkbd_timer_callback(unsigned long data)
|
||||
{
|
||||
struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
|
||||
|
||||
spitzkbd_scankeyboard(spitzkbd_data);
|
||||
}
|
||||
|
||||
/*
|
||||
* The hinge switches generate an interrupt.
|
||||
* We debounce the switches and pass them to the input system.
|
||||
*/
|
||||
|
||||
static irqreturn_t spitzkbd_hinge_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct spitzkbd *spitzkbd_data = dev_id;
|
||||
|
||||
if (!timer_pending(&spitzkbd_data->htimer))
|
||||
mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define HINGE_STABLE_COUNT 2
|
||||
static int sharpsl_hinge_state;
|
||||
static int hinge_count;
|
||||
|
||||
static void spitzkbd_hinge_timer(unsigned long data)
|
||||
{
|
||||
struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
|
||||
unsigned long state;
|
||||
unsigned long flags;
|
||||
|
||||
state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB));
|
||||
state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT));
|
||||
if (state != sharpsl_hinge_state) {
|
||||
hinge_count = 0;
|
||||
sharpsl_hinge_state = state;
|
||||
} else if (hinge_count < HINGE_STABLE_COUNT) {
|
||||
hinge_count++;
|
||||
}
|
||||
|
||||
if (hinge_count >= HINGE_STABLE_COUNT) {
|
||||
spin_lock_irqsave(&spitzkbd_data->lock, flags);
|
||||
|
||||
input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0));
|
||||
input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0));
|
||||
input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0));
|
||||
input_sync(spitzkbd_data->input);
|
||||
|
||||
spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
|
||||
} else {
|
||||
mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int spitzkbd_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
int i;
|
||||
struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
|
||||
spitzkbd->suspended = 1;
|
||||
|
||||
/* Set Strobe lines as inputs - *except* strobe line 0 leave this
|
||||
enabled so we can detect a power button press for resume */
|
||||
for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(spitz_strobes[i] | GPIO_IN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spitzkbd_resume(struct platform_device *dev)
|
||||
{
|
||||
int i;
|
||||
struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH);
|
||||
|
||||
/* Upon resume, ignore the suspend key for a short while */
|
||||
spitzkbd->suspend_jiffies = jiffies;
|
||||
spitzkbd->suspended = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define spitzkbd_suspend NULL
|
||||
#define spitzkbd_resume NULL
|
||||
#endif
|
||||
|
||||
static int __init spitzkbd_probe(struct platform_device *dev)
|
||||
{
|
||||
struct spitzkbd *spitzkbd;
|
||||
struct input_dev *input_dev;
|
||||
int i, err = -ENOMEM;
|
||||
|
||||
spitzkbd = kzalloc(sizeof(struct spitzkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!spitzkbd || !input_dev)
|
||||
goto fail;
|
||||
|
||||
platform_set_drvdata(dev, spitzkbd);
|
||||
strcpy(spitzkbd->phys, "spitzkbd/input0");
|
||||
|
||||
spin_lock_init(&spitzkbd->lock);
|
||||
|
||||
/* Init Keyboard rescan timer */
|
||||
init_timer(&spitzkbd->timer);
|
||||
spitzkbd->timer.function = spitzkbd_timer_callback;
|
||||
spitzkbd->timer.data = (unsigned long) spitzkbd;
|
||||
|
||||
/* Init Hinge Timer */
|
||||
init_timer(&spitzkbd->htimer);
|
||||
spitzkbd->htimer.function = spitzkbd_hinge_timer;
|
||||
spitzkbd->htimer.data = (unsigned long) spitzkbd;
|
||||
|
||||
spitzkbd->suspend_jiffies = jiffies;
|
||||
|
||||
spitzkbd->input = input_dev;
|
||||
|
||||
input_dev->private = spitzkbd;
|
||||
input_dev->name = "Spitz Keyboard";
|
||||
input_dev->phys = spitzkbd->phys;
|
||||
input_dev->cdev.dev = &dev->dev;
|
||||
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
|
||||
input_dev->keycode = spitzkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(spitzkbd_keycode);
|
||||
|
||||
memcpy(spitzkbd->keycode, spitzkbd_keycode, sizeof(spitzkbd->keycode));
|
||||
for (i = 0; i < ARRAY_SIZE(spitzkbd_keycode); i++)
|
||||
set_bit(spitzkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
set_bit(SW_LID, input_dev->swbit);
|
||||
set_bit(SW_TABLET_MODE, input_dev->swbit);
|
||||
set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
||||
mod_timer(&spitzkbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
|
||||
|
||||
/* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
|
||||
for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) {
|
||||
pxa_gpio_mode(spitz_senses[i] | GPIO_IN);
|
||||
if (request_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd_interrupt,
|
||||
IRQF_DISABLED|IRQF_TRIGGER_RISING,
|
||||
"Spitzkbd Sense", spitzkbd))
|
||||
printk(KERN_WARNING "spitzkbd: Can't get Sense IRQ: %d!\n", i);
|
||||
}
|
||||
|
||||
/* Set Strobe lines as outputs - set high */
|
||||
for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++)
|
||||
pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH);
|
||||
|
||||
pxa_gpio_mode(SPITZ_GPIO_SYNC | GPIO_IN);
|
||||
pxa_gpio_mode(SPITZ_GPIO_ON_KEY | GPIO_IN);
|
||||
pxa_gpio_mode(SPITZ_GPIO_SWA | GPIO_IN);
|
||||
pxa_gpio_mode(SPITZ_GPIO_SWB | GPIO_IN);
|
||||
|
||||
request_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd_interrupt,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"Spitzkbd Sync", spitzkbd);
|
||||
request_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd_interrupt,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"Spitzkbd PwrOn", spitzkbd);
|
||||
request_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd_hinge_isr,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"Spitzkbd SWA", spitzkbd);
|
||||
request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"Spitzkbd SWB", spitzkbd);
|
||||
request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
|
||||
IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"Spitzkbd HP", spitzkbd);
|
||||
|
||||
return 0;
|
||||
|
||||
fail: input_free_device(input_dev);
|
||||
kfree(spitzkbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int spitzkbd_remove(struct platform_device *dev)
|
||||
{
|
||||
int i;
|
||||
struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
|
||||
|
||||
for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++)
|
||||
free_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd);
|
||||
|
||||
free_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd);
|
||||
free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd);
|
||||
free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd);
|
||||
free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd);
|
||||
free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd);
|
||||
|
||||
del_timer_sync(&spitzkbd->htimer);
|
||||
del_timer_sync(&spitzkbd->timer);
|
||||
|
||||
input_unregister_device(spitzkbd->input);
|
||||
|
||||
kfree(spitzkbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver spitzkbd_driver = {
|
||||
.probe = spitzkbd_probe,
|
||||
.remove = spitzkbd_remove,
|
||||
.suspend = spitzkbd_suspend,
|
||||
.resume = spitzkbd_resume,
|
||||
.driver = {
|
||||
.name = "spitz-keyboard",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __devinit spitzkbd_init(void)
|
||||
{
|
||||
return platform_driver_register(&spitzkbd_driver);
|
||||
}
|
||||
|
||||
static void __exit spitzkbd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&spitzkbd_driver);
|
||||
}
|
||||
|
||||
module_init(spitzkbd_init);
|
||||
module_exit(spitzkbd_exit);
|
||||
|
||||
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
|
||||
MODULE_DESCRIPTION("Spitz Keyboard Driver");
|
||||
MODULE_LICENSE("GPLv2");
|
||||
185
drivers/input/keyboard/stowaway.c
Normal file
185
drivers/input/keyboard/stowaway.c
Normal file
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Stowaway keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Marek Vasut
|
||||
*
|
||||
* Based on Newton keyboard driver for Linux
|
||||
* by Justin Cormack
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail:
|
||||
* Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
#define DRIVER_DESC "Stowaway keyboard driver"
|
||||
|
||||
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define SKBD_KEY_MASK 0x7f
|
||||
#define SKBD_RELEASE 0x80
|
||||
|
||||
static unsigned char skbd_keycode[128] = {
|
||||
KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7,
|
||||
0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE,
|
||||
KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE,
|
||||
KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N,
|
||||
KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC,
|
||||
KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P,
|
||||
KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
|
||||
KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT,
|
||||
KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0,
|
||||
KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
|
||||
KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0
|
||||
};
|
||||
|
||||
struct skbd {
|
||||
unsigned char keycode[128];
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct skbd *skbd = serio_get_drvdata(serio);
|
||||
struct input_dev *dev = skbd->dev;
|
||||
|
||||
if (skbd->keycode[data & SKBD_KEY_MASK]) {
|
||||
input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK],
|
||||
!(data & SKBD_RELEASE));
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int skbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct skbd *skbd;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!skbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
skbd->serio = serio;
|
||||
skbd->dev = input_dev;
|
||||
snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys);
|
||||
memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode));
|
||||
|
||||
input_dev->name = "Stowaway Keyboard";
|
||||
input_dev->phys = skbd->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_STOWAWAY;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->private = skbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
input_dev->keycode = skbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(skbd_keycode);
|
||||
for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++)
|
||||
set_bit(skbd_keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, skbd);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(skbd->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(skbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void skbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct skbd *skbd = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(skbd->dev);
|
||||
kfree(skbd);
|
||||
}
|
||||
|
||||
static struct serio_device_id skbd_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_STOWAWAY,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, skbd_serio_ids);
|
||||
|
||||
static struct serio_driver skbd_drv = {
|
||||
.driver = {
|
||||
.name = "stowaway",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = skbd_serio_ids,
|
||||
.interrupt = skbd_interrupt,
|
||||
.connect = skbd_connect,
|
||||
.disconnect = skbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init skbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&skbd_drv);
|
||||
}
|
||||
|
||||
static void __exit skbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&skbd_drv);
|
||||
}
|
||||
|
||||
module_init(skbd_init);
|
||||
module_exit(skbd_exit);
|
||||
364
drivers/input/keyboard/sunkbd.c
Normal file
364
drivers/input/keyboard/sunkbd.c
Normal file
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* $Id: sunkbd.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* Sun keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define DRIVER_DESC "Sun keyboard driver"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static unsigned char sunkbd_keycode[128] = {
|
||||
0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
|
||||
65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
|
||||
4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
|
||||
116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
|
||||
26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
|
||||
104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
|
||||
79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
|
||||
};
|
||||
|
||||
#define SUNKBD_CMD_RESET 0x1
|
||||
#define SUNKBD_CMD_BELLON 0x2
|
||||
#define SUNKBD_CMD_BELLOFF 0x3
|
||||
#define SUNKBD_CMD_CLICK 0xa
|
||||
#define SUNKBD_CMD_NOCLICK 0xb
|
||||
#define SUNKBD_CMD_SETLED 0xe
|
||||
#define SUNKBD_CMD_LAYOUT 0xf
|
||||
|
||||
#define SUNKBD_RET_RESET 0xff
|
||||
#define SUNKBD_RET_ALLUP 0x7f
|
||||
#define SUNKBD_RET_LAYOUT 0xfe
|
||||
|
||||
#define SUNKBD_LAYOUT_5_MASK 0x20
|
||||
#define SUNKBD_RELEASE 0x80
|
||||
#define SUNKBD_KEY 0x7f
|
||||
|
||||
/*
|
||||
* Per-keyboard data.
|
||||
*/
|
||||
|
||||
struct sunkbd {
|
||||
unsigned char keycode[128];
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
struct work_struct tq;
|
||||
wait_queue_head_t wait;
|
||||
char name[64];
|
||||
char phys[32];
|
||||
char type;
|
||||
unsigned char enabled;
|
||||
volatile s8 reset;
|
||||
volatile s8 layout;
|
||||
};
|
||||
|
||||
/*
|
||||
* sunkbd_interrupt() is called by the low level driver when a character
|
||||
* is received.
|
||||
*/
|
||||
|
||||
static irqreturn_t sunkbd_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct sunkbd* sunkbd = serio_get_drvdata(serio);
|
||||
|
||||
if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
|
||||
sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
|
||||
wake_up_interruptible(&sunkbd->wait);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sunkbd->layout == -1) {
|
||||
sunkbd->layout = data;
|
||||
wake_up_interruptible(&sunkbd->wait);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (data) {
|
||||
|
||||
case SUNKBD_RET_RESET:
|
||||
schedule_work(&sunkbd->tq);
|
||||
sunkbd->reset = -1;
|
||||
break;
|
||||
|
||||
case SUNKBD_RET_LAYOUT:
|
||||
sunkbd->layout = -1;
|
||||
break;
|
||||
|
||||
case SUNKBD_RET_ALLUP: /* All keys released */
|
||||
break;
|
||||
|
||||
default:
|
||||
if (!sunkbd->enabled)
|
||||
break;
|
||||
|
||||
if (sunkbd->keycode[data & SUNKBD_KEY]) {
|
||||
input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
|
||||
input_sync(sunkbd->dev);
|
||||
} else {
|
||||
printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
|
||||
data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
|
||||
}
|
||||
}
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* sunkbd_event() handles events from the input module.
|
||||
*/
|
||||
|
||||
static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
|
||||
{
|
||||
struct sunkbd *sunkbd = dev->private;
|
||||
|
||||
switch (type) {
|
||||
|
||||
case EV_LED:
|
||||
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
|
||||
sunkbd->serio->write(sunkbd->serio,
|
||||
(!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
|
||||
(!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
|
||||
return 0;
|
||||
|
||||
case EV_SND:
|
||||
|
||||
switch (code) {
|
||||
|
||||
case SND_CLICK:
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
|
||||
return 0;
|
||||
|
||||
case SND_BELL:
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* sunkbd_initialize() checks for a Sun keyboard attached, and determines
|
||||
* its type.
|
||||
*/
|
||||
|
||||
static int sunkbd_initialize(struct sunkbd *sunkbd)
|
||||
{
|
||||
sunkbd->reset = -2;
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
|
||||
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
|
||||
if (sunkbd->reset < 0)
|
||||
return -1;
|
||||
|
||||
sunkbd->type = sunkbd->reset;
|
||||
|
||||
if (sunkbd->type == 4) { /* Type 4 keyboard */
|
||||
sunkbd->layout = -2;
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
|
||||
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
|
||||
if (sunkbd->layout < 0) return -1;
|
||||
if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* sunkbd_reinit() sets leds and beeps to a state the computer remembers they
|
||||
* were in.
|
||||
*/
|
||||
|
||||
static void sunkbd_reinit(struct work_struct *work)
|
||||
{
|
||||
struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
|
||||
|
||||
wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
|
||||
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
|
||||
sunkbd->serio->write(sunkbd->serio,
|
||||
(!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
|
||||
(!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led));
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
|
||||
sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
|
||||
}
|
||||
|
||||
static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
|
||||
{
|
||||
serio_pause_rx(sunkbd->serio);
|
||||
sunkbd->enabled = enable;
|
||||
serio_continue_rx(sunkbd->serio);
|
||||
}
|
||||
|
||||
/*
|
||||
* sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
|
||||
*/
|
||||
|
||||
static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct sunkbd *sunkbd;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!sunkbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
sunkbd->serio = serio;
|
||||
sunkbd->dev = input_dev;
|
||||
init_waitqueue_head(&sunkbd->wait);
|
||||
INIT_WORK(&sunkbd->tq, sunkbd_reinit);
|
||||
snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
|
||||
|
||||
serio_set_drvdata(serio, sunkbd);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
if (sunkbd_initialize(sunkbd) < 0) {
|
||||
err = -ENODEV;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
|
||||
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
|
||||
|
||||
input_dev->name = sunkbd->name;
|
||||
input_dev->phys = sunkbd->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_SUNKBD;
|
||||
input_dev->id.product = sunkbd->type;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->private = sunkbd;
|
||||
input_dev->event = sunkbd_event;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_SND) | BIT(EV_REP);
|
||||
input_dev->ledbit[0] = BIT(LED_CAPSL) | BIT(LED_COMPOSE) | BIT(LED_SCROLLL) | BIT(LED_NUML);
|
||||
input_dev->sndbit[0] = BIT(SND_CLICK) | BIT(SND_BELL);
|
||||
|
||||
input_dev->keycode = sunkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
|
||||
for (i = 0; i < 128; i++)
|
||||
set_bit(sunkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
sunkbd_enable(sunkbd, 1);
|
||||
|
||||
err = input_register_device(sunkbd->dev);
|
||||
if (err)
|
||||
goto fail4;
|
||||
|
||||
return 0;
|
||||
|
||||
fail4: sunkbd_enable(sunkbd, 0);
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(sunkbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* sunkbd_disconnect() unregisters and closes behind us.
|
||||
*/
|
||||
|
||||
static void sunkbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct sunkbd *sunkbd = serio_get_drvdata(serio);
|
||||
|
||||
sunkbd_enable(sunkbd, 0);
|
||||
input_unregister_device(sunkbd->dev);
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
kfree(sunkbd);
|
||||
}
|
||||
|
||||
static struct serio_device_id sunkbd_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_SUNKBD,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_UNKNOWN, /* sunkbd does probe */
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
|
||||
|
||||
static struct serio_driver sunkbd_drv = {
|
||||
.driver = {
|
||||
.name = "sunkbd",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = sunkbd_serio_ids,
|
||||
.interrupt = sunkbd_interrupt,
|
||||
.connect = sunkbd_connect,
|
||||
.disconnect = sunkbd_disconnect,
|
||||
};
|
||||
|
||||
/*
|
||||
* The functions for insering/removing us as a module.
|
||||
*/
|
||||
|
||||
static int __init sunkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&sunkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit sunkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&sunkbd_drv);
|
||||
}
|
||||
|
||||
module_init(sunkbd_init);
|
||||
module_exit(sunkbd_exit);
|
||||
186
drivers/input/keyboard/xtkbd.c
Normal file
186
drivers/input/keyboard/xtkbd.c
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* $Id: xtkbd.c,v 1.1.1.1 2007/06/12 07:27:09 eyryu Exp $
|
||||
*
|
||||
* Copyright (c) 1999-2001 Vojtech Pavlik
|
||||
*/
|
||||
|
||||
/*
|
||||
* XT keyboard driver for Linux
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Should you need to contact me, the author, you can do so either by
|
||||
* e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
|
||||
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
#define DRIVER_DESC "XT keyboard driver"
|
||||
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define XTKBD_EMUL0 0xe0
|
||||
#define XTKBD_EMUL1 0xe1
|
||||
#define XTKBD_KEY 0x7f
|
||||
#define XTKBD_RELEASE 0x80
|
||||
|
||||
static unsigned char xtkbd_keycode[256] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
||||
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
|
||||
80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
|
||||
106
|
||||
};
|
||||
|
||||
struct xtkbd {
|
||||
unsigned char keycode[256];
|
||||
struct input_dev *dev;
|
||||
struct serio *serio;
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static irqreturn_t xtkbd_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct xtkbd *xtkbd = serio_get_drvdata(serio);
|
||||
|
||||
switch (data) {
|
||||
case XTKBD_EMUL0:
|
||||
case XTKBD_EMUL1:
|
||||
break;
|
||||
default:
|
||||
|
||||
if (xtkbd->keycode[data & XTKBD_KEY]) {
|
||||
input_report_key(xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
|
||||
input_sync(xtkbd->dev);
|
||||
} else {
|
||||
printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
|
||||
data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
|
||||
}
|
||||
}
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct xtkbd *xtkbd;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
int i;
|
||||
|
||||
xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!xtkbd || !input_dev)
|
||||
goto fail1;
|
||||
|
||||
xtkbd->serio = serio;
|
||||
xtkbd->dev = input_dev;
|
||||
snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys);
|
||||
memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
|
||||
|
||||
input_dev->name = "XT Keyboard";
|
||||
input_dev->phys = xtkbd->phys;
|
||||
input_dev->id.bustype = BUS_XTKBD;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->cdev.dev = &serio->dev;
|
||||
input_dev->private = xtkbd;
|
||||
|
||||
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
|
||||
input_dev->keycode = xtkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(xtkbd_keycode);
|
||||
|
||||
for (i = 0; i < 255; i++)
|
||||
set_bit(xtkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, xtkbd);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = input_register_device(xtkbd->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3: serio_close(serio);
|
||||
fail2: serio_set_drvdata(serio, NULL);
|
||||
fail1: input_free_device(input_dev);
|
||||
kfree(xtkbd);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void xtkbd_disconnect(struct serio *serio)
|
||||
{
|
||||
struct xtkbd *xtkbd = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(xtkbd->dev);
|
||||
kfree(xtkbd);
|
||||
}
|
||||
|
||||
static struct serio_device_id xtkbd_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_XT,
|
||||
.proto = SERIO_ANY,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids);
|
||||
|
||||
static struct serio_driver xtkbd_drv = {
|
||||
.driver = {
|
||||
.name = "xtkbd",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = xtkbd_serio_ids,
|
||||
.interrupt = xtkbd_interrupt,
|
||||
.connect = xtkbd_connect,
|
||||
.disconnect = xtkbd_disconnect,
|
||||
};
|
||||
|
||||
static int __init xtkbd_init(void)
|
||||
{
|
||||
return serio_register_driver(&xtkbd_drv);
|
||||
}
|
||||
|
||||
static void __exit xtkbd_exit(void)
|
||||
{
|
||||
serio_unregister_driver(&xtkbd_drv);
|
||||
}
|
||||
|
||||
module_init(xtkbd_init);
|
||||
module_exit(xtkbd_exit);
|
||||
Reference in New Issue
Block a user