sunmouse.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:14k
- /* sunmouse.c: Sun mouse driver for the Sparc
- *
- * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
- * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
- *
- * Parts based on the psaux.c driver written by:
- * Johan Myreen.
- *
- * Dec/19/95 Added SunOS mouse ioctls - miguel.
- * Jan/5/96 Added VUID support, sigio support - miguel.
- * Mar/5/96 Added proper mouse stream support - miguel.
- * Sep/96 Allow more than one reader -miguel.
- * Aug/97 Added PCI 8042 controller support -DaveM
- */
- /* The mouse is run off of one of the Zilog serial ports. On
- * that port is the mouse and the keyboard, each gets a zs channel.
- * The mouse itself is mouse-systems in nature. So the protocol is:
- *
- * Byte 1) Button state which is bit-encoded as
- * 0x4 == left-button down, else up
- * 0x2 == middle-button down, else up
- * 0x1 == right-button down, else up
- *
- * Byte 2) Delta-x
- * Byte 3) Delta-y
- * Byte 4) Delta-x again
- * Byte 5) Delta-y again
- *
- * One day this driver will have to support more than one mouse in the system.
- *
- * This driver has two modes of operation: the default VUID_NATIVE is
- * set when the device is opened and allows the application to see the
- * mouse character stream as we get it from the serial (for gpm for
- * example). The second method, VUID_FIRM_EVENT will provide cooked
- * events in Firm_event records as expected by SunOS/Solaris applications.
- *
- * FIXME: We need to support more than one mouse.
- * */
- #include <linux/config.h>
- #include <linux/kernel.h>
- #include <linux/sched.h>
- #include <linux/fcntl.h>
- #include <linux/signal.h>
- #include <linux/timer.h>
- #include <linux/errno.h>
- #include <linux/miscdevice.h>
- #include <linux/mm.h>
- #include <linux/poll.h>
- #include <linux/spinlock.h>
- #include <linux/smp_lock.h>
- #include <linux/init.h>
- #include <asm/uaccess.h>
- #include <asm/system.h>
- #include <asm/vuid_event.h>
- #include <linux/random.h>
- /* The following keeps track of software state for the Sun
- * mouse.
- */
- #define STREAM_SIZE 2048
- #define EV_SIZE (STREAM_SIZE/sizeof (Firm_event))
- #define BUTTON_LEFT 4
- #define BUTTON_MIDDLE 2
- #define BUTTON_RIGHT 1
- struct sun_mouse {
- unsigned char transaction[5]; /* Each protocol transaction */
- unsigned char byte; /* Counter, starts at 0 */
- unsigned char button_state; /* Current button state */
- unsigned char prev_state; /* Previous button state */
- int delta_x; /* Current delta-x */
- int delta_y; /* Current delta-y */
- int active; /* set if device is open */
- int vuid_mode; /* VUID_NATIVE or VUID_FIRM_EVENT */
- wait_queue_head_t proc_list;
- struct fasync_struct *fasync;
-
- /* The event/stream queue */
- spinlock_t lock;
- unsigned int head;
- unsigned int tail;
- union {
- char stream [STREAM_SIZE];
- Firm_event ev [EV_SIZE];
- } queue;
- };
- static struct sun_mouse sunmouse;
- #define gen_events (sunmouse.vuid_mode != VUID_NATIVE)
- #define bstate sunmouse.button_state
- #define pstate sunmouse.prev_state
- extern void mouse_put_char(char ch);
- #undef SMOUSE_DEBUG
- static int
- push_event (Firm_event *ev)
- {
- unsigned long flags;
- int next, ret;
-
- spin_lock_irqsave(&sunmouse.lock, flags);
- next = (sunmouse.head + 1) % EV_SIZE;
- ret = 0;
- if (next != sunmouse.tail) {
- sunmouse.queue.ev [sunmouse.head] = *ev;
- sunmouse.head = next;
- ret = 1;
- }
- spin_unlock_irqrestore(&sunmouse.lock, flags);
- return ret;
- }
- static int
- queue_empty (void)
- {
- return sunmouse.head == sunmouse.tail;
- }
- /* Must be invoked under the sunmouse.lock */
- static void get_from_queue (Firm_event *p)
- {
- *p = sunmouse.queue.ev [sunmouse.tail];
- sunmouse.tail = (sunmouse.tail + 1) % EV_SIZE;
- }
- static void
- push_char (char c)
- {
- unsigned long flags;
- int next;
- spin_lock_irqsave(&sunmouse.lock, flags);
- next = (sunmouse.head + 1) % STREAM_SIZE;
- if (next != sunmouse.tail) {
- #ifdef SMOUSE_DEBUG
- printk("P<%02x>n", (unsigned char)c);
- #endif
- sunmouse.queue.stream [sunmouse.head] = c;
- sunmouse.head = next;
- }
- spin_unlock_irqrestore(&sunmouse.lock, flags);
- kill_fasync (&sunmouse.fasync, SIGIO, POLL_IN);
- wake_up_interruptible (&sunmouse.proc_list);
- }
- /* Auto baud rate "detection". ;-) */
- static int mouse_baud = 4800; /* Initial rate set by zilog driver. */
- /* Change the baud rate after receiving too many "bogon bytes". */
- void sun_mouse_change_baud(void)
- {
- extern void rs_change_mouse_baud(int newbaud);
- if(mouse_baud == 1200)
- mouse_baud = 2400;
- else if(mouse_baud == 2400)
- mouse_baud = 4800;
- else if(mouse_baud == 4800)
- mouse_baud = 9600;
- else
- mouse_baud = 1200;
- rs_change_mouse_baud(mouse_baud);
- }
- /* This tries to monitor the mouse state so that it
- * can automatically adjust to the correct baud rate.
- * The mouse spits out BRKs when the baud rate is
- * incorrect.
- *
- * It returns non-zero if we should ignore this byte.
- */
- int mouse_baud_detection(unsigned char c, int is_break)
- {
- static int mouse_got_break = 0;
- static int ctr = 0;
- if (is_break) {
- /* Let a few normal bytes go by before
- * we jump the gun and say we need to
- * try another baud rate.
- */
- if (mouse_got_break && ctr < 8)
- return 1;
- /* OK, we need to try another baud rate. */
- sun_mouse_change_baud();
- ctr = 0;
- mouse_got_break = 1;
- return 1;
- }
- if (mouse_got_break) {
- ctr++;
- if (c == 0x87) {
- printk(KERN_INFO "sunmouse: Successfully "
- "adjusted to %d baud.n", mouse_baud);
- mouse_got_break = 0;
- }
- return 1;
- }
- return 0;
- }
- /* You ask me, why does this cap the lower bound at -127 and not
- * -128? Because the xf86 mouse code is crap and treats -128
- * as an illegal value and resets it's protocol state machine
- * when it sees this value.
- */
- #define CLIP(__X) (((__X) > 127) ? 127 : (((__X) < -127) ? -127 : (__X)))
- /* The following is called from the serial driver when bytes/breaks
- * are received on the Mouse line.
- */
- void
- sun_mouse_inbyte(unsigned char byte, int is_break)
- {
- signed char mvalue;
- int d, pushed = 0;
- Firm_event ev;
- add_mouse_randomness (byte);
- #if 0
- {
- static int xxx = 0;
- printk("mouse(%02x:%d) ",
- byte, is_break);
- if (byte == 0x87) {
- xxx = 0;
- printk("n");
- }
- }
- #endif
- if (mouse_baud_detection(byte, is_break))
- return;
- if(!sunmouse.active)
- return;
- /* Ignore this if it is garbage. */
- if (sunmouse.byte == 69) {
- if (byte != 0x87)
- return;
- /* Ok, we've begun the state machine. */
- sunmouse.byte = 0;
- }
- #if 0
- /* If the mouse sends us a byte from 0x80 to 0x87
- * we are starting at byte zero in the transaction
- * protocol.
- */
- if((byte & ~0x0f) == 0x80)
- sunmouse.byte = 0;
- #endif
- mvalue = (signed char) byte;
- switch(sunmouse.byte) {
- case 0:
- /* If we get a bogus button byte, just skip it.
- * When we get here the baud detection code has
- * passed, so the only other things which can
- * cause this are dropped serial characters and
- * confused mouse. We check this because otherwise
- * begin posting erroneous mouse events.
- */
- if ((byte & 0xf0) != 0x80)
- return;
- /* Button state */
- sunmouse.button_state = (~byte) & 0x7;
- #ifdef SMOUSE_DEBUG
- printk("B<Left %s, Middle %s, Right %s>",
- ((sunmouse.button_state & 0x4) ? "DOWN" : "UP"),
- ((sunmouse.button_state & 0x2) ? "DOWN" : "UP"),
- ((sunmouse.button_state & 0x1) ? "DOWN" : "UP"));
- #endif
- /* To deal with the Sparcbook 3 */
- if (byte & 0x8) {
- sunmouse.byte += 2;
- sunmouse.delta_y = 0;
- sunmouse.delta_x = 0;
- }
- sunmouse.byte++;
- return;
- case 1:
- /* Delta-x 1 */
- #ifdef SMOUSE_DEBUG
- printk("DX1<%d>", mvalue);
- #endif
- sunmouse.delta_x = mvalue;
- sunmouse.byte++;
- return;
- case 2:
- /* Delta-y 1 */
- #ifdef SMOUSE_DEBUG
- printk("DY1<%d>", mvalue);
- #endif
- sunmouse.delta_y = mvalue;
- sunmouse.byte++;
- return;
- case 3:
- /* Delta-x 2 */
- #ifdef SMOUSE_DEBUG
- printk("DX2<%d>", mvalue);
- #endif
- sunmouse.delta_x += mvalue;
- sunmouse.delta_x = CLIP(sunmouse.delta_x);
- sunmouse.byte++;
- return;
- case 4:
- /* Last byte, Delta-y 2 */
- #ifdef SMOUSE_DEBUG
- printk("DY2<%d>", mvalue);
- #endif
- sunmouse.delta_y += mvalue;
- sunmouse.delta_y = CLIP(sunmouse.delta_y);
- sunmouse.byte = 0; /* Back to button state */
- break;
- case 69:
- /* Until we get the (0x80 -> 0x87) value we aren't
- * in the middle of a real transaction, so just
- * return.
- */
- return;
- default:
- printk("sunmouse: bogon transaction staten");
- sunmouse.byte = 69; /* What could cause this? */
- return;
- };
- if (!gen_events) {
- push_char (~sunmouse.button_state & 0x87);
- push_char (sunmouse.delta_x);
- push_char (sunmouse.delta_y);
- return;
- }
- d = bstate ^ pstate;
- pstate = bstate;
- if (d) {
- if (d & BUTTON_LEFT) {
- ev.id = MS_LEFT;
- ev.value = bstate & BUTTON_LEFT;
- }
- if (d & BUTTON_RIGHT) {
- ev.id = MS_RIGHT;
- ev.value = bstate & BUTTON_RIGHT;
- }
- if (d & BUTTON_MIDDLE) {
- ev.id = MS_MIDDLE;
- ev.value = bstate & BUTTON_MIDDLE;
- }
- ev.time = xtime;
- ev.value = ev.value ? VKEY_DOWN : VKEY_UP;
- pushed += push_event (&ev);
- }
- if (sunmouse.delta_x) {
- ev.id = LOC_X_DELTA;
- ev.time = xtime;
- ev.value = sunmouse.delta_x;
- pushed += push_event (&ev);
- sunmouse.delta_x = 0;
- }
- if (sunmouse.delta_y) {
- ev.id = LOC_Y_DELTA;
- ev.time = xtime;
- ev.value = sunmouse.delta_y;
- pushed += push_event (&ev);
- }
-
- if (pushed != 0) {
- /* We just completed a transaction, wake up whoever is awaiting
- * this event.
- */
- kill_fasync (&sunmouse.fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&sunmouse.proc_list);
- }
- return;
- }
- static int
- sun_mouse_open(struct inode * inode, struct file * file)
- {
- spin_lock_irq(&sunmouse.lock);
- if (sunmouse.active++)
- goto out;
- sunmouse.delta_x = sunmouse.delta_y = 0;
- sunmouse.button_state = 0x80;
- sunmouse.vuid_mode = VUID_NATIVE;
- out:
- spin_unlock_irq(&sunmouse.lock);
- return 0;
- }
- static int sun_mouse_fasync (int fd, struct file *filp, int on)
- {
- int retval;
- retval = fasync_helper (fd, filp, on, &sunmouse.fasync);
- if (retval < 0)
- return retval;
- return 0;
- }
- static int
- sun_mouse_close(struct inode *inode, struct file *file)
- {
- sun_mouse_fasync (-1, file, 0);
- spin_lock_irq(&sunmouse.lock);
- sunmouse.active--;
- spin_unlock_irq(&sunmouse.lock);
- return 0;
- }
- static ssize_t
- sun_mouse_write(struct file *file, const char *buffer,
- size_t count, loff_t *ppos)
- {
- return -EINVAL; /* foo on you */
- }
- static ssize_t
- sun_mouse_read(struct file *file, char *buffer,
- size_t count, loff_t *ppos)
- {
- DECLARE_WAITQUEUE(wait, current);
- unsigned long flags;
- if (queue_empty ()) {
- if (file->f_flags & O_NONBLOCK)
- return -EWOULDBLOCK;
- add_wait_queue (&sunmouse.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 (&sunmouse.proc_list, &wait);
- }
- if (gen_events) {
- char *p = buffer, *end = buffer+count;
-
- spin_lock_irqsave(&sunmouse.lock, flags);
- while (p < end && !queue_empty ()){
- Firm_event this_event;
- get_from_queue(&this_event);
- spin_unlock_irqrestore(&sunmouse.lock, flags);
- #ifdef CONFIG_SPARC32_COMPAT
- if (current->thread.flags & SPARC_FLAG_32BIT) {
- if ((end - p) <
- ((sizeof(Firm_event) - sizeof(struct timeval) +
- (sizeof(u32) * 2))))
- break;
- if (copy_to_user((Firm_event *)p, &this_event,
- sizeof(Firm_event)-sizeof(struct timeval)))
- return -EFAULT;
- p += sizeof(Firm_event)-sizeof(struct timeval);
- if (__put_user(this_event.time.tv_sec, (u32 *)p))
- return -EFAULT;
- p += sizeof(u32);
- if (__put_user(this_event.time.tv_usec, (u32 *)p))
- return -EFAULT;
- p += sizeof(u32);
- } else
- #endif
- {
- if ((end - p) < sizeof(Firm_event))
- break;
- if (copy_to_user((Firm_event *)p, &this_event,
- sizeof(Firm_event)))
- return -EFAULT;
- p += sizeof (Firm_event);
- }
- spin_lock_irqsave(&sunmouse.lock, flags);
- }
- spin_unlock_irqrestore(&sunmouse.lock, flags);
- file->f_dentry->d_inode->i_atime = CURRENT_TIME;
- return p-buffer;
- } else {
- int c, limit = 3;
- if (count < limit)
- limit = count;
- for (c = 0; c < limit; c++) {
- unsigned char val;
- int empty = 0;
- spin_lock_irqsave(&sunmouse.lock, flags);
- if (queue_empty()) {
- empty = 1;
- val = 0;
- } else {
- val = sunmouse.queue.stream[sunmouse.tail];
- sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
- }
- spin_unlock_irqrestore(&sunmouse.lock, flags);
- if (empty)
- break;
- put_user(val, buffer);
- buffer++;
- }
- while (c < count) {
- if (c >= 5)
- break;
- put_user(0, buffer);
- buffer++;
- c++;
- }
- file->f_dentry->d_inode->i_atime = CURRENT_TIME;
- return c;
- }
- /* Only called if nothing was sent */
- if (signal_pending(current))
- return -ERESTARTSYS;
- return 0;
- }
- static unsigned int sun_mouse_poll(struct file *file, poll_table *wait)
- {
- poll_wait(file, &sunmouse.proc_list, wait);
- if(!queue_empty())
- return POLLIN | POLLRDNORM;
- return 0;
- }
- int
- sun_mouse_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
- {
- int i;
-
- switch (cmd){
- /* VUIDGFORMAT - Get input device byte stream format */
- case _IOR('v', 2, int):
- if (put_user(sunmouse.vuid_mode, (int *) arg))
- return -EFAULT;
- break;
- /* VUIDSFORMAT - Set input device byte stream format*/
- case _IOW('v', 1, int):
- if (get_user(i, (int *) arg))
- return -EFAULT;
- if (i == VUID_NATIVE || i == VUID_FIRM_EVENT){
- int value;
- if (get_user(value, (int *)arg))
- return -EFAULT;
- spin_lock_irq(&sunmouse.lock);
- sunmouse.vuid_mode = value;
- sunmouse.head = sunmouse.tail = 0;
- spin_unlock_irq(&sunmouse.lock);
- } else
- return -EINVAL;
- break;
- case 0x8024540b:
- case 0x40245408:
- /* This is a buggy application doing termios on the mouse driver */
- /* we ignore it. I keep this check here so that we will notice */
- /* future mouse vuid ioctls */
- return -ENOTTY;
-
- default:
- #ifdef DEBUG
- printk ("[MOUSE-ioctl: %8.8x]n", cmd);
- #endif
- return -EINVAL;
- }
- return 0;
- }
- struct file_operations sun_mouse_fops = {
- read: sun_mouse_read,
- write: sun_mouse_write,
- poll: sun_mouse_poll,
- ioctl: sun_mouse_ioctl,
- open: sun_mouse_open,
- release: sun_mouse_close,
- fasync: sun_mouse_fasync,
- };
- static struct miscdevice sun_mouse_mouse = {
- SUN_MOUSE_MINOR, "sunmouse", &sun_mouse_fops
- };
- void sun_mouse_zsinit(void)
- {
- printk("Sun Mouse-Systems mouse driver version 1.00n");
- sunmouse.active = 0;
- misc_register (&sun_mouse_mouse);
- sunmouse.delta_x = sunmouse.delta_y = 0;
- sunmouse.button_state = 0x80;
- init_waitqueue_head(&sunmouse.proc_list);
- spin_lock_init(&sunmouse.lock);
- sunmouse.byte = 69;
- }