Commit b65e5bd5 authored by Vojtech Pavlik's avatar Vojtech Pavlik

Add new serio modules for PS/2 AUX/KBD.

parent 07b8fb25
......@@ -12,6 +12,18 @@ CONFIG_SERIO
The module will be called serio.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
CONFIG_SERIO_I8042
i8042 is the chip over which the standard AT keyboard and PS/2
mouse are connected to the computer. If you use these devices,
you'll need to say Y here.
If unsure, say Y.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called i8042.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
CONFIG_SERIO_SERPORT
Say Y here if you plan to use an input device (mouse, joystick,
tablet, 6dof) that communicates over the RS232 serial (COM) port.
......@@ -24,3 +36,38 @@ CONFIG_SERIO_SERPORT
inserted in and removed from the running kernel whenever you want).
The module will be called serport.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
CONFIG_SERIO_CT82C710
Say Y here if you have a Texas Instruments TravelMate notebook
equipped with the ct82c710 chip and want to use a mouse connected
to the "QuickPort".
If unsure, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called ct82c710.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
CONFIG_SERIO_PARKBD
Say Y here if you built a simple parallel port adapter to attach
an additional AT keyboard, XT keyboard or PS/2 mouse.
More information is available: <file:Documentation/input/input.txt>
If unsure, say N.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called parkbd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
CONFIG_SERIO_ACORN
Say Y here if you have the Acorn RiscPC and want to use an AT
keyboard connected to its keyboard controller.
This driver is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called rpckbd.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
......@@ -4,4 +4,16 @@
tristate 'Serial i/o support' CONFIG_SERIO
dep_tristate ' i8042 PC Keyboard controller' CONFIG_SERIO_I8042 $CONFIG_SERIO $CONFIG_ISA
if [ "$CONFIG_INPUT_I8042" != "n" ]; then
hex ' Register Base Address' CONFIG_I8042_REG_BASE 60
int ' PS/2 Keyboard IRQ' CONFIG_I8042_KBD_IRQ 1
int ' PS/2 AUX IRQ' CONFIG_I8042_AUX_IRQ 12
fi
dep_tristate ' Serial port line discipline' CONFIG_SERIO_SERPORT $CONFIG_SERIO
dep_tristate ' ct82c710 Aux port controller' CONFIG_SERIO_CT82C710 $CONFIG_SERIO $CONFIG_ISA
dep_tristate ' Parallel port keyboard adapter' CONFIG_SERIO_PARKBD $CONFIG_SERIO $CONFIG_PARPORT
if [ "$CONFIG_ARCH_ACORN" = "y" ]; then
dep_tristate ' Acorn RiscPC keyboard controller' CONFIG_SERIO_ACORN $CONFIG_SERIO
fi
......@@ -9,7 +9,11 @@ export-objs := serio.o
# Each configuration option enables a list of files.
obj-$(CONFIG_SERIO) += serio.o
obj-$(CONFIG_SERIO_I8042) += i8042.o
obj-$(CONFIG_SERIO_PARKBD) += parkbd.o
obj-$(CONFIG_SERIO_SERPORT) += serport.o
obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o
obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o
# The global Rules.make.
......
/*
* $Id: ct82c710.c,v 1.11 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* 82C710 C&T mouse port chip 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 <asm/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/errno.h>
#include <linux/sched.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("82C710 C&T mouse port chip driver");
MODULE_LICENSE("GPL");
static char ct82c710_name[] = "C&T 82c710 mouse port";
static char ct82c710_phys[16];
/*
* ct82c710 interface
*/
#define CT82C710_DEV_IDLE 0x01 /* Device Idle */
#define CT82C710_RX_FULL 0x02 /* Device Char received */
#define CT82C710_TX_IDLE 0x04 /* Device XMIT Idle */
#define CT82C710_RESET 0x08 /* Device Reset */
#define CT82C710_INTS_ON 0x10 /* Device Interrupt On */
#define CT82C710_ERROR_FLAG 0x20 /* Device Error */
#define CT82C710_CLEAR 0x40 /* Device Clear */
#define CT82C710_ENABLE 0x80 /* Device Enable */
#define CT82C710_IRQ 12
static int ct82c710_data = 0;
static int ct82c710_status = 0;
static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs);
/*
* Wait for device to send output char and flush any input char.
*/
static int ct82c170_wait(void)
{
int timeout = 60000;
while ((inb(ct82c710_status) & (CT82C710_RX_FULL | CT82C710_TX_IDLE | CT82C710_DEV_IDLE))
!= (CT82C710_DEV_IDLE | CT82C710_TX_IDLE) && timeout) {
if (inb_p(ct82c710_status) & CT82C710_RX_FULL) inb_p(ct82c710_data);
udelay(1);
timeout--;
}
return !timeout;
}
static void ct82c710_close(struct serio *serio)
{
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
outb_p(inb_p(ct82c710_status) & ~(CT82C710_ENABLE | CT82C710_INTS_ON), ct82c710_status);
if (ct82c170_wait())
printk(KERN_WARNING "ct82c710.c: Device busy in close()\n");
free_irq(CT82C710_IRQ, NULL);
}
static int ct82c710_open(struct serio *serio)
{
unsigned char status;
if (request_irq(CT82C710_IRQ, ct82c710_interrupt, 0, "ct82c710", NULL))
return -1;
status = inb_p(ct82c710_status);
status |= (CT82C710_ENABLE | CT82C710_RESET);
outb_p(status, ct82c710_status);
status &= ~(CT82C710_RESET);
outb_p(status, ct82c710_status);
status |= CT82C710_INTS_ON;
outb_p(status, ct82c710_status); /* Enable interrupts */
while (ct82c170_wait()) {
printk(KERN_ERR "ct82c710: Device busy in open()\n");
status &= ~(CT82C710_ENABLE | CT82C710_INTS_ON);
outb_p(status, ct82c710_status);
free_irq(CT82C710_IRQ, NULL);
return -1;
}
return 0;
}
/*
* Write to the 82C710 mouse device.
*/
static int ct82c710_write(struct serio *port, unsigned char c)
{
if (ct82c170_wait()) return -1;
outb_p(c, ct82c710_data);
return 0;
}
static struct serio ct82c710_port =
{
type: SERIO_8042,
name: ct82c710_name,
phys: ct82c710_phys,
write: ct82c710_write,
open: ct82c710_open,
close: ct82c710_close,
};
/*
* Interrupt handler for the 82C710 mouse port. A character
* is waiting in the 82C710.
*/
static void ct82c710_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
{
if (ct82c710_port.dev)
ct82c710_port.dev->interrupt(&ct82c710_port, inb(ct82c710_data), 0);
}
/*
* See if we can find a 82C710 device. Read mouse address.
*/
static int __init ct82c710_probe(void)
{
outb_p(0x55, 0x2fa); /* Any value except 9, ff or 36 */
outb_p(0xaa, 0x3fa); /* Inverse of 55 */
outb_p(0x36, 0x3fa); /* Address the chip */
outb_p(0xe4, 0x3fa); /* 390/4; 390 = config address */
outb_p(0x1b, 0x2fa); /* Inverse of e4 */
outb_p(0x0f, 0x390); /* Write index */
if (inb_p(0x391) != 0xe4) /* Config address found? */
return -1; /* No: no 82C710 here */
outb_p(0x0d, 0x390); /* Write index */
ct82c710_data = inb_p(0x391) << 2; /* Get mouse I/O address */
ct82c710_status = ct82c710_data + 1;
outb_p(0x0f, 0x390);
outb_p(0x0f, 0x391); /* Close config mode */
return 0;
}
int __init ct82c710_init(void)
{
if (ct82c710_probe())
return -ENODEV;
if (request_region(ct82c710_data, 2, "ct82c710"))
return -EBUSY;
sprintf(ct82c710_phys, "isa%04x/serio0", ct82c710_data);
serio_register_port(&ct82c710_port);
printk(KERN_INFO "serio: C&T 82c710 mouse port at %#x irq %d\n",
ct82c710_data, CT82C710_IRQ);
return 0;
}
void __exit ct82c710_exit(void)
{
serio_unregister_port(&ct82c710_port);
release_region(ct82c710_data, 2);
}
module_init(ct82c710_init);
module_exit(ct82c710_exit);
/*
* $Id: i8042.c,v 1.21 2002/03/01 22:09:27 jsimmons Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* i8042 keyboard and mouse controller 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 <asm/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/config.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/sched.h> /* request/free_irq */
#include "i8042.h"
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("i8042 keyboard and mouse controller driver");
MODULE_LICENSE("GPL");
MODULE_PARM(i8042_noaux, "1i");
MODULE_PARM(i8042_unlock, "1i");
MODULE_PARM(i8042_reset, "1i");
MODULE_PARM(i8042_direct, "1i");
static int i8042_noaux;
static int i8042_unlock;
static int i8042_reset;
static int i8042_direct;
spinlock_t i8042_lock = SPIN_LOCK_UNLOCKED;
struct i8042_values {
int irq;
unsigned char disable;
unsigned char irqen;
unsigned char exists;
unsigned char *name;
unsigned char *phys;
};
static struct serio i8042_kbd_port;
static struct serio i8042_aux_port;
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
#ifdef I8042_DEBUG_IO
static unsigned long i8042_start;
#endif
static unsigned long i8042_unxlate_seen[128 / BITS_PER_LONG];
static unsigned char i8042_unxlate_table[128] = {
0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
};
static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*
* The i8042_wait_read() and i8042_wait_write functions wait for the i8042 to
* be ready for reading values from it / writing values to it.
*/
static int i8042_wait_read(void)
{
int i = 0;
while ((~inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
udelay(50);
i++;
}
return -(i == I8042_CTL_TIMEOUT);
}
static int i8042_wait_write(void)
{
int i = 0;
while ((inb(I8042_STATUS_REG) & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
udelay(50);
i++;
}
return -(i == I8042_CTL_TIMEOUT);
}
/*
* i8042_flush() flushes all data that may be in the keyboard and mouse buffers
* of the i8042 down the toilet.
*/
static int i8042_flush(void)
{
unsigned long flags;
int i = 0;
spin_lock_irqsave(&i8042_lock, flags);
while ((inb(I8042_STATUS_REG) & I8042_STR_OBF) && (i++ < I8042_BUFFER_SIZE))
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (flush) [%d]\n",
inb(I8042_DATA_REG), (int) (jiffies - i8042_start));
#else
inb(I8042_DATA_REG);
#endif
spin_unlock_irqrestore(&i8042_lock, flags);
return i;
}
/*
* i8042_command() executes a command on the i8042. It also sends the input parameter(s)
* of the commands to it, and receives the output value(s). The parameters are to be
* stored in the param array, and the output is placed into the same array. The number
* of the parameters and output values is encoded in bits 8-11 of the command
* number.
*/
static int i8042_command(unsigned char *param, int command)
{
unsigned long flags;
int retval = 0, i = 0;
spin_lock_irqsave(&i8042_lock, flags);
retval = i8042_wait_write();
if (!retval) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (command) [%d]\n",
command & 0xff, (int) (jiffies - i8042_start));
#endif
outb(command & 0xff, I8042_COMMAND_REG);
}
if (!retval)
for (i = 0; i < ((command >> 12) & 0xf); i++) {
if ((retval = i8042_wait_write())) break;
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (parameter) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
outb(param[i], I8042_DATA_REG);
}
if (!retval)
for (i = 0; i < ((command >> 8) & 0xf); i++) {
if ((retval = i8042_wait_read())) break;
if (inb(I8042_STATUS_REG) & I8042_STR_AUXDATA)
param[i] = ~inb(I8042_DATA_REG);
else
param[i] = inb(I8042_DATA_REG);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (return) [%d]\n",
param[i], (int) (jiffies - i8042_start));
#endif
}
spin_unlock_irqrestore(&i8042_lock, flags);
#ifdef I8042_DEBUG_IO
if (retval)
printk(KERN_DEBUG "i8042.c: -- i8042 (timeout) [%d]\n",
(int) (jiffies - i8042_start));
#endif
return retval;
}
/*
* i8042_kbd_write() sends a byte out through the keyboard interface.
* It also automatically refreshes the CTR value, since some i8042's
* trash their CTR after attempting to send data to an nonexistent
* device.
*/
static int i8042_kbd_write(struct serio *port, unsigned char c)
{
unsigned long flags;
int retval = 0;
spin_lock_irqsave(&i8042_lock, flags);
if(!(retval = i8042_wait_write())) {
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x -> i8042 (kbd-data) [%d]\n",
c, (int) (jiffies - i8042_start));
#endif
outb(c, I8042_DATA_REG);
}
spin_unlock_irqrestore(&i8042_lock, flags);
return retval;
}
/*
* i8042_aux_write() sends a byte out through the aux interface.
*/
static int i8042_aux_write(struct serio *port, unsigned char c)
{
int retval;
/*
* Send the byte out.
*/
retval = i8042_command(&c, I8042_CMD_AUX_SEND);
/*
* Here we restore the CTR value. I don't know why, but i8042's in half-AT
* mode tend to trash their CTR when doing the AUX_SEND command.
*/
retval += i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR);
/*
* Make sure the interrupt happens and the character is received even
* in the case the IRQ isn't wired, so that we can receive further
* characters later.
*/
i8042_interrupt(0, port, NULL);
return retval;
}
/*
* i8042_open() is called when a port is open by the higher layer.
* It allocates an interrupt and enables the port.
*/
static int i8042_open(struct serio *port)
{
struct i8042_values *values = port->driver;
/*
* Allocate the interrupt
*/
if (request_irq(values->irq, i8042_interrupt, 0, "i8042", NULL)) {
printk(KERN_ERR "i8042.c: Can't get irq %d for %s\n", values->irq, values->name);
return -1;
}
/*
* Enable the device and its interrupt.
*/
i8042_ctr |= values->irqen;
i8042_ctr &= ~values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while opening %s.\n", values->name);
return -1;
}
/*
* Flush buffers
*/
i8042_flush();
return 0;
}
/*
* i8042_close() frees the interrupt, and disables the interface when the
* upper layer doesn't need it anymore.
*/
static void i8042_close(struct serio *port)
{
struct i8042_values *values = port->driver;
/*
* Disable the device and its interrupt.
*/
i8042_ctr &= ~values->irqen;
i8042_ctr |= values->disable;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while closing %s.\n", values->name);
return;
}
/*
* Free the interrupt
*/
free_irq(values->irq, NULL);
}
/*
* Structures for registering the devices in the serio.c module.
*/
static struct i8042_values i8042_kbd_values = {
irq: I8042_KBD_IRQ,
irqen: I8042_CTR_KBDINT,
disable: I8042_CTR_KBDDIS,
name: "KBD",
exists: 0,
};
static struct serio i8042_kbd_port =
{
type: SERIO_8042,
write: i8042_kbd_write,
open: i8042_open,
close: i8042_close,
driver: &i8042_kbd_values,
name: "i8042 Kbd Port",
phys: "isa0060/serio0",
};
static struct i8042_values i8042_aux_values = {
irq: I8042_AUX_IRQ,
irqen: I8042_CTR_AUXINT,
disable: I8042_CTR_AUXDIS,
name: "AUX",
exists: 0,
};
static struct serio i8042_aux_port =
{
type: SERIO_8042,
write: i8042_aux_write,
open: i8042_open,
close: i8042_close,
driver: &i8042_aux_values,
name: "i8042 Aux Port",
phys: "isa0060/serio1",
};
/*
* i8042_interrupt() is the most important function in this driver -
* it handles the interrupts from the i8042, and sends incoming bytes
* to the upper layers.
*/
static void i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
unsigned char str, data;
spin_lock_irqsave(&i8042_lock, flags);
while ((str = inb(I8042_STATUS_REG)) & I8042_STR_OBF) {
data = inb(I8042_DATA_REG);
#ifdef I8042_DEBUG_IO
printk(KERN_DEBUG "i8042.c: %02x <- i8042 (interrupt-%s) [%d]\n",
data, (str & I8042_STR_AUXDATA) ? "aux" : "kbd", (int) (jiffies - i8042_start));
#endif
if (i8042_aux_values.exists && (str & I8042_STR_AUXDATA)) {
if (i8042_aux_port.dev)
i8042_aux_port.dev->interrupt(&i8042_aux_port, data, 0);
} else {
if (i8042_kbd_values.exists && i8042_kbd_port.dev) {
if (!i8042_direct) {
if (data > 0x7f) {
if (test_and_clear_bit(data & 0x7f, i8042_unxlate_seen)) {
i8042_kbd_port.dev->interrupt(&i8042_kbd_port, 0xf0, 0);
data = i8042_unxlate_table[data & 0x7f];
}
} else {
set_bit(data, i8042_unxlate_seen);
data = i8042_unxlate_table[data];
}
}
i8042_kbd_port.dev->interrupt(&i8042_kbd_port, data, 0);
}
}
}
spin_unlock_irqrestore(&i8042_lock, flags);
}
/*
* i8042_controller init initializes the i8042 controller, and,
* most importantly, sets it into non-xlated mode.
*/
static int __init i8042_controller_init(void)
{
/*
* Check the i/o region before we touch it.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
if (check_region(I8042_DATA_REG, 16)) {
printk(KERN_ERR "i8042.c: %#x port already in use!\n", I8042_DATA_REG);
return -1;
}
#endif
/*
* Test the i8042. We need to know if it thinks it's working correctly
* before doing anything else.
*/
i8042_flush();
if (i8042_reset) {
unsigned char param;
if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
return -1;
}
if (param != I8042_RET_CTL_TEST) {
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
param, I8042_RET_CTL_TEST);
return -1;
}
}
/*
* Read the CTR.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
return -1;
}
/*
* Save the CTR for restoral on unload / reboot.
*/
i8042_initial_ctr = i8042_ctr;
/*
* Disable both interfaces and their interrupts.
*/
i8042_ctr |= I8042_CTR_KBDDIS;
i8042_ctr &= ~I8042_CTR_KBDINT;
/*
* Handle keylock.
*/
if (~inb(I8042_STATUS_REG) & I8042_STR_KEYLOCK) {
if (i8042_unlock) {
i8042_ctr |= I8042_CTR_IGNKEYLOCK;
} else {
printk(KERN_WARNING "i8042.c: Warning: Keylock active.\n");
}
}
/*
* If the chip is configured into nontranslated mode by the BIOS, don't
* bother enabling translating and just use that happily.
*/
if (~i8042_ctr & I8042_CTR_XLATE)
i8042_direct = 1;
/*
* Set nontranslated mode for the kbd interface if requested by an option.
* This is vital for a working scancode set 3 support. After this the kbd
* interface becomes a simple serial in/out, like the aux interface is. If
* the user doesn't wish this, the driver tries to untranslate the values
* after the i8042 translates them.
*/
if (i8042_direct)
i8042_ctr &= ~I8042_CTR_XLATE;
/*
* Write CTR back.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
return -1;
}
return 0;
}
/*
* Here we try to reset everything back to a state in which the BIOS will be
* able to talk to the hardware when rebooting.
*/
void i8042_controller_cleanup(void)
{
/*
* Reset the controller.
*/
if (i8042_reset) {
unsigned char param;
if (i8042_command(&param, I8042_CMD_CTL_TEST))
printk(KERN_ERR "i8042.c: i8042 controller reset timeout.\n");
}
/*
* Restore the original control register setting.
*/
i8042_ctr = i8042_initial_ctr;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
/*
* Reset anything that is connected to the ports if the ports
* are enabled in the original config.
*/
if (i8042_kbd_values.exists)
i8042_kbd_write(&i8042_kbd_port, 0xff);
if (i8042_aux_values.exists)
i8042_aux_write(&i8042_aux_port, 0xff);
}
/*
* i8042_check_aux() applies as much paranoia as it can at detecting
* the presence of an AUX interface.
*/
static int __init i8042_check_aux(struct i8042_values *values, struct serio *port)
{
unsigned char param;
i8042_flush();
/*
* Internal loopback test - filters out AT-type i8042's
*/
param = 0x5a;
if (i8042_command(&param, I8042_CMD_AUX_LOOP) || param != 0xa5)
return -1;
/*
* External connection test - filters out AT-soldered PS/2 i8042's
*/
if (i8042_command(&param, I8042_CMD_AUX_TEST) || param)
return -1;
/*
* Bit assignment test - filters out PS/2 i8042's in AT mode
*/
if (i8042_command(&param, I8042_CMD_AUX_DISABLE))
return -1;
if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (~param & I8042_CTR_AUXDIS))
return -1;
if (i8042_command(&param, I8042_CMD_AUX_TEST) || param) {
/*
* We've got an old AMI i8042 with 'Bad Cache' commands.
*/
i8042_command(&param, I8042_CMD_AUX_ENABLE);
return -1;
}
if (i8042_command(&param, I8042_CMD_AUX_ENABLE))
return -1;
if (i8042_command(&param, I8042_CMD_CTL_RCTR) || (param & I8042_CTR_AUXDIS))
return -1;
/*
* Disable the interface.
*/
i8042_ctr |= I8042_CTR_AUXDIS;
i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
return -1;
return 0;
}
/*
* i8042_port_register() marks the device as existing,
* registers it, and reports to the user.
*/
static int __init i8042_port_register(struct i8042_values *values, struct serio *port)
{
values->exists = 1;
serio_register_port(port);
printk(KERN_INFO "serio: i8042 %s port at %#x,%#x irq %d\n",
values->name, I8042_DATA_REG, I8042_COMMAND_REG, values->irq);
return 0;
}
/*
* Module init and cleanup functions.
*/
void __init i8042_setup(char *str, int *ints)
{
if (!strcmp(str, "i8042_reset=1"))
i8042_reset = 1;
if (!strcmp(str, "i8042_noaux=1"))
i8042_noaux = 1;
if (!strcmp(str, "i8042_unlock=1"))
i8042_unlock = 1;
if (!strcmp(str, "i8042_direct=1"))
i8042_direct = 1;
}
/*
* Reset the 8042 back to original mode.
*/
static int i8042_notify_sys(struct notifier_block *this, unsigned long code,
void *unused)
{
if (code==SYS_DOWN || code==SYS_HALT)
i8042_controller_cleanup();
return NOTIFY_DONE;
}
static struct notifier_block i8042_notifier=
{
i8042_notify_sys,
NULL,
0
};
int __init i8042_init(void)
{
#ifdef I8042_DEBUG_IO
i8042_start = jiffies;
#endif
if (i8042_controller_init())
return -ENODEV;
i8042_port_register(&i8042_kbd_values, &i8042_kbd_port);
if (!i8042_noaux && !i8042_check_aux(&i8042_aux_values, &i8042_aux_port))
i8042_port_register(&i8042_aux_values, &i8042_aux_port);
/*
* On ix86 platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on ix86 boxes.
*/
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
request_region(I8042_DATA_REG, 16, "i8042");
#endif
register_reboot_notifier(&i8042_notifier);
return 0;
}
void __exit i8042_exit(void)
{
unregister_reboot_notifier(&i8042_notifier);
if (i8042_kbd_values.exists)
serio_unregister_port(&i8042_kbd_port);
if (i8042_aux_values.exists)
serio_unregister_port(&i8042_aux_port);
i8042_controller_cleanup();
#if !defined(__i386__) && !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16);
#endif
}
module_init(i8042_init);
module_exit(i8042_exit);
#ifndef _I8042_H
#define _I8042_H
/*
* $Id: i8042.h,v 1.6 2001/10/05 22:48:09 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* 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
*/
/*
* If you want to reset your i8042 upon boot, define this.
*/
#undef I8042_RESET
/*
* If you want to trace all the i/o the i8042 module does for
* debugging purposes, define this.
*/
#undef I8042_DEBUG_IO
/*
* On most PC based systems the keyboard IRQ is 1.
*/
#define I8042_KBD_IRQ CONFIG_I8042_KBD_IRQ
/*
* On most PC based systems the aux port IRQ is 12. There are exceptions,
* though. Unfortunately IRQ probing is not possible without touching
* the device attached to the port.
*/
#define I8042_AUX_IRQ CONFIG_I8042_AUX_IRQ
/*
* This is in 50us units, the time we wait for the i8042 to react. This
* has to be long enough for the i8042 itself to timeout on sending a byte
* to a non-existent mouse.
*/
#define I8042_CTL_TIMEOUT 10000
/*
* Register numbers.
*/
#define I8042_COMMAND_REG CONFIG_I8042_REG_BASE + 4
#define I8042_STATUS_REG CONFIG_I8042_REG_BASE + 4
#define I8042_DATA_REG CONFIG_I8042_REG_BASE
/*
* Status register bits.
*/
#define I8042_STR_PARITY 0x80
#define I8042_STR_TIMEOUT 0x40
#define I8042_STR_AUXDATA 0x20
#define I8042_STR_KEYLOCK 0x10
#define I8042_STR_CMDDAT 0x08
#define I8042_STR_IBF 0x02
#define I8042_STR_OBF 0x01
/*
* Control register bits.
*/
#define I8042_CTR_KBDINT 0x01
#define I8042_CTR_AUXINT 0x02
#define I8042_CTR_IGNKEYLOCK 0x08
#define I8042_CTR_KBDDIS 0x10
#define I8042_CTR_AUXDIS 0x20
#define I8042_CTR_XLATE 0x40
/*
* Commands.
*/
#define I8042_CMD_CTL_RCTR 0x0120
#define I8042_CMD_CTL_WCTR 0x1060
#define I8042_CMD_CTL_TEST 0x01aa
#define I8042_CMD_KBD_DISABLE 0x00ad
#define I8042_CMD_KBD_ENABLE 0x00ae
#define I8042_CMD_KBD_TEST 0x01ab
#define I8042_CMD_KBD_LOOP 0x11d2
#define I8042_CMD_AUX_DISABLE 0x00a7
#define I8042_CMD_AUX_ENABLE 0x00a8
#define I8042_CMD_AUX_TEST 0x01a9
#define I8042_CMD_AUX_SEND 0x10d4
#define I8042_CMD_AUX_LOOP 0x11d3
/*
* Return codes.
*/
#define I8042_RET_CTL_TEST 0x55
/*
* Expected maximum internal i8042 buffer size. This is used for flushing
* the i8042 buffers. 32 should be more than enough.
*/
#define I8042_BUFFER_SIZE 32
#endif /* _I8042_H */
/*
* $Id: parkbd.c,v 1.10 2002/03/13 10:09:20 vojtech Exp $
*
* Copyright (c) 1999-2001 Vojtech Pavlik
*/
/*
* Parallel port to Keyboard port adapter 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/module.h>
#include <linux/parport.h>
#include <linux/init.h>
#include <linux/serio.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Parallel port to Keyboard port adapter driver");
MODULE_LICENSE("GPL");
MODULE_PARM(parkbd, "1i");
MODULE_PARM(parkbd_mode, "1i");
#define PARKBD_CLOCK 0x01 /* Strobe & Ack */
#define PARKBD_DATA 0x02 /* AutoFd & Busy */
static int parkbd = 0;
static int parkbd_mode = SERIO_8042;
static int parkbd_buffer = 0;
static int parkbd_counter = 0;
static int parkbd_last = 0;
static int parkbd_writing = 0;
static unsigned long parkbd_start = 0;
static struct pardevice *parkbd_dev;
static char parkbd_name[] = "PARKBD AT/XT keyboard adapter";
static char parkbd_phys[32];
static int parkbd_readlines(void)
{
return (parport_read_status(parkbd_dev->port) >> 6) ^ 2;
}
static void parkbd_writelines(int data)
{
parport_write_control(parkbd_dev->port, (~data & 3) | 0x10);
}
static int parkbd_write(struct serio *port, unsigned char c)
{
unsigned char p;
if (!parkbd_mode) return -1;
p = c ^ (c >> 4);
p = p ^ (p >> 2);
p = p ^ (p >> 1);
parkbd_counter = 0;
parkbd_writing = 1;
parkbd_buffer = c | (((int) (~p & 1)) << 8) | 0x600;
parkbd_writelines(2);
return 0;
}
static int parkbd_open(struct serio *port)
{
return 0;
}
static void parkbd_close(struct serio *port)
{
}
static struct serio parkbd_port =
{
write: parkbd_write,
open: parkbd_open,
close: parkbd_close,
name: parkbd_name,
phys: parkbd_phys,
};
static void parkbd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
if (parkbd_writing) {
if (parkbd_counter && ((parkbd_counter == 11) || time_after(jiffies, parkbd_last + HZ/100))) {
parkbd_counter = 0;
parkbd_buffer = 0;
parkbd_writing = 0;
parkbd_writelines(3);
return;
}
parkbd_writelines(((parkbd_buffer >> parkbd_counter++) & 1) | 2);
if (parkbd_counter == 11) {
parkbd_counter = 0;
parkbd_buffer = 0;
parkbd_writing = 0;
parkbd_writelines(3);
}
} else {
if ((parkbd_counter == parkbd_mode + 10) || time_after(jiffies, parkbd_last + HZ/100)) {
parkbd_counter = 0;
parkbd_buffer = 0;
}
parkbd_buffer |= (parkbd_readlines() >> 1) << parkbd_counter++;
if (parkbd_counter == parkbd_mode + 10) {
if (parkbd_port.dev)
parkbd_port.dev->interrupt(&parkbd_port, (parkbd_buffer >> (2 - parkbd_mode)) & 0xff, 0);
}
}
parkbd_last = jiffies;
}
static int parkbd_getport(void)
{
struct parport *pp;
if (parkbd < 0) {
printk(KERN_ERR "parkbd: no port specified\n");
return -ENODEV;
}
for (pp = parport_enumerate(); pp != NULL && (parkbd > 0); pp = pp->next) parkbd--;
if (pp == NULL) {
printk(KERN_ERR "parkbd: no such parport\n");
return -ENODEV;
}
parkbd_dev = parport_register_device(pp, "parkbd", NULL, NULL, parkbd_interrupt, PARPORT_DEV_EXCL, NULL);
if (!parkbd_dev)
return -ENODEV;
if (parport_claim(parkbd_dev)) {
parport_unregister_device(parkbd_dev);
return -EBUSY;
}
parkbd_start = jiffies;
return 0;
}
int __init parkbd_init(void)
{
if (parkbd_getport()) return -1;
parkbd_writelines(3);
parkbd_port.type = parkbd_mode;
sprintf(parkbd_phys, "%s/serio0", parkbd_dev->port->name);
serio_register_port(&parkbd_port);
printk(KERN_INFO "serio: PARKBD %s adapter on %s\n",
parkbd_mode ? "AT" : "XT", parkbd_dev->port->name);
return 0;
}
void __exit parkbd_exit(void)
{
parport_release(parkbd_dev);
serio_unregister_port(&parkbd_port);
parport_unregister_device(parkbd_dev);
}
module_init(parkbd_init);
module_exit(parkbd_exit);
/*
* $Id: rpckbd.c,v 1.7 2001/09/25 10:12:07 vojtech Exp $
*
* Copyright (c) 2000-2001 Vojtech Pavlik
*
* Based on the work of:
* unknown author
*/
/*
* Acorn RiscPC PS/2 keyboard controller 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
*
* 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/serio.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/iomd.h>
#include <asm/system.h>
MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Acorn RiscPC PS/2 keyboard controller driver");
MODULE_LICENSE("GPL");
static inline void rpckbd_write(unsigned char val)
{
while(!(inb(IOMD_KCTRL) & (1 << 7)));
outb(val, IOMD_KARTTX);
}
static struct serio rpckbd_port =
{
type: SERIO_8042,
write: rpckbd_write,
name: "RiscPC PS/2 kbd port",
phys: "rpckbd/serio0",
};
static void rpckbd_rx(int irq, void *dev_id, struct pt_regs *regs)
{
kbd_pt_regs = regs;
while (inb(IOMD_KCTRL) & (1 << 5))
if (rpckbd_port.dev)
rpckbd_port.dev->interrupt(&rpckbd_port, inb(IOMD_KARTRX), 0);
}
static void rpckbd_tx(int irq, void *dev_id, struct pt_regs *regs)
{
}
static int __init rpckbd_init(void)
{
unsigned long flags;
/* Reset the keyboard state machine. */
outb(0, IOMD_KCTRL);
outb(8, IOMD_KCTRL);
save_flags_cli(flags);
if (request_irq(IRQ_KEYBOARDRX, rpckbd_rx, 0, "rpckbd", NULL) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard receive IRQ!\n")
return -EBUSY;
}
if (request_irq(IRQ_KEYBOARDTX, rpckbd_tx, 0, "rpckbd", NULL) != 0) {
printk(KERN_ERR "rpckbd.c: Could not allocate keyboard transmit IRQ!\n")
free_irq(IRQ_KEYBOARDRX, NULL);
return -EBUSY;
}
disable_irq(IRQ_KEYBOARDTX);
(void)IOMD_KARTRX;
restore_flags(flags);
register_serio_port(&rpckbd_port);
printk(KERN_INFO "serio: RiscPC PS/2 kbd port irq %d %d\n", IRQ_KEYBOARDRX, IRQ_KEYBOARDTX);
return 0;
}
static void __exit rpckbd_exit(void)
{
free_irq(IRQ_KEYBOARDRX, NULL);
free_irq(IRQ_KEYBOARDTX, NULL);
unregister_serio_port(&rpckbd_port);
}
module_init(rpckbd_init);
module_exit(rpckbd_exit);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment