linux-old/drivers/sbus/char/sunmouse.c History
<<
>>
Prefs
   1/* sunmouse.c: Sun mouse driver for the Sparc
   2 *
   3 * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu)
   4 * Copyright (C) 1995 Miguel de Icaza (miguel@nuclecu.unam.mx)
   5 *
   6 * Parts based on the psaux.c driver written by:
   7 * Johan Myreen.
   8 *
   9 * Dec/19/95 Added SunOS mouse ioctls - miguel.
  10 * Jan/5/96  Added VUID support, sigio support - miguel.
  11 * Mar/5/96  Added proper mouse stream support - miguel.
  12 * Sep/96    Allow more than one reader -miguel.
  13 * Aug/97    Added PCI 8042 controller support -DaveM
  14 */
  15
  16/* The mouse is run off of one of the Zilog serial ports.  On
  17 * that port is the mouse and the keyboard, each gets a zs channel.
  18 * The mouse itself is mouse-systems in nature.  So the protocol is:
  19 *
  20 * Byte 1) Button state which is bit-encoded as
  21 *            0x4 == left-button down, else up
  22 *            0x2 == middle-button down, else up
  23 *            0x1 == right-button down, else up
  24 *
  25 * Byte 2) Delta-x
  26 * Byte 3) Delta-y
  27 * Byte 4) Delta-x again
  28 * Byte 5) Delta-y again
  29 *
  30 * One day this driver will have to support more than one mouse in the system.
  31 *
  32 * This driver has two modes of operation: the default VUID_NATIVE is
  33 * set when the device is opened and allows the application to see the
  34 * mouse character stream as we get it from the serial (for gpm for
  35 * example).  The second method, VUID_FIRM_EVENT will provide cooked
  36 * events in Firm_event records as expected by SunOS/Solaris applications.
  37 *
  38 * FIXME: We need to support more than one mouse.
  39 * */
  40
  41#include <linux/config.h>
  42#include <linux/kernel.h>
  43#include <linux/sched.h>
  44#include <linux/fcntl.h>
  45#include <linux/signal.h>
  46#include <linux/timer.h>
  47#include <linux/errno.h>
  48#include <linux/miscdevice.h>
  49#include <linux/mm.h>
  50#include <linux/poll.h>
  51#include <linux/spinlock.h>
  52#include <linux/smp_lock.h>
  53#include <linux/init.h>
  54#include <asm/uaccess.h>
  55#include <asm/system.h>
  56#include <asm/vuid_event.h>
  57#include <linux/random.h>
  58/* The following keeps track of software state for the Sun
  59 * mouse.
  60 */
  61#define STREAM_SIZE   2048
  62#define EV_SIZE       (STREAM_SIZE/sizeof (Firm_event))
  63#define BUTTON_LEFT   4
  64#define BUTTON_MIDDLE 2
  65#define BUTTON_RIGHT  1
  66
  67struct sun_mouse {
  68        unsigned char transaction[5];  /* Each protocol transaction */
  69        unsigned char byte;            /* Counter, starts at 0 */
  70        unsigned char button_state;    /* Current button state */
  71        unsigned char prev_state;      /* Previous button state */
  72        int delta_x;                   /* Current delta-x */
  73        int delta_y;                   /* Current delta-y */
  74        int active;                    /* set if device is open */
  75        int vuid_mode;                 /* VUID_NATIVE or VUID_FIRM_EVENT */
  76        wait_queue_head_t proc_list;
  77        struct fasync_struct *fasync;
  78        
  79        /* The event/stream queue */
  80        spinlock_t lock;
  81        unsigned int head;
  82        unsigned int tail;
  83        union {
  84                char stream [STREAM_SIZE];
  85                Firm_event ev [EV_SIZE];
  86        } queue;
  87};
  88
  89static struct sun_mouse sunmouse;
  90#define gen_events (sunmouse.vuid_mode != VUID_NATIVE)
  91#define bstate sunmouse.button_state
  92#define pstate sunmouse.prev_state
  93
  94extern void mouse_put_char(char ch);
  95
  96#undef SMOUSE_DEBUG
  97
  98static int
  99push_event (Firm_event *ev)
 100{
 101        unsigned long flags;
 102        int next, ret;
 103        
 104        spin_lock_irqsave(&sunmouse.lock, flags);
 105
 106        next = (sunmouse.head + 1) % EV_SIZE;
 107        ret = 0;
 108        if (next != sunmouse.tail) {
 109                sunmouse.queue.ev [sunmouse.head] = *ev;
 110                sunmouse.head = next;
 111                ret = 1;
 112        }
 113
 114        spin_unlock_irqrestore(&sunmouse.lock, flags);
 115
 116        return ret;
 117}
 118
 119static int
 120queue_empty (void)
 121{
 122        return sunmouse.head == sunmouse.tail;
 123}
 124
 125/* Must be invoked under the sunmouse.lock */
 126static void get_from_queue (Firm_event *p)
 127{
 128        *p = sunmouse.queue.ev [sunmouse.tail];
 129        sunmouse.tail = (sunmouse.tail + 1) % EV_SIZE;
 130}
 131
 132static void
 133push_char (char c)
 134{
 135        unsigned long flags;
 136        int next;
 137
 138        spin_lock_irqsave(&sunmouse.lock, flags);
 139
 140        next = (sunmouse.head + 1) % STREAM_SIZE;
 141        if (next != sunmouse.tail) {
 142#ifdef SMOUSE_DEBUG
 143                printk("P<%02x>\n", (unsigned char)c);
 144#endif
 145                sunmouse.queue.stream [sunmouse.head] = c;
 146                sunmouse.head = next;
 147        }
 148
 149        spin_unlock_irqrestore(&sunmouse.lock, flags);
 150
 151        kill_fasync (&sunmouse.fasync, SIGIO, POLL_IN);
 152        wake_up_interruptible (&sunmouse.proc_list);
 153}
 154
 155/* Auto baud rate "detection".  ;-) */
 156static int mouse_baud = 4800;           /* Initial rate set by zilog driver. */
 157
 158/* Change the baud rate after receiving too many "bogon bytes". */
 159void sun_mouse_change_baud(void)
 160{
 161        extern void rs_change_mouse_baud(int newbaud);
 162
 163        if(mouse_baud == 1200)
 164                mouse_baud = 2400;
 165        else if(mouse_baud == 2400)
 166                mouse_baud = 4800;
 167        else if(mouse_baud == 4800)
 168                mouse_baud = 9600;
 169        else
 170                mouse_baud = 1200;
 171
 172        rs_change_mouse_baud(mouse_baud);
 173}
 174
 175/* This tries to monitor the mouse state so that it
 176 * can automatically adjust to the correct baud rate.
 177 * The mouse spits out BRKs when the baud rate is
 178 * incorrect.
 179 *
 180 * It returns non-zero if we should ignore this byte.
 181 */
 182int mouse_baud_detection(unsigned char c, int is_break)
 183{
 184        static int mouse_got_break = 0;
 185        static int ctr = 0;
 186
 187        if (is_break) {
 188                /* Let a few normal bytes go by before
 189                 * we jump the gun and say we need to
 190                 * try another baud rate.
 191                 */
 192                if (mouse_got_break && ctr < 8)
 193                        return 1;
 194
 195                /* OK, we need to try another baud rate. */
 196                sun_mouse_change_baud();
 197                ctr = 0;
 198                mouse_got_break = 1;
 199                return 1;
 200        }
 201        if (mouse_got_break) {
 202                ctr++;
 203                if (c == 0x87) {
 204                        printk(KERN_INFO "sunmouse: Successfully "
 205                               "adjusted to %d baud.\n", mouse_baud);
 206                        mouse_got_break = 0;
 207                }
 208                return 1;
 209        }
 210
 211        return 0;
 212}
 213
 214/* You ask me, why does this cap the lower bound at -127 and not
 215 * -128?  Because the xf86 mouse code is crap and treats -128
 216 * as an illegal value and resets it's protocol state machine
 217 * when it sees this value.
 218 */
 219#define CLIP(__X)       (((__X) > 127) ? 127 : (((__X) < -127) ? -127 : (__X)))
 220
 221/* The following is called from the serial driver when bytes/breaks
 222 * are received on the Mouse line.
 223 */
 224void
 225sun_mouse_inbyte(unsigned char byte, int is_break)
 226{
 227        signed char mvalue;
 228        int d, pushed = 0;
 229        Firm_event ev;
 230
 231        add_mouse_randomness (byte);
 232#if 0
 233        {
 234                static int xxx = 0;
 235                printk("mouse(%02x:%d) ",
 236                       byte, is_break);
 237                if (byte == 0x87) {
 238                        xxx = 0;
 239                        printk("\n");
 240                }
 241        }
 242#endif
 243        if (mouse_baud_detection(byte, is_break))
 244                return;
 245
 246        if(!sunmouse.active)
 247                return;
 248
 249        /* Ignore this if it is garbage. */
 250        if (sunmouse.byte == 69) {
 251                if (byte != 0x87)
 252                        return;
 253
 254                /* Ok, we've begun the state machine. */
 255                sunmouse.byte = 0;
 256        }
 257#if 0
 258        /* If the mouse sends us a byte from 0x80 to 0x87
 259         * we are starting at byte zero in the transaction
 260         * protocol.
 261         */
 262        if((byte & ~0x0f) == 0x80) 
 263                sunmouse.byte = 0;
 264#endif
 265
 266        mvalue = (signed char) byte;
 267        switch(sunmouse.byte) {
 268        case 0:
 269                /* If we get a bogus button byte, just skip it.
 270                 * When we get here the baud detection code has
 271                 * passed, so the only other things which can
 272                 * cause this are dropped serial characters and
 273                 * confused mouse.  We check this because otherwise
 274                 * begin posting erroneous mouse events.
 275                 */
 276                if ((byte & 0xf0) != 0x80)
 277                        return;
 278
 279                /* Button state */
 280                sunmouse.button_state = (~byte) & 0x7;
 281#ifdef SMOUSE_DEBUG
 282                printk("B<Left %s, Middle %s, Right %s>",
 283                       ((sunmouse.button_state & 0x4) ? "DOWN" : "UP"),
 284                       ((sunmouse.button_state & 0x2) ? "DOWN" : "UP"),
 285                       ((sunmouse.button_state & 0x1) ? "DOWN" : "UP"));
 286#endif
 287                /* To deal with the Sparcbook 3 */
 288                if (byte & 0x8) {
 289                        sunmouse.byte += 2;
 290                        sunmouse.delta_y = 0;
 291                        sunmouse.delta_x = 0;
 292                }
 293                sunmouse.byte++;
 294                return;
 295        case 1:
 296                /* Delta-x 1 */
 297#ifdef SMOUSE_DEBUG
 298                printk("DX1<%d>", mvalue);
 299#endif
 300                sunmouse.delta_x = mvalue;
 301                sunmouse.byte++;
 302                return;
 303        case 2:
 304                /* Delta-y 1 */
 305#ifdef SMOUSE_DEBUG
 306                printk("DY1<%d>", mvalue);
 307#endif
 308                sunmouse.delta_y = mvalue;
 309                sunmouse.byte++;
 310                return;
 311        case 3:
 312                /* Delta-x 2 */
 313#ifdef SMOUSE_DEBUG
 314                printk("DX2<%d>", mvalue);
 315#endif
 316                sunmouse.delta_x += mvalue;
 317                sunmouse.delta_x = CLIP(sunmouse.delta_x);
 318                sunmouse.byte++;
 319                return;
 320        case 4:
 321                /* Last byte, Delta-y 2 */
 322#ifdef SMOUSE_DEBUG
 323                printk("DY2<%d>", mvalue);
 324#endif
 325                sunmouse.delta_y += mvalue;
 326                sunmouse.delta_y = CLIP(sunmouse.delta_y);
 327                sunmouse.byte = 0;  /* Back to button state */
 328                break;
 329        case 69:
 330                /* Until we get the (0x80 -> 0x87) value we aren't
 331                 * in the middle of a real transaction, so just
 332                 * return.
 333                 */
 334                return;
 335        default:
 336                printk("sunmouse: bogon transaction state\n");
 337                sunmouse.byte = 69;  /* What could cause this? */
 338                return;
 339        };
 340
 341        if (!gen_events) {
 342                push_char (~sunmouse.button_state & 0x87);
 343                push_char (sunmouse.delta_x);
 344                push_char (sunmouse.delta_y);
 345                return;
 346        }
 347
 348        d = bstate ^ pstate;
 349        pstate = bstate;
 350        if (d) {
 351                if (d & BUTTON_LEFT) {
 352                        ev.id = MS_LEFT;
 353                        ev.value = bstate & BUTTON_LEFT;
 354                }
 355                if (d & BUTTON_RIGHT) {
 356                        ev.id = MS_RIGHT;
 357                        ev.value = bstate & BUTTON_RIGHT;
 358                }
 359                if (d & BUTTON_MIDDLE) {
 360                        ev.id = MS_MIDDLE;
 361                        ev.value = bstate & BUTTON_MIDDLE;
 362                }
 363                ev.time = xtime;
 364                ev.value = ev.value ? VKEY_DOWN : VKEY_UP;
 365                pushed += push_event (&ev);
 366        }
 367        if (sunmouse.delta_x) {
 368                ev.id = LOC_X_DELTA;
 369                ev.time = xtime;
 370                ev.value = sunmouse.delta_x;
 371                pushed += push_event (&ev);
 372                sunmouse.delta_x = 0;
 373        }
 374        if (sunmouse.delta_y) {
 375                ev.id = LOC_Y_DELTA;
 376                ev.time = xtime;
 377                ev.value = sunmouse.delta_y;
 378                pushed += push_event (&ev);
 379        }
 380        
 381        if (pushed != 0) {
 382                /* We just completed a transaction, wake up whoever is awaiting
 383                 * this event.
 384                 */
 385                kill_fasync (&sunmouse.fasync, SIGIO, POLL_IN);
 386                wake_up_interruptible(&sunmouse.proc_list);
 387        }
 388        return;
 389}
 390
 391static int
 392sun_mouse_open(struct inode * inode, struct file * file)
 393{
 394        spin_lock_irq(&sunmouse.lock);
 395        if (sunmouse.active++)
 396                goto out;
 397        sunmouse.delta_x = sunmouse.delta_y = 0;
 398        sunmouse.button_state = 0x80;
 399        sunmouse.vuid_mode = VUID_NATIVE;
 400out:
 401        spin_unlock_irq(&sunmouse.lock);
 402        return 0;
 403}
 404
 405static int sun_mouse_fasync (int fd, struct file *filp, int on)
 406{
 407        int retval;
 408
 409        retval = fasync_helper (fd, filp, on, &sunmouse.fasync);
 410        if (retval < 0)
 411                return retval;
 412        return 0;
 413}
 414
 415static int
 416sun_mouse_close(struct inode *inode, struct file *file)
 417{
 418        sun_mouse_fasync (-1, file, 0);
 419
 420        spin_lock_irq(&sunmouse.lock);
 421        sunmouse.active--;
 422        spin_unlock_irq(&sunmouse.lock);
 423
 424        return 0;
 425}
 426
 427static ssize_t
 428sun_mouse_write(struct file *file, const char *buffer,
 429                size_t count, loff_t *ppos)
 430{
 431        return -EINVAL;  /* foo on you */
 432}
 433
 434static ssize_t
 435sun_mouse_read(struct file *file, char *buffer,
 436               size_t count, loff_t *ppos)
 437{
 438        DECLARE_WAITQUEUE(wait, current);
 439        unsigned long flags;
 440
 441        if (queue_empty ()) {
 442                if (file->f_flags & O_NONBLOCK)
 443                        return -EWOULDBLOCK;
 444                add_wait_queue (&sunmouse.proc_list, &wait);
 445repeat:
 446                set_current_state(TASK_INTERRUPTIBLE);
 447                if (queue_empty() && !signal_pending(current)) {
 448                        schedule();
 449                        goto repeat;
 450                }
 451                current->state = TASK_RUNNING;
 452                remove_wait_queue (&sunmouse.proc_list, &wait);
 453        }
 454        if (gen_events) {
 455                char *p = buffer, *end = buffer+count;
 456                
 457                spin_lock_irqsave(&sunmouse.lock, flags);
 458                while (p < end && !queue_empty ()){
 459                        Firm_event this_event;
 460
 461                        get_from_queue(&this_event);
 462                        spin_unlock_irqrestore(&sunmouse.lock, flags);
 463
 464#ifdef CONFIG_SPARC32_COMPAT
 465                        if (current->thread.flags & SPARC_FLAG_32BIT) {
 466                                if ((end - p) <
 467                                    ((sizeof(Firm_event) - sizeof(struct timeval) +
 468                                      (sizeof(u32) * 2))))
 469                                        break;
 470                                if (copy_to_user((Firm_event *)p, &this_event,
 471                                                 sizeof(Firm_event)-sizeof(struct timeval)))
 472                                        return -EFAULT;
 473                                p += sizeof(Firm_event)-sizeof(struct timeval);
 474                                if (__put_user(this_event.time.tv_sec, (u32 *)p))
 475                                        return -EFAULT;
 476                                p += sizeof(u32);
 477                                if (__put_user(this_event.time.tv_usec, (u32 *)p))
 478                                        return -EFAULT;
 479                                p += sizeof(u32);
 480                        } else
 481#endif  
 482                        {       
 483                                if ((end - p) < sizeof(Firm_event))
 484                                        break;
 485                                if (copy_to_user((Firm_event *)p, &this_event,
 486                                                 sizeof(Firm_event)))
 487                                        return -EFAULT;
 488                                p += sizeof (Firm_event);
 489                        }
 490                        spin_lock_irqsave(&sunmouse.lock, flags);
 491                }
 492                spin_unlock_irqrestore(&sunmouse.lock, flags);
 493                file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 494                return p-buffer;
 495        } else {
 496                int c, limit = 3;
 497
 498                if (count < limit)
 499                        limit = count;
 500                for (c = 0; c < limit; c++) {
 501                        unsigned char val;
 502                        int empty = 0;
 503
 504                        spin_lock_irqsave(&sunmouse.lock, flags);
 505                        if (queue_empty()) {
 506                                empty = 1;
 507                                val = 0;
 508                        } else {
 509                                val = sunmouse.queue.stream[sunmouse.tail];
 510                                sunmouse.tail = (sunmouse.tail + 1) % STREAM_SIZE;
 511                        }
 512                        spin_unlock_irqrestore(&sunmouse.lock, flags);
 513
 514                        if (empty)
 515                                break;
 516
 517                        put_user(val, buffer);
 518                        buffer++;
 519                }
 520                while (c < count) {
 521                        if (c >= 5)
 522                                break;
 523                        put_user(0, buffer);
 524                        buffer++;
 525                        c++;
 526                }
 527                file->f_dentry->d_inode->i_atime = CURRENT_TIME;
 528                return c;
 529        }
 530        /* Only called if nothing was sent */
 531        if (signal_pending(current))
 532                return -ERESTARTSYS;
 533        return 0;
 534}
 535
 536static unsigned int sun_mouse_poll(struct file *file, poll_table *wait)
 537{
 538        poll_wait(file, &sunmouse.proc_list, wait);
 539        if(!queue_empty())
 540                return POLLIN | POLLRDNORM;
 541        return 0;
 542}
 543int
 544sun_mouse_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 545{
 546        int i;
 547        
 548        switch (cmd){
 549                /* VUIDGFORMAT - Get input device byte stream format */
 550        case _IOR('v', 2, int):
 551                if (put_user(sunmouse.vuid_mode, (int *) arg))
 552                        return -EFAULT;
 553                break;
 554
 555                /* VUIDSFORMAT - Set input device byte stream format*/
 556        case _IOW('v', 1, int):
 557                if (get_user(i, (int *) arg))
 558                        return -EFAULT;
 559                if (i == VUID_NATIVE || i == VUID_FIRM_EVENT){
 560                        int value;
 561
 562                        if (get_user(value, (int *)arg))
 563                                return -EFAULT;
 564
 565                        spin_lock_irq(&sunmouse.lock);
 566                        sunmouse.vuid_mode = value;
 567                        sunmouse.head = sunmouse.tail = 0;
 568                        spin_unlock_irq(&sunmouse.lock);
 569                } else
 570                        return -EINVAL;
 571                break;
 572
 573        case 0x8024540b:
 574        case 0x40245408:
 575                /* This is a buggy application doing termios on the mouse driver */
 576                /* we ignore it.  I keep this check here so that we will notice   */
 577                /* future mouse vuid ioctls */
 578                return -ENOTTY;
 579                
 580        default:
 581#ifdef DEBUG
 582                printk ("[MOUSE-ioctl: %8.8x]\n", cmd);
 583#endif
 584                return -EINVAL;
 585        }
 586        return 0;
 587}
 588
 589struct file_operations sun_mouse_fops = {
 590        read:           sun_mouse_read,
 591        write:          sun_mouse_write,
 592        poll:           sun_mouse_poll,
 593        ioctl:          sun_mouse_ioctl,
 594        open:           sun_mouse_open,
 595        release:        sun_mouse_close,
 596        fasync:         sun_mouse_fasync,
 597};
 598
 599static struct miscdevice sun_mouse_mouse = {
 600        SUN_MOUSE_MINOR, "sunmouse", &sun_mouse_fops
 601};
 602
 603void sun_mouse_zsinit(void)
 604{
 605        printk("Sun Mouse-Systems mouse driver version 1.00\n");
 606
 607        sunmouse.active = 0;
 608        misc_register (&sun_mouse_mouse);
 609        sunmouse.delta_x = sunmouse.delta_y = 0;
 610        sunmouse.button_state = 0x80;
 611        init_waitqueue_head(&sunmouse.proc_list);
 612        spin_lock_init(&sunmouse.lock);
 613        sunmouse.byte = 69;
 614}
 615
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.