linux-old/drivers/acorn/char/mouse_ps2.c
<<
>>
Prefs
   1/* 
   2 * Driver for PS/2 mouse on IOMD interface
   3 */
   4#include <linux/sched.h>
   5#include <linux/interrupt.h>
   6#include <linux/tty.h>
   7#include <linux/tty_flip.h>
   8#include <linux/mm.h>
   9#include <linux/slab.h>
  10#include <linux/ptrace.h>
  11#include <linux/signal.h>
  12#include <linux/timer.h>
  13#include <linux/random.h>
  14#include <linux/ctype.h>
  15#include <linux/kbd_ll.h>
  16#include <linux/delay.h>
  17#include <linux/init.h>
  18#include <linux/poll.h>
  19#include <linux/miscdevice.h>
  20
  21#include <asm/bitops.h>
  22#include <asm/irq.h>
  23#include <asm/hardware.h>
  24#include <asm/io.h>
  25#include <asm/hardware/iomd.h>
  26#include <asm/system.h>
  27#include <asm/uaccess.h>
  28
  29/*
  30 *      PS/2 Auxiliary Device
  31 */
  32
  33static struct aux_queue *queue; /* Mouse data buffer. */
  34static int aux_count = 0;
  35/* used when we send commands to the mouse that expect an ACK. */
  36static unsigned char mouse_reply_expected = 0;
  37
  38#define MAX_RETRIES     60              /* some aux operations take long time*/
  39
  40/*
  41 *      Mouse Commands
  42 */
  43
  44#define AUX_SET_RES             0xE8    /* Set resolution */
  45#define AUX_SET_SCALE11         0xE6    /* Set 1:1 scaling */
  46#define AUX_SET_SCALE21         0xE7    /* Set 2:1 scaling */
  47#define AUX_GET_SCALE           0xE9    /* Get scaling factor */
  48#define AUX_SET_STREAM          0xEA    /* Set stream mode */
  49#define AUX_SET_SAMPLE          0xF3    /* Set sample rate */
  50#define AUX_ENABLE_DEV          0xF4    /* Enable aux device */
  51#define AUX_DISABLE_DEV         0xF5    /* Disable aux device */
  52#define AUX_RESET               0xFF    /* Reset aux device */
  53#define AUX_ACK                 0xFA    /* Command byte ACK. */
  54
  55#define AUX_BUF_SIZE            2048    /* This might be better divisible by
  56                                           three to make overruns stay in sync
  57                                           but then the read function would 
  58                                           need a lock etc - ick */
  59
  60struct aux_queue {
  61        unsigned long head;
  62        unsigned long tail;
  63        wait_queue_head_t proc_list;
  64        struct fasync_struct *fasync;
  65        unsigned char buf[AUX_BUF_SIZE];
  66};
  67
  68/*
  69 * Send a byte to the mouse.
  70 */
  71static void aux_write_dev(int val)
  72{
  73        while (!(iomd_readb(IOMD_MSECTL) & 0x80));
  74        iomd_writeb(val, IOMD_MSEDAT);
  75}
  76
  77/*
  78 * Send a byte to the mouse & handle returned ack
  79 */
  80static void aux_write_ack(int val)
  81{
  82        while (!(iomd_readb(IOMD_MSECTL) & 0x80));
  83        iomd_writeb(val, IOMD_MSEDAT);
  84
  85        /* we expect an ACK in response. */
  86        mouse_reply_expected++;
  87}
  88
  89static unsigned char get_from_queue(void)
  90{
  91        unsigned char result;
  92
  93        result = queue->buf[queue->tail];
  94        queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
  95        return result;
  96}
  97
  98static void psaux_interrupt(int irq, void *dev_id, struct pt_regs *regs)
  99{
 100        int val = iomd_readb(IOMD_MSEDAT);
 101
 102        if (mouse_reply_expected) {
 103                if (val == AUX_ACK) {
 104                        mouse_reply_expected--;
 105                        return;
 106                }
 107                mouse_reply_expected = 0;
 108        }
 109
 110        add_mouse_randomness(val);
 111        if (aux_count) {
 112                int head = queue->head;
 113
 114                queue->buf[head] = val;
 115                head = (head + 1) & (AUX_BUF_SIZE-1);
 116                if (head != queue->tail) {
 117                        queue->head = head;
 118                        kill_fasync(&queue->fasync, SIGIO, POLL_IN);
 119                        wake_up_interruptible(&queue->proc_list);
 120                }
 121        }
 122}
 123
 124static inline int queue_empty(void)
 125{
 126        return queue->head == queue->tail;
 127}
 128
 129static int fasync_aux(int fd, struct file *filp, int on)
 130{
 131        int retval;
 132
 133        retval = fasync_helper(fd, filp, on, &queue->fasync);
 134        if (retval < 0)
 135                return retval;
 136        return 0;
 137}
 138
 139
 140/*
 141 * Random magic cookie for the aux device
 142 */
 143#define AUX_DEV ((void *)queue)
 144
 145static int release_aux(struct inode * inode, struct file * file)
 146{
 147        fasync_aux(-1, file, 0);
 148        if (--aux_count)
 149                return 0;
 150        free_irq(IRQ_MOUSERX, AUX_DEV);
 151        return 0;
 152}
 153
 154/*
 155 * Install interrupt handler.
 156 * Enable auxiliary device.
 157 */
 158
 159static int open_aux(struct inode * inode, struct file * file)
 160{
 161        if (aux_count++)
 162                return 0;
 163
 164        queue->head = queue->tail = 0;          /* Flush input queue */
 165        if (request_irq(IRQ_MOUSERX, psaux_interrupt, SA_SHIRQ, "ps/2 mouse", 
 166                        AUX_DEV)) {
 167                aux_count--;
 168                return -EBUSY;
 169        }
 170
 171        aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
 172
 173        return 0;
 174}
 175
 176/*
 177 * Put bytes from input queue to buffer.
 178 */
 179
 180static ssize_t read_aux(struct file * file, char * buffer,
 181                        size_t count, loff_t *ppos)
 182{
 183        DECLARE_WAITQUEUE(wait, current);
 184        ssize_t i = count;
 185        unsigned char c;
 186
 187        if (queue_empty()) {
 188                if (file->f_flags & O_NONBLOCK)
 189                        return -EAGAIN;
 190                add_wait_queue(&queue->proc_list, &wait);
 191repeat:
 192                current->state = TASK_INTERRUPTIBLE;
 193                if (queue_empty() && !signal_pending(current)) {
 194                        schedule();
 195                        goto repeat;
 196                }
 197                current->state = TASK_RUNNING;
 198                remove_wait_queue(&queue->proc_list, &wait);
 199        }
 200        while (i > 0 && !queue_empty()) {
 201                c = get_from_queue();
 202                put_user(c, buffer++);
 203                i--;
 204        }
 205        if (count-i) {
 206                file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 207                return count-i;
 208        }
 209        if (signal_pending(current))
 210                return -ERESTARTSYS;
 211        return 0;
 212}
 213
 214/*
 215 * Write to the aux device.
 216 */
 217
 218static ssize_t write_aux(struct file * file, const char * buffer,
 219                         size_t count, loff_t *ppos)
 220{
 221        ssize_t retval = 0;
 222
 223        if (count) {
 224                ssize_t written = 0;
 225
 226                if (count > 32)
 227                        count = 32; /* Limit to 32 bytes. */
 228                do {
 229                        char c;
 230                        get_user(c, buffer++);
 231                        aux_write_dev(c);
 232                        written++;
 233                } while (--count);
 234                retval = -EIO;
 235                if (written) {
 236                        retval = written;
 237                        file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
 238                }
 239        }
 240
 241        return retval;
 242}
 243
 244static unsigned int aux_poll(struct file *file, poll_table * wait)
 245{
 246        poll_wait(file, &queue->proc_list, wait);
 247        if (!queue_empty())
 248                return POLLIN | POLLRDNORM;
 249        return 0;
 250}
 251
 252struct file_operations psaux_fops = {
 253        read:           read_aux,
 254        write:          write_aux,
 255        poll:           aux_poll,
 256        open:           open_aux,
 257        release:        release_aux,
 258        fasync:         fasync_aux,
 259};
 260
 261/*
 262 * Initialize driver.
 263 */
 264static struct miscdevice psaux_mouse = {
 265        PSMOUSE_MINOR, "psaux", &psaux_fops
 266};
 267
 268int __init psaux_init(void)
 269{
 270        /* Reset the mouse state machine. */
 271        iomd_writeb(0, IOMD_MSECTL);
 272        iomd_writeb(8, IOMD_MSECTL);
 273  
 274        queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
 275        if (queue == NULL)
 276                return -ENOMEM;
 277
 278        if (misc_register(&psaux_mouse)) {
 279                kfree(queue);
 280                return -ENODEV;
 281        }
 282
 283        memset(queue, 0, sizeof(*queue));
 284        queue->head = queue->tail = 0;
 285        init_waitqueue_head(&queue->proc_list);
 286
 287        aux_write_ack(AUX_SET_SAMPLE);
 288        aux_write_ack(100);                     /* 100 samples/sec */
 289        aux_write_ack(AUX_SET_RES);
 290        aux_write_ack(3);                       /* 8 counts per mm */
 291        aux_write_ack(AUX_SET_SCALE21);         /* 2:1 scaling */
 292
 293        return 0;
 294}
 295
 296module_init(psaux_init);
 297
 298
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.