blob: 42bdc16a0dd15464aebe38fbc83dea187f234216 [file] [log] [blame]
/*
* linux/drivers/char/busmouse.c
*
* Copyright (C) 1995 - 1998 Russell King <linux@arm.linux.org.uk>
* Protocol taken from original busmouse.c
* read() waiting taken from psaux.c
*
* Medium-level interface for quadrature or bus mice.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/random.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/io.h>
#include "busmouse.h"
/* Uncomment this if your mouse drivers expect the kernel to
* return with EAGAIN if the mouse does not have any events
* available, even if the mouse is opened in blocking mode.
* Please report use of this "feature" to the author using the
* above address.
*/
/*#define BROKEN_MOUSE*/
struct busmouse_data {
struct miscdevice miscdev;
struct busmouse *ops;
spinlock_t lock;
wait_queue_head_t wait;
struct fasync_struct *fasyncptr;
char active;
char buttons;
char ready;
int dxpos;
int dypos;
};
#define NR_MICE 15
#define FIRST_MOUSE 0
#define DEV_TO_MOUSE(dev) MINOR_TO_MOUSE(MINOR(dev))
#define MINOR_TO_MOUSE(minor) ((minor) - FIRST_MOUSE)
/*
* List of mice and guarding semaphore. You must take the semaphore
* before you take the misc device semaphore if you need both
*/
static struct busmouse_data *busmouse_data[NR_MICE];
static DECLARE_MUTEX(mouse_sem);
/**
* busmouse_add_movement - notification of a change of mouse position
* @mousedev: mouse number
* @dx: delta X movement
* @dy: delta Y movement
* @buttons: new button state
*
* Updates the mouse position and button information. The mousedev
* parameter is the value returned from register_busmouse. The
* movement information is updated, and the new button state is
* saved. A waiting user thread is woken.
*/
void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons)
{
struct busmouse_data *mse = busmouse_data[mousedev];
int changed;
spin_lock(&mse->lock);
changed = (dx != 0 || dy != 0 || mse->buttons != buttons);
if (changed) {
add_mouse_randomness((buttons << 16) + (dy << 8) + dx);
mse->buttons = buttons;
mse->dxpos += dx;
mse->dypos += dy;
mse->ready = 1;
/*
* keep dx/dy reasonable, but still able to track when X (or
* whatever) must page or is busy (i.e. long waits between
* reads)
*/
if (mse->dxpos < -2048)
mse->dxpos = -2048;
if (mse->dxpos > 2048)
mse->dxpos = 2048;
if (mse->dypos < -2048)
mse->dypos = -2048;
if (mse->dypos > 2048)
mse->dypos = 2048;
}
spin_unlock(&mse->lock);
if (changed) {
wake_up(&mse->wait);
kill_fasync(&mse->fasyncptr, SIGIO, POLL_IN);
}
}
/**
* busmouse_add_movement - notification of a change of mouse position
* @mousedev: mouse number
* @dx: delta X movement
* @dy: delta Y movement
*
* Updates the mouse position. The mousedev parameter is the value
* returned from register_busmouse. The movement information is
* updated, and a waiting user thread is woken.
*/
void busmouse_add_movement(int mousedev, int dx, int dy)
{
struct busmouse_data *mse = busmouse_data[mousedev];
busmouse_add_movementbuttons(mousedev, dx, dy, mse->buttons);
}
/**
* busmouse_add_buttons - notification of a change of button state
* @mousedev: mouse number
* @clear: mask of buttons to clear
* @eor: mask of buttons to change
*
* Updates the button state. The mousedev parameter is the value
* returned from register_busmouse. The buttons are updated by:
* new_state = (old_state & ~clear) ^ eor
* A waiting user thread is woken up.
*/
void busmouse_add_buttons(int mousedev, int clear, int eor)
{
struct busmouse_data *mse = busmouse_data[mousedev];
busmouse_add_movementbuttons(mousedev, 0, 0, (mse->buttons & ~clear) ^ eor);
}
static int busmouse_fasync(int fd, struct file *filp, int on)
{
struct busmouse_data *mse = (struct busmouse_data *)filp->private_data;
int retval;
retval = fasync_helper(fd, filp, on, &mse->fasyncptr);
if (retval < 0)
return retval;
return 0;
}
static int busmouse_release(struct inode *inode, struct file *file)
{
struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
int ret = 0;
lock_kernel();
busmouse_fasync(-1, file, 0);
if (--mse->active == 0) {
if (mse->ops->release)
ret = mse->ops->release(inode, file);
if (mse->ops->owner)
__MOD_DEC_USE_COUNT(mse->ops->owner);
mse->ready = 0;
}
unlock_kernel();
return ret;
}
static int busmouse_open(struct inode *inode, struct file *file)
{
struct busmouse_data *mse;
unsigned int mousedev;
int ret;
mousedev = DEV_TO_MOUSE(inode->i_rdev);
if (mousedev >= NR_MICE)
return -EINVAL;
down(&mouse_sem);
mse = busmouse_data[mousedev];
ret = -ENODEV;
if (!mse || !mse->ops) /* shouldn't happen, but... */
goto end;
if (mse->ops->owner && !try_inc_mod_count(mse->ops->owner))
goto end;
ret = 0;
if (mse->ops->open) {
ret = mse->ops->open(inode, file);
if (ret && mse->ops->owner)
__MOD_DEC_USE_COUNT(mse->ops->owner);
}
if (ret)
goto end;
file->private_data = mse;
if (mse->active++)
goto end;
spin_lock_irq(&mse->lock);
mse->ready = 0;
mse->dxpos = 0;
mse->dypos = 0;
mse->buttons = mse->ops->init_button_state;
spin_unlock_irq(&mse->lock);
end:
up(&mouse_sem);
return ret;
}
static ssize_t busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
return -EINVAL;
}
static ssize_t busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
DECLARE_WAITQUEUE(wait, current);
int dxpos, dypos, buttons;
if (count < 3)
return -EINVAL;
spin_lock_irq(&mse->lock);
if (!mse->ready) {
#ifdef BROKEN_MOUSE
spin_unlock_irq(&mse->lock);
return -EAGAIN;
#else
if (file->f_flags & O_NONBLOCK) {
spin_unlock_irq(&mse->lock);
return -EAGAIN;
}
add_wait_queue(&mse->wait, &wait);
repeat:
set_current_state(TASK_INTERRUPTIBLE);
if (!mse->ready && !signal_pending(current)) {
spin_unlock_irq(&mse->lock);
schedule();
spin_lock_irq(&mse->lock);
goto repeat;
}
current->state = TASK_RUNNING;
remove_wait_queue(&mse->wait, &wait);
if (signal_pending(current)) {
spin_unlock_irq(&mse->lock);
return -ERESTARTSYS;
}
#endif
}
dxpos = mse->dxpos;
dypos = mse->dypos;
buttons = mse->buttons;
if (dxpos < -127)
dxpos =- 127;
if (dxpos > 127)
dxpos = 127;
if (dypos < -127)
dypos =- 127;
if (dypos > 127)
dypos = 127;
mse->dxpos -= dxpos;
mse->dypos -= dypos;
/* This is something that many drivers have apparantly
* forgotten... If the X and Y positions still contain
* information, we still have some info ready for the
* user program...
*/
mse->ready = mse->dxpos || mse->dypos;
spin_unlock_irq(&mse->lock);
/* Write out data to the user. Format is:
* byte 0 - identifer (0x80) and (inverted) mouse buttons
* byte 1 - X delta position +/- 127
* byte 2 - Y delta position +/- 127
*/
if (put_user((char)buttons | 128, buffer) ||
put_user((char)dxpos, buffer + 1) ||
put_user((char)dypos, buffer + 2))
return -EFAULT;
if (count > 3 && clear_user(buffer + 3, count - 3))
return -EFAULT;
file->f_dentry->d_inode->i_atime = CURRENT_TIME;
return count;
}
/* No kernel lock held - fine */
static unsigned int busmouse_poll(struct file *file, poll_table *wait)
{
struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
poll_wait(file, &mse->wait, wait);
if (mse->ready)
return POLLIN | POLLRDNORM;
return 0;
}
struct file_operations busmouse_fops=
{
owner: THIS_MODULE,
read: busmouse_read,
write: busmouse_write,
poll: busmouse_poll,
open: busmouse_open,
release: busmouse_release,
fasync: busmouse_fasync,
};
/**
* register_busmouse - register a bus mouse interface
* @ops: busmouse structure for the mouse
*
* Registers a mouse with the driver. The return is mouse number on
* success and a negative errno code on an error. The passed ops
* structure most not be freed until the mouser is unregistered
*/
int register_busmouse(struct busmouse *ops)
{
unsigned int msedev = MINOR_TO_MOUSE(ops->minor);
struct busmouse_data *mse;
int ret;
if (msedev >= NR_MICE) {
printk(KERN_ERR "busmouse: trying to allocate mouse on minor %d\n",
ops->minor);
return -EINVAL;
}
mse = kmalloc(sizeof(*mse), GFP_KERNEL);
if (!mse)
return -ENOMEM;
down(&mouse_sem);
if (busmouse_data[msedev])
{
up(&mouse_sem);
kfree(mse);
return -EBUSY;
}
memset(mse, 0, sizeof(*mse));
mse->miscdev.minor = ops->minor;
mse->miscdev.name = ops->name;
mse->miscdev.fops = &busmouse_fops;
mse->ops = ops;
mse->lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
init_waitqueue_head(&mse->wait);
busmouse_data[msedev] = mse;
ret = misc_register(&mse->miscdev);
if (!ret)
ret = msedev;
up(&mouse_sem);
return ret;
}
/**
* unregister_busmouse - unregister a bus mouse interface
* @mousedev: Mouse number to release
*
* Unregister a previously installed mouse handler. The mousedev
* passed is the return code from a previous call to register_busmouse
*/
int unregister_busmouse(int mousedev)
{
int err = -EINVAL;
if (mousedev < 0)
return 0;
if (mousedev >= NR_MICE) {
printk(KERN_ERR "busmouse: trying to free mouse on"
" mousedev %d\n", mousedev);
return -EINVAL;
}
down(&mouse_sem);
if (!busmouse_data[mousedev]) {
printk(KERN_WARNING "busmouse: trying to free free mouse"
" on mousedev %d\n", mousedev);
goto fail;
}
if (busmouse_data[mousedev]->active) {
printk(KERN_ERR "busmouse: trying to free active mouse"
" on mousedev %d\n", mousedev);
goto fail;
}
err = misc_deregister(&busmouse_data[mousedev]->miscdev);
kfree(busmouse_data[mousedev]);
busmouse_data[mousedev] = NULL;
fail:
up(&mouse_sem);
return err;
}
EXPORT_SYMBOL(busmouse_add_movementbuttons);
EXPORT_SYMBOL(busmouse_add_movement);
EXPORT_SYMBOL(busmouse_add_buttons);
EXPORT_SYMBOL(register_busmouse);
EXPORT_SYMBOL(unregister_busmouse);
MODULE_LICENSE("GPL");