linux-old/drivers/char/qpmouse.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/char/qpmouse.c
   3 *
   4 * Driver for a 82C710 C&T mouse interface chip.
   5 *
   6 * Based on the PS/2 driver by Johan Myreen.
   7 *
   8 * Corrections in device setup for some laptop mice & trackballs.
   9 * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
  10 *
  11 * Modified by Johan Myreen (jem@iki.fi) 04Aug93
  12 *   to include support for QuickPort mouse.
  13 *
  14 * Changed references to "QuickPort" with "82C710" since "QuickPort"
  15 * is not what this driver is all about -- QuickPort is just a
  16 * connector type, and this driver is for the mouse port on the Chips
  17 * & Technologies 82C710 interface chip. 15Nov93 jem@iki.fi
  18 *
  19 * Added support for SIGIO. 28Jul95 jem@iki.fi
  20 *
  21 * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
  22 *
  23 * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
  24 */
  25
  26#include <linux/module.h>
  27#include <linux/kernel.h>
  28#include <linux/sched.h>
  29#include <linux/interrupt.h>
  30#include <linux/fcntl.h>
  31#include <linux/errno.h>
  32#include <linux/timer.h>
  33#include <linux/slab.h>
  34#include <linux/miscdevice.h>
  35#include <linux/random.h>
  36#include <linux/poll.h>
  37#include <linux/init.h>
  38#include <linux/smp_lock.h>
  39
  40#include <asm/io.h>
  41#include <asm/uaccess.h>
  42#include <asm/system.h>
  43#include <asm/semaphore.h>
  44
  45#include <linux/pc_keyb.h>              /* mouse enable command.. */
  46
  47
  48/*
  49 * We use the same minor number as the PS/2 mouse for (bad) historical
  50 * reasons..
  51 */
  52#define PSMOUSE_MINOR      1                    /* Minor device # for this mouse */
  53#define QP_BUF_SIZE     2048
  54
  55struct qp_queue {
  56        unsigned long head;
  57        unsigned long tail;
  58        wait_queue_head_t proc_list;
  59        struct fasync_struct *fasync;
  60        unsigned char buf[QP_BUF_SIZE];
  61};
  62
  63static struct qp_queue *queue;
  64
  65static unsigned int get_from_queue(void)
  66{
  67        unsigned int result;
  68        unsigned long flags;
  69
  70        save_flags(flags);
  71        cli();
  72        result = queue->buf[queue->tail];
  73        queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1);
  74        restore_flags(flags);
  75        return result;
  76}
  77
  78
  79static inline int queue_empty(void)
  80{
  81        return queue->head == queue->tail;
  82}
  83
  84static int fasync_qp(int fd, struct file *filp, int on)
  85{
  86        int retval;
  87
  88        retval = fasync_helper(fd, filp, on, &queue->fasync);
  89        if (retval < 0)
  90                return retval;
  91        return 0;
  92}
  93
  94/*
  95 *      82C710 Interface
  96 */
  97
  98#define QP_DATA         0x310           /* Data Port I/O Address */
  99#define QP_STATUS       0x311           /* Status Port I/O Address */
 100
 101#define QP_DEV_IDLE     0x01            /* Device Idle */
 102#define QP_RX_FULL      0x02            /* Device Char received */
 103#define QP_TX_IDLE      0x04            /* Device XMIT Idle */
 104#define QP_RESET        0x08            /* Device Reset */
 105#define QP_INTS_ON      0x10            /* Device Interrupt On */
 106#define QP_ERROR_FLAG   0x20            /* Device Error */
 107#define QP_CLEAR        0x40            /* Device Clear */
 108#define QP_ENABLE       0x80            /* Device Enable */
 109
 110#define QP_IRQ          12
 111
 112static int qp_present;
 113static int qp_count;
 114static int qp_data = QP_DATA;
 115static int qp_status = QP_STATUS;
 116
 117static int poll_qp_status(void);
 118static int probe_qp(void);
 119
 120/*
 121 * Interrupt handler for the 82C710 mouse port. A character
 122 * is waiting in the 82C710.
 123 */
 124
 125static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
 126{
 127        int head = queue->head;
 128        int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1);
 129
 130        add_mouse_randomness(queue->buf[head] = inb(qp_data));
 131        if (head != maxhead) {
 132                head++;
 133                head &= QP_BUF_SIZE-1;
 134        }
 135        queue->head = head;
 136        kill_fasync(&queue->fasync, SIGIO, POLL_IN);
 137        wake_up_interruptible(&queue->proc_list);
 138}
 139
 140static int release_qp(struct inode * inode, struct file * file)
 141{
 142        unsigned char status;
 143
 144        lock_kernel();
 145        fasync_qp(-1, file, 0);
 146        if (!--qp_count) {
 147                if (!poll_qp_status())
 148                        printk(KERN_WARNING "Warning: Mouse device busy in release_qp()\n");
 149                status = inb_p(qp_status);
 150                outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
 151                if (!poll_qp_status())
 152                        printk(KERN_WARNING "Warning: Mouse device busy in release_qp()\n");
 153                free_irq(QP_IRQ, NULL);
 154        }
 155        unlock_kernel();
 156        return 0;
 157}
 158
 159/*
 160 * Install interrupt handler.
 161 * Enable the device, enable interrupts. 
 162 */
 163
 164static int open_qp(struct inode * inode, struct file * file)
 165{
 166        unsigned char status;
 167
 168        if (!qp_present)
 169                return -EINVAL;
 170
 171        if (qp_count++)
 172                return 0;
 173
 174        if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
 175                qp_count--;
 176                return -EBUSY;
 177        }
 178
 179        status = inb_p(qp_status);
 180        status |= (QP_ENABLE|QP_RESET);
 181        outb_p(status, qp_status);
 182        status &= ~(QP_RESET);
 183        outb_p(status, qp_status);
 184
 185        queue->head = queue->tail = 0;          /* Flush input queue */
 186        status |= QP_INTS_ON;
 187        outb_p(status, qp_status);              /* Enable interrupts */
 188
 189        while (!poll_qp_status()) {
 190                printk(KERN_ERR "Error: Mouse device busy in open_qp()\n");
 191                qp_count--;
 192                status &= ~(QP_ENABLE|QP_INTS_ON);
 193                outb_p(status, qp_status);
 194                free_irq(QP_IRQ, NULL);
 195                return -EBUSY;
 196        }
 197
 198        outb_p(AUX_ENABLE_DEV, qp_data);        /* Wake up mouse */
 199        return 0;
 200}
 201
 202/*
 203 * Write to the 82C710 mouse device.
 204 */
 205
 206static ssize_t write_qp(struct file * file, const char * buffer,
 207                        size_t count, loff_t *ppos)
 208{
 209        ssize_t i = count;
 210
 211        while (i--) {
 212                char c;
 213                if (!poll_qp_status())
 214                        return -EIO;
 215                get_user(c, buffer++);
 216                outb_p(c, qp_data);
 217        }
 218        file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 219        return count;
 220}
 221
 222static unsigned int poll_qp(struct file *file, poll_table * wait)
 223{
 224        poll_wait(file, &queue->proc_list, wait);
 225        if (!queue_empty())
 226                return POLLIN | POLLRDNORM;
 227        return 0;
 228}
 229
 230/*
 231 * Wait for device to send output char and flush any input char.
 232 */
 233
 234#define MAX_RETRIES (60)
 235
 236static int poll_qp_status(void)
 237{
 238        int retries=0;
 239
 240        while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
 241                       != (QP_DEV_IDLE|QP_TX_IDLE)
 242                       && retries < MAX_RETRIES) {
 243
 244                if (inb_p(qp_status)&(QP_RX_FULL))
 245                        inb_p(qp_data);
 246                current->state = TASK_INTERRUPTIBLE;
 247                schedule_timeout((5*HZ + 99) / 100);
 248                retries++;
 249        }
 250        return !(retries==MAX_RETRIES);
 251}
 252
 253/*
 254 * Put bytes from input queue to buffer.
 255 */
 256
 257static ssize_t read_qp(struct file * file, char * buffer,
 258                        size_t count, loff_t *ppos)
 259{
 260        DECLARE_WAITQUEUE(wait, current);
 261        ssize_t i = count;
 262        unsigned char c;
 263
 264        if (queue_empty()) {
 265                if (file->f_flags & O_NONBLOCK)
 266                        return -EAGAIN;
 267                add_wait_queue(&queue->proc_list, &wait);
 268repeat:
 269                set_current_state(TASK_INTERRUPTIBLE);
 270                if (queue_empty() && !signal_pending(current)) {
 271                        schedule();
 272                        goto repeat;
 273                }
 274                current->state = TASK_RUNNING;
 275                remove_wait_queue(&queue->proc_list, &wait);
 276        }
 277        while (i > 0 && !queue_empty()) {
 278                c = get_from_queue();
 279                put_user(c, buffer++);
 280                i--;
 281        }
 282        if (count-i) {
 283                file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 284                return count-i;
 285        }
 286        if (signal_pending(current))
 287                return -ERESTARTSYS;
 288        return 0;
 289}
 290
 291struct file_operations qp_fops = {
 292        owner:          THIS_MODULE,
 293        read:           read_qp,
 294        write:          write_qp,
 295        poll:           poll_qp,
 296        open:           open_qp,
 297        release:        release_qp,
 298        fasync:         fasync_qp,
 299};
 300
 301/*
 302 * Initialize driver.
 303 */
 304static struct miscdevice qp_mouse = {
 305        minor:          PSMOUSE_MINOR,
 306        name:           "QPmouse",
 307        fops:           &qp_fops,
 308};
 309
 310/*
 311 * Function to read register in 82C710.
 312 */
 313
 314static inline unsigned char read_710(unsigned char index)
 315{
 316        outb_p(index, 0x390);                   /* Write index */
 317        return inb_p(0x391);                    /* Read the data */
 318}
 319
 320
 321/*
 322 * See if we can find a 82C710 device. Read mouse address.
 323 */
 324
 325static int __init probe_qp(void)
 326{
 327        outb_p(0x55, 0x2fa);                    /* Any value except 9, ff or 36 */
 328        outb_p(0xaa, 0x3fa);                    /* Inverse of 55 */
 329        outb_p(0x36, 0x3fa);                    /* Address the chip */
 330        outb_p(0xe4, 0x3fa);                    /* 390/4; 390 = config address */
 331        outb_p(0x1b, 0x2fa);                    /* Inverse of e4 */
 332        if (read_710(0x0f) != 0xe4)             /* Config address found? */
 333          return 0;                             /* No: no 82C710 here */
 334        qp_data = read_710(0x0d)*4;             /* Get mouse I/O address */
 335        qp_status = qp_data+1;
 336        outb_p(0x0f, 0x390);
 337        outb_p(0x0f, 0x391);                    /* Close config mode */
 338        return 1;
 339}
 340
 341static char msg_banner[] __initdata = KERN_INFO "82C710 type pointing device detected -- driver installed.\n";
 342static char msg_nomem[]  __initdata = KERN_ERR "qpmouse: no queue memory.\n";
 343
 344static int __init qpmouse_init_driver(void)
 345{
 346        if (!probe_qp())
 347                return -EIO;
 348
 349        printk(msg_banner);
 350
 351/*      printk("82C710 address = %x (should be 0x310)\n", qp_data); */
 352        queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 353        if (queue == NULL) {
 354                printk(msg_nomem);
 355                return -ENOMEM;
 356        }
 357        qp_present = 1;
 358        misc_register(&qp_mouse);
 359        memset(queue, 0, sizeof(*queue));
 360        queue->head = queue->tail = 0;
 361        init_waitqueue_head(&queue->proc_list);
 362        return 0;
 363}
 364
 365static void __exit qpmouse_exit_driver(void)
 366{
 367        misc_deregister(&qp_mouse);
 368        kfree(queue);
 369}
 370
 371module_init(qpmouse_init_driver);
 372module_exit(qpmouse_exit_driver);
 373
 374
 375MODULE_LICENSE("GPL");
 376EXPORT_NO_SYMBOLS;
 377
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.