linux-old/drivers/char/busmouse.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/char/busmouse.c
   3 *
   4 * Copyright (C) 1995 - 1998 Russell King <linux@arm.linux.org.uk>
   5 *  Protocol taken from original busmouse.c
   6 *  read() waiting taken from psaux.c
   7 *
   8 * Medium-level interface for quadrature or bus mice.
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/sched.h>
  14#include <linux/signal.h>
  15#include <linux/slab.h>
  16#include <linux/errno.h>
  17#include <linux/mm.h>
  18#include <linux/poll.h>
  19#include <linux/miscdevice.h>
  20#include <linux/random.h>
  21#include <linux/init.h>
  22#include <linux/smp_lock.h>
  23
  24#include <asm/uaccess.h>
  25#include <asm/system.h>
  26#include <asm/io.h>
  27
  28#include "busmouse.h"
  29
  30/* Uncomment this if your mouse drivers expect the kernel to
  31 * return with EAGAIN if the mouse does not have any events
  32 * available, even if the mouse is opened in blocking mode.
  33 * Please report use of this "feature" to the author using the
  34 * above address.
  35 */
  36/*#define BROKEN_MOUSE*/
  37
  38struct busmouse_data {
  39        struct miscdevice       miscdev;
  40        struct busmouse         *ops;
  41        spinlock_t              lock;
  42
  43        wait_queue_head_t       wait;
  44        struct fasync_struct    *fasyncptr;
  45        char                    active;
  46        char                    buttons;
  47        char                    ready;
  48        int                     dxpos;
  49        int                     dypos;
  50};
  51
  52#define NR_MICE                 15
  53#define FIRST_MOUSE             0
  54#define DEV_TO_MOUSE(dev)       MINOR_TO_MOUSE(MINOR(dev))
  55#define MINOR_TO_MOUSE(minor)   ((minor) - FIRST_MOUSE)
  56
  57/*
  58 *      List of mice and guarding semaphore. You must take the semaphore
  59 *      before you take the misc device semaphore if you need both
  60 */
  61 
  62static struct busmouse_data *busmouse_data[NR_MICE];
  63static DECLARE_MUTEX(mouse_sem);
  64
  65/**
  66 *      busmouse_add_movement - notification of a change of mouse position
  67 *      @mousedev: mouse number
  68 *      @dx: delta X movement
  69 *      @dy: delta Y movement
  70 *      @buttons: new button state
  71 *
  72 *      Updates the mouse position and button information. The mousedev
  73 *      parameter is the value returned from register_busmouse. The
  74 *      movement information is updated, and the new button state is
  75 *      saved.  A waiting user thread is woken.
  76 */
  77 
  78void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons)
  79{
  80        struct busmouse_data *mse = busmouse_data[mousedev];
  81        int changed;
  82
  83        spin_lock(&mse->lock);
  84        changed = (dx != 0 || dy != 0 || mse->buttons != buttons);
  85
  86        if (changed) {
  87                add_mouse_randomness((buttons << 16) + (dy << 8) + dx);
  88
  89                mse->buttons = buttons;
  90                mse->dxpos += dx;
  91                mse->dypos += dy;
  92                mse->ready = 1;
  93
  94                /*
  95                 * keep dx/dy reasonable, but still able to track when X (or
  96                 * whatever) must page or is busy (i.e. long waits between
  97                 * reads)
  98                 */
  99                if (mse->dxpos < -2048)
 100                        mse->dxpos = -2048;
 101                if (mse->dxpos > 2048)
 102                        mse->dxpos = 2048;
 103                if (mse->dypos < -2048)
 104                        mse->dypos = -2048;
 105                if (mse->dypos > 2048)
 106                        mse->dypos = 2048;
 107        }
 108
 109        spin_unlock(&mse->lock);
 110
 111        if (changed) {
 112                wake_up(&mse->wait);
 113
 114                kill_fasync(&mse->fasyncptr, SIGIO, POLL_IN);
 115        }
 116}
 117
 118/**
 119 *      busmouse_add_movement - notification of a change of mouse position
 120 *      @mousedev: mouse number
 121 *      @dx: delta X movement
 122 *      @dy: delta Y movement
 123 *
 124 *      Updates the mouse position. The mousedev parameter is the value
 125 *      returned from register_busmouse. The movement information is
 126 *      updated, and a waiting user thread is woken.
 127 */
 128 
 129void busmouse_add_movement(int mousedev, int dx, int dy)
 130{
 131        struct busmouse_data *mse = busmouse_data[mousedev];
 132
 133        busmouse_add_movementbuttons(mousedev, dx, dy, mse->buttons);
 134}
 135
 136/**
 137 *      busmouse_add_buttons - notification of a change of button state
 138 *      @mousedev: mouse number
 139 *      @clear: mask of buttons to clear
 140 *      @eor: mask of buttons to change
 141 *
 142 *      Updates the button state. The mousedev parameter is the value
 143 *      returned from register_busmouse. The buttons are updated by:
 144 *              new_state = (old_state & ~clear) ^ eor
 145 *      A waiting user thread is woken up.
 146 */
 147 
 148void busmouse_add_buttons(int mousedev, int clear, int eor)
 149{
 150        struct busmouse_data *mse = busmouse_data[mousedev];
 151
 152        busmouse_add_movementbuttons(mousedev, 0, 0, (mse->buttons & ~clear) ^ eor);
 153}
 154
 155static int busmouse_fasync(int fd, struct file *filp, int on)
 156{
 157        struct busmouse_data *mse = (struct busmouse_data *)filp->private_data;
 158        int retval;
 159
 160        retval = fasync_helper(fd, filp, on, &mse->fasyncptr);
 161        if (retval < 0)
 162                return retval;
 163        return 0;
 164}
 165
 166static int busmouse_release(struct inode *inode, struct file *file)
 167{
 168        struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 169        int ret = 0;
 170
 171        lock_kernel();
 172        busmouse_fasync(-1, file, 0);
 173
 174        if (--mse->active == 0) {
 175                if (mse->ops->release)
 176                        ret = mse->ops->release(inode, file);
 177                if (mse->ops->owner)
 178                        __MOD_DEC_USE_COUNT(mse->ops->owner);
 179                mse->ready = 0;
 180        }
 181        unlock_kernel();
 182
 183        return ret;
 184}
 185
 186static int busmouse_open(struct inode *inode, struct file *file)
 187{
 188        struct busmouse_data *mse;
 189        unsigned int mousedev;
 190        int ret;
 191
 192        mousedev = DEV_TO_MOUSE(inode->i_rdev);
 193        if (mousedev >= NR_MICE)
 194                return -EINVAL;
 195
 196        down(&mouse_sem);
 197        mse = busmouse_data[mousedev];
 198        ret = -ENODEV;
 199        if (!mse || !mse->ops)  /* shouldn't happen, but... */
 200                goto end;
 201
 202        if (mse->ops->owner && !try_inc_mod_count(mse->ops->owner))
 203                goto end;
 204
 205        ret = 0;
 206        if (mse->ops->open) {
 207                ret = mse->ops->open(inode, file);
 208                if (ret && mse->ops->owner)
 209                        __MOD_DEC_USE_COUNT(mse->ops->owner);
 210        }
 211
 212        if (ret)
 213                goto end;
 214
 215        file->private_data = mse;
 216
 217        if (mse->active++)
 218                goto end;
 219
 220        spin_lock_irq(&mse->lock);
 221
 222        mse->ready   = 0;
 223        mse->dxpos   = 0;
 224        mse->dypos   = 0;
 225        mse->buttons = mse->ops->init_button_state;
 226
 227        spin_unlock_irq(&mse->lock);
 228end:
 229        up(&mouse_sem);
 230        return ret;
 231}
 232
 233static ssize_t busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
 234{
 235        return -EINVAL;
 236}
 237
 238static ssize_t busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 239{
 240        struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 241        DECLARE_WAITQUEUE(wait, current);
 242        int dxpos, dypos, buttons;
 243
 244        if (count < 3)
 245                return -EINVAL;
 246
 247        spin_lock_irq(&mse->lock);
 248
 249        if (!mse->ready) {
 250#ifdef BROKEN_MOUSE
 251                spin_unlock_irq(&mse->lock);
 252                return -EAGAIN;
 253#else
 254                if (file->f_flags & O_NONBLOCK) {
 255                        spin_unlock_irq(&mse->lock);
 256                        return -EAGAIN;
 257                }
 258
 259                add_wait_queue(&mse->wait, &wait);
 260repeat:
 261                set_current_state(TASK_INTERRUPTIBLE);
 262                if (!mse->ready && !signal_pending(current)) {
 263                        spin_unlock_irq(&mse->lock);
 264                        schedule();
 265                        spin_lock_irq(&mse->lock);
 266                        goto repeat;
 267                }
 268
 269                current->state = TASK_RUNNING;
 270                remove_wait_queue(&mse->wait, &wait);
 271
 272                if (signal_pending(current)) {
 273                        spin_unlock_irq(&mse->lock);
 274                        return -ERESTARTSYS;
 275                }
 276#endif
 277        }
 278
 279        dxpos = mse->dxpos;
 280        dypos = mse->dypos;
 281        buttons = mse->buttons;
 282
 283        if (dxpos < -127)
 284                dxpos =- 127;
 285        if (dxpos > 127)
 286                dxpos = 127;
 287        if (dypos < -127)
 288                dypos =- 127;
 289        if (dypos > 127)
 290                dypos = 127;
 291
 292        mse->dxpos -= dxpos;
 293        mse->dypos -= dypos;
 294
 295        /* This is something that many drivers have apparantly
 296         * forgotten...  If the X and Y positions still contain
 297         * information, we still have some info ready for the
 298         * user program...
 299         */
 300        mse->ready = mse->dxpos || mse->dypos;
 301
 302        spin_unlock_irq(&mse->lock);
 303
 304        /* Write out data to the user.  Format is:
 305         *   byte 0 - identifer (0x80) and (inverted) mouse buttons
 306         *   byte 1 - X delta position +/- 127
 307         *   byte 2 - Y delta position +/- 127
 308         */
 309        if (put_user((char)buttons | 128, buffer) ||
 310            put_user((char)dxpos, buffer + 1) ||
 311            put_user((char)dypos, buffer + 2))
 312                return -EFAULT;
 313
 314        if (count > 3 && clear_user(buffer + 3, count - 3))
 315                return -EFAULT;
 316
 317        file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 318
 319        return count;
 320}
 321
 322/* No kernel lock held - fine */
 323static unsigned int busmouse_poll(struct file *file, poll_table *wait)
 324{
 325        struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
 326
 327        poll_wait(file, &mse->wait, wait);
 328
 329        if (mse->ready)
 330                return POLLIN | POLLRDNORM;
 331
 332        return 0;
 333}
 334
 335struct file_operations busmouse_fops=
 336{
 337        owner:          THIS_MODULE,
 338        read:           busmouse_read,
 339        write:          busmouse_write,
 340        poll:           busmouse_poll,
 341        open:           busmouse_open,
 342        release:        busmouse_release,
 343        fasync:         busmouse_fasync,
 344};
 345
 346/**
 347 *      register_busmouse - register a bus mouse interface
 348 *      @ops: busmouse structure for the mouse
 349 *
 350 *      Registers a mouse with the driver. The return is mouse number on
 351 *      success and a negative errno code on an error. The passed ops
 352 *      structure most not be freed until the mouser is unregistered
 353 */
 354 
 355int register_busmouse(struct busmouse *ops)
 356{
 357        unsigned int msedev = MINOR_TO_MOUSE(ops->minor);
 358        struct busmouse_data *mse;
 359        int ret;
 360
 361        if (msedev >= NR_MICE) {
 362                printk(KERN_ERR "busmouse: trying to allocate mouse on minor %d\n",
 363                       ops->minor);
 364                return -EINVAL;
 365        }
 366
 367        mse = kmalloc(sizeof(*mse), GFP_KERNEL);
 368        if (!mse)
 369                return -ENOMEM;
 370
 371        down(&mouse_sem);
 372        if (busmouse_data[msedev])
 373        {
 374                up(&mouse_sem);
 375                kfree(mse);
 376                return -EBUSY;
 377        }
 378
 379        memset(mse, 0, sizeof(*mse));
 380
 381        mse->miscdev.minor = ops->minor;
 382        mse->miscdev.name = ops->name;
 383        mse->miscdev.fops = &busmouse_fops;
 384        mse->ops = ops;
 385        mse->lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
 386        init_waitqueue_head(&mse->wait);
 387
 388        busmouse_data[msedev] = mse;
 389
 390        ret = misc_register(&mse->miscdev);
 391        if (!ret)
 392                ret = msedev;
 393        up(&mouse_sem);
 394        
 395        return ret;
 396}
 397
 398/**
 399 *      unregister_busmouse - unregister a bus mouse interface
 400 *      @mousedev: Mouse number to release
 401 *
 402 *      Unregister a previously installed mouse handler. The mousedev
 403 *      passed is the return code from a previous call to register_busmouse
 404 */
 405 
 406
 407int unregister_busmouse(int mousedev)
 408{
 409        int err = -EINVAL;
 410
 411        if (mousedev < 0)
 412                return 0;
 413        if (mousedev >= NR_MICE) {
 414                printk(KERN_ERR "busmouse: trying to free mouse on"
 415                       " mousedev %d\n", mousedev);
 416                return -EINVAL;
 417        }
 418
 419        down(&mouse_sem);
 420        
 421        if (!busmouse_data[mousedev]) {
 422                printk(KERN_WARNING "busmouse: trying to free free mouse"
 423                       " on mousedev %d\n", mousedev);
 424                goto fail;
 425        }
 426
 427        if (busmouse_data[mousedev]->active) {
 428                printk(KERN_ERR "busmouse: trying to free active mouse"
 429                       " on mousedev %d\n", mousedev);
 430                goto fail;
 431        }
 432
 433        err = misc_deregister(&busmouse_data[mousedev]->miscdev);
 434
 435        kfree(busmouse_data[mousedev]);
 436        busmouse_data[mousedev] = NULL;
 437fail:
 438        up(&mouse_sem);
 439        return err;
 440}
 441
 442EXPORT_SYMBOL(busmouse_add_movementbuttons);
 443EXPORT_SYMBOL(busmouse_add_movement);
 444EXPORT_SYMBOL(busmouse_add_buttons);
 445EXPORT_SYMBOL(register_busmouse);
 446EXPORT_SYMBOL(unregister_busmouse);
 447
 448MODULE_LICENSE("GPL");
 449
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.