Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

View 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

View 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 ]

View 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");

View 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);

File diff suppressed because it is too large Load Diff

View 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");

View 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");

View 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);

View 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);

View 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

View 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);

View 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);

View 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);

View 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");

File diff suppressed because it is too large Load Diff

View 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");

View 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_ */

View 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");

View 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);

View 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);

View 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);