Viewing file: busmouse.c (10.04 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * 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");
|