/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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", ((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 state\n"); 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) { if (sunmouse.active++) return 0; sunmouse.delta_x = sunmouse.delta_y = 0; sunmouse.button_state = 0x80; sunmouse.vuid_mode = VUID_NATIVE; 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); sunmouse.active--; 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; copy_to_user_ret((Firm_event *)p, &this_event, sizeof(Firm_event)-sizeof(struct timeval), -EFAULT); p += sizeof(Firm_event)-sizeof(struct timeval); __put_user_ret(this_event.time.tv_sec, (u32 *)p, -EFAULT); p += sizeof(u32); __put_user_ret(this_event.time.tv_usec, (u32 *)p, -EFAULT); p += sizeof(u32); } else #endif { if ((end - p) < sizeof(Firm_event)) break; copy_to_user_ret((Firm_event *)p, &this_event, sizeof(Firm_event), -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): put_user_ret(sunmouse.vuid_mode, (int *) arg, -EFAULT); break; /* VUIDSFORMAT - Set input device byte stream format*/ case _IOW('v', 1, int): get_user_ret(i, (int *) arg, -EFAULT); if (i == VUID_NATIVE || i == VUID_FIRM_EVENT){ int value; get_user_ret(value, (int *)arg, -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.00\n"); 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; }