Commit c98d5e01 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Linus Torvalds

[PATCH] PS/2 support for PARISC

This converts the PA-RISC PS/2 keyboard & mouse driver to the input
layer.  New driver written by Laurent Canet & Thibaut Varene.
parent e80f40a9
......@@ -11,7 +11,7 @@ endif
AFLAGS_entry.o := -traditional
AFLAGS_pacache.o := -traditional
export-objs := parisc_ksyms.o keyboard.o
export-objs := parisc_ksyms.o
obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \
pa7300lc.o syscall.o entry.o sys_parisc.o firmware.o \
......@@ -22,7 +22,6 @@ obj-y := cache.o pacache.o setup.o traps.o time.o irq.o \
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PA11) += pci-dma.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_VT) += keyboard.o
obj-$(CONFIG_PARISC64) += binfmt_elf32.o sys_parisc32.o \
ioctl32.o signal32.o
# only supported for PCX-W/U in 64-bit mode at the moment
......
/*
* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
* ---------------------------------------------------------------
* This source file will be removed as soon as we have converted
* hp_psaux.c and hp_keyb.c to the input layer !
*
*/
/*
* linux/arch/parisc/kernel/keyboard.c
*
* Alex deVries <adevries@thepuffingroup.com>
* Copyright (1999) The Puffin Group
* Mostly rewritten by Philipp Rumpf <prumpf@tux.org>
* Copyright 2000 Philipp Rumpf
*/
#include <linux/errno.h>
#include <linux/keyboard.h>
#include <asm/keyboard.h>
#include <linux/module.h>
static int def_setkeycode(unsigned int x, unsigned int y)
{
return 0;
}
static int def_getkeycode(unsigned int x)
{
return 0;
}
static int def_translate(unsigned char scancode, unsigned char *keycode,
char raw)
{
*keycode = scancode;
return 1;
}
static char def_unexpected_up(unsigned char c)
{
return 128;
}
static void def_leds(unsigned char leds)
{
}
static void def_init_hw(void)
{
}
static char def_sysrq_xlate[NR_KEYS];
#define DEFAULT_KEYB_OPS \
.setkeycode = def_setkeycode, \
.getkeycode = def_getkeycode, \
.translate = def_translate, \
.unexpected_up = def_unexpected_up, \
.leds = def_leds, \
.init_hw = def_init_hw, \
.sysrq_key = 0xff, \
.sysrq_xlate = def_sysrq_xlate,
static struct kbd_ops def_kbd_ops = {
DEFAULT_KEYB_OPS
};
struct kbd_ops *kbd_ops = &def_kbd_ops;
void unregister_kbd_ops(void)
{
struct kbd_ops new_kbd_ops = { DEFAULT_KEYB_OPS };
register_kbd_ops(&new_kbd_ops);
}
EXPORT_SYMBOL(unregister_kbd_ops);
void register_kbd_ops(struct kbd_ops *ops)
{
if(ops->setkeycode)
kbd_ops->setkeycode = ops->setkeycode;
if(ops->getkeycode)
kbd_ops->getkeycode = ops->getkeycode;
if(ops->translate)
kbd_ops->translate = ops->translate;
if(ops->unexpected_up)
kbd_ops->unexpected_up = ops->unexpected_up;
if(ops->leds)
kbd_ops->leds = ops->leds;
if(ops->init_hw)
kbd_ops->init_hw = ops->init_hw;
kbd_ops->sysrq_key = ops->sysrq_key;
kbd_ops->sysrq_xlate = ops->sysrq_xlate;
}
/*
* linux/drivers/char/hp_keyb.c
* helper-functions for the keyboard/psaux driver for HP-PARISC workstations
*
* based on pc_keyb.c by Geert Uytterhoeven & Martin Mares
*
* 2000/10/26 Debacker Xavier <debackex@esiee.fr>
* Marteau Thomas <marteaut@esiee.fr>
* Djoudi Malek <djoudim@esiee.fr>
* - fixed some keysym defines
*
* 2001/04/28 Debacker Xavier <debackex@esiee.fr>
* - scancode translation rewritten in handle_at_scancode()
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/random.h>
#include <linux/ctype.h>
#include <linux/kbd_ll.h>
#include <linux/init.h>
#include <asm/bitops.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/system.h>
#define KBD_REPORT_ERR
#define KBD_REPORT_UNKN
#define KBD_ESCAPEE0 0xe0 /* in */
#define KBD_ESCAPEE1 0xe1 /* in */
#define ESCE0(x) (0xe000|(x))
#define ESCE1(x) (0xe100|(x))
#define KBD_BAT 0xaa /* in */
#define KBD_SETLEDS 0xed /* out */
#define KBD_ECHO 0xee /* in/out */
#define KBD_BREAK 0xf0 /* in */
#define KBD_TYPRATEDLY 0xf3 /* out */
#define KBD_SCANENABLE 0xf4 /* out */
#define KBD_DEFDISABLE 0xf5 /* out */
#define KBD_DEFAULT 0xf6 /* out */
#define KBD_ACK 0xfa /* in */
#define KBD_DIAGFAIL 0xfd /* in */
#define KBD_RESEND 0xfe /* in/out */
#define KBD_RESET 0xff /* out */
#define CODE_BREAK 1
#define CODE_ESCAPEE0 2
#define CODE_ESCAPEE1 4
#define CODE_ESCAPE12 8
#define K_NONE 0x7f
#define K_ESC 0x01
#define K_F1 0x3b
#define K_F2 0x3c
#define K_F3 0x3d
#define K_F4 0x3e
#define K_F5 0x3f
#define K_F6 0x40
#define K_F7 0x41
#define K_F8 0x42
#define K_F9 0x43
#define K_F10 0x44
#define K_F11 0x57
#define K_F12 0x58
#define K_PRNT 0x54
#define K_SCRL 0x46
#define K_BRK 0x77
#define K_AGR 0x29
#define K_1 0x02
#define K_2 0x03
#define K_3 0x04
#define K_4 0x05
#define K_5 0x06
#define K_6 0x07
#define K_7 0x08
#define K_8 0x09
#define K_9 0x0a
#define K_0 0x0b
#define K_MINS 0x0c
#define K_EQLS 0x0d
#define K_BKSP 0x0e
#define K_INS 0x6e
#define K_HOME 0x66
#define K_PGUP 0x68
#define K_NUML 0x45
#define KP_SLH 0x62
#define KP_STR 0x37
#define KP_MNS 0x4a
#define K_TAB 0x0f
#define K_Q 0x10
#define K_W 0x11
#define K_E 0x12
#define K_R 0x13
#define K_T 0x14
#define K_Y 0x15
#define K_U 0x16
#define K_I 0x17
#define K_O 0x18
#define K_P 0x19
#define K_LSBK 0x1a
#define K_RSBK 0x1b
#define K_ENTR 0x1c
#define K_DEL 111
#define K_END 0x6b
#define K_PGDN 0x6d
#define KP_7 0x47
#define KP_8 0x48
#define KP_9 0x49
#define KP_PLS 0x4e
#define K_CAPS 0x3a
#define K_A 0x1e
#define K_S 0x1f
#define K_D 0x20
#define K_F 0x21
#define K_G 0x22
#define K_H 0x23
#define K_J 0x24
#define K_K 0x25
#define K_L 0x26
#define K_SEMI 0x27
#define K_SQOT 0x28
#define K_HASH K_NONE
#define KP_4 0x4b
#define KP_5 0x4c
#define KP_6 0x4d
#define K_LSFT 0x2a
#define K_BSLH 0x2b
#define K_Z 0x2c
#define K_X 0x2d
#define K_C 0x2e
#define K_V 0x2f
#define K_B 0x30
#define K_N 0x31
#define K_M 0x32
#define K_COMA 0x33
#define K_DOT 0x34
#define K_FSLH 0x35
#define K_RSFT 0x36
#define K_UP 0x67
#define KP_1 0x4f
#define KP_2 0x50
#define KP_3 0x51
#define KP_ENT 0x60
#define K_LCTL 0x1d
#define K_LALT 0x38
#define K_SPCE 0x39
#define K_RALT 0x64
#define K_RCTL 0x61
#define K_LEFT 0x69
#define K_DOWN 0x6c
#define K_RGHT 0x6a
#define KP_0 0x52
#define KP_DOT 0x53
static unsigned char keycode_translate[256] =
{
/* 00 */ K_NONE, K_F9 , K_NONE, K_F5 , K_F3 , K_F1 , K_F2 , K_F12 ,
/* 08 */ K_NONE, K_F10 , K_F8 , K_F6 , K_F4 , K_TAB , K_AGR , K_NONE,
/* 10 */ K_NONE, K_LALT, K_LSFT, K_NONE, K_LCTL, K_Q , K_1 , K_NONE,
/* 18 */ K_NONE, K_NONE, K_Z , K_S , K_A , K_W , K_2 , K_NONE,
/* 20 */ K_NONE, K_C , K_X , K_D , K_E , K_4 , K_3 , K_NONE,
/* 28 */ K_NONE, K_SPCE, K_V , K_F , K_T , K_R , K_5 , K_NONE,
/* 30 */ K_NONE, K_N , K_B , K_H , K_G , K_Y , K_6 , K_NONE,
/* 38 */ K_NONE, K_NONE, K_M , K_J , K_U , K_7 , K_8 , K_NONE,
/* 40 */ K_NONE, K_COMA, K_K , K_I , K_O , K_0 , K_9 , K_NONE,
/* 48 */ K_NONE, K_DOT , K_FSLH, K_L , K_SEMI, K_P , K_MINS, K_NONE,
/* 50 */ K_NONE, K_NONE, K_SQOT, K_NONE, K_LSBK, K_EQLS, K_NONE, K_NONE,
/* 58 */ K_CAPS, K_RSFT, K_ENTR, K_RSBK, K_NONE, K_BSLH, K_NONE, K_NONE,
/* 60 */ K_NONE, K_HASH, K_NONE, K_NONE, K_NONE, K_NONE, K_BKSP, K_NONE,
/* 68 */ K_NONE, KP_1 , K_NONE, KP_4 , KP_7 , K_NONE, K_NONE, K_NONE,
/* 70 */ KP_0 , KP_DOT, KP_2 , KP_5 , KP_6 , KP_8 , K_ESC , K_NUML,
/* 78 */ K_F11 , KP_PLS, KP_3 , KP_MNS, KP_STR, KP_9 , K_SCRL, K_PRNT,
/* 80 */ K_NONE, K_NONE, K_NONE, K_F7 , K_NONE, K_NONE, K_NONE, K_NONE,
/* 88 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* 90 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* 98 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* a0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* a8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* b0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* b8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* c0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* c8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* d0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* d8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* e0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* e8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* f0 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
/* f8 */ K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, KBD_RESEND, K_NONE
};
/* ----- the following code stolen from pc_keyb.c */
#ifdef CONFIG_MAGIC_SYSRQ
unsigned char hp_ps2kbd_sysrq_xlate[128] =
"\000\0331234567890-=\177\t" /* 0x00 - 0x0f */
"qwertyuiop[]\r\000as" /* 0x10 - 0x1f */
"dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */
"bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */
"\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */
"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
"\r\000/"; /* 0x60 - 0x6f */
#endif
/*
* Translation of escaped scancodes to keycodes.
* This is now user-settable.
* The keycodes 1-88,96-111,119 are fairly standard, and
* should probably not be changed - changing might confuse X.
* X also interprets scancode 0x5d (KEY_Begin).
*
* For 1-88 keycode equals scancode.
*/
#define E0_KPENTER 96
#define E0_RCTRL 97
#define E0_KPSLASH 98
#define E0_PRSCR 99
#define E0_RALT 100
#define E0_BREAK 101 /* (control-pause) */
#define E0_HOME 102
#define E0_UP 103
#define E0_PGUP 104
#define E0_LEFT 105
#define E0_RIGHT 106
#define E0_END 107
#define E0_DOWN 108
#define E0_PGDN 109
#define E0_INS 110
#define E0_DEL 111
#define E1_PAUSE 119
/*
* The keycodes below are randomly located in 89-95,112-118,120-127.
* They could be thrown away (and all occurrences below replaced by 0),
* but that would force many users to use the `setkeycodes' utility, where
* they needed not before. It does not matter that there are duplicates, as
* long as no duplication occurs for any single keyboard.
*/
#define SC_LIM 89 /* 0x59 == 89 */
#define FOCUS_PF1 85 /* actual code! */
#define FOCUS_PF2 89
#define FOCUS_PF3 90
#define FOCUS_PF4 91
#define FOCUS_PF5 92
#define FOCUS_PF6 93
#define FOCUS_PF7 94
#define FOCUS_PF8 95
#define FOCUS_PF9 120
#define FOCUS_PF10 121
#define FOCUS_PF11 122
#define FOCUS_PF12 123
#define JAP_86 124
/* tfj@olivia.ping.dk:
* The four keys are located over the numeric keypad, and are
* labelled A1-A4. It's an rc930 keyboard, from
* Regnecentralen/RC International, Now ICL.
* Scancodes: 59, 5a, 5b, 5c.
*/
#define RGN1 124
#define RGN2 125
#define RGN3 126
#define RGN4 127
static unsigned char high_keys[128 - SC_LIM] = {
RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x59-0x5f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x68-0x6f */
0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x70-0x77 */
FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x78-0x7b */
FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x7c-0x7f */
};
/* BTC */
#define E0_MACRO 112
/* LK450 */
#define E0_F13 113
#define E0_F14 114
#define E0_HELP 115
#define E0_DO 116
#define E0_F17 117
#define E0_KPMINPLUS 118
/*
* My OmniKey generates e0 4c for the "OMNI" key and the
* right alt key does nada. [kkoller@nyx10.cs.du.edu]
*/
#define E0_OK 124
/*
* New microsoft keyboard is rumoured to have
* e0 5b (left window button), e0 5c (right window button),
* e0 5d (menu button). [or: LBANNER, RBANNER, RMENU]
* [or: Windows_L, Windows_R, TaskMan]
*/
#define E0_MSLW 125
#define E0_MSRW 126
#define E0_MSTM 127
static unsigned char e0_keys[128] = {
0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x17 */
0, 0, 0, 0, E0_KPENTER, E0_RCTRL, 0, 0, /* 0x18-0x1f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */
0, 0, 0, 0, 0, E0_KPSLASH, 0, E0_PRSCR, /* 0x30-0x37 */
E0_RALT, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x38-0x3f */
E0_DO, E0_F17, 0, 0, 0, 0, E0_BREAK, E0_HOME, /* 0x40-0x47 */
E0_UP, E0_PGUP, 0, E0_LEFT, E0_OK, E0_RIGHT, E0_KPMINPLUS, E0_END,/* 0x48-0x4f */
E0_DOWN, E0_PGDN, E0_INS, E0_DEL, 0, 0, 0, 0, /* 0x50-0x57 */
0, 0, 0, E0_MSLW, E0_MSRW, E0_MSTM, 0, 0, /* 0x58-0x5f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
0, 0, 0, 0, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */
};
int pckbd_setkeycode(unsigned int scancode, unsigned int keycode)
{
if (scancode < SC_LIM || scancode > 255 || keycode > 127)
return -EINVAL;
if (scancode < 128)
high_keys[scancode - SC_LIM] = keycode;
else
e0_keys[scancode - 128] = keycode;
return 0;
}
int pckbd_getkeycode(unsigned int scancode)
{
return
(scancode < SC_LIM || scancode > 255) ? -EINVAL :
(scancode < 128) ? high_keys[scancode - SC_LIM] :
e0_keys[scancode - 128];
}
int pckbd_translate(unsigned char scancode, unsigned char *keycode,
char raw_mode)
{
static int prev_scancode;
/* special prefix scancodes.. */
if (scancode == 0xe0 || scancode == 0xe1) {
prev_scancode = scancode;
return 0;
}
/* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */
if (scancode == 0x00 || scancode == 0xff) {
prev_scancode = 0;
return 0;
}
scancode &= 0x7f;
if (prev_scancode) {
/*
* usually it will be 0xe0, but a Pause key generates
* e1 1d 45 e1 9d c5 when pressed, and nothing when released
*/
if (prev_scancode != 0xe0) {
if (prev_scancode == 0xe1 && scancode == 0x1d) {
prev_scancode = 0x100;
return 0;
} else if (prev_scancode == 0x100 && scancode == 0x45) {
*keycode = E1_PAUSE;
prev_scancode = 0;
} else {
#ifdef KBD_REPORT_UNKN
if (!raw_mode)
printk(KERN_INFO "keyboard: unknown e1 escape sequence\n");
#endif
prev_scancode = 0;
return 0;
}
} else {
prev_scancode = 0;
/*
* The keyboard maintains its own internal caps lock and
* num lock statuses. In caps lock mode E0 AA precedes make
* code and E0 2A follows break code. In num lock mode,
* E0 2A precedes make code and E0 AA follows break code.
* We do our own book-keeping, so we will just ignore these.
*/
/*
* For my keyboard there is no caps lock mode, but there are
* both Shift-L and Shift-R modes. The former mode generates
* E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs.
* So, we should also ignore the latter. - aeb@cwi.nl
*/
if (scancode == 0x2a || scancode == 0x36)
return 0;
if (e0_keys[scancode])
*keycode = e0_keys[scancode];
else {
#ifdef KBD_REPORT_UNKN
if (!raw_mode)
printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n",
scancode);
#endif
return 0;
}
}
} else if (scancode >= SC_LIM) {
/* This happens with the FOCUS 9000 keyboard
Its keys PF1..PF12 are reported to generate
55 73 77 78 79 7a 7b 7c 74 7e 6d 6f
Moreover, unless repeated, they do not generate
key-down events, so we have to zero up_flag below */
/* Also, Japanese 86/106 keyboards are reported to
generate 0x73 and 0x7d for \ - and \ | respectively. */
/* Also, some Brazilian keyboard is reported to produce
0x73 and 0x7e for \ ? and KP-dot, respectively. */
*keycode = high_keys[scancode - SC_LIM];
if (!*keycode) {
if (!raw_mode) {
#ifdef KBD_REPORT_UNKN
printk(KERN_INFO "keyboard: unrecognized scancode (%02x)"
" - ignored\n", scancode);
#endif
}
return 0;
}
} else
*keycode = scancode;
return 1;
}
/* ----- end of stolen part ------ */
void kbd_reset_setup(void)
{
}
void handle_at_scancode(int keyval)
{
static int brk;
static int esc0;
static int esc1;
int scancode = 0;
switch (keyval) {
case KBD_BREAK :
/* sets the "release_key" bit when a key is
released. HP keyboard send f0 followed by
the keycode while AT keyboard send the keycode
with this bit set. */
brk = 0x80;
return;
case KBD_ESCAPEE0 :
/* 2chars sequence, commonly used to differenciate
the two ALT keys and the two ENTER keys and so
on... */
esc0 = 2; /* e0-xx are 2 chars */
scancode = keyval;
break;
case KBD_ESCAPEE1 :
/* 3chars sequence, only used by the Pause key. */
esc1 = 3; /* e1-xx-xx are 3 chars */
scancode = keyval;
break;
#if 0
case KBD_RESEND :
/* dunno what to do when it happens. RFC */
printk(KERN_INFO "keyboard: KBD_RESEND received.\n");
return;
#endif
case 0x14 :
/* translate e1-14-77-e1-f0-14-f0-77 to
e1-1d-45-e1-9d-c5 (the Pause key) */
if (esc1==2) scancode = brk | 0x1d;
break;
case 0x77 :
if (esc1==1) scancode = brk | 0x45;
break;
case 0x12 :
/* an extended key is e0-12-e0-xx e0-f0-xx-e0-f0-12
on HP, while it is e0-2a-e0-xx e0-(xx|80)-f0-aa
on AT. */
if (esc0==1) scancode = brk | 0x2a;
break;
}
/* translates HP scancodes to AT scancodes */
if (!scancode) scancode = brk | keycode_translate[keyval];
if (!scancode) printk(KERN_INFO "keyboard: unexpected key code %02x\n",keyval);
/* now behave like an AT keyboard */
handle_scancode(scancode,!(scancode&0x80));
if (esc0) esc0--;
if (esc1) esc1--;
/* release key bit must be unset for the next key */
brk = 0;
}
/*
* LASI PS/2 keyboard/psaux driver for HP-PARISC workstations
*
* (c) Copyright 1999 The Puffin Group Inc.
* by Alex deVries <adevries@thepuffingroup.com>
* Copyright 1999, 2000 Philipp Rumpf <prumpf@tux.org>
*
* 2000/10/26 Debacker Xavier (debackex@esiee.fr)
* Marteau Thomas (marteaut@esiee.fr)
* Djoudi Malek (djoudim@esiee.fr)
* fixed leds control
* implemented the psaux and controlled the mouse scancode based on pc_keyb.c
*/
#include <linux/config.h>
#include <asm/hardware.h>
#include <asm/keyboard.h>
#include <asm/gsc.h>
#include <linux/types.h>
#include <linux/ptrace.h> /* interrupt.h wants struct pt_regs defined */
#include <linux/interrupt.h>
#include <linux/sched.h> /* for request_irq/free_irq */
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/pc_keyb.h>
#include <linux/kbd_kern.h>
/* mouse includes */
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/spinlock.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <linux/poll.h>
/* HP specific LASI PS/2 keyboard and psaux constants */
#define AUX_REPLY_ACK 0xFA /* Command byte ACK. */
#define AUX_RECONNECT 0xAA /* scancode when ps2 device is plugged (back) in */
#define LASI_PSAUX_OFFSET 0x0100 /* offset from keyboard to psaux port */
#define LASI_ID 0x00 /* ID and reset port offsets */
#define LASI_RESET 0x00
#define LASI_RCVDATA 0x04 /* receive and transmit port offsets */
#define LASI_XMTDATA 0x04
#define LASI_CONTROL 0x08 /* see: control register bits */
#define LASI_STATUS 0x0C /* see: status register bits */
/* control register bits */
#define LASI_CTRL_ENBL 0x01 /* enable interface */
#define LASI_CTRL_LPBXR 0x02 /* loopback operation */
#define LASI_CTRL_DIAG 0x20 /* directly control clock/data line */
#define LASI_CTRL_DATDIR 0x40 /* data line direct control */
#define LASI_CTRL_CLKDIR 0x80 /* clock line direct control */
/* status register bits */
#define LASI_STAT_RBNE 0x01
#define LASI_STAT_TBNE 0x02
#define LASI_STAT_TERR 0x04
#define LASI_STAT_PERR 0x08
#define LASI_STAT_CMPINTR 0x10
#define LASI_STAT_DATSHD 0x40
#define LASI_STAT_CLKSHD 0x80
static void *lasikbd_hpa;
static void *lasips2_hpa;
static inline u8 read_input(void *hpa)
{
return gsc_readb(hpa+LASI_RCVDATA);
}
static inline u8 read_control(void *hpa)
{
return gsc_readb(hpa+LASI_CONTROL);
}
static inline void write_control(u8 val, void *hpa)
{
gsc_writeb(val, hpa+LASI_CONTROL);
}
static inline u8 read_status(void *hpa)
{
return gsc_readb(hpa+LASI_STATUS);
}
static int write_output(u8 val, void *hpa)
{
int wait = 0;
while (read_status(hpa) & LASI_STAT_TBNE) {
wait++;
if (wait>10000) {
/* printk(KERN_WARNING "Lasi PS/2 transmit buffer timeout\n"); */
return 0;
}
}
if (wait)
printk(KERN_DEBUG "Lasi PS/2 wait %d\n", wait);
gsc_writeb(val, hpa+LASI_XMTDATA);
return 1;
}
/* This function is the PA-RISC adaptation of i386 source */
static inline int aux_write_ack(u8 val)
{
return write_output(val, lasikbd_hpa+LASI_PSAUX_OFFSET);
}
static void lasikbd_leds(unsigned char leds)
{
write_output(KBD_CMD_SET_LEDS, lasikbd_hpa);
write_output(leds, lasikbd_hpa);
write_output(KBD_CMD_ENABLE, lasikbd_hpa);
}
#if 0
/* this might become useful again at some point. not now -prumpf */
int lasi_ps2_test(void *hpa)
{
u8 control,c;
int i, ret = 0;
control = read_control(hpa);
write_control(control | LASI_CTRL_LPBXR | LASI_CTRL_ENBL, hpa);
for (i=0; i<256; i++) {
write_output(i, hpa);
while (!(read_status(hpa) & LASI_STAT_RBNE))
/* just wait */;
c = read_input(hpa);
if (c != i)
ret--;
}
write_control(control, hpa);
return ret;
}
#endif
static int __init lasi_ps2_reset(void *hpa, int id)
{
u8 control;
int ret = 1;
/* reset the interface */
gsc_writeb(0xff, hpa+LASI_RESET);
gsc_writeb(0x0 , hpa+LASI_RESET);
/* enable it */
control = read_control(hpa);
write_control(control | LASI_CTRL_ENBL, hpa);
/* initializes the leds at the default state */
if (id==0) {
write_output(KBD_CMD_SET_LEDS, hpa);
write_output(0, hpa);
ret = write_output(KBD_CMD_ENABLE, hpa);
}
return ret;
}
static int inited;
static void lasi_ps2_init_hw(void)
{
++inited;
}
/* Greatly inspired by pc_keyb.c */
/*
* Wait for keyboard controller input buffer to drain.
*
* Don't use 'jiffies' so that we don't depend on
* interrupts..
*
* Quote from PS/2 System Reference Manual:
*
* "Address hex 0060 and address hex 0064 should be written only when
* the input-buffer-full bit and output-buffer-full bit in the
* Controller Status register are set 0."
*/
#ifdef CONFIG_PSMOUSE
static struct aux_queue *queue;
static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
static unsigned char mouse_reply_expected;
static int aux_count;
static int fasync_aux(int fd, struct file *filp, int on)
{
int retval;
retval = fasync_helper(fd, filp, on, &queue->fasync);
if (retval < 0)
return retval;
return 0;
}
static inline void handle_mouse_scancode(unsigned char scancode)
{
if (mouse_reply_expected) {
if (scancode == AUX_REPLY_ACK) {
mouse_reply_expected--;
return;
}
mouse_reply_expected = 0;
}
else if (scancode == AUX_RECONNECT) {
queue->head = queue->tail = 0; /* Flush input queue */
return;
}
add_mouse_randomness(scancode);
if (aux_count) {
int head = queue->head;
queue->buf[head] = scancode;
head = (head + 1) & (AUX_BUF_SIZE-1);
if (head != queue->tail) {
queue->head = head;
kill_fasync(&queue->fasync, SIGIO, POLL_IN);
wake_up_interruptible(&queue->proc_list);
}
}
}
static inline int queue_empty(void)
{
return queue->head == queue->tail;
}
static unsigned char get_from_queue(void)
{
unsigned char result;
unsigned long flags;
spin_lock_irqsave(&kbd_controller_lock, flags);
result = queue->buf[queue->tail];
queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
spin_unlock_irqrestore(&kbd_controller_lock, flags);
return result;
}
/*
* Write to the aux device.
*/
static ssize_t write_aux(struct file * file, const char * buffer,
size_t count, loff_t *ppos)
{
ssize_t retval = 0;
if (count) {
ssize_t written = 0;
if (count > 32)
count = 32; /* Limit to 32 bytes. */
do {
char c;
get_user(c, buffer++);
written++;
} while (--count);
retval = -EIO;
if (written) {
retval = written;
file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
}
}
return retval;
}
static ssize_t read_aux(struct file * file, char * buffer,
size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
ssize_t i = count;
unsigned char c;
if (queue_empty()) {
if (file->f_flags & O_NONBLOCK)
return -EAGAIN;
add_wait_queue(&queue->proc_list, &wait);
repeat:
set_current_state(TASK_INTERRUPTIBLE);
if (queue_empty() && !signal_pending(current)) {
schedule();
goto repeat;
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&queue->proc_list, &wait);
}
while (i > 0 && !queue_empty()) {
c = get_from_queue();
put_user(c, buffer++);
i--;
}
if (count-i) {
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return count-i;
}
if (signal_pending(current))
return -ERESTARTSYS;
return 0;
}
static int open_aux(struct inode * inode, struct file * file)
{
if (aux_count++)
return 0;
queue->head = queue->tail = 0; /* Flush input queue */
aux_count = 1;
aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
return 0;
}
/* No kernel lock held - fine */
static unsigned int aux_poll(struct file *file, poll_table * wait)
{
poll_wait(file, &queue->proc_list, wait);
if (!queue_empty())
return POLLIN | POLLRDNORM;
return 0;
}
static int release_aux(struct inode * inode, struct file * file)
{
lock_kernel();
fasync_aux(-1, file, 0);
if (--aux_count) {
unlock_kernel();
return 0;
}
unlock_kernel();
return 0;
}
static struct file_operations psaux_fops = {
.read = read_aux,
.write = write_aux,
.poll = aux_poll,
.open = open_aux,
.release = release_aux,
.fasync = fasync_aux,
};
static struct miscdevice psaux_mouse = {
.minor = PSMOUSE_MINOR,
.name = "psaux",
.fops = &psaux_fops,
};
#endif /* CONFIG_PSMOUSE */
/* This function is looking at the PS2 controller and empty the two buffers */
static u8 handle_lasikbd_event(void *hpa)
{
u8 status_keyb,status_mouse,scancode,id;
extern void handle_at_scancode(int); /* in drivers/char/keyb_at.c */
/* Mask to get the base address of the PS/2 controller */
id = gsc_readb(hpa+LASI_ID) & 0x0f;
if (id==1)
hpa -= LASI_PSAUX_OFFSET;
lasikbd_hpa = hpa;
status_keyb = read_status(hpa);
status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
while ((status_keyb|status_mouse) & LASI_STAT_RBNE){
while (status_keyb & LASI_STAT_RBNE) {
scancode = read_input(hpa);
/* XXX don't know if this is a valid fix, but filtering
* 0xfa avoids 'unknown scancode' errors on, eg, capslock
* on some keyboards.
*/
if (inited && scancode != 0xfa)
handle_at_scancode(scancode);
status_keyb =read_status(hpa);
}
#ifdef CONFIG_PSMOUSE
while (status_mouse & LASI_STAT_RBNE) {
scancode = read_input(hpa+LASI_PSAUX_OFFSET);
handle_mouse_scancode(scancode);
status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
}
status_mouse = read_status(hpa+LASI_PSAUX_OFFSET);
#endif /* CONFIG_PSMOUSE */
status_keyb = read_status(hpa);
}
tasklet_schedule(&keyboard_tasklet);
return (status_keyb|status_mouse);
}
extern struct pt_regs *kbd_pt_regs;
static void lasikbd_interrupt(int irq, void *dev, struct pt_regs *regs)
{
lasips2_hpa = dev; /* save "hpa" for lasikbd_leds() */
kbd_pt_regs = regs;
handle_lasikbd_event(lasips2_hpa);
}
extern int pckbd_translate(unsigned char, unsigned char *, char);
static struct kbd_ops gsc_ps2_kbd_ops = {
.translate = pckbd_translate,
.init_hw = lasi_ps2_init_hw,
.leds = lasikbd_leds,
#ifdef CONFIG_MAGIC_SYSRQ
.sysrq_key = 0x54,
.sysrq_xlate = hp_ps2kbd_sysrq_xlate,
#endif
};
static int __init
lasi_ps2_register(struct hp_device *d, struct pa_iodc_driver *dri)
{
void *hpa = (void *) d->hpa;
unsigned int irq;
char *name;
int device_found;
u8 id;
id = gsc_readb(hpa+LASI_ID) & 0x0f;
switch (id) {
case 0:
name = "keyboard";
lasikbd_hpa = hpa;
break;
case 1:
name = "psaux";
break;
default:
printk(KERN_WARNING "%s: Unknown PS/2 port (id=%d) - ignored.\n",
__FUNCTION__, id );
return 0;
}
/* reset the PS/2 port */
device_found = lasi_ps2_reset(hpa,id);
/* allocate the irq and memory region for that device */
if (!(irq = busdevice_alloc_irq(d)))
return -ENODEV;
if (request_irq(irq, lasikbd_interrupt, 0, name, hpa))
return -ENODEV;
if (!request_mem_region((unsigned long)hpa, LASI_STATUS + 4, name))
return -ENODEV;
switch (id) {
case 0:
register_kbd_ops(&gsc_ps2_kbd_ops);
break;
case 1:
#ifdef CONFIG_PSMOUSE
queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
if (!queue)
return -ENOMEM;
memset(queue, 0, sizeof(*queue));
queue->head = queue->tail = 0;
init_waitqueue_head(&queue->proc_list);
misc_register(&psaux_mouse);
aux_write_ack(AUX_ENABLE_DEV);
/* try it a second time, this will give status if the device is
* available */
device_found = aux_write_ack(AUX_ENABLE_DEV);
break;
#else
/* return without printing any unnecessary and misleading info */
return 0;
#endif
} /* of case */
printk(KERN_INFO "PS/2 %s controller at 0x%08lx (irq %d) found, "
"%sdevice attached.\n",
name, (unsigned long)hpa, irq,
device_found ? "":"no ");
return 0;
}
static struct pa_iodc_driver lasi_psaux_drivers_for[] __initdata = {
{HPHW_FIO, 0x0, 0,0x00084, 0, 0,
DRIVER_CHECK_HWTYPE + DRIVER_CHECK_SVERSION,
"Lasi psaux", "generic", (void *) lasi_ps2_register},
{ 0, }
};
static int __init gsc_ps2_init(void)
{
return pdc_register_driver(lasi_psaux_drivers_for);
}
module_init(gsc_ps2_init);
......@@ -56,3 +56,12 @@ config INPUT_UINPUT
The module will be called uinput.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
config INPUT_GSC
tristate "PA-RISC GSC PS/2 keyboard/mouse support"
depends on GSC && INPUT && INPUT_MISC
help
Say Y here if you have a PS/2 keyboard and/or mouse attached
to your PA-RISC box. HP run the keyboard in AT mode rather than
XT mode like everyone else, so we need our own driver.
Furthermore, the GSC PS/2 controller shares IRQ between mouse and
keyboard.
......@@ -8,7 +8,4 @@ obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
# The global Rules.make.
include $(TOPDIR)/Rules.make
obj-$(CONFIG_INPUT_GSC) += gsc_ps2.o
/*
* drivers/input/misc/gsc_ps2.c
*
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
* Copyright (c) 2002 Thibaut Varene <varenet@esiee.fr>
*
* Pieces of code based on linux-2.4's hp_mouse.c & hp_keyb.c
* Copyright (c) 1999 Alex deVries <adevries@thepuffingroup.com>
* Copyright (c) 1999-2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
* Copyright (c) 2000-2001 Thomas Marteau <marteaut@esiee.fr>
*
* HP PS/2 Keyboard, found in PA/RISC Workstations
* very similar to AT keyboards, but without i8042
*
* 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* STATUS:
* 11/09: lc: Only basic keyboard is supported, mouse still needs to be done.
* 11/12: tv: switching iomapping; cleaning code; improving module stuff.
* 11/13: lc & tv: leds aren't working. auto_repeat/meta are. Generaly good behavior.
* 11/15: tv: 2AM: leds ARE working !
* 11/16: tv: 3AM: escaped keycodes emulation *handled*, some keycodes are
* still deliberately ignored (18), what are they used for ?
* 11/21: lc: mouse is now working
* 11/29: tv: first try for error handling in init sequence
*
* TODO:
* Error handling in init sequence
* SysRq handling
* Pause key handling
* Intellimouse & other rodents handling (at least send an error when
* such a mouse is plugged : it will totally fault)
* Mouse: set scaling / Dino testing
* Bug chasing...
*
*/
#include <linux/input.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ptrace.h> /* interrupt.h wants struct pt_regs defined */
#include <linux/interrupt.h>
#include <linux/sched.h> /* for request_irq/free_irq */
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/pc_keyb.h>
#include <linux/ioport.h>
#include <linux/kd.h>
#include <asm/irq.h>
#include <asm/io.h>
/* Debugging stuff */
#undef KBD_DEBUG
#ifdef KBD_DEBUG
#define DPRINTK(fmt,args...) printk(KERN_DEBUG __FILE__ ":" fmt, ##args)
#else
#define DPRINTK(x,...)
#endif
/*
* Driver constants
*/
/* PS/2 keyboard and mouse constants */
#define AUX_RECONNECT 0xAA /* PS/2 Mouse end of test successful */
#define AUX_REPLY_ACK 0xFA
/* Order of the mouse bytes coming to the host */
#define PACKET_X 1
#define PACKET_Y 2
#define PACKET_CTRL 0
#define GSC_MOUSE_OFFSET 0x0100 /* offset from keyboard to mouse port */
#define GSC_DINO_OFFSET 0x800 /* offset for DINO controller versus LASI one */
#define GSC_ID 0x00 /* ID and reset port offsets */
#define GSC_RESET 0x00
#define GSC_RCVDATA 0x04 /* receive and transmit port offsets */
#define GSC_XMTDATA 0x04
#define GSC_CONTROL 0x08 /* see: control register bits */
#define GSC_STATUS 0x0C /* see: status register bits */
/* Control register bits */
#define GSC_CTRL_ENBL 0x01 /* enable interface */
#define GSC_CTRL_LPBXR 0x02 /* loopback operation */
#define GSC_CTRL_DIAG 0x20 /* directly control clock/data line */
#define GSC_CTRL_DATDIR 0x40 /* data line direct control */
#define GSC_CTRL_CLKDIR 0x80 /* clock line direct control */
/* Status register bits */
#define GSC_STAT_RBNE 0x01 /* Receive Buffer Not Empty */
#define GSC_STAT_TBNE 0x02 /* Transmit Buffer Not Empty */
#define GSC_STAT_TERR 0x04 /* Timeout Error */
#define GSC_STAT_PERR 0x08 /* Parity Error */
#define GSC_STAT_CMPINTR 0x10 /* Composite Interrupt */
#define GSC_STAT_DATSHD 0x40 /* Data Line Shadow */
#define GSC_STAT_CLKSHD 0x80 /* Clock Line Shadow */
/* Keycode map */
#define KBD_ESCAPE0 0xe0
#define KBD_ESCAPE1 0xe1
#define KBD_RELEASE 0xf0
#define KBD_ACK 0xfa
#define KBD_RESEND 0xfe
#define KBD_UNKNOWN 0
#define KBD_TBLSIZE 512
/* Mouse */
#define MOUSE_LEFTBTN 0x1
#define MOUSE_MIDBTN 0x4
#define MOUSE_RIGHTBTN 0x2
#define MOUSE_ALWAYS1 0x8
#define MOUSE_XSIGN 0x10
#define MOUSE_YSIGN 0x20
#define MOUSE_XOVFLOW 0x40
#define MOUSE_YOVFLOW 0x80
static unsigned char hpkeyb_keycode[KBD_TBLSIZE] =
{
/* 00 */ KBD_UNKNOWN, KEY_F9, KBD_UNKNOWN, KEY_F5, KEY_F3, KEY_F1, KEY_F2, KEY_F12,
/* 08 */ KBD_UNKNOWN, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KBD_UNKNOWN,
/* 10 */ KBD_UNKNOWN, KEY_LEFTALT, KEY_LEFTSHIFT, KBD_UNKNOWN, KEY_LEFTCTRL, KEY_Q, KEY_1, KBD_UNKNOWN,
/* 18 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KBD_UNKNOWN,
/* 20 */ KBD_UNKNOWN, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KBD_UNKNOWN,
/* 28 */ KBD_UNKNOWN, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KBD_UNKNOWN,
/* 30 */ KBD_UNKNOWN, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KBD_UNKNOWN,
/* 38 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KBD_UNKNOWN,
/* 40 */ KBD_UNKNOWN, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KBD_UNKNOWN,
/* 48 */ KBD_UNKNOWN, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KBD_UNKNOWN,
/* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_APOSTROPHE,KBD_UNKNOWN, KEY_LEFTBRACE, KEY_EQUAL, KBD_UNKNOWN, KBD_UNKNOWN,
/* 58 */ KEY_CAPSLOCK, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KBD_UNKNOWN, KEY_BACKSLASH,KBD_UNKNOWN, KBD_UNKNOWN,
/* 60 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_BACKSPACE, KBD_UNKNOWN,
/* 68 */ KBD_UNKNOWN, KEY_KP1, KBD_UNKNOWN, KEY_KP4, KEY_KP7, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 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_103RD,
/* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KEY_F7, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* e0 */ KBD_ESCAPE0, KBD_ESCAPE1, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* f0 */ KBD_RELEASE, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_ACK, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_RESEND, KBD_UNKNOWN,
/* These are offset for escaped keycodes */
/* 00 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 08 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 10 */ KBD_UNKNOWN, KEY_RIGHTALT, KBD_UNKNOWN, KBD_UNKNOWN, KEY_RIGHTCTRL, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 18 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 20 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 28 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 30 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 38 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 40 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 48 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPSLASH, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 50 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 58 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_KPENTER, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 60 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 68 */ KBD_UNKNOWN, KEY_END, KBD_UNKNOWN, KEY_LEFT, KEY_HOME, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 70 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KBD_UNKNOWN, KEY_RIGHT, KEY_UP, KBD_UNKNOWN, KBD_UNKNOWN,
/* 78 */ KBD_UNKNOWN, KBD_UNKNOWN, KEY_PAGEDOWN, KBD_UNKNOWN, KEY_SYSRQ, KEY_PAGEUP, KBD_UNKNOWN, KBD_UNKNOWN,
/* 80 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 88 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 90 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* 98 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* a0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* a8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* b0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* b8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* c0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* c8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* d0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* d8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* e0 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* e8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* f0 */ KBD_RELEASE, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN,
/* f8 */ KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN, KBD_UNKNOWN
};
/* Keyboard struct */
static struct {
struct input_dev dev;
char * addr;
unsigned int irq;
unsigned int scancode;
unsigned int escaped;
unsigned int released;
unsigned int initialized;
}
hpkeyb = {
.escaped = 0,
.released = 0,
.initialized = 0
};
/* Mouse struct */
static struct {
struct input_dev dev;
char * addr;
unsigned long irq;
unsigned long initialized;
int nbread;
unsigned char bytes[3];
unsigned long last;
}
hpmouse = {
.initialized = 0,
.nbread = 0
};
static spinlock_t gscps2_lock = SPIN_LOCK_UNLOCKED;
/*
* Various HW level routines
*/
#define gscps2_readb_input(x) readb(x+GSC_RCVDATA)
#define gscps2_readb_control(x) readb(x+GSC_CONTROL)
#define gscps2_readb_status(x) readb(x+GSC_STATUS)
#define gscps2_writeb_control(x, y) writeb(x, y+GSC_CONTROL)
static inline void gscps2_writeb_output(u8 val, char * addr)
{
int wait = 250; /* Keyboard is expected to react within 250ms */
while (gscps2_readb_status(addr) & GSC_STAT_TBNE) {
if (!--wait)
return; /* This should not happen */
mdelay(1);
}
writeb(val, addr+GSC_XMTDATA);
}
static inline unsigned char gscps2_wait_input(char * addr)
{
int wait = 250; /* Keyboard is expected to react within 250ms */
while (!(gscps2_readb_status(addr) & GSC_STAT_RBNE)) {
if (!--wait)
return 0; /* This should not happen */
mdelay(1);
}
return gscps2_readb_input(addr);
}
static int gscps2_writeb_safe_output(u8 val)
{
/* This function waits for keyboard's ACK */
u8 scanread = KBD_UNKNOWN;
int loop = 5;
while (hpkeyb_keycode[scanread]!=KBD_ACK && --loop > 0) {
gscps2_writeb_output(val, hpkeyb.addr);
mdelay(5);
scanread = gscps2_wait_input(hpkeyb.addr);
}
if (loop <= 0)
return -1;
return 0;
}
/* Reset the PS2 port */
static void __init gscps2_reset(char * addr)
{
/* reset the interface */
writeb(0xff, addr+GSC_RESET);
writeb(0x0 , addr+GSC_RESET);
/* enable it */
gscps2_writeb_control(gscps2_readb_control(addr) | GSC_CTRL_ENBL, addr);
}
/**
* gscps2_kbd_docode() - PS2 Keyboard basic handler
*
* Receives a keyboard scancode, analyses it and sends it to the input layer.
*/
static void gscps2_kbd_docode(void)
{
int scancode = gscps2_readb_input(hpkeyb.addr);
DPRINTK("rel=%d scancode=%d, esc=%d ", hpkeyb.released, scancode, hpkeyb.escaped);
/* Handle previously escaped scancodes */
if (hpkeyb.escaped == KBD_ESCAPE0)
scancode |= 0x100; /* jump to the next 256 chars of the table */
switch (hpkeyb_keycode[scancode]) {
case KBD_RELEASE:
DPRINTK("release\n");
hpkeyb.released = 1;
break;
case KBD_RESEND:
DPRINTK("resend request\n");
break;
case KBD_ACK:
DPRINTK("ACK\n");
break;
case KBD_ESCAPE0:
case KBD_ESCAPE1:
DPRINTK("escape code %d\n", hpkeyb_keycode[scancode]);
hpkeyb.escaped = hpkeyb_keycode[scancode];
break;
case KBD_UNKNOWN:
DPRINTK("received unknown scancode %d, escape %d.\n",
scancode, hpkeyb.escaped); /* This is a DPRINTK atm since we do not handle escaped scancodes cleanly */
if (hpkeyb.escaped)
hpkeyb.escaped = 0;
if (hpkeyb.released)
hpkeyb.released = 0;
return;
default:
hpkeyb.scancode = scancode;
DPRINTK("sent=%d, rel=%d\n",hpkeyb.scancode, hpkeyb.released);
input_report_key(&hpkeyb.dev, hpkeyb_keycode[hpkeyb.scancode], !hpkeyb.released);
input_sync(&hpkeyb.dev);
if (hpkeyb.escaped)
hpkeyb.escaped = 0;
if (hpkeyb.released)
hpkeyb.released = 0;
break;
}
}
/**
* gscps2_mouse_docode() - PS2 Mouse basic handler
*
* Receives mouse codes, processes them by packets of three, and sends
* correct events to the input layer.
*/
static void gscps2_mouse_docode(void)
{
int xrel, yrel;
/* process BAT (end of basic tests) command */
if ((hpmouse.nbread == 1) && (hpmouse.bytes[0] == AUX_RECONNECT))
hpmouse.nbread--;
/* stolen from psmouse.c */
if (hpmouse.nbread && time_after(jiffies, hpmouse.last + HZ/20)) {
printk(KERN_DEBUG "%s:%d : Lost mouse synchronization, throwing %d bytes away.\n", __FILE__, __LINE__,
hpmouse.nbread);
hpmouse.nbread = 0;
}
hpmouse.last = jiffies;
hpmouse.bytes[hpmouse.nbread++] = gscps2_readb_input(hpmouse.addr);
/* process packet */
if (hpmouse.nbread == 3) {
if (!(hpmouse.bytes[PACKET_CTRL] & MOUSE_ALWAYS1))
DPRINTK("Mouse: error on packet always1 bit checking\n");
/* XXX should exit now, bad data on the line! */
if ((hpmouse.bytes[PACKET_CTRL] & (MOUSE_XOVFLOW | MOUSE_YOVFLOW)))
DPRINTK("Mouse: position overflow\n");
input_report_key(&hpmouse.dev, BTN_LEFT, hpmouse.bytes[PACKET_CTRL] & MOUSE_LEFTBTN);
input_report_key(&hpmouse.dev, BTN_MIDDLE, hpmouse.bytes[PACKET_CTRL] & MOUSE_MIDBTN);
input_report_key(&hpmouse.dev, BTN_RIGHT, hpmouse.bytes[PACKET_CTRL] & MOUSE_RIGHTBTN);
xrel = hpmouse.bytes[PACKET_X];
yrel = hpmouse.bytes[PACKET_Y];
/* Data sent by mouse are 9-bit signed, the sign bit is in the control packet */
if (xrel && (hpmouse.bytes[PACKET_CTRL] & MOUSE_XSIGN))
xrel = xrel - 0x100;
if (yrel && (hpmouse.bytes[PACKET_CTRL] & MOUSE_YSIGN))
yrel = yrel - 0x100;
input_report_rel(&hpmouse.dev, REL_X, xrel);
input_report_rel(&hpmouse.dev, REL_Y, -yrel); /* Y axis is received upside-down */
input_sync(&hpmouse.dev);
hpmouse.nbread = 0;
}
}
/**
* gscps2_interrupt() - Interruption service routine
*
* This processes the list of scancodes queued and sends appropriate
* key value to the system.
*/
static void gscps2_interrupt(int irq, void *dev, struct pt_regs *reg)
{
/* process mouse actions */
while (gscps2_readb_status(hpmouse.addr) & GSC_STAT_RBNE)
gscps2_mouse_docode();
/* process keyboard scancode */
while (gscps2_readb_status(hpkeyb.addr) & GSC_STAT_RBNE)
gscps2_kbd_docode();
}
/**
* gscps2_hpkeyb_event() - Event handler
* @return: success/error report
*
* Currently only updates leds on keyboard
*/
int gscps2_hpkeyb_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
DPRINTK("Calling %s, type=%d, code=%d, value=%d\n",
__FUNCTION__, type, code, value);
if (!hpkeyb.initialized)
return -1;
if (type == EV_LED) {
u8 leds[2];
if (gscps2_writeb_safe_output(KBD_CMD_SET_LEDS)) {
printk(KERN_ERR "gsckbd_leds: timeout\n");
return -1;
}
DPRINTK("KBD_CMD_SET_LEDS\n");
*leds = (test_bit(LED_SCROLLL, dev->led) ? LED_SCR : 0)
| (test_bit(LED_NUML, dev->led) ? LED_NUM : 0)
| (test_bit(LED_CAPSL, dev->led) ? LED_CAP : 0);
DPRINTK("Sending leds=%x\n", *leds);
if (gscps2_writeb_safe_output(*leds)) {
printk(KERN_ERR "gsckbd_leds: timeout\n");
return -1;
}
DPRINTK("leds sent\n");
if (gscps2_writeb_safe_output(KBD_CMD_ENABLE)) {
printk(KERN_ERR "gsckbd_leds: timeout\n");
return -1;
}
DPRINTK("End\n");
return 0;
}
return -1;
}
/**
* gscps2_kbd_probe() - Probes keyboard device and init input_dev structure
* @return: number of device initialized (1, 0 on error)
*/
static int __init gscps2_kbd_probe(void)
{
int i, res = 0;
unsigned long flags;
if (hpkeyb.initialized) {
printk(KERN_ERR "GSC PS/2 keyboard driver already registered\n");
return 0;
}
spin_lock_irqsave(&gscps2_lock, flags);
if (!gscps2_writeb_safe_output(KBD_CMD_SET_LEDS) &&
!gscps2_writeb_safe_output(0) &&
!gscps2_writeb_safe_output(KBD_CMD_ENABLE))
res = 1;
spin_unlock_irqrestore(&gscps2_lock, flags);
if (!res)
printk(KERN_ERR "Keyboard initialization sequence failled\n");
init_input_dev(&hpkeyb.dev);
for (i = 0; i < KBD_TBLSIZE; i++)
if (hpkeyb_keycode[i] != KBD_UNKNOWN)
set_bit(hpkeyb_keycode[i], hpkeyb.dev.keybit);
hpkeyb.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP);
hpkeyb.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
hpkeyb.dev.keycode = hpkeyb_keycode;
hpkeyb.dev.keycodesize = sizeof(unsigned char);
hpkeyb.dev.keycodemax = KBD_TBLSIZE;
hpkeyb.dev.name = "GSC Keyboard";
hpkeyb.dev.phys = "hpkbd/input0";
hpkeyb.dev.event = gscps2_hpkeyb_event;
/* TODO These need some adjustement, are they really useful ? */
hpkeyb.dev.id.bustype = BUS_GSC;
hpkeyb.dev.id.vendor = 0x0001;
hpkeyb.dev.id.product = 0x0001;
hpkeyb.dev.id.version = 0x0010;
hpkeyb.initialized = 1;
return 1;
}
/**
* gscps2_mouse_probe() - Probes mouse device and init input_dev structure
* @return: number of device initialized (1, 0 on error)
*
* Currently no check on initialization is performed
*/
static int __init gscps2_mouse_probe(void)
{
if (hpmouse.initialized) {
printk(KERN_ERR "GSC PS/2 Mouse driver already registered\n");
return 0;
}
init_input_dev(&hpmouse.dev);
hpmouse.dev.name = "GSC Mouse";
hpmouse.dev.phys = "hpmouse/input0";
hpmouse.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
hpmouse.dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_MIDDLE) | BIT(BTN_RIGHT);
hpmouse.dev.relbit[0] = BIT(REL_X) | BIT(REL_Y);
hpmouse.last = 0;
gscps2_writeb_output(AUX_ENABLE_DEV, hpmouse.addr);
/* Try it a second time, this will give status if the device is available */
gscps2_writeb_output(AUX_ENABLE_DEV, hpmouse.addr);
/* TODO These need some adjustement, are they really useful ? */
hpmouse.dev.id.bustype = BUS_GSC;
hpmouse.dev.id.vendor = 0x0001;
hpmouse.dev.id.product = 0x0001;
hpmouse.dev.id.version = 0x0010;
hpmouse.initialized = 1;
return 1; /* XXX: we don't check if initialization failed */
}
/**
* gscps2_probe() - Probes PS2 devices
* @return: success/error report
*/
static int __init gscps2_probe(struct parisc_device *dev)
{
u8 id;
char *addr, *name;
int ret, device_found = 0;
unsigned long hpa = dev->hpa;
if (!dev->irq)
goto fail_pitifully;
/* Offset for DINO PS/2. Works with LASI even */
if (dev->id.sversion == 0x96)
hpa += GSC_DINO_OFFSET;
addr = ioremap(hpa, 256);
if (!hpmouse.initialized || !hpkeyb.initialized)
gscps2_reset(addr);
ret = -EINVAL;
id = readb(addr+GSC_ID) & 0x0f;
switch (id) {
case 0: /* keyboard */
hpkeyb.addr = addr;
name = "keyboard";
device_found = gscps2_kbd_probe();
break;
case 1: /* mouse */
hpmouse.addr = addr;
name = "mouse";
device_found = gscps2_mouse_probe();
break;
default:
printk(KERN_WARNING "%s: Unsupported PS/2 port (id=%d) ignored\n",
__FUNCTION__, id);
goto fail_miserably;
}
/* No valid device found */
ret = -ENODEV;
if (!device_found)
goto fail_miserably;
/* Here we claim only if we have a device attached */
/* Allocate the irq and memory region for that device */
ret = -EBUSY;
if (request_irq(dev->irq, gscps2_interrupt, 0, name, NULL))
goto fail_miserably;
if (!request_mem_region(hpa, GSC_STATUS + 4, name))
goto fail_request_mem;
/* Finalize input struct and register it */
switch (id) {
case 0: /* keyboard */
hpkeyb.irq = dev->irq;
input_register_device(&hpkeyb.dev);
break;
case 1: /* mouse */
hpmouse.irq = dev->irq;
input_register_device(&hpmouse.dev);
break;
default:
break;
}
printk(KERN_INFO "input: PS/2 %s port at 0x%08lx (irq %d) found and attached\n",
name, hpa, dev->irq);
return 0;
fail_request_mem: free_irq(dev->irq, NULL);
fail_miserably: iounmap(addr);
fail_pitifully: return ret;
}
static struct parisc_device_id gscps2_device_tbl[] = {
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 }, /* LASI PS/2 */
/* { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00096 }, DINO PS/2 (XXX Not yet tested) */
{ 0, } /* 0 terminated list */
};
static struct parisc_driver gscps2_driver = {
.name = "GSC PS2",
.id_table = gscps2_device_tbl,
.probe = gscps2_probe,
};
static int __init gscps2_init(void)
{
if (register_parisc_driver(&gscps2_driver))
return -EBUSY;
return 0;
}
static void __exit gscps2_exit(void)
{
/* TODO this is probably not very good and needs to be checked */
if (hpkeyb.initialized) {
free_irq(hpkeyb.irq, gscps2_interrupt);
iounmap(hpkeyb.addr);
hpkeyb.initialized = 0;
input_unregister_device(&hpkeyb.dev);
}
if (hpmouse.initialized) {
free_irq(hpmouse.irq, gscps2_interrupt);
iounmap(hpmouse.addr);
hpmouse.initialized = 0;
input_unregister_device(&hpmouse.dev);
}
unregister_parisc_driver(&gscps2_driver);
}
MODULE_AUTHOR("Laurent Canet <canetl@esiee.fr>, Thibaut Varene <varenet@esiee.fr>");
MODULE_DESCRIPTION("GSC PS/2 keyboard/mouse driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(parisc, gscps2_device_tbl);
module_init(gscps2_init);
module_exit(gscps2_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