hp_psaux.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:14k
- /*
- * 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)
- * implemented the psaux and controlled the mouse scancode based on pc_keyb.c
- * Marteau Thomas (marteaut@esiee.fr)
- * fixed leds control
- *
- * 2001/12/17 Marteau Thomas (marteaut@esiee.fr)
- * get nice initialisation procedure
- */
- #include <linux/config.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/module.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 <linux/poll.h>
- #include <asm/hardware.h>
- #include <asm/keyboard.h>
- #include <asm/gsc.h>
- #include <asm/uaccess.h>
- /* HP specific LASI PS/2 keyboard and psaux constants */
- #define AUX_REPLY_ACK 0xFA /* Command byte ACK. */
- #define AUX_RESEND 0xFE /* Sent by the keyb. Asking for resending the last command. */
- #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 spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
- static unsigned long lasikbd_hpa;
- static volatile int cmd_status;
- static inline u8 read_input(unsigned long hpa)
- {
- return gsc_readb(hpa+LASI_RCVDATA);
- }
- static inline u8 read_control(unsigned long hpa)
- {
- return gsc_readb(hpa+LASI_CONTROL);
- }
- static inline void write_control(u8 val, unsigned long hpa)
- {
- gsc_writeb(val, hpa+LASI_CONTROL);
- }
- static inline u8 read_status(unsigned long hpa)
- {
- return gsc_readb(hpa+LASI_STATUS);
- }
- /* XXX should this grab the spinlock? */
- static int write_output(u8 val, unsigned long hpa)
- {
- int wait = 250;
- while (read_status(hpa) & LASI_STAT_TBNE) {
- if (!--wait) {
- return 0;
- }
- mdelay(1);
- }
- gsc_writeb(val, hpa+LASI_XMTDATA);
- return 1;
- }
- /* XXX should this grab the spinlock? */
- static u8 wait_input(unsigned long hpa)
- {
- int wait = 250;
- while (!(read_status(hpa) & LASI_STAT_RBNE)) {
- if (!--wait) {
- return 0;
- }
- mdelay(1);
- }
- return read_input(hpa);
- }
- /* 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);
- }
- /* This is wrong, should do something like the pc driver, which sends
- * the command up to 3 times at 1 second intervals, checking once
- * per millisecond for an acknowledge.
- */
- static void lasikbd_leds(unsigned char leds)
- {
- int loop = 1000;
- if (!lasikbd_hpa)
- return;
- cmd_status=2;
- while (cmd_status!=0 && --loop > 0) {
- write_output(KBD_CMD_SET_LEDS, lasikbd_hpa);
- mdelay(5);
- }
-
- cmd_status=2;
- while (cmd_status!=0 && --loop > 0) {
- write_output(leds, lasikbd_hpa);
- mdelay(5);
- }
- cmd_status=2;
- while (cmd_status!=0 && --loop > 0) {
- write_output(KBD_CMD_ENABLE, lasikbd_hpa);
- mdelay(5);
- }
- if (loop <= 0)
- printk("lasikbd_leds: timeoutn");
- }
- #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_keyb(unsigned long hpa)
- {
- int res = 0;
- unsigned long flags;
- spin_lock_irqsave(&kbd_controller_lock, flags);
- if (write_output(KBD_CMD_SET_LEDS, hpa) &&
- wait_input(hpa) == AUX_REPLY_ACK &&
- write_output(0, hpa) &&
- wait_input(hpa) == AUX_REPLY_ACK &&
- write_output(KBD_CMD_ENABLE, hpa) &&
- wait_input(hpa) == AUX_REPLY_ACK)
- res = 1;
- spin_unlock_irqrestore(&kbd_controller_lock, flags);
- return res;
- }
- static void __init lasi_ps2_reset(unsigned long hpa)
- {
- u8 control;
- /* 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);
- }
- /* 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 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;
- }
- 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(unsigned long 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;
-
- 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 (scancode == AUX_REPLY_ACK)
- cmd_status=0;
-
- else if (scancode == AUX_RESEND)
- cmd_status=1;
- else
- 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)
- {
- kbd_pt_regs = regs;
- handle_lasikbd_event((unsigned long) dev);
- }
- extern int pckbd_translate(unsigned char, unsigned char *, char);
- extern int pckbd_setkeycode(unsigned int, unsigned int);
- extern int pckbd_getkeycode(unsigned int);
- static struct kbd_ops gsc_ps2_kbd_ops = {
- setkeycode: pckbd_setkeycode,
- getkeycode: pckbd_getkeycode,
- translate: pckbd_translate,
- leds: lasikbd_leds,
- #ifdef CONFIG_MAGIC_SYSRQ
- sysrq_key: 0x54,
- sysrq_xlate: hp_ps2kbd_sysrq_xlate,
- #endif
- };
- #if 1
- /* XXX: HACK !!!
- * remove this function and the call in hil_kbd.c
- * if hp_psaux.c/hp_keyb.c is converted to the input layer... */
- int register_ps2_keybfuncs(void)
- {
- gsc_ps2_kbd_ops.leds = NULL;
- register_kbd_ops(&gsc_ps2_kbd_ops);
- }
- EXPORT_SYMBOL(register_ps2_keybfuncs);
- #endif
- static int __init
- lasi_ps2_register(struct parisc_device *dev)
- {
- unsigned long hpa = dev->hpa;
- char *name;
- int device_found = 0;
- u8 id;
- id = gsc_readb(hpa+LASI_ID) & 0x0f;
- switch (id) {
- case 0:
- name = "keyboard";
- lasikbd_hpa = hpa; /* save "hpa" for lasikbd_leds() */
- 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 */
- lasi_ps2_reset(hpa);
- switch (id) {
- case 0:
- device_found = init_keyb(hpa);
- if (device_found) 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 */
-
- if (device_found) {
- /* Here we claim only if we have a device attached */
- /* allocate the irq and memory region for that device */
- if (!dev->irq)
- return -ENODEV;
-
- if (request_irq(dev->irq, lasikbd_interrupt, 0, name, (void *)hpa))
- return -ENODEV;
-
- if (!request_mem_region(hpa, LASI_STATUS + 4, name))
- return -ENODEV;
- }
-
- printk(KERN_INFO "PS/2 %s port at 0x%08lx (irq %d) found, "
- "%sdevice attached.n",
- name, hpa, dev->irq, device_found ? "":"no ");
- return 0;
- }
- static struct parisc_device_id lasi_psaux_tbl[] = {
- { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00084 },
- { 0, } /* 0 terminated list */
- };
- MODULE_DEVICE_TABLE(parisc, lasi_psaux_tbl);
- static struct parisc_driver lasi_psaux_driver = {
- name: "Lasi psaux",
- id_table: lasi_psaux_tbl,
- probe: lasi_ps2_register,
- };
- static int __init gsc_ps2_init(void)
- {
- return register_parisc_driver(&lasi_psaux_driver);
- }
- module_init(gsc_ps2_init);