/*
|
/*
|
* linux/arch/arm/drivers/block/keyboard.c
|
* linux/arch/arm/drivers/block/keyboard.c
|
*
|
*
|
* Keyboard driver for Linux using Latin-1.
|
* Keyboard driver for Linux using Latin-1.
|
*
|
*
|
* Written for linux by Johan Myreen as a translation from
|
* Written for linux by Johan Myreen as a translation from
|
* the assembly version by Linus (with diacriticals added)
|
* the assembly version by Linus (with diacriticals added)
|
*
|
*
|
* Some additional features added by Christoph Niemann (ChN), March 1993
|
* Some additional features added by Christoph Niemann (ChN), March 1993
|
*
|
*
|
* Loadable keymaps by Risto Kankkunen, May 1993
|
* Loadable keymaps by Risto Kankkunen, May 1993
|
*
|
*
|
* Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
|
* Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
|
* Added decr/incr_console, dynamic keymaps, Unicode support,
|
* Added decr/incr_console, dynamic keymaps, Unicode support,
|
* dynamic function/string keys, led setting, Sept 1994
|
* dynamic function/string keys, led setting, Sept 1994
|
* `Sticky' modifier keys, 951006.
|
* `Sticky' modifier keys, 951006.
|
*
|
*
|
* Majorly altered for use with arm Russell King (rmk@ecs.soton.ac.uk)
|
* Majorly altered for use with arm Russell King (rmk@ecs.soton.ac.uk)
|
* The low-level keyboard driver is now separate.
|
* The low-level keyboard driver is now separate.
|
* Interfaces:
|
* Interfaces:
|
* Driver supplies:
|
* Driver supplies:
|
* kbd_drv_init - initialise keyboard driver proper
|
* kbd_drv_init - initialise keyboard driver proper
|
* kbd_drv_setleds - set leds on keyboard
|
* kbd_drv_setleds - set leds on keyboard
|
* Driver uses:
|
* Driver uses:
|
* kbd_keyboardkey - process a keypress/release
|
* kbd_keyboardkey - process a keypress/release
|
* kbd_reset - reset keyboard down array
|
* kbd_reset - reset keyboard down array
|
* kbd_setregs - set regs for scroll-lock debug
|
* kbd_setregs - set regs for scroll-lock debug
|
*/
|
*/
|
|
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
#include <linux/tty.h>
|
#include <linux/tty.h>
|
#include <linux/tty_flip.h>
|
#include <linux/tty_flip.h>
|
#include <linux/mm.h>
|
#include <linux/mm.h>
|
#include <linux/malloc.h>
|
#include <linux/malloc.h>
|
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
#include <linux/signal.h>
|
#include <linux/signal.h>
|
#include <linux/timer.h>
|
#include <linux/timer.h>
|
#include <linux/random.h>
|
#include <linux/random.h>
|
#include <linux/ctype.h>
|
#include <linux/ctype.h>
|
|
|
#include <asm/bitops.h>
|
#include <asm/bitops.h>
|
#include <asm/system.h>
|
#include <asm/system.h>
|
#include <asm/irq.h>
|
#include <asm/irq.h>
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#include "kbd_kern.h"
|
#include "kbd_kern.h"
|
#include "diacr.h"
|
#include "diacr.h"
|
#include "vt_kern.h"
|
#include "vt_kern.h"
|
|
|
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
|
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
|
|
|
#define KBD_REPORT_ERR
|
#define KBD_REPORT_ERR
|
#define KBD_REPORT_UNKN
|
#define KBD_REPORT_UNKN
|
|
|
#ifndef KBD_DEFMODE
|
#ifndef KBD_DEFMODE
|
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
|
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* Starts with NumLock off.
|
* Starts with NumLock off.
|
*/
|
*/
|
#ifndef KBD_DEFLEDS
|
#ifndef KBD_DEFLEDS
|
#define KBD_DEFLEDS 0
|
#define KBD_DEFLEDS 0
|
#endif
|
#endif
|
|
|
#ifndef KBD_DEFLOCK
|
#ifndef KBD_DEFLOCK
|
#define KBD_DEFLOCK 0
|
#define KBD_DEFLOCK 0
|
#endif
|
#endif
|
|
|
#define REPEAT_TIMEOUT HZ*300/1000
|
#define REPEAT_TIMEOUT HZ*300/1000
|
#define REPEAT_RATE HZ*30/1000
|
#define REPEAT_RATE HZ*30/1000
|
|
|
extern void ctrl_alt_del (void);
|
extern void ctrl_alt_del (void);
|
extern void scrollback(int);
|
extern void scrollback(int);
|
extern void scrollfront(int);
|
extern void scrollfront(int);
|
extern int kbd_drv_init(void);
|
extern int kbd_drv_init(void);
|
extern void kbd_drv_setleds(unsigned int leds);
|
extern void kbd_drv_setleds(unsigned int leds);
|
extern unsigned int keymap_count;
|
extern unsigned int keymap_count;
|
|
|
/*
|
/*
|
* these are the valid i/o ports we're allowed to change. they map all the
|
* these are the valid i/o ports we're allowed to change. they map all the
|
* video ports
|
* video ports
|
*/
|
*/
|
#define GPFIRST 0x3b4
|
#define GPFIRST 0x3b4
|
#define GPLAST 0x3df
|
#define GPLAST 0x3df
|
#define GPNUM (GPLAST - GPFIRST + 1)
|
#define GPNUM (GPLAST - GPFIRST + 1)
|
|
|
/*
|
/*
|
* global state includes the following, and various static variables
|
* global state includes the following, and various static variables
|
* in this module: shift_state, diacr, npadch, dead_key_next.
|
* in this module: shift_state, diacr, npadch, dead_key_next.
|
* (last_console is now a global variable)
|
* (last_console is now a global variable)
|
*/
|
*/
|
|
|
/* shift state counters.. */
|
/* shift state counters.. */
|
static unsigned char k_down[NR_SHIFT] = {0, };
|
static unsigned char k_down[NR_SHIFT] = {0, };
|
/* keyboard key bitmap */
|
/* keyboard key bitmap */
|
#define BITS_PER_LONG (8*sizeof (unsigned long))
|
#define BITS_PER_LONG (8*sizeof (unsigned long))
|
static unsigned long key_down[256/BITS_PER_LONG] = {0, };
|
static unsigned long key_down[256/BITS_PER_LONG] = {0, };
|
static int dead_key_next;
|
static int dead_key_next;
|
|
|
/*
|
/*
|
* In order to retrieve the shift_state (for the mouse server), either
|
* In order to retrieve the shift_state (for the mouse server), either
|
* the variable must be global, or a new procedure must be created to
|
* the variable must be global, or a new procedure must be created to
|
* return the value. I chose the former way.
|
* return the value. I chose the former way.
|
*/
|
*/
|
/*static*/ int shift_state;
|
/*static*/ int shift_state;
|
static int npadch = -1; /* -1 or number assembled on pad */
|
static int npadch = -1; /* -1 or number assembled on pad */
|
static unsigned char diacr;
|
static unsigned char diacr;
|
static char rep; /* Flag telling character repeat */
|
static char rep; /* Flag telling character repeat */
|
static int kbd_repeatkey = -1;
|
static int kbd_repeatkey = -1;
|
static int kbd_repeattimeout = REPEAT_TIMEOUT;
|
static int kbd_repeattimeout = REPEAT_TIMEOUT;
|
static int kbd_repeatrate = REPEAT_RATE;
|
static int kbd_repeatrate = REPEAT_RATE;
|
|
|
static struct kbd_struct * kbd;
|
static struct kbd_struct * kbd;
|
static struct tty_struct * tty;
|
static struct tty_struct * tty;
|
|
|
extern void compute_shiftstate (void);
|
extern void compute_shiftstate (void);
|
|
|
typedef void (*k_hand)(unsigned char value, char up_flag);
|
typedef void (*k_hand)(unsigned char value, char up_flag);
|
typedef void (k_handfn)(unsigned char value, char up_flag);
|
typedef void (k_handfn)(unsigned char value, char up_flag);
|
|
|
static k_handfn
|
static k_handfn
|
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
|
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
|
do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
|
do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
|
|
|
static k_hand key_handler[16] = {
|
static k_hand key_handler[16] = {
|
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
|
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
|
do_meta, do_ascii, do_lock, do_lowercase, do_slock,
|
do_meta, do_ascii, do_lock, do_lowercase, do_slock,
|
do_ignore, do_ignore, do_ignore
|
do_ignore, do_ignore, do_ignore
|
};
|
};
|
|
|
typedef void (*void_fnp)(void);
|
typedef void (*void_fnp)(void);
|
typedef void (void_fn)(void);
|
typedef void (void_fn)(void);
|
|
|
static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
|
static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
|
num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
|
num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
|
SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
|
SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
|
|
|
static void_fnp spec_fn_table[] = {
|
static void_fnp spec_fn_table[] = {
|
do_null, enter, show_ptregs, show_mem,
|
do_null, enter, show_ptregs, show_mem,
|
show_state, send_intr, lastcons, caps_toggle,
|
show_state, send_intr, lastcons, caps_toggle,
|
num, hold, scroll_forw, scroll_back,
|
num, hold, scroll_forw, scroll_back,
|
boot_it, caps_on, compose, SAK,
|
boot_it, caps_on, compose, SAK,
|
decr_console, incr_console, show_stack, bare_num
|
decr_console, incr_console, show_stack, bare_num
|
};
|
};
|
|
|
/* maximum values each key_handler can handle */
|
/* maximum values each key_handler can handle */
|
const int max_vals[] = {
|
const int max_vals[] = {
|
255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
|
255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
|
NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
|
NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
|
255, NR_ASCII - 1, NR_LOCK - 1, 255,
|
255, NR_ASCII - 1, NR_LOCK - 1, 255,
|
NR_LOCK - 1
|
NR_LOCK - 1
|
};
|
};
|
|
|
const int NR_TYPES = SIZE(max_vals);
|
const int NR_TYPES = SIZE(max_vals);
|
|
|
static void put_queue(int);
|
static void put_queue(int);
|
static unsigned char handle_diacr(unsigned char);
|
static unsigned char handle_diacr(unsigned char);
|
|
|
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
|
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
|
static struct pt_regs * pt_regs;
|
static struct pt_regs * pt_regs;
|
|
|
/*
|
/*
|
* Many other routines do put_queue, but I think either
|
* Many other routines do put_queue, but I think either
|
* they produce ASCII, or they produce some user-assigned
|
* they produce ASCII, or they produce some user-assigned
|
* string, and in both cases we might assume that it is
|
* string, and in both cases we might assume that it is
|
* in utf-8 already.
|
* in utf-8 already.
|
*/
|
*/
|
void to_utf8(ushort c)
|
void to_utf8(ushort c)
|
{
|
{
|
if (c < 0x80)
|
if (c < 0x80)
|
put_queue(c); /* 0******* */
|
put_queue(c); /* 0******* */
|
else if (c < 0x800) {
|
else if (c < 0x800) {
|
put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */
|
put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */
|
put_queue(0x80 | (c & 0x3f));
|
put_queue(0x80 | (c & 0x3f));
|
} else {
|
} else {
|
put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */
|
put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */
|
put_queue(0x80 | ((c >> 6) & 0x3f));
|
put_queue(0x80 | ((c >> 6) & 0x3f));
|
put_queue(0x80 | (c & 0x3f));
|
put_queue(0x80 | (c & 0x3f));
|
}
|
}
|
/* UTF-8 is defined for words of up to 31 bits,
|
/* UTF-8 is defined for words of up to 31 bits,
|
but we need only 16 bits here */
|
but we need only 16 bits here */
|
}
|
}
|
|
|
/*
|
/*
|
* As yet, we don't support setting and getting the
|
* As yet, we don't support setting and getting the
|
* key codes. I'll have to find out where this is used.
|
* key codes. I'll have to find out where this is used.
|
*/
|
*/
|
int setkeycode(unsigned int scancode, unsigned int keycode)
|
int setkeycode(unsigned int scancode, unsigned int keycode)
|
{
|
{
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
int getkeycode(unsigned int scancode)
|
int getkeycode(unsigned int scancode)
|
{
|
{
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
|
|
static void key_callback(unsigned long nr)
|
static void key_callback(unsigned long nr)
|
{
|
{
|
rep = 1;
|
rep = 1;
|
mark_bh (KEYBOARD_BH);
|
mark_bh (KEYBOARD_BH);
|
}
|
}
|
|
|
static struct timer_list key_timer =
|
static struct timer_list key_timer =
|
{
|
{
|
NULL, NULL, 0, 0, key_callback
|
NULL, NULL, 0, 0, key_callback
|
};
|
};
|
|
|
void kbd_reset(void)
|
void kbd_reset(void)
|
{
|
{
|
int i;
|
int i;
|
|
|
for (i = 0; i < NR_SHIFT; i++)
|
for (i = 0; i < NR_SHIFT; i++)
|
k_down[i] = 0;
|
k_down[i] = 0;
|
for (i = 0; i < 256/BITS_PER_LONG; i++)
|
for (i = 0; i < 256/BITS_PER_LONG; i++)
|
key_down[i] = 0;
|
key_down[i] = 0;
|
shift_state = 0;
|
shift_state = 0;
|
}
|
}
|
|
|
void kbd_setregs(struct pt_regs *regs)
|
void kbd_setregs(struct pt_regs *regs)
|
{
|
{
|
pt_regs = regs;
|
pt_regs = regs;
|
}
|
}
|
|
|
static void kbd_processkey(unsigned int keycode, unsigned int up_flag, unsigned int autorepeat)
|
static void kbd_processkey(unsigned int keycode, unsigned int up_flag, unsigned int autorepeat)
|
{
|
{
|
del_timer(&key_timer);
|
del_timer(&key_timer);
|
show_console();
|
show_console();
|
add_keyboard_randomness (keycode|(up_flag?0x100:0));
|
add_keyboard_randomness (keycode|(up_flag?0x100:0));
|
|
|
tty = *vtdata.fgconsole->tty;
|
tty = *vtdata.fgconsole->tty;
|
kbd = vtdata.fgconsole->kbd;
|
kbd = vtdata.fgconsole->kbd;
|
|
|
if (up_flag) {
|
if (up_flag) {
|
rep = 0;
|
rep = 0;
|
clear_bit (keycode, key_down);
|
clear_bit (keycode, key_down);
|
/* don't report an error if key is already up - happens when a ghost key is released */
|
/* don't report an error if key is already up - happens when a ghost key is released */
|
} else
|
} else
|
rep = set_bit (keycode, key_down);
|
rep = set_bit (keycode, key_down);
|
|
|
if (rep && !autorepeat)
|
if (rep && !autorepeat)
|
return;
|
return;
|
|
|
if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
|
if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
|
put_queue(keycode + (up_flag ? 0x80 : 0));
|
put_queue(keycode + (up_flag ? 0x80 : 0));
|
return;
|
return;
|
}
|
}
|
|
|
/*
|
/*
|
* We have to do our own auto-repeat processing...
|
* We have to do our own auto-repeat processing...
|
*/
|
*/
|
if (!up_flag) {
|
if (!up_flag) {
|
kbd_repeatkey = keycode;
|
kbd_repeatkey = keycode;
|
if (vc_kbd_mode(kbd, VC_REPEAT)) {
|
if (vc_kbd_mode(kbd, VC_REPEAT)) {
|
if (rep)
|
if (rep)
|
key_timer.expires = jiffies + kbd_repeatrate;
|
key_timer.expires = jiffies + kbd_repeatrate;
|
else
|
else
|
key_timer.expires = jiffies + kbd_repeattimeout;
|
key_timer.expires = jiffies + kbd_repeattimeout;
|
add_timer(&key_timer);
|
add_timer(&key_timer);
|
}
|
}
|
} else
|
} else
|
kbd_repeatkey = -1;
|
kbd_repeatkey = -1;
|
|
|
/*
|
/*
|
* Repeat a key only if the input buffers are empty or the
|
* Repeat a key only if the input buffers are empty or the
|
* characters get echoed locally. This makes key repeat usable
|
* characters get echoed locally. This makes key repeat usable
|
* with slow applications and under heavy loads.
|
* with slow applications and under heavy loads.
|
*/
|
*/
|
if (!rep ||
|
if (!rep ||
|
(vc_kbd_mode (kbd, VC_REPEAT) && tty &&
|
(vc_kbd_mode (kbd, VC_REPEAT) && tty &&
|
(L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
|
(L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
|
u_short keysym;
|
u_short keysym;
|
u_char type;
|
u_char type;
|
|
|
/* the XOR below used to be an OR */
|
/* the XOR below used to be an OR */
|
int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
|
int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
|
u_short *key_map = key_maps[shift_final];
|
u_short *key_map = key_maps[shift_final];
|
|
|
if (key_map != NULL) {
|
if (key_map != NULL) {
|
keysym = key_map[keycode];
|
keysym = key_map[keycode];
|
type = KTYP(keysym);
|
type = KTYP(keysym);
|
|
|
if (type >= 0xf0) {
|
if (type >= 0xf0) {
|
type -= 0xf0;
|
type -= 0xf0;
|
if (type == KT_LETTER) {
|
if (type == KT_LETTER) {
|
type = KT_LATIN;
|
type = KT_LATIN;
|
if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
|
if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
|
key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
|
key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
|
if (key_map)
|
if (key_map)
|
keysym = key_map[keycode];
|
keysym = key_map[keycode];
|
}
|
}
|
}
|
}
|
(*key_handler[type])(keysym & 0xff, up_flag);
|
(*key_handler[type])(keysym & 0xff, up_flag);
|
if (type != KT_SLOCK)
|
if (type != KT_SLOCK)
|
kbd->slockstate = 0;
|
kbd->slockstate = 0;
|
} else {
|
} else {
|
/* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
|
/* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
|
if (!up_flag)
|
if (!up_flag)
|
to_utf8(keysym);
|
to_utf8(keysym);
|
}
|
}
|
} else {
|
} else {
|
/*
|
/*
|
* maybe beep?
|
* maybe beep?
|
* we have at least to update shift_state
|
* we have at least to update shift_state
|
* how? two almost equivalent choices follow
|
* how? two almost equivalent choices follow
|
*/
|
*/
|
#if 1
|
#if 1
|
compute_shiftstate ();
|
compute_shiftstate ();
|
#else
|
#else
|
keysym = U(plain_map[keycode]);
|
keysym = U(plain_map[keycode]);
|
type = KTYP(keysym);
|
type = KTYP(keysym);
|
if (type == KT_SHIFT)
|
if (type == KT_SHIFT)
|
(*key_handler[type]) (keysym & 0xff, up_flag);
|
(*key_handler[type]) (keysym & 0xff, up_flag);
|
#endif
|
#endif
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
void kbd_keyboardkey(unsigned int keycode, unsigned int up_flag)
|
void kbd_keyboardkey(unsigned int keycode, unsigned int up_flag)
|
{
|
{
|
kbd_processkey(keycode, up_flag, 0);
|
kbd_processkey(keycode, up_flag, 0);
|
}
|
}
|
|
|
static void put_queue (int ch)
|
static void put_queue (int ch)
|
{
|
{
|
wake_up (&keypress_wait);
|
wake_up (&keypress_wait);
|
if (tty) {
|
if (tty) {
|
tty_insert_flip_char (tty, ch, 0);
|
tty_insert_flip_char (tty, ch, 0);
|
tty_schedule_flip (tty);
|
tty_schedule_flip (tty);
|
}
|
}
|
}
|
}
|
|
|
static void puts_queue(char *cp)
|
static void puts_queue(char *cp)
|
{
|
{
|
wake_up (&keypress_wait);
|
wake_up (&keypress_wait);
|
if (!tty)
|
if (!tty)
|
return;
|
return;
|
|
|
while (*cp) {
|
while (*cp) {
|
tty_insert_flip_char (tty, *cp, 0);
|
tty_insert_flip_char (tty, *cp, 0);
|
cp++;
|
cp++;
|
}
|
}
|
tty_schedule_flip (tty);
|
tty_schedule_flip (tty);
|
}
|
}
|
|
|
static void show_stack (void)
|
static void show_stack (void)
|
{
|
{
|
unsigned long *p;
|
unsigned long *p;
|
unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
|
unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
|
int i;
|
int i;
|
__asm__("mov %0, sp\n\t": "=r" (p));
|
__asm__("mov %0, sp\n\t": "=r" (p));
|
|
|
for (i = 0; p < q; p++, i++) {
|
for (i = 0; p < q; p++, i++) {
|
if(i && !(i & 7))
|
if(i && !(i & 7))
|
printk("\n");
|
printk("\n");
|
printk("%08lX ", *p);
|
printk("%08lX ", *p);
|
}
|
}
|
}
|
}
|
|
|
static void applkey(int key, char mode)
|
static void applkey(int key, char mode)
|
{
|
{
|
static char buf[] = { 0x1b, 'O', 0x00, 0x00};
|
static char buf[] = { 0x1b, 'O', 0x00, 0x00};
|
|
|
buf[1] = (mode ? 'O' : '[');
|
buf[1] = (mode ? 'O' : '[');
|
buf[2] = key;
|
buf[2] = key;
|
puts_queue(buf);
|
puts_queue(buf);
|
}
|
}
|
|
|
static void enter(void)
|
static void enter(void)
|
{
|
{
|
put_queue (13);
|
put_queue (13);
|
if (vc_kbd_mode(kbd, VC_CRLF))
|
if (vc_kbd_mode(kbd, VC_CRLF))
|
put_queue (10);
|
put_queue (10);
|
}
|
}
|
|
|
static void caps_toggle(void)
|
static void caps_toggle(void)
|
{
|
{
|
if(rep)
|
if(rep)
|
return;
|
return;
|
chg_vc_kbd_led(kbd, VC_CAPSLOCK);
|
chg_vc_kbd_led(kbd, VC_CAPSLOCK);
|
}
|
}
|
|
|
static void caps_on (void)
|
static void caps_on (void)
|
{
|
{
|
if (rep)
|
if (rep)
|
return;
|
return;
|
set_vc_kbd_led (kbd, VC_CAPSLOCK);
|
set_vc_kbd_led (kbd, VC_CAPSLOCK);
|
}
|
}
|
|
|
static void show_ptregs (void)
|
static void show_ptregs (void)
|
{
|
{
|
if (pt_regs)
|
if (pt_regs)
|
show_regs (pt_regs);
|
show_regs (pt_regs);
|
}
|
}
|
|
|
static void hold (void)
|
static void hold (void)
|
{
|
{
|
if(rep || !tty)
|
if(rep || !tty)
|
return;
|
return;
|
|
|
/*
|
/*
|
* Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
|
* Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
|
* these routines are also activated by ^S/^Q.
|
* these routines are also activated by ^S/^Q.
|
* (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
|
* (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
|
*/
|
*/
|
if (tty->stopped)
|
if (tty->stopped)
|
start_tty (tty);
|
start_tty (tty);
|
else
|
else
|
stop_tty (tty);
|
stop_tty (tty);
|
}
|
}
|
|
|
static void num (void)
|
static void num (void)
|
{
|
{
|
if(vc_kbd_mode(kbd,VC_APPLIC))
|
if(vc_kbd_mode(kbd,VC_APPLIC))
|
applkey('P', 1);
|
applkey('P', 1);
|
else
|
else
|
bare_num ();
|
bare_num ();
|
}
|
}
|
|
|
/*
|
/*
|
* Bind this to Shift-NumLock if you work in application keypad mode
|
* Bind this to Shift-NumLock if you work in application keypad mode
|
* but want to be able to change the NumLock flag.
|
* but want to be able to change the NumLock flag.
|
* Bind this to NumLock if you prefer that the NumLock key always
|
* Bind this to NumLock if you prefer that the NumLock key always
|
* changes the NumLock flag.
|
* changes the NumLock flag.
|
*/
|
*/
|
static void bare_num (void)
|
static void bare_num (void)
|
{
|
{
|
if (!rep)
|
if (!rep)
|
chg_vc_kbd_led (kbd, VC_NUMLOCK);
|
chg_vc_kbd_led (kbd, VC_NUMLOCK);
|
}
|
}
|
|
|
static void lastcons (void)
|
static void lastcons (void)
|
{
|
{
|
/* switch to the last used console, ChN */
|
/* switch to the last used console, ChN */
|
set_console(last_console);
|
set_console(last_console);
|
}
|
}
|
|
|
static void decr_console (void)
|
static void decr_console (void)
|
{
|
{
|
int i;
|
int i;
|
int fgnum = vtdata.fgconsole->num - 1;
|
int fgnum = vtdata.fgconsole->num - 1;
|
|
|
for (i = fgnum - 1; i != fgnum; i--) {
|
for (i = fgnum - 1; i != fgnum; i--) {
|
if (i == -1)
|
if (i == -1)
|
i = MAX_NR_CONSOLES - 1;
|
i = MAX_NR_CONSOLES - 1;
|
if (vt_allocated (vt_con_data + i))
|
if (vt_allocated (vt_con_data + i))
|
break;
|
break;
|
}
|
}
|
set_console(vt_con_data + i);
|
set_console(vt_con_data + i);
|
}
|
}
|
|
|
static void incr_console (void)
|
static void incr_console (void)
|
{
|
{
|
int i;
|
int i;
|
int fgnum = vtdata.fgconsole->num - 1;
|
int fgnum = vtdata.fgconsole->num - 1;
|
|
|
for (i = fgnum + 1; i != fgnum; i++) {
|
for (i = fgnum + 1; i != fgnum; i++) {
|
if (i == MAX_NR_CONSOLES)
|
if (i == MAX_NR_CONSOLES)
|
i = 0;
|
i = 0;
|
if (vt_allocated (vt_con_data + i))
|
if (vt_allocated (vt_con_data + i))
|
break;
|
break;
|
}
|
}
|
set_console(vt_con_data + i);
|
set_console(vt_con_data + i);
|
}
|
}
|
|
|
static void send_intr (void)
|
static void send_intr (void)
|
{
|
{
|
if (!tty)
|
if (!tty)
|
return;
|
return;
|
tty_insert_flip_char (tty, 0, TTY_BREAK);
|
tty_insert_flip_char (tty, 0, TTY_BREAK);
|
tty_schedule_flip (tty);
|
tty_schedule_flip (tty);
|
}
|
}
|
|
|
static void scroll_forw (void)
|
static void scroll_forw (void)
|
{
|
{
|
scrollfront(0);
|
scrollfront(0);
|
}
|
}
|
|
|
static void scroll_back (void)
|
static void scroll_back (void)
|
{
|
{
|
scrollback(0);
|
scrollback(0);
|
}
|
}
|
|
|
static void boot_it (void)
|
static void boot_it (void)
|
{
|
{
|
ctrl_alt_del();
|
ctrl_alt_del();
|
}
|
}
|
|
|
static void compose (void)
|
static void compose (void)
|
{
|
{
|
dead_key_next = 1;
|
dead_key_next = 1;
|
}
|
}
|
|
|
int spawnpid, spawnsig;
|
int spawnpid, spawnsig;
|
|
|
static void spawn_console (void)
|
static void spawn_console (void)
|
{
|
{
|
if (spawnpid)
|
if (spawnpid)
|
if (kill_proc (spawnpid, spawnsig, 1))
|
if (kill_proc (spawnpid, spawnsig, 1))
|
spawnpid = 0;
|
spawnpid = 0;
|
}
|
}
|
|
|
static void SAK (void)
|
static void SAK (void)
|
{
|
{
|
do_SAK(tty);
|
do_SAK(tty);
|
#if 0
|
#if 0
|
/*
|
/*
|
* Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
|
* Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
|
* vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
|
* vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
|
* handling.
|
* handling.
|
*
|
*
|
* We should do this some day --- the whole point of a secure
|
* We should do this some day --- the whole point of a secure
|
* attention key is that it should be guaranteed to always
|
* attention key is that it should be guaranteed to always
|
* work.
|
* work.
|
*/
|
*/
|
vt_reset (vtdata.fgconsole);
|
vt_reset (vtdata.fgconsole);
|
do_unblank_screen (); /* not in interrupt routine? */
|
do_unblank_screen (); /* not in interrupt routine? */
|
#endif
|
#endif
|
}
|
}
|
|
|
static void do_ignore (unsigned char value, char up_flag)
|
static void do_ignore (unsigned char value, char up_flag)
|
{
|
{
|
}
|
}
|
|
|
static void do_null (void)
|
static void do_null (void)
|
{
|
{
|
compute_shiftstate ();
|
compute_shiftstate ();
|
}
|
}
|
|
|
static void do_spec (unsigned char value,char up_flag)
|
static void do_spec (unsigned char value,char up_flag)
|
{
|
{
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
if (value >= SIZE(spec_fn_table))
|
if (value >= SIZE(spec_fn_table))
|
return;
|
return;
|
spec_fn_table[value] ();
|
spec_fn_table[value] ();
|
}
|
}
|
|
|
static void do_lowercase (unsigned char value, char up_flag)
|
static void do_lowercase (unsigned char value, char up_flag)
|
{
|
{
|
printk(KERN_ERR "keyboard.c: do_lowercase was called - impossible\n");
|
printk(KERN_ERR "keyboard.c: do_lowercase was called - impossible\n");
|
}
|
}
|
|
|
static void do_self(unsigned char value, char up_flag)
|
static void do_self(unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag)
|
if (up_flag)
|
return; /* no action, if this is a key release */
|
return; /* no action, if this is a key release */
|
|
|
if (diacr)
|
if (diacr)
|
value = handle_diacr (value);
|
value = handle_diacr (value);
|
|
|
if (dead_key_next) {
|
if (dead_key_next) {
|
dead_key_next = 0;
|
dead_key_next = 0;
|
diacr = value;
|
diacr = value;
|
return;
|
return;
|
}
|
}
|
|
|
put_queue (value);
|
put_queue (value);
|
}
|
}
|
|
|
#define A_GRAVE '`'
|
#define A_GRAVE '`'
|
#define A_ACUTE '\''
|
#define A_ACUTE '\''
|
#define A_CFLEX '^'
|
#define A_CFLEX '^'
|
#define A_TILDE '~'
|
#define A_TILDE '~'
|
#define A_DIAER '"'
|
#define A_DIAER '"'
|
#define A_CEDIL ','
|
#define A_CEDIL ','
|
static unsigned char ret_diacr[] =
|
static unsigned char ret_diacr[] =
|
{A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL };
|
{A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL };
|
|
|
/* If a dead key pressed twice, output a character corresponding to it, */
|
/* If a dead key pressed twice, output a character corresponding to it, */
|
/* otherwise just remember the dead key. */
|
/* otherwise just remember the dead key. */
|
|
|
static void do_dead(unsigned char value, char up_flag)
|
static void do_dead(unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
|
|
value = ret_diacr[value];
|
value = ret_diacr[value];
|
if (diacr == value) { /* pressed twice */
|
if (diacr == value) { /* pressed twice */
|
diacr = 0;
|
diacr = 0;
|
put_queue (value);
|
put_queue (value);
|
return;
|
return;
|
}
|
}
|
diacr = value;
|
diacr = value;
|
}
|
}
|
|
|
|
|
/* If space is pressed, return the character corresponding the pending */
|
/* If space is pressed, return the character corresponding the pending */
|
/* dead key, otherwise try to combine the two. */
|
/* dead key, otherwise try to combine the two. */
|
|
|
static unsigned char handle_diacr(unsigned char ch)
|
static unsigned char handle_diacr(unsigned char ch)
|
{
|
{
|
int d = diacr;
|
int d = diacr;
|
int i;
|
int i;
|
|
|
diacr = 0;
|
diacr = 0;
|
if (ch == ' ')
|
if (ch == ' ')
|
return d;
|
return d;
|
|
|
for (i = 0; i < accent_table_size; i++) {
|
for (i = 0; i < accent_table_size; i++) {
|
if (accent_table[i].diacr == d && accent_table[i].base == ch)
|
if (accent_table[i].diacr == d && accent_table[i].base == ch)
|
return accent_table[i].result;
|
return accent_table[i].result;
|
}
|
}
|
|
|
put_queue (d);
|
put_queue (d);
|
return ch;
|
return ch;
|
}
|
}
|
|
|
static void do_cons(unsigned char value, char up_flag)
|
static void do_cons(unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
set_console(vt_con_data + value);
|
set_console(vt_con_data + value);
|
}
|
}
|
|
|
static void do_fn(unsigned char value, char up_flag)
|
static void do_fn(unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
if (value < SIZE(func_table)) {
|
if (value < SIZE(func_table)) {
|
if (func_table[value])
|
if (func_table[value])
|
puts_queue (func_table[value]);
|
puts_queue (func_table[value]);
|
} else
|
} else
|
printk (KERN_ERR "do_fn called with value=%d\n",value);
|
printk (KERN_ERR "do_fn called with value=%d\n",value);
|
}
|
}
|
|
|
static void do_pad(unsigned char value, char up_flag)
|
static void do_pad(unsigned char value, char up_flag)
|
{
|
{
|
static const char *pad_chars = "0123456789+-*/\015,.?#";
|
static const char *pad_chars = "0123456789+-*/\015,.?#";
|
static const char *app_map = "pqrstuvwxylmRQMnn?S";
|
static const char *app_map = "pqrstuvwxylmRQMnn?S";
|
|
|
if (up_flag)
|
if (up_flag)
|
return; /* no action, if this is a key release */
|
return; /* no action, if this is a key release */
|
|
|
/* kludge... shift forces cursor/number keys */
|
/* kludge... shift forces cursor/number keys */
|
if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
|
if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
|
applkey (app_map[value], 1);
|
applkey (app_map[value], 1);
|
return;
|
return;
|
}
|
}
|
|
|
if (!vc_kbd_led (kbd,VC_NUMLOCK))
|
if (!vc_kbd_led (kbd,VC_NUMLOCK))
|
switch (value) {
|
switch (value) {
|
case KVAL(K_PCOMMA):
|
case KVAL(K_PCOMMA):
|
case KVAL(K_PDOT):
|
case KVAL(K_PDOT):
|
do_fn (KVAL(K_REMOVE), 0);
|
do_fn (KVAL(K_REMOVE), 0);
|
return;
|
return;
|
case KVAL(K_P0):
|
case KVAL(K_P0):
|
do_fn (KVAL(K_INSERT), 0);
|
do_fn (KVAL(K_INSERT), 0);
|
return;
|
return;
|
case KVAL(K_P1):
|
case KVAL(K_P1):
|
do_fn (KVAL(K_SELECT), 0);
|
do_fn (KVAL(K_SELECT), 0);
|
return;
|
return;
|
case KVAL(K_P2):
|
case KVAL(K_P2):
|
do_cur(KVAL(K_DOWN), 0);
|
do_cur(KVAL(K_DOWN), 0);
|
return;
|
return;
|
case KVAL(K_P3):
|
case KVAL(K_P3):
|
do_fn (KVAL(K_PGDN), 0);
|
do_fn (KVAL(K_PGDN), 0);
|
return;
|
return;
|
case KVAL(K_P4):
|
case KVAL(K_P4):
|
do_cur(KVAL(K_LEFT), 0);
|
do_cur(KVAL(K_LEFT), 0);
|
return;
|
return;
|
case KVAL(K_P6):
|
case KVAL(K_P6):
|
do_cur(KVAL(K_RIGHT), 0);
|
do_cur(KVAL(K_RIGHT), 0);
|
return;
|
return;
|
case KVAL(K_P7):
|
case KVAL(K_P7):
|
do_fn (KVAL(K_FIND), 0);
|
do_fn (KVAL(K_FIND), 0);
|
return;
|
return;
|
case KVAL(K_P8):
|
case KVAL(K_P8):
|
do_cur(KVAL(K_UP), 0);
|
do_cur(KVAL(K_UP), 0);
|
return;
|
return;
|
case KVAL(K_P9):
|
case KVAL(K_P9):
|
do_fn (KVAL(K_PGUP), 0);
|
do_fn (KVAL(K_PGUP), 0);
|
return;
|
return;
|
case KVAL(K_P5):
|
case KVAL(K_P5):
|
applkey('G',vc_kbd_mode(kbd, VC_APPLIC));
|
applkey('G',vc_kbd_mode(kbd, VC_APPLIC));
|
return;
|
return;
|
}
|
}
|
|
|
put_queue (pad_chars[value]);
|
put_queue (pad_chars[value]);
|
if (value == KVAL(K_PENTER) && vc_kbd_mode (kbd, VC_CRLF))
|
if (value == KVAL(K_PENTER) && vc_kbd_mode (kbd, VC_CRLF))
|
put_queue (10);
|
put_queue (10);
|
}
|
}
|
|
|
static void do_cur(unsigned char value, char up_flag)
|
static void do_cur(unsigned char value, char up_flag)
|
{
|
{
|
static const char *cur_chars = "BDCA";
|
static const char *cur_chars = "BDCA";
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
|
|
applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
|
applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
|
}
|
}
|
|
|
static void do_shift(unsigned char value, char up_flag)
|
static void do_shift(unsigned char value, char up_flag)
|
{
|
{
|
int old_state = shift_state;
|
int old_state = shift_state;
|
|
|
if (rep)
|
if (rep)
|
return;
|
return;
|
|
|
/* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
|
/* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
|
if (value == KVAL(K_CAPSSHIFT)) {
|
if (value == KVAL(K_CAPSSHIFT)) {
|
value = KVAL(K_SHIFT);
|
value = KVAL(K_SHIFT);
|
if (!up_flag)
|
if (!up_flag)
|
clr_vc_kbd_led (kbd, VC_CAPSLOCK);
|
clr_vc_kbd_led (kbd, VC_CAPSLOCK);
|
}
|
}
|
|
|
if(up_flag) {
|
if(up_flag) {
|
/* handle the case that two shift or control
|
/* handle the case that two shift or control
|
keys are depressed simultaneously */
|
keys are depressed simultaneously */
|
if(k_down[value])
|
if(k_down[value])
|
k_down[value]--;
|
k_down[value]--;
|
} else
|
} else
|
k_down[value]++;
|
k_down[value]++;
|
|
|
if(k_down[value])
|
if(k_down[value])
|
shift_state |= (1 << value);
|
shift_state |= (1 << value);
|
else
|
else
|
shift_state &= ~ (1 << value);
|
shift_state &= ~ (1 << value);
|
|
|
/* kludge */
|
/* kludge */
|
if(up_flag && shift_state != old_state && npadch != -1) {
|
if(up_flag && shift_state != old_state && npadch != -1) {
|
if(kbd->kbdmode == VC_UNICODE)
|
if(kbd->kbdmode == VC_UNICODE)
|
to_utf8(npadch & 0xffff);
|
to_utf8(npadch & 0xffff);
|
else
|
else
|
put_queue(npadch & 0xff);
|
put_queue(npadch & 0xff);
|
npadch = -1;
|
npadch = -1;
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Called after returning from RAW mode or when changing consoles -
|
* Called after returning from RAW mode or when changing consoles -
|
* recompute k_down[] and shift_state from key_down[]
|
* recompute k_down[] and shift_state from key_down[]
|
* Maybe called when keymap is undefined so that shift key release is seen
|
* Maybe called when keymap is undefined so that shift key release is seen
|
*/
|
*/
|
void compute_shiftstate(void)
|
void compute_shiftstate(void)
|
{
|
{
|
int i, j, k, sym, val;
|
int i, j, k, sym, val;
|
|
|
shift_state = 0;
|
shift_state = 0;
|
for (i = 0; i < SIZE(k_down); i++)
|
for (i = 0; i < SIZE(k_down); i++)
|
k_down[i] = 0;
|
k_down[i] = 0;
|
|
|
for (i = 0; i < SIZE(key_down); i++)
|
for (i = 0; i < SIZE(key_down); i++)
|
if (key_down[i]) { /* skip this word if not a single bit on */
|
if (key_down[i]) { /* skip this word if not a single bit on */
|
k = i * BITS_PER_LONG;
|
k = i * BITS_PER_LONG;
|
for (j = 0; j < BITS_PER_LONG; j++, k++)
|
for (j = 0; j < BITS_PER_LONG; j++, k++)
|
if (test_bit (k, key_down)) {
|
if (test_bit (k, key_down)) {
|
sym = U(plain_map[k]);
|
sym = U(plain_map[k]);
|
if (KTYP(sym) == KT_SHIFT) {
|
if (KTYP(sym) == KT_SHIFT) {
|
val = KVAL (sym);
|
val = KVAL (sym);
|
if (val == KVAL(K_CAPSSHIFT))
|
if (val == KVAL(K_CAPSSHIFT))
|
val = KVAL(K_SHIFT);
|
val = KVAL(K_SHIFT);
|
k_down[val] ++;
|
k_down[val] ++;
|
shift_state |= (1 << val);
|
shift_state |= (1 << val);
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
|
static void do_meta(unsigned char value, char up_flag)
|
static void do_meta(unsigned char value, char up_flag)
|
{
|
{
|
if(up_flag)
|
if(up_flag)
|
return;
|
return;
|
|
|
if(vc_kbd_mode(kbd, VC_META)) {
|
if(vc_kbd_mode(kbd, VC_META)) {
|
put_queue ('\033');
|
put_queue ('\033');
|
put_queue (value);
|
put_queue (value);
|
} else
|
} else
|
put_queue (value | 0x80);
|
put_queue (value | 0x80);
|
}
|
}
|
|
|
static void do_ascii(unsigned char value, char up_flag)
|
static void do_ascii(unsigned char value, char up_flag)
|
{
|
{
|
int base;
|
int base;
|
|
|
if (up_flag)
|
if (up_flag)
|
return;
|
return;
|
|
|
if (value < 10) /* decimal input of code, while Alt depressed */
|
if (value < 10) /* decimal input of code, while Alt depressed */
|
base = 10;
|
base = 10;
|
else { /* hexadecimal input of code, while AltGr depressed */
|
else { /* hexadecimal input of code, while AltGr depressed */
|
value -= 10;
|
value -= 10;
|
base = 16;
|
base = 16;
|
}
|
}
|
|
|
if(npadch == -1)
|
if(npadch == -1)
|
npadch = value;
|
npadch = value;
|
else
|
else
|
npadch = npadch * base + value;
|
npadch = npadch * base + value;
|
}
|
}
|
|
|
static void do_lock (unsigned char value, char up_flag)
|
static void do_lock (unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag || rep)
|
if (up_flag || rep)
|
return;
|
return;
|
chg_vc_kbd_lock (kbd, value);
|
chg_vc_kbd_lock (kbd, value);
|
}
|
}
|
|
|
static void do_slock (unsigned char value, char up_flag)
|
static void do_slock (unsigned char value, char up_flag)
|
{
|
{
|
if (up_flag || rep)
|
if (up_flag || rep)
|
return;
|
return;
|
chg_vc_kbd_slock (kbd, value);
|
chg_vc_kbd_slock (kbd, value);
|
}
|
}
|
|
|
/*
|
/*
|
* The leds display either
|
* The leds display either
|
* (i) The status of NumLock, CapsLock and ScrollLock.
|
* (i) The status of NumLock, CapsLock and ScrollLock.
|
* (ii) Whatever pattern of lights people wait to show using KDSETLED.
|
* (ii) Whatever pattern of lights people wait to show using KDSETLED.
|
* (iii) Specified bits of specified words in kernel memory.
|
* (iii) Specified bits of specified words in kernel memory.
|
*/
|
*/
|
|
|
static unsigned char ledstate = 0xff; /* undefined */
|
static unsigned char ledstate = 0xff; /* undefined */
|
static unsigned char ledioctl;
|
static unsigned char ledioctl;
|
|
|
unsigned char getledstate(void)
|
unsigned char getledstate(void)
|
{
|
{
|
return ledstate;
|
return ledstate;
|
}
|
}
|
|
|
void setledstate(struct kbd_struct *kbd, unsigned int led)
|
void setledstate(struct kbd_struct *kbd, unsigned int led)
|
{
|
{
|
if(!(led & ~7)) {
|
if(!(led & ~7)) {
|
ledioctl = led;
|
ledioctl = led;
|
kbd->ledmode = LED_SHOW_IOCTL;
|
kbd->ledmode = LED_SHOW_IOCTL;
|
} else
|
} else
|
kbd->ledmode = LED_SHOW_FLAGS;
|
kbd->ledmode = LED_SHOW_FLAGS;
|
set_leds();
|
set_leds();
|
}
|
}
|
|
|
static struct ledptr {
|
static struct ledptr {
|
unsigned int *addr;
|
unsigned int *addr;
|
unsigned int mask;
|
unsigned int mask;
|
unsigned char valid:1;
|
unsigned char valid:1;
|
} ledptrs[3];
|
} ledptrs[3];
|
|
|
void register_leds(int console, unsigned int led,
|
void register_leds(int console, unsigned int led,
|
unsigned int *addr, unsigned int mask)
|
unsigned int *addr, unsigned int mask)
|
{
|
{
|
struct kbd_struct *kbd = vt_con_data[console].kbd;
|
struct kbd_struct *kbd = vt_con_data[console].kbd;
|
if(led < 3) {
|
if(led < 3) {
|
ledptrs[led].addr = addr;
|
ledptrs[led].addr = addr;
|
ledptrs[led].mask = mask;
|
ledptrs[led].mask = mask;
|
ledptrs[led].valid= 1;
|
ledptrs[led].valid= 1;
|
kbd->ledmode = LED_SHOW_MEM;
|
kbd->ledmode = LED_SHOW_MEM;
|
} else
|
} else
|
kbd->ledmode = LED_SHOW_FLAGS;
|
kbd->ledmode = LED_SHOW_FLAGS;
|
}
|
}
|
|
|
static inline unsigned char getleds(void)
|
static inline unsigned char getleds(void)
|
{
|
{
|
struct kbd_struct *kbd = vtdata.fgconsole->kbd;
|
struct kbd_struct *kbd = vtdata.fgconsole->kbd;
|
unsigned char leds;
|
unsigned char leds;
|
|
|
if(kbd->ledmode == LED_SHOW_IOCTL)
|
if(kbd->ledmode == LED_SHOW_IOCTL)
|
return ledioctl;
|
return ledioctl;
|
leds = kbd->ledflagstate;
|
leds = kbd->ledflagstate;
|
if (kbd->ledmode == LED_SHOW_MEM) {
|
if (kbd->ledmode == LED_SHOW_MEM) {
|
if (ledptrs[0].valid) {
|
if (ledptrs[0].valid) {
|
if (*ledptrs[0].addr & ledptrs[0].mask)
|
if (*ledptrs[0].addr & ledptrs[0].mask)
|
leds |= 1;
|
leds |= 1;
|
else
|
else
|
leds &= ~1;
|
leds &= ~1;
|
}
|
}
|
if (ledptrs[1].valid) {
|
if (ledptrs[1].valid) {
|
if (*ledptrs[1].addr & ledptrs[1].mask)
|
if (*ledptrs[1].addr & ledptrs[1].mask)
|
leds |= 2;
|
leds |= 2;
|
else
|
else
|
leds &= ~2;
|
leds &= ~2;
|
}
|
}
|
if (ledptrs[2].valid) {
|
if (ledptrs[2].valid) {
|
if(*ledptrs[2].addr & ledptrs[2].mask)
|
if(*ledptrs[2].addr & ledptrs[2].mask)
|
leds |= 4;
|
leds |= 4;
|
else
|
else
|
leds &= ~4;
|
leds &= ~4;
|
}
|
}
|
}
|
}
|
return leds;
|
return leds;
|
}
|
}
|
|
|
/*
|
/*
|
* This routine is the bottom half of the keyboard interrupt
|
* This routine is the bottom half of the keyboard interrupt
|
* routine, and runs with all interrupts enabled. It does
|
* routine, and runs with all interrupts enabled. It does
|
* console changing, led setting and copy_to_cooked, which can
|
* console changing, led setting and copy_to_cooked, which can
|
* take a reasonably long time.
|
* take a reasonably long time.
|
*
|
*
|
* Aside from timing (which isn't really that important for
|
* Aside from timing (which isn't really that important for
|
* keyboard interrupts as they happen often), using the software
|
* keyboard interrupts as they happen often), using the software
|
* interrupt routines for this thing allows us to easily mask
|
* interrupt routines for this thing allows us to easily mask
|
* this when we don't want any of the above to happen. Not yet
|
* this when we don't want any of the above to happen. Not yet
|
* used, but this allows for easy and efficient race-condition
|
* used, but this allows for easy and efficient race-condition
|
* prevention later on.
|
* prevention later on.
|
*/
|
*/
|
static void kbd_bh(void)
|
static void kbd_bh(void)
|
{
|
{
|
unsigned char leds = getleds();
|
unsigned char leds = getleds();
|
|
|
tty = *vtdata.fgconsole->tty;
|
tty = *vtdata.fgconsole->tty;
|
kbd = vtdata.fgconsole->kbd;
|
kbd = vtdata.fgconsole->kbd;
|
|
|
if (rep) {
|
if (rep) {
|
/*
|
/*
|
* This prevents the kbd_key routine from being called
|
* This prevents the kbd_key routine from being called
|
* twice, once by this BH, and once by the interrupt
|
* twice, once by this BH, and once by the interrupt
|
* routine. Maybe we should put the key press in a
|
* routine. Maybe we should put the key press in a
|
* buffer or variable, and then call the BH...
|
* buffer or variable, and then call the BH...
|
*/
|
*/
|
disable_irq (IRQ_KEYBOARDRX);
|
disable_irq (IRQ_KEYBOARDRX);
|
if (kbd_repeatkey != -1)
|
if (kbd_repeatkey != -1)
|
kbd_processkey (kbd_repeatkey, 0, 1);
|
kbd_processkey (kbd_repeatkey, 0, 1);
|
enable_irq (IRQ_KEYBOARDRX);
|
enable_irq (IRQ_KEYBOARDRX);
|
rep = 0;
|
rep = 0;
|
}
|
}
|
|
|
if (leds != ledstate) {
|
if (leds != ledstate) {
|
ledstate = leds;
|
ledstate = leds;
|
kbd_drv_setleds(leds);
|
kbd_drv_setleds(leds);
|
}
|
}
|
}
|
}
|
|
|
/*
|
/*
|
* Initialise a kbd struct
|
* Initialise a kbd struct
|
*/
|
*/
|
int kbd_struct_init (struct vt *vt, int init)
|
int kbd_struct_init (struct vt *vt, int init)
|
{
|
{
|
vt->kbd->ledflagstate = vt->kbd->default_ledflagstate = KBD_DEFLEDS;
|
vt->kbd->ledflagstate = vt->kbd->default_ledflagstate = KBD_DEFLEDS;
|
vt->kbd->ledmode = LED_SHOW_FLAGS;
|
vt->kbd->ledmode = LED_SHOW_FLAGS;
|
vt->kbd->lockstate = KBD_DEFLOCK;
|
vt->kbd->lockstate = KBD_DEFLOCK;
|
vt->kbd->slockstate = 0;
|
vt->kbd->slockstate = 0;
|
vt->kbd->modeflags = KBD_DEFMODE;
|
vt->kbd->modeflags = KBD_DEFMODE;
|
vt->kbd->kbdmode = VC_XLATE;
|
vt->kbd->kbdmode = VC_XLATE;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg)
|
int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg)
|
{
|
{
|
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
|
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
|
int i;
|
int i;
|
|
|
switch (cmd) {
|
switch (cmd) {
|
case KDGKBTYPE:
|
case KDGKBTYPE:
|
/*
|
/*
|
* This is naive.
|
* This is naive.
|
*/
|
*/
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned char));
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned char));
|
if (!i)
|
if (!i)
|
put_user (KB_101, (char *)arg);
|
put_user (KB_101, (char *)arg);
|
return i;
|
return i;
|
|
|
case KDADDIO:
|
case KDADDIO:
|
case KDDELIO:
|
case KDDELIO:
|
/*
|
/*
|
* KDADDIO and KDDELIO may be able to add ports beyond what
|
* KDADDIO and KDDELIO may be able to add ports beyond what
|
* we reject here, but to be safe...
|
* we reject here, but to be safe...
|
*/
|
*/
|
if (arg < GPFIRST || arg > GPLAST)
|
if (arg < GPFIRST || arg > GPLAST)
|
return -EINVAL;
|
return -EINVAL;
|
return sys_ioperm (arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
|
return sys_ioperm (arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
|
|
|
case KDENABIO:
|
case KDENABIO:
|
case KDDISABIO:
|
case KDDISABIO:
|
return sys_ioperm (GPFIRST, GPLAST, (cmd == KDENABIO)) ? -ENXIO : 0;
|
return sys_ioperm (GPFIRST, GPLAST, (cmd == KDENABIO)) ? -ENXIO : 0;
|
|
|
case KDMAPDISP:
|
case KDMAPDISP:
|
case KDUNMAPDISP:
|
case KDUNMAPDISP:
|
/*
|
/*
|
* These work like a combination of mmap and KDENABIO.
|
* These work like a combination of mmap and KDENABIO.
|
* this could easily be finished.
|
* this could easily be finished.
|
*/
|
*/
|
return -EINVAL;
|
return -EINVAL;
|
|
|
case KDSKBMODE:
|
case KDSKBMODE:
|
switch (arg) {
|
switch (arg) {
|
case K_RAW:
|
case K_RAW:
|
vt->kbd->kbdmode = VC_RAW;
|
vt->kbd->kbdmode = VC_RAW;
|
break;
|
break;
|
case K_MEDIUMRAW:
|
case K_MEDIUMRAW:
|
vt->kbd->kbdmode = VC_MEDIUMRAW;
|
vt->kbd->kbdmode = VC_MEDIUMRAW;
|
break;
|
break;
|
case K_XLATE:
|
case K_XLATE:
|
vt->kbd->kbdmode = VC_XLATE;
|
vt->kbd->kbdmode = VC_XLATE;
|
compute_shiftstate ();
|
compute_shiftstate ();
|
break;
|
break;
|
case K_UNICODE:
|
case K_UNICODE:
|
vt->kbd->kbdmode = VC_UNICODE;
|
vt->kbd->kbdmode = VC_UNICODE;
|
compute_shiftstate ();
|
compute_shiftstate ();
|
break;
|
break;
|
default:
|
default:
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
if (tty->ldisc.flush_buffer)
|
if (tty->ldisc.flush_buffer)
|
tty->ldisc.flush_buffer (*vt->tty);
|
tty->ldisc.flush_buffer (*vt->tty);
|
return 0;
|
return 0;
|
|
|
case KDGKBMODE:
|
case KDGKBMODE:
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
|
if (!i) {
|
if (!i) {
|
int ucval;
|
int ucval;
|
ucval = ((vt->kbd->kbdmode == VC_RAW) ? K_RAW :
|
ucval = ((vt->kbd->kbdmode == VC_RAW) ? K_RAW :
|
(vt->kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
|
(vt->kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
|
(vt->kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE);
|
(vt->kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE);
|
put_user (ucval, (int *)arg);
|
put_user (ucval, (int *)arg);
|
}
|
}
|
return i;
|
return i;
|
|
|
case KDSKBMETA:
|
case KDSKBMETA:
|
/*
|
/*
|
* this could be folded into KDSKBMODE, but for compatibility
|
* this could be folded into KDSKBMODE, but for compatibility
|
* reasons, it is not so easy to fold kDGKBMETA into KDGKBMODE.
|
* reasons, it is not so easy to fold kDGKBMETA into KDGKBMODE.
|
*/
|
*/
|
switch (arg) {
|
switch (arg) {
|
case K_METABIT:
|
case K_METABIT:
|
clr_vc_kbd_mode (vt->kbd, VC_META);
|
clr_vc_kbd_mode (vt->kbd, VC_META);
|
break;
|
break;
|
case K_ESCPREFIX:
|
case K_ESCPREFIX:
|
set_vc_kbd_mode (vt->kbd, VC_META);
|
set_vc_kbd_mode (vt->kbd, VC_META);
|
break;
|
break;
|
default:
|
default:
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
return 0;
|
return 0;
|
|
|
case KDGKBMETA:
|
case KDGKBMETA:
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
|
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
|
if (!i) {
|
if (!i) {
|
int ucval;
|
int ucval;
|
ucval = (vc_kbd_mode (vt->kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
|
ucval = (vc_kbd_mode (vt->kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
|
put_user (ucval, (int *)arg);
|
put_user (ucval, (int *)arg);
|
}
|
}
|
return i;
|
return i;
|
|
|
case KDGETKEYCODE: {
|
case KDGETKEYCODE: {
|
struct kbkeycode *const a = (struct kbkeycode *)arg;
|
struct kbkeycode *const a = (struct kbkeycode *)arg;
|
|
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbkeycode));
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbkeycode));
|
if (!i) {
|
if (!i) {
|
unsigned int sc;
|
unsigned int sc;
|
|
|
sc = get_user (&a->scancode);
|
sc = get_user (&a->scancode);
|
i = getkeycode (sc);
|
i = getkeycode (sc);
|
if (i > 0)
|
if (i > 0)
|
put_user (i, &a->keycode);
|
put_user (i, &a->keycode);
|
i = 0;
|
i = 0;
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
case KDSETKEYCODE: {
|
case KDSETKEYCODE: {
|
struct kbkeycode *const a = (struct kbkeycode *)arg;
|
struct kbkeycode *const a = (struct kbkeycode *)arg;
|
|
|
i = verify_area (VERIFY_READ, a, sizeof (struct kbkeycode));
|
i = verify_area (VERIFY_READ, a, sizeof (struct kbkeycode));
|
if (!i) {
|
if (!i) {
|
unsigned int sc, kc;
|
unsigned int sc, kc;
|
sc = get_user (&a->scancode);
|
sc = get_user (&a->scancode);
|
kc = get_user (&a->keycode);
|
kc = get_user (&a->keycode);
|
i = setkeycode (sc, kc);
|
i = setkeycode (sc, kc);
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
case KDGKBENT: {
|
case KDGKBENT: {
|
struct kbentry *const a = (struct kbentry *)arg;
|
struct kbentry *const a = (struct kbentry *)arg;
|
|
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
|
if (!i) {
|
if (!i) {
|
ushort *keymap, val;
|
ushort *keymap, val;
|
u_char s;
|
u_char s;
|
i = get_user (&a->kb_index);
|
i = get_user (&a->kb_index);
|
if (i >= NR_KEYS)
|
if (i >= NR_KEYS)
|
return -EINVAL;
|
return -EINVAL;
|
s = get_user (&a->kb_table);
|
s = get_user (&a->kb_table);
|
if (s >= MAX_NR_KEYMAPS)
|
if (s >= MAX_NR_KEYMAPS)
|
return -EINVAL;
|
return -EINVAL;
|
keymap = key_maps[s];
|
keymap = key_maps[s];
|
if (keymap) {
|
if (keymap) {
|
val = U(keymap[i]);
|
val = U(keymap[i]);
|
if (vt->kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
|
if (vt->kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
|
val = K_HOLE;
|
val = K_HOLE;
|
} else
|
} else
|
val = (i ? K_HOLE : K_NOSUCHMAP);
|
val = (i ? K_HOLE : K_NOSUCHMAP);
|
put_user (val, &a->kb_value);
|
put_user (val, &a->kb_value);
|
i = 0;
|
i = 0;
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
case KDSKBENT: {
|
case KDSKBENT: {
|
struct kbentry *const a = (struct kbentry *)arg;
|
struct kbentry *const a = (struct kbentry *)arg;
|
|
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
|
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
|
if (!i) {
|
if (!i) {
|
ushort *key_map;
|
ushort *key_map;
|
u_char s;
|
u_char s;
|
u_short v, ov;
|
u_short v, ov;
|
|
|
if ((i = get_user(&a->kb_index)) >= NR_KEYS)
|
if ((i = get_user(&a->kb_index)) >= NR_KEYS)
|
return -EINVAL;
|
return -EINVAL;
|
if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
|
if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
|
return -EINVAL;
|
return -EINVAL;
|
v = get_user(&a->kb_value);
|
v = get_user(&a->kb_value);
|
if (!i && v == K_NOSUCHMAP) {
|
if (!i && v == K_NOSUCHMAP) {
|
/* disallocate map */
|
/* disallocate map */
|
key_map = key_maps[s];
|
key_map = key_maps[s];
|
if (s && key_map) {
|
if (s && key_map) {
|
key_maps[s] = 0;
|
key_maps[s] = 0;
|
if (key_map[0] == U(K_ALLOCATED)) {
|
if (key_map[0] == U(K_ALLOCATED)) {
|
kfree_s(key_map, sizeof(plain_map));
|
kfree_s(key_map, sizeof(plain_map));
|
keymap_count--;
|
keymap_count--;
|
}
|
}
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (KTYP(v) < NR_TYPES) {
|
if (KTYP(v) < NR_TYPES) {
|
if (KVAL(v) > max_vals[KTYP(v)])
|
if (KVAL(v) > max_vals[KTYP(v)])
|
return -EINVAL;
|
return -EINVAL;
|
} else
|
} else
|
if (kbd->kbdmode != VC_UNICODE)
|
if (kbd->kbdmode != VC_UNICODE)
|
return -EINVAL;
|
return -EINVAL;
|
|
|
/* assignment to entry 0 only tests validity of args */
|
/* assignment to entry 0 only tests validity of args */
|
if (!i)
|
if (!i)
|
return 0;
|
return 0;
|
|
|
if (!(key_map = key_maps[s])) {
|
if (!(key_map = key_maps[s])) {
|
int j;
|
int j;
|
|
|
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
|
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
|
return -EPERM;
|
return -EPERM;
|
|
|
key_map = (ushort *) kmalloc(sizeof(plain_map),
|
key_map = (ushort *) kmalloc(sizeof(plain_map),
|
GFP_KERNEL);
|
GFP_KERNEL);
|
if (!key_map)
|
if (!key_map)
|
return -ENOMEM;
|
return -ENOMEM;
|
key_maps[s] = key_map;
|
key_maps[s] = key_map;
|
key_map[0] = U(K_ALLOCATED);
|
key_map[0] = U(K_ALLOCATED);
|
for (j = 1; j < NR_KEYS; j++)
|
for (j = 1; j < NR_KEYS; j++)
|
key_map[j] = U(K_HOLE);
|
key_map[j] = U(K_HOLE);
|
keymap_count++;
|
keymap_count++;
|
}
|
}
|
ov = U(key_map[i]);
|
ov = U(key_map[i]);
|
if (v == ov)
|
if (v == ov)
|
return 0; /* nothing to do */
|
return 0; /* nothing to do */
|
/*
|
/*
|
* Only the Superuser can set or unset the Secure
|
* Only the Superuser can set or unset the Secure
|
* Attention Key.
|
* Attention Key.
|
*/
|
*/
|
if (((ov == K_SAK) || (v == K_SAK)) && !suser())
|
if (((ov == K_SAK) || (v == K_SAK)) && !suser())
|
return -EPERM;
|
return -EPERM;
|
key_map[i] = U(v);
|
key_map[i] = U(v);
|
if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
|
if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
|
compute_shiftstate();
|
compute_shiftstate();
|
return 0;
|
return 0;
|
}
|
}
|
return i;
|
return i;
|
}
|
}
|
|
|
case KDGKBSENT: {
|
case KDGKBSENT: {
|
struct kbsentry *a = (struct kbsentry *)arg;
|
struct kbsentry *a = (struct kbsentry *)arg;
|
char *p;
|
char *p;
|
u_char *q;
|
u_char *q;
|
int sz;
|
int sz;
|
|
|
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
|
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
|
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
|
return -EINVAL;
|
return -EINVAL;
|
sz = sizeof(a->kb_string) - 1; /* sz should have been
|
sz = sizeof(a->kb_string) - 1; /* sz should have been
|
a struct member */
|
a struct member */
|
q = a->kb_string;
|
q = a->kb_string;
|
p = func_table[i];
|
p = func_table[i];
|
if(p)
|
if(p)
|
for ( ; *p && sz; p++, sz--)
|
for ( ; *p && sz; p++, sz--)
|
put_user(*p, q++);
|
put_user(*p, q++);
|
put_user('\0', q);
|
put_user('\0', q);
|
return ((p && *p) ? -EOVERFLOW : 0);
|
return ((p && *p) ? -EOVERFLOW : 0);
|
}
|
}
|
|
|
case KDSKBSENT: {
|
case KDSKBSENT: {
|
struct kbsentry * const a = (struct kbsentry *)arg;
|
struct kbsentry * const a = (struct kbsentry *)arg;
|
int delta;
|
int delta;
|
char *first_free, *fj, *fnw;
|
char *first_free, *fj, *fnw;
|
int j, k, sz;
|
int j, k, sz;
|
u_char *p;
|
u_char *p;
|
char *q;
|
char *q;
|
|
|
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
|
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
|
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
|
return -EINVAL;
|
return -EINVAL;
|
q = func_table[i];
|
q = func_table[i];
|
|
|
first_free = funcbufptr + (funcbufsize - funcbufleft);
|
first_free = funcbufptr + (funcbufsize - funcbufleft);
|
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
|
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
|
if (j < MAX_NR_FUNC)
|
if (j < MAX_NR_FUNC)
|
fj = func_table[j];
|
fj = func_table[j];
|
else
|
else
|
fj = first_free;
|
fj = first_free;
|
|
|
delta = (q ? -strlen(q) : 1);
|
delta = (q ? -strlen(q) : 1);
|
sz = sizeof(a->kb_string); /* sz should have been
|
sz = sizeof(a->kb_string); /* sz should have been
|
a struct member */
|
a struct member */
|
for (p = a->kb_string; get_user(p) && sz; p++,sz--)
|
for (p = a->kb_string; get_user(p) && sz; p++,sz--)
|
delta++;
|
delta++;
|
if (!sz)
|
if (!sz)
|
return -EOVERFLOW;
|
return -EOVERFLOW;
|
if (delta <= funcbufleft) { /* it fits in current buf */
|
if (delta <= funcbufleft) { /* it fits in current buf */
|
if (j < MAX_NR_FUNC) {
|
if (j < MAX_NR_FUNC) {
|
memmove(fj + delta, fj, first_free - fj);
|
memmove(fj + delta, fj, first_free - fj);
|
for (k = j; k < MAX_NR_FUNC; k++)
|
for (k = j; k < MAX_NR_FUNC; k++)
|
if (func_table[k])
|
if (func_table[k])
|
func_table[k] += delta;
|
func_table[k] += delta;
|
}
|
}
|
if (!q)
|
if (!q)
|
func_table[i] = fj;
|
func_table[i] = fj;
|
funcbufleft -= delta;
|
funcbufleft -= delta;
|
} else { /* allocate a larger buffer */
|
} else { /* allocate a larger buffer */
|
sz = 256;
|
sz = 256;
|
while (sz < funcbufsize - funcbufleft + delta)
|
while (sz < funcbufsize - funcbufleft + delta)
|
sz <<= 1;
|
sz <<= 1;
|
fnw = (char *) kmalloc(sz, GFP_KERNEL);
|
fnw = (char *) kmalloc(sz, GFP_KERNEL);
|
if(!fnw)
|
if(!fnw)
|
return -ENOMEM;
|
return -ENOMEM;
|
|
|
if (!q)
|
if (!q)
|
func_table[i] = fj;
|
func_table[i] = fj;
|
if (fj > funcbufptr)
|
if (fj > funcbufptr)
|
memmove(fnw, funcbufptr, fj - funcbufptr);
|
memmove(fnw, funcbufptr, fj - funcbufptr);
|
for (k = 0; k < j; k++)
|
for (k = 0; k < j; k++)
|
if (func_table[k])
|
if (func_table[k])
|
func_table[k] = fnw + (func_table[k] - funcbufptr);
|
func_table[k] = fnw + (func_table[k] - funcbufptr);
|
|
|
if (first_free > fj) {
|
if (first_free > fj) {
|
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
|
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
|
for (k = j; k < MAX_NR_FUNC; k++)
|
for (k = j; k < MAX_NR_FUNC; k++)
|
if (func_table[k])
|
if (func_table[k])
|
func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
|
func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
|
}
|
}
|
if (funcbufptr != func_buf)
|
if (funcbufptr != func_buf)
|
kfree_s(funcbufptr, funcbufsize);
|
kfree_s(funcbufptr, funcbufsize);
|
funcbufptr = fnw;
|
funcbufptr = fnw;
|
funcbufleft = funcbufleft - delta + sz - funcbufsize;
|
funcbufleft = funcbufleft - delta + sz - funcbufsize;
|
funcbufsize = sz;
|
funcbufsize = sz;
|
}
|
}
|
for (p = a->kb_string, q = func_table[i]; ; p++, q++)
|
for (p = a->kb_string, q = func_table[i]; ; p++, q++)
|
if (!(*q = get_user(p)))
|
if (!(*q = get_user(p)))
|
break;
|
break;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
case KDGKBDIACR: {
|
case KDGKBDIACR: {
|
struct kbdiacrs *a = (struct kbdiacrs *)arg;
|
struct kbdiacrs *a = (struct kbdiacrs *)arg;
|
|
|
i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
|
i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
put_user(accent_table_size, &a->kb_cnt);
|
put_user(accent_table_size, &a->kb_cnt);
|
memcpy_tofs(a->kbdiacr, accent_table,
|
memcpy_tofs(a->kbdiacr, accent_table,
|
accent_table_size*sizeof(struct kbdiacr));
|
accent_table_size*sizeof(struct kbdiacr));
|
return 0;
|
return 0;
|
}
|
}
|
|
|
case KDSKBDIACR: {
|
case KDSKBDIACR: {
|
struct kbdiacrs *a = (struct kbdiacrs *)arg;
|
struct kbdiacrs *a = (struct kbdiacrs *)arg;
|
unsigned int ct;
|
unsigned int ct;
|
|
|
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
|
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
ct = get_user(&a->kb_cnt);
|
ct = get_user(&a->kb_cnt);
|
if (ct >= MAX_DIACR)
|
if (ct >= MAX_DIACR)
|
return -EINVAL;
|
return -EINVAL;
|
accent_table_size = ct;
|
accent_table_size = ct;
|
memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
|
memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* the ioctls below read/set the flags usually shown in the leds */
|
/* the ioctls below read/set the flags usually shown in the leds */
|
/* don't use them - they will go away without warning */
|
/* don't use them - they will go away without warning */
|
case KDGKBLED:
|
case KDGKBLED:
|
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
|
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
put_user(vt->kbd->ledflagstate |
|
put_user(vt->kbd->ledflagstate |
|
(vt->kbd->default_ledflagstate << 4), (char *) arg);
|
(vt->kbd->default_ledflagstate << 4), (char *) arg);
|
return 0;
|
return 0;
|
|
|
case KDSKBLED:
|
case KDSKBLED:
|
if (arg & ~0x77)
|
if (arg & ~0x77)
|
return -EINVAL;
|
return -EINVAL;
|
vt->kbd->ledflagstate = (arg & 7);
|
vt->kbd->ledflagstate = (arg & 7);
|
vt->kbd->default_ledflagstate = ((arg >> 4) & 7);
|
vt->kbd->default_ledflagstate = ((arg >> 4) & 7);
|
set_leds ();
|
set_leds ();
|
return 0;
|
return 0;
|
|
|
/* the ioctls below only set the lights, not the functions */
|
/* the ioctls below only set the lights, not the functions */
|
/* for those, see KDGKBLED and KDSKBLED above */
|
/* for those, see KDGKBLED and KDSKBLED above */
|
case KDGETLED:
|
case KDGETLED:
|
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
|
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
|
if (i)
|
if (i)
|
return i;
|
return i;
|
put_user(getledstate(), (char *) arg);
|
put_user(getledstate(), (char *) arg);
|
return 0;
|
return 0;
|
|
|
case KDSETLED:
|
case KDSETLED:
|
setledstate(kbd, arg);
|
setledstate(kbd, arg);
|
return 0;
|
return 0;
|
|
|
/*
|
/*
|
* A process can indicate its willingness to accept signals
|
* A process can indicate its willingness to accept signals
|
* generated by pressing an appropriate key combination.
|
* generated by pressing an appropriate key combination.
|
* Thus, one can have a daemon that e.g. spawns a new console
|
* Thus, one can have a daemon that e.g. spawns a new console
|
* upon a keypess and then changes to it.
|
* upon a keypess and then changes to it.
|
* Probably init should be changed to do this (and have a
|
* Probably init should be changed to do this (and have a
|
* field ks (`keyboard signal') in inittab describing the
|
* field ks (`keyboard signal') in inittab describing the
|
* desired acion), so that the number of background daemons
|
* desired acion), so that the number of background daemons
|
* does not increase.
|
* does not increase.
|
*/
|
*/
|
case KDSIGACCEPT: {
|
case KDSIGACCEPT: {
|
if (arg < 1 || arg > NSIG || arg == SIGKILL)
|
if (arg < 1 || arg > NSIG || arg == SIGKILL)
|
return -EINVAL;
|
return -EINVAL;
|
spawnpid = current->pid;
|
spawnpid = current->pid;
|
spawnsig = arg;
|
spawnsig = arg;
|
return 0;
|
return 0;
|
}
|
}
|
default:
|
default:
|
return -ENOIOCTLCMD;
|
return -ENOIOCTLCMD;
|
}
|
}
|
}
|
}
|
|
|
int kbd_init (void)
|
int kbd_init (void)
|
{
|
{
|
int ret;
|
int ret;
|
init_bh(KEYBOARD_BH, kbd_bh);
|
init_bh(KEYBOARD_BH, kbd_bh);
|
ret = kbd_drv_init();
|
ret = kbd_drv_init();
|
mark_bh(KEYBOARD_BH);
|
mark_bh(KEYBOARD_BH);
|
return ret;
|
return ret;
|
}
|
}
|
|
|