diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1997-04-29 21:13:14 +0000 |
commit | 19c9bba94152148523ba0f7ef7cffe3d45656b11 (patch) | |
tree | 40b1cb534496a7f1ca0f5c314a523c69f1fee464 /drivers/sbus/char/sunkbd.c | |
parent | 7206675c40394c78a90e74812bbdbf8cf3cca1be (diff) |
Import of Linux/MIPS 2.1.36
Diffstat (limited to 'drivers/sbus/char/sunkbd.c')
-rw-r--r-- | drivers/sbus/char/sunkbd.c | 264 |
1 files changed, 210 insertions, 54 deletions
diff --git a/drivers/sbus/char/sunkbd.c b/drivers/sbus/char/sunkbd.c index b5d3c42ea..87fb0fea4 100644 --- a/drivers/sbus/char/sunkbd.c +++ b/drivers/sbus/char/sunkbd.c @@ -14,11 +14,16 @@ #include <linux/signal.h> #include <linux/string.h> #include <linux/fcntl.h> +#include <linux/poll.h> +#include <linux/random.h> +#include <linux/init.h> + #include <asm/kbio.h> #include <asm/vuid_event.h> #include <asm/delay.h> #include <asm/bitops.h> #include <asm/oplib.h> +#include <asm/uaccess.h> #include "../../char/kbd_kern.h" #include "../../char/diacr.h" @@ -58,7 +63,15 @@ extern void reset_vc(unsigned int new_console); extern void scrollback(int); extern void scrollfront(int); -unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ +unsigned char kbd_read_mask = 0x01; /* modified by psaux.c */ +unsigned char aux_device_present = 0x00; /* To make kernel/ksyms.c happy */ + +struct wait_queue * keypress_wait = NULL; + +void keyboard_wait_for_keypress(void) +{ + sleep_on(&keypress_wait); +} /* * global state includes the following, and various static variables @@ -89,6 +102,9 @@ struct kbd_struct kbd_table[MAX_NR_CONSOLES]; static struct tty_struct **ttytab; static struct kbd_struct * kbd = kbd_table; static struct tty_struct * tty = NULL; +static int compose_led_on = 0; +static int kbd_delay_ticks = HZ / 5; +static int kbd_rate_ticks = HZ / 20; extern void compute_shiftstate(void); @@ -141,8 +157,8 @@ volatile unsigned char sunkbd_type; #define SUNKBD_TYPE3 0x03 #define SUNKBD_TYPE4 0x04 -#define SUNKBD_LOUT_TYP4 0x00 -#define SUNKBD_LOUT_TYP5 0x22 +#define SUNKBD_LOUT_TYP4 0x00 +#define SUNKBD_LOUT_TYP5_MASK 0x20 volatile int kbd_reset_pending; volatile int kbd_layout_pending; @@ -213,6 +229,8 @@ static inline unsigned char vcleds_to_sunkbd(unsigned char vcleds) retval |= LED_NLOCK; if(vcleds & (1<<VC_CAPSLOCK)) retval |= LED_CLOCK; + if(compose_led_on) + retval |= LED_CMPOSE; return retval; } @@ -331,6 +349,21 @@ static unsigned char e0_keys[128] = { 0, 0, 0, 0, 0, 0, 0, 0 /* 0x78-0x7f */ }; +/* we use this map to determine if a particular key should not be + autorepeated. We don't autorepeat CONTROL, LSHIFT, CAPS, + ALT, LMETA, RSHIFT, RMETA, ALTG and COMPOSE */ +static unsigned char norepeat_keys[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0x00-0x0f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x2f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30-0x3f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, /* 0x40-0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50-0x5f */ + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 0x60-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, /* 0x70-0x7f */ +}; + + int setkeycode(unsigned int scancode, unsigned int keycode) { if (scancode < SC_LIM || scancode > 255 || keycode > 127) @@ -350,7 +383,7 @@ int getkeycode(unsigned int scancode) e0_keys[scancode - 128]; } -void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs); +void sunkbd_inchar(unsigned char ch, struct pt_regs *regs); static void keyboard_timer (unsigned long ignored); static struct timer_list @@ -367,16 +400,18 @@ keyboard_timer (unsigned long ignored) save_flags(flags); cli(); /* Auto repeat: send regs = 0 to indicate autorepeat */ - sunkbd_inchar (last_keycode, 0, 0); + sunkbd_inchar (last_keycode, 0); del_timer (&auto_repeat_timer); - auto_repeat_timer.expires = jiffies + HZ/20; - add_timer (&auto_repeat_timer); + if (kbd_rate_ticks) { + auto_repeat_timer.expires = jiffies + kbd_rate_ticks; + add_timer (&auto_repeat_timer); + } restore_flags(flags); } /* #define SKBD_DEBUG */ /* This is our keyboard 'interrupt' routine. */ -void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) +void sunkbd_inchar(unsigned char ch, struct pt_regs *regs) { unsigned char keycode; char up_flag; /* 0 or SUNKBD_UBIT */ @@ -401,7 +436,9 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) kbd_layout_pending = 0; return; } else if(ch == SKBD_ALLUP) { - /* eat it */ + del_timer (&auto_repeat_timer); + memset(key_down, 0, sizeof(key_down)); + compute_shiftstate(); return; } #ifdef SKBD_DEBUG @@ -413,11 +450,13 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) #endif /* Whee, a real character. */ - if (regs){ + if(regs) { pt_regs = regs; last_keycode = keycode = ch; - } else + } else { keycode = ch; + } + add_keyboard_randomness(keycode); mark_bh(KEYBOARD_BH); do_poke_blanked_console = 1; @@ -441,8 +480,13 @@ void sunkbd_inchar(unsigned char ch, unsigned char status, struct pt_regs *regs) rep = 0; clear_bit(keycode, key_down); } else { - auto_repeat_timer.expires = jiffies+HZ/5; - add_timer (&auto_repeat_timer); + if (!norepeat_keys[keycode]) { + if (kbd_rate_ticks) { + auto_repeat_timer.expires = + jiffies + kbd_delay_ticks; + add_timer (&auto_repeat_timer); + } + } rep = set_bit(keycode, key_down); } @@ -661,6 +705,8 @@ static void boot_it(void) static void compose(void) { dead_key_next = 1; + compose_led_on = 1; + set_leds(); } int spawnpid, spawnsig; @@ -718,8 +764,11 @@ static void do_self(unsigned char value, char up_flag) if (up_flag) return; /* no action, if this is a key release */ - if (diacr) + if (diacr) { value = handle_diacr(value); + compose_led_on = 0; + set_leds(); + } if (dead_key_next) { dead_key_next = 0; @@ -782,7 +831,7 @@ static void do_cons(unsigned char value, char up_flag) { if (up_flag) return; - want_console = value; + set_console(value); } static void do_fn(unsigned char value, char up_flag) @@ -971,6 +1020,7 @@ static void do_lock(unsigned char value, char up_flag) */ static unsigned char ledstate = 0xff; /* undefined */ +static unsigned char sunkbd_ledstate = 0xff; /* undefined */ static unsigned char ledioctl; unsigned char getledstate(void) { @@ -1050,15 +1100,56 @@ static inline unsigned char getleds(void){ static void kbd_bh(void) { unsigned char leds = getleds(); + unsigned char kbd_leds = vcleds_to_sunkbd(leds); - if (leds != ledstate) { + if (kbd_leds != sunkbd_ledstate) { ledstate = leds; + sunkbd_ledstate = kbd_leds; send_cmd(SKBDCMD_SETLED); - send_cmd(vcleds_to_sunkbd(leds)); + send_cmd(kbd_leds); } } -int kbd_init(void) +/* Support for keyboard "beeps". */ + +/* Timer routine to turn off the beep after the interval expires. */ +static void sunkbd_kd_nosound(unsigned long __unused) +{ + send_cmd(SKBDCMD_BELLOFF); +} + +/* + * Initiate a keyboard beep. If the frequency is zero, then we stop + * the beep. Any other frequency will start a monotone beep. The beep + * will be stopped by a timer after "ticks" jiffies. If ticks is 0, + * then we do not start a timer. + */ +static void sunkbd_kd_mksound(unsigned int hz, unsigned int ticks) +{ + unsigned long flags; + static struct timer_list sound_timer = { NULL, NULL, 0, 0, + sunkbd_kd_nosound }; + + save_flags(flags); + cli(); + + del_timer(&sound_timer); + + if (hz) { + send_cmd(SKBDCMD_BELLON); + if (ticks) { + sound_timer.expires = jiffies + ticks; + add_timer(&sound_timer); + } + } else + send_cmd(SKBDCMD_BELLOFF); + + restore_flags(flags); +} + +extern void (*kd_mksound)(unsigned int hz, unsigned int ticks); + +__initfunc(int kbd_init(void)) { int i, opt_node; struct kbd_struct kbd0; @@ -1075,6 +1166,8 @@ int kbd_init(void) ttytab = console_driver.table; + kd_mksound = sunkbd_kd_mksound; + /* XXX Check keyboard-click? property in 'options' PROM node XXX */ if(sparc_cpu_model != sun4) { opt_node = prom_getchild(prom_root_node); @@ -1098,6 +1191,7 @@ int kbd_init(void) static Firm_event kbd_queue [KBD_QSIZE]; static int kbd_head, kbd_tail; char kbd_opened; +static int kbd_active = 0; static struct wait_queue *kbd_wait; static struct fasync_struct *kb_fasync; @@ -1119,8 +1213,8 @@ push_kbd (int scan) wake_up_interruptible (&kbd_wait); } -static int -kbd_read (struct inode *inode, struct file *f, char *buffer, int count) +static long +kbd_read (struct inode *inode, struct file *f, char *buffer, unsigned long count) { struct wait_queue wait = { current, NULL }; char *end, *p; @@ -1141,7 +1235,7 @@ kbd_read (struct inode *inode, struct file *f, char *buffer, int count) end = buffer+count; p = buffer; for (; p < end && kbd_head != kbd_tail; p += sizeof (Firm_event)){ - *(Firm_event *)p = kbd_queue [kbd_tail]; + copy_to_user_ret((Firm_event *)p, &kbd_queue [kbd_tail], sizeof(Firm_event), -EFAULT); #ifdef KBD_DEBUG printk ("[%s]", kbd_queue [kbd_tail].value == VKEY_UP ? "UP" : "DOWN"); #endif @@ -1163,47 +1257,40 @@ kbd_fasync (struct inode *inode, struct file *filp, int on) return 0; } -static int -kbd_select (struct inode *i, struct file *f, int sel_type, select_table *wait) +static unsigned int kbd_poll (struct file *f, poll_table *wait) { - if (sel_type != SEL_IN) - return 0; + poll_wait(&kbd_wait, wait); if (kbd_head != kbd_tail) - return 1; - select_wait (&kbd_wait, wait); + return POLLIN | POLLRDNORM; return 0; } static int kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) { + unsigned char c; + unsigned char leds = 0; + int value; + switch (cmd){ - case KIOCTYPE: /* return keyboard type */ - if (verify_area (VERIFY_WRITE, (void *)arg, sizeof (int))) - return -EFAULT; - *(int *) arg = sunkbd_type; + case KIOCTYPE: /* return keyboard type */ + put_user_ret(sunkbd_type, (int *) arg, -EFAULT); break; case KIOCGTRANS: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; - *(int *) arg = TR_UNTRANS_EVENT; + put_user_ret(TR_UNTRANS_EVENT, (int *) arg, -EFAULT); break; case KIOCTRANS: - if (verify_area (VERIFY_READ, (void *) arg, sizeof (int))) - return -EFAULT; - if (*(int *) arg != TR_UNTRANS_EVENT) + get_user_ret(value, (int *) arg, -EFAULT); + if (value != TR_UNTRANS_EVENT) return -EINVAL; break; case KIOCLAYOUT: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; - *(int *) arg = sunkbd_layout; + put_user_ret(sunkbd_layout, (int *) arg, -EFAULT); break; case KIOCSDIRECT: - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; #ifndef CODING_NEW_DRIVER - if (*(int *) arg) + get_user_ret(value, (int *) arg, -EFAULT); + if(value) kbd_redirected = fg_console + 1; else kbd_redirected = 0; @@ -1211,16 +1298,75 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) #endif break; case KIOCCMD: - /* Need to support beep on/off, keyclick on/off */ + get_user_ret(value, (int *) arg, -EFAULT); + c = (unsigned char) value; + switch (c) { + case SKBDCMD_CLICK: + case SKBDCMD_NOCLICK: + send_cmd(c); + return 0; + case SKBDCMD_BELLON: + kd_mksound(1,0); + return 0; + case SKBDCMD_BELLOFF: + kd_mksound(0,0); + return 0; + default: + return -EINVAL; + } + case KIOCSLED: + get_user_ret(c, (unsigned char *) arg, -EFAULT); + + if (c & LED_SCRLCK) leds |= (1 << VC_SCROLLOCK); + if (c & LED_NLOCK) leds |= (1 << VC_NUMLOCK); + if (c & LED_CLOCK) leds |= (1 << VC_CAPSLOCK); + compose_led_on = !!(c & LED_CMPOSE); + setledstate(kbd_table + fg_console, leds); + break; + case KIOCGLED: + put_user_ret(vcleds_to_sunkbd(getleds()), (unsigned char *) arg, -EFAULT); + break; + case KIOCGRATE: + { + struct kbd_rate rate; + + rate.delay = kbd_delay_ticks; + if (kbd_rate_ticks) + rate.rate = HZ / kbd_rate_ticks; + else + rate.rate = 0; + + copy_to_user_ret((struct kbd_rate *)arg, &rate, + sizeof(struct kbd_rate), -EFAULT); + + return 0; + } + case KIOCSRATE: + { + struct kbd_rate rate; + + if (verify_area(VERIFY_READ, (void *)arg, + sizeof(struct kbd_rate))) + return -EFAULT; + copy_from_user(&rate, (struct kbd_rate *)arg, + sizeof(struct kbd_rate)); + + if (rate.rate > 50) + return -EINVAL; + if (rate.rate == 0) + kbd_rate_ticks = 0; + else + kbd_rate_ticks = HZ / rate.rate; + kbd_delay_ticks = rate.delay; + return 0; + } case FIONREAD: /* return number of bytes in kbd queue */ { int count; - if (verify_area (VERIFY_WRITE, (void *) arg, sizeof (int))) - return -EFAULT; count = kbd_head - kbd_tail; - * (int *)arg = (count < 0) ? KBD_QSIZE - count : count; + put_user_ret((count < 0) ? KBD_QSIZE - count : count, (int *) arg, -EFAULT); return 0; } default: @@ -1233,22 +1379,30 @@ kbd_ioctl (struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) static int kbd_open (struct inode *i, struct file *f) { + kbd_active++; + if (kbd_opened) return 0; + kbd_opened = fg_console + 1; kbd_head = kbd_tail = 0; return 0; } -static void +static int kbd_close (struct inode *i, struct file *f) { + if (--kbd_active) + return 0; + if (kbd_redirected) kbd_table [kbd_opened-1].kbdmode = VC_XLATE; + kbd_redirected = 0; kbd_opened = 0; kbd_fasync (i, f, 0); + return 0; } static struct @@ -1258,7 +1412,7 @@ file_operations kbd_fops = kbd_read, /* read */ NULL, /* write */ NULL, /* readdir */ - kbd_select, /* select */ + kbd_poll, /* poll */ kbd_ioctl, /* ioctl */ NULL, /* mmap */ kbd_open, /* open */ @@ -1269,8 +1423,7 @@ file_operations kbd_fops = NULL, /* revalidate */ }; -void -keyboard_zsinit(void) +__initfunc(void keyboard_zsinit(void)) { int timeout = 0; @@ -1295,15 +1448,18 @@ keyboard_zsinit(void) while(timeout++ < 500000) barrier(); printk("Sun TYPE %d keyboard detected ", - ((sunkbd_layout==SUNKBD_LOUT_TYP5) ? 5 : 4)); + ((sunkbd_layout & SUNKBD_LOUT_TYP5_MASK) ? 5 : 4)); } if(sunkbd_type == SUNKBD_TYPE2) sunkbd_clickp = 0; - if(sunkbd_clickp) + if(sunkbd_clickp) { + send_cmd(SKBDCMD_CLICK); printk("with keyclick\n"); - else + } else { + send_cmd(SKBDCMD_NOCLICK); printk("without keyclick\n"); + } /* Dork with led lights, then turn them all off */ send_cmd(SKBDCMD_SETLED); send_cmd(0xf); /* All on */ |