diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-01-04 16:03:48 +0000 |
commit | 78c388aed2b7184182c08428db1de6c872d815f5 (patch) | |
tree | 4b2003b1b4ceb241a17faa995da8dd1004bb8e45 /drivers/char/pc_keyb.c | |
parent | eb7a5bf93aaa4be1d7c6181100ab7639e74d67f7 (diff) |
Merge with Linux 2.1.131 and more MIPS goodies.
(Did I mention that CVS is buggy ...)
Diffstat (limited to 'drivers/char/pc_keyb.c')
-rw-r--r-- | drivers/char/pc_keyb.c | 386 |
1 files changed, 346 insertions, 40 deletions
diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index faebe09c3..a795cf21c 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -5,57 +5,116 @@ * See keyboard.c for the whole history. * * Major cleanup by Martin Mares, May 1997 + * + * Combined the keyboard and PS/2 mouse handling into one file, + * because they share the same hardware. + * Johan Myreen <jem@iki.fi> 1998-10-08. + * */ #include <linux/config.h> +#include <asm/spinlock.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/tty.h> #include <linux/mm.h> #include <linux/signal.h> -#include <linux/ioport.h> #include <linux/init.h> #include <linux/kbd_ll.h> #include <linux/delay.h> +#include <linux/random.h> +#include <linux/poll.h> +#include <linux/miscdevice.h> +#include <linux/malloc.h> #include <asm/keyboard.h> #include <asm/bitops.h> -#include <asm/io.h> +#include <asm/uaccess.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/irq.h> /* Some configuration switches are present in the include file... */ -#include "pc_keyb.h" +#include <linux/pc_keyb.h> + +/* Simple translation table for the SysRq keys */ + +#ifdef CONFIG_MAGIC_SYSRQ +unsigned char pckbd_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 -unsigned char pckbd_read_mask = KBD_STAT_OBF; /* Modified by psaux.c */ +static void kbd_write_command_w(int data); +static void kbd_write_output_w(int data); + +spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; /* used only by send_data - set by keyboard_interrupt */ static volatile unsigned char reply_expected = 0; static volatile unsigned char acknowledge = 0; static volatile unsigned char resend = 0; + +#if defined CONFIG_PSMOUSE /* - * Wait for keyboard controller input buffer is empty. + * PS/2 Auxiliary Device + */ + +static int __init psaux_init(void); + +static struct aux_queue *queue; /* Mouse data buffer. */ +static int aux_count = 0; + +#define AUX_INTS_OFF (KBD_MODE_KCC | KBD_MODE_DISABLE_MOUSE | KBD_MODE_SYS | KBD_MODE_KBD_INT) +#define AUX_INTS_ON (KBD_MODE_KCC | KBD_MODE_SYS | KBD_MODE_MOUSE_INT | KBD_MODE_KBD_INT) + +#define MAX_RETRIES 60 /* some aux operations take long time*/ +#endif /* CONFIG_PSMOUSE */ + +/* + * Wait for keyboard controller input buffer is empty. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. * - * 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." */ static inline void kb_wait(void) { unsigned long timeout = KBC_TIMEOUT; + unsigned char status; do { - if (! (kbd_read_status() & KBD_STAT_IBF)) + status = kbd_read_status(); + kbd_pause(); + if (status & KBD_STAT_OBF) { + if (status & KBD_STAT_MOUSE_OBF) + kbd_read_input(); /* Flush */ + kbd_pause(); + } + + status = kbd_read_status(); + kbd_pause(); + if (!(status & KBD_STAT_IBF)) return; mdelay(1); timeout--; } while (timeout); #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timed out\n"); + printk(KERN_WARNING "Keyboard timed out[1]\n"); #endif } @@ -202,7 +261,7 @@ static inline void send_cmd(unsigned char c) kbd_write_command(c); } -#define disable_keyboard() do { send_cmd(KBD_CCMD_KBD_DISABLE); kb_wait(); } while (0) +#define disable_keyboard() send_cmd(KBD_CCMD_KBD_DISABLE) #define enable_keyboard() send_cmd(KBD_CCMD_KBD_ENABLE) #else #define disable_keyboard() /* nothing */ @@ -355,33 +414,46 @@ char pckbd_unexpected_up(unsigned char keycode) void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + unsigned long flags; unsigned char status; - kbd_pt_regs = regs; + spin_lock_irqsave(&kbd_controller_lock, flags); disable_keyboard(); + kbd_pt_regs = regs; status = kbd_read_status(); - do { + while (status & KBD_STAT_OBF) { unsigned char scancode; - /* mouse data? */ - if (status & pckbd_read_mask & KBD_STAT_MOUSE_OBF) { -#if defined(CONFIG_SGI) && defined(CONFIG_PSMOUSE) - scancode = kbd_read_input(); - aux_interrupt(status, scancode); + scancode = kbd_read_input(); + + if (status & KBD_STAT_MOUSE_OBF) { +#ifdef CONFIG_PSMOUSE + /* Mouse data. */ + if (aux_count) { + int head = queue->head; + queue->buf[head] = scancode; + add_mouse_randomness(scancode); + head = (head + 1) & (AUX_BUF_SIZE-1); + if (head != queue->tail) { + queue->head = head; + if (queue->fasync) + kill_fasync(queue->fasync, SIGIO); + wake_up_interruptible(&queue->proc_list); + } + } #endif - break; + } else { + if (do_acknowledge(scancode)) + handle_scancode(scancode); + mark_bh(KEYBOARD_BH); } - scancode = kbd_read_input(); - if ((status & KBD_STAT_OBF) && do_acknowledge(scancode)) - handle_scancode(scancode); - status = kbd_read_status(); - } while (status & KBD_STAT_OBF); + } - mark_bh(KEYBOARD_BH); enable_keyboard(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } /* @@ -398,12 +470,10 @@ static int send_data(unsigned char data) do { unsigned long timeout = KBD_TIMEOUT; - kb_wait(); - acknowledge = 0; + acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */ resend = 0; reply_expected = 1; - kbd_write_output(data); - kbd_pause(); + kbd_write_output_w(data); for (;;) { if (acknowledge) return 1; @@ -412,7 +482,7 @@ static int send_data(unsigned char data) mdelay(1); if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timeout\n"); + printk(KERN_WARNING "Keyboard timeout[2]\n"); #endif return 0; } @@ -493,25 +563,39 @@ static int __init kbd_wait_for_input(void) return -1; } -static void __init kbd_write_command_w(int data) +static void kbd_write_command_w(int data) { - int status; + unsigned long flags; - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); kbd_write_command(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } -static void __init kbd_write_output_w(int data) +static void kbd_write_output_w(int data) { - int status; + unsigned long flags; - do { - status = kbd_read_status(); - } while (status & KBD_STAT_IBF); + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); kbd_write_output(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +#if defined CONFIG_PSMOUSE +static void kbd_write_cmd(int cmd) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MODE); + kb_wait(); + kbd_write_output(cmd); + spin_unlock_irqrestore(&kbd_controller_lock, flags); } +#endif /* CONFIG_PSMOUSE */ static char * __init initialize_kbd(void) { @@ -614,7 +698,7 @@ static char * __init initialize_kbd(void) void __init pckbd_init_hw(void) { - keyboard_setup(); + kbd_request_region(); /* Flush any pending input. */ kbd_clear_input(); @@ -624,4 +708,226 @@ void __init pckbd_init_hw(void) if (msg) printk(KERN_WARNING "initialize_kbd: %s\n", msg); } + +#if defined CONFIG_PSMOUSE + psaux_init(); +#endif + + /* Ok, finally allocate the IRQ, and off we go.. */ + kbd_request_irq(keyboard_interrupt); +} + +#if defined CONFIG_PSMOUSE +/* + * Send a byte to the mouse. + */ +static void aux_write_dev(int val) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(KBD_CCMD_WRITE_MOUSE); + kb_wait(); + kbd_write_output(val); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static unsigned int get_from_queue(void) +{ + unsigned int result; + unsigned long flags; + + save_flags(flags); + cli(); + result = queue->buf[queue->tail]; + queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1); + restore_flags(flags); + return result; +} + + +static inline int queue_empty(void) +{ + return queue->head == queue->tail; +} + +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 int release_aux(struct inode * inode, struct file * file) +{ + fasync_aux(-1, file, 0); + if (--aux_count) + return 0; + kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints */ + kbd_write_command_w(KBD_CCMD_MOUSE_DISABLE); + aux_free_irq(inode); + return 0; +} + +/* + * Install interrupt handler. + * Enable auxiliary device. + */ + +static int open_aux(struct inode * inode, struct file * file) +{ + if (aux_count++) { + return 0; + } + queue->head = queue->tail = 0; /* Flush input queue */ + if (aux_request_irq(keyboard_interrupt, inode)) { + aux_count--; + return -EBUSY; + } + kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable the + auxiliary port on + controller. */ + aux_write_dev(AUX_ENABLE_DEV); /* Enable aux device */ + kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */ + + return 0; } + +/* + * Put bytes from input queue to buffer. + */ + +static ssize_t read_aux(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + struct wait_queue wait = { current, NULL }; + 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: + current->state = TASK_INTERRUPTIBLE; + if (queue_empty() && !signal_pending(current)) { + schedule(); + goto repeat; + } + 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; +} + +/* + * 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++); + aux_write_dev(c); + written++; + } while (--count); + retval = -EIO; + if (written) { + retval = written; + file->f_dentry->d_inode->i_mtime = CURRENT_TIME; + } + } + + return retval; +} + +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; +} + +struct file_operations psaux_fops = { + NULL, /* seek */ + read_aux, + write_aux, + NULL, /* readdir */ + aux_poll, + NULL, /* ioctl */ + NULL, /* mmap */ + open_aux, + NULL, /* flush */ + release_aux, + NULL, + fasync_aux, +}; + +/* + * Initialize driver. + */ +static struct miscdevice psaux_mouse = { + PSMOUSE_MINOR, "psaux", &psaux_fops +}; + +static int __init psaux_init(void) +{ +#if 0 + /* + * Don't bother with the BIOS flag: even if we don't have + * a mouse connected at bootup we may still want to connect + * one later, and we don't want to just let the BIOS tell + * us that it has no mouse.. + */ + if (aux_device_present != 0xaa) + return -EIO; + + printk(KERN_INFO "PS/2 auxiliary pointing device detected -- driver installed.\n"); +#endif + misc_register(&psaux_mouse); + queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL); + memset(queue, 0, sizeof(*queue)); + queue->head = queue->tail = 0; + queue->proc_list = NULL; + +#ifdef INITIALIZE_MOUSE + kbd_write_command_w(KBD_CCMD_MOUSE_ENABLE); /* Enable Aux. */ + aux_write_dev(AUX_SET_SAMPLE); + aux_write_dev(100); /* 100 samples/sec */ + aux_write_dev(AUX_SET_RES); + aux_write_dev(3); /* 8 counts per mm */ + aux_write_dev(AUX_SET_SCALE21); /* 2:1 scaling */ +#endif /* INITIALIZE_MOUSE */ + kbd_write_command(KBD_CCMD_MOUSE_DISABLE); /* Disable aux device. */ + kbd_write_cmd(AUX_INTS_OFF); /* Disable controller ints. */ + + return 0; +} + +#endif /* CONFIG_PSMOUSE */ |