linux-old/drivers/char/pc110pad.c
<<
>>
Prefs
   1/*
   2 *      Linux driver for the PC110 pad
   3 */
   4 
   5/**
   6 *      DOC: PC110 Digitizer Hardware
   7 *
   8 *      The pad provides triples of data. The first byte has
   9 *      0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down
  10 *      The second byte is bits 0-6 X
  11 *      The third is bits 0-6 Y
  12 *
  13 *      This is read internally and used to synthesize a stream of
  14 *      triples in the form expected from a PS/2 device. Specialist
  15 *      applications can choose to obtain the pad data in other formats
  16 *      including a debugging mode.
  17 *
  18 *      It would be good to add a joystick driver mode to this pad so
  19 *      that doom and other game playing are better. One possible approach
  20 *      would be to deactive the mouse mode while the joystick port is opened.
  21 */
  22 
  23/*
  24 *      History
  25 *
  26 *      0.0 1997-05-16 Alan Cox <alan@redhat.com> - Pad reader
  27 *      0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
  28 *      0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
  29 *      0.3 1997-06-27 Alan Cox <alan@redhat.com> - 2.1 commit
  30 *      0.4 1997-11-09 Alan Cox <alan@redhat.com> - Single Unix VFS API changes
  31 *      0.5 2000-02-10 Alan Cox <alan@redhat.com> - 2.3.x cleanup, documentation
  32 */
  33
  34#include <linux/module.h>
  35#include <linux/kernel.h>
  36#include <linux/signal.h>
  37#include <linux/errno.h>
  38#include <linux/mm.h>
  39#include <linux/miscdevice.h>
  40#include <linux/ptrace.h>
  41#include <linux/poll.h>
  42#include <linux/ioport.h>
  43#include <linux/interrupt.h>
  44#include <linux/smp_lock.h>
  45#include <linux/init.h>
  46
  47#include <asm/signal.h>
  48#include <asm/io.h>
  49#include <asm/irq.h>
  50#include <asm/semaphore.h>
  51#include <linux/spinlock.h>
  52#include <asm/uaccess.h>
  53
  54#include "pc110pad.h"
  55
  56
  57static struct pc110pad_params default_params = {
  58        mode:                   PC110PAD_PS2,
  59        bounce_interval:        50 MS,
  60        tap_interval:           200 MS,
  61        irq:                    10,
  62        io:                     0x15E0,
  63};
  64
  65static struct pc110pad_params current_params;
  66
  67
  68/* driver/filesystem interface management */
  69static wait_queue_head_t queue;
  70static struct fasync_struct *asyncptr;
  71static int active;      /* number of concurrent open()s */
  72static struct semaphore reader_lock;
  73
  74/**
  75 *      wake_readers:
  76 *
  77 *      Take care of letting any waiting processes know that
  78 *      now would be a good time to do a read().  Called
  79 *      whenever a state transition occurs, real or synthetic. Also
  80 *      issue any SIGIO's to programs that use SIGIO on mice (eg
  81 *      Executor)
  82 */
  83 
  84static void wake_readers(void)
  85{
  86        wake_up_interruptible(&queue);
  87        kill_fasync(&asyncptr, SIGIO, POLL_IN);
  88}
  89
  90
  91/*****************************************************************************/
  92/*
  93 * Deal with the messy business of synthesizing button tap and drag
  94 * events.
  95 *
  96 * Exports:
  97 *      notify_pad_up_down()
  98 *              Must be called whenever debounced pad up/down state changes.
  99 *      button_pending
 100 *              Flag is set whenever read_button() has new values
 101 *              to return.
 102 *      read_button()
 103 *              Obtains the current synthetic mouse button state.
 104 */
 105
 106/*
 107 * These keep track of up/down transitions needed to generate the
 108 * synthetic mouse button events.  While recent_transition is set,
 109 * up/down events cause transition_count to increment.  tap_timer
 110 * turns off the recent_transition flag and may cause some synthetic
 111 * up/down mouse events to be created by incrementing synthesize_tap.
 112 */
 113 
 114static int button_pending;
 115static int recent_transition;
 116static int transition_count;
 117static int synthesize_tap;
 118static void tap_timeout(unsigned long data);
 119static struct timer_list tap_timer = { function: tap_timeout };
 120
 121
 122/**
 123 * tap_timeout:
 124 * @data: Unused
 125 *
 126 * This callback goes off a short time after an up/down transition;
 127 * before it goes off, transitions will be considered part of a
 128 * single PS/2 event and counted in transition_count.  Once the
 129 * timeout occurs the recent_transition flag is cleared and
 130 * any synthetic mouse up/down events are generated.
 131 */
 132 
 133static void tap_timeout(unsigned long data)
 134{
 135        if(!recent_transition)
 136        {
 137                printk(KERN_ERR "pc110pad: tap_timeout but no recent transition!\n");
 138        }
 139        if( transition_count==2 || transition_count==4 || transition_count==6 )
 140        {
 141                synthesize_tap+=transition_count;
 142                button_pending = 1;
 143                wake_readers();
 144        }
 145        recent_transition=0;
 146}
 147
 148
 149/**
 150 * notify_pad_up_down:
 151 *
 152 * Called by the raw pad read routines when a (debounced) up/down
 153 * transition is detected.
 154 */
 155 
 156void notify_pad_up_down(void)
 157{
 158        if(recent_transition)
 159        {
 160                transition_count++;
 161        }
 162        else
 163        {
 164                transition_count=1;
 165                recent_transition=1;
 166        }
 167        mod_timer(&tap_timer, jiffies + current_params.tap_interval);
 168
 169        /* changes to transition_count can cause reported button to change */
 170        button_pending = 1;
 171        wake_readers();
 172}
 173
 174/**
 175 *      read_button:
 176 *      @b: pointer to the button status.
 177 *
 178 *      The actual button state depends on what we are seeing. We have to check
 179 *      for the tap gesture and also for dragging.
 180 */
 181
 182static void read_button(int *b)
 183{
 184        if(synthesize_tap)
 185        {
 186                *b=--synthesize_tap & 1;
 187        }
 188        else
 189        {
 190                *b=(!recent_transition && transition_count==3); /* drag */
 191        }
 192        button_pending=(synthesize_tap>0);
 193}
 194
 195
 196/*****************************************************************************/
 197/*
 198 * Read pad absolute co-ordinates and debounced up/down state.
 199 *
 200 * Exports:
 201 *      pad_irq()
 202 *              Function to be called whenever the pad signals
 203 *              that it has new data available.
 204 *      read_raw_pad()
 205 *              Returns the most current pad state.
 206 *      xy_pending
 207 *              Flag is set whenever read_raw_pad() has new values
 208 *              to return.
 209 * Imports:
 210 *      wake_readers()
 211 *              Called when movement occurs.
 212 *      notify_pad_up_down()
 213 *              Called when debounced up/down status changes.
 214 */
 215
 216/*
 217 * These are up/down state and absolute co-ords read directly from pad 
 218 */
 219
 220static int raw_data[3];
 221static int raw_data_count;
 222static int raw_x, raw_y;        /* most recent absolute co-ords read */
 223static int raw_down;            /* raw up/down state */
 224static int debounced_down;      /* up/down state after debounce processing */
 225static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE;
 226                                /* set just after an up/down transition */
 227static int xy_pending;  /* set if new data have not yet been read */
 228
 229/* 
 230 * Timer goes off a short while after an up/down transition and copies
 231 * the value of raw_down to debounced_down.
 232 */
 233 
 234static void bounce_timeout(unsigned long data);
 235static struct timer_list bounce_timer = { function: bounce_timeout };
 236
 237
 238
 239/**
 240 * bounce_timeout:
 241 * @data: Unused
 242 *
 243 * No further up/down transitions happened within the
 244 * bounce period, so treat this as a genuine transition.
 245 */
 246
 247static void bounce_timeout(unsigned long data)
 248{
 249        switch(bounce)
 250        {
 251                case NO_BOUNCE:
 252                {
 253                        /*
 254                         * Strange; the timer callback should only go off if
 255                         * we were expecting to do bounce processing!
 256                         */
 257                        printk(KERN_WARNING "pc110pad, bounce_timeout: bounce flag not set!\n");
 258                        break;
 259                }
 260                case JUST_GONE_UP:
 261                {
 262                        /*
 263                         * The last up we spotted really was an up, so set
 264                         * debounced state the same as raw state.
 265                         */
 266                        bounce=NO_BOUNCE;
 267                        if(debounced_down==raw_down)
 268                        {
 269                                printk(KERN_WARNING "pc110pad, bounce_timeout: raw already debounced!\n");
 270                        }
 271                        debounced_down=raw_down;
 272
 273                        notify_pad_up_down();
 274                        break;
 275                }
 276                case JUST_GONE_DOWN:
 277                {
 278                        /*
 279                         * We don't debounce down events, but we still time
 280                         * out soon after one occurs so we can avoid the (x,y)
 281                         * skittering that sometimes happens.
 282                         */
 283                        bounce=NO_BOUNCE;
 284                        break;
 285                }
 286        }
 287}
 288
 289
 290/**
 291 * pad_irq:
 292 * @irq: Interrupt number
 293 * @ptr: Unused
 294 * @regs: Unused
 295 *
 296 * Callback when pad's irq goes off; copies values in to raw_* globals;
 297 * initiates debounce processing. This isn't SMP safe however there are
 298 * no SMP machines with a PC110 touchpad on them.
 299 */
 300 
 301static void pad_irq(int irq, void *ptr, struct pt_regs *regs)
 302{
 303
 304        /* Obtain byte from pad and prime for next byte */
 305        {
 306                int value=inb_p(current_params.io);
 307                int handshake=inb_p(current_params.io+2);
 308                outb_p(handshake | 1, current_params.io+2);
 309                outb_p(handshake &~1, current_params.io+2);
 310                inb_p(0x64);
 311
 312                raw_data[raw_data_count++]=value;
 313        }
 314
 315        if(raw_data_count==3)
 316        {
 317                int new_down=raw_data[0]&0x01;
 318                int new_x=raw_data[1];
 319                int new_y=raw_data[2];
 320                if(raw_data[0]&0x10) new_x+=128;
 321                if(raw_data[0]&0x80) new_x+=256;
 322                if(raw_data[0]&0x08) new_y+=128;
 323
 324                if( (raw_x!=new_x) || (raw_y!=new_y) )
 325                {
 326                        raw_x=new_x;
 327                        raw_y=new_y;
 328                        xy_pending=1;
 329                }
 330
 331                if(new_down != raw_down)
 332                {
 333                        /* Down state has changed.  raw_down always holds
 334                         * the most recently observed state.
 335                         */
 336                        raw_down=new_down;
 337
 338                        /* Forget any earlier bounce processing */
 339                        if(bounce)
 340                        {
 341                                del_timer(&bounce_timer);
 342                                bounce=NO_BOUNCE;
 343                        }
 344
 345                        if(new_down)
 346                        {
 347                                if(debounced_down)
 348                                {
 349                                        /* pad gone down, but we were reporting
 350                                         * it down anyway because we suspected
 351                                         * (correctly) that the last up was just
 352                                         * a bounce
 353                                         */
 354                                }
 355                                else
 356                                {
 357                                        bounce=JUST_GONE_DOWN;
 358                                        mod_timer(&bounce_timer,
 359                                                jiffies+current_params.bounce_interval);
 360                                        /* start new stroke/tap */
 361                                        debounced_down=new_down;
 362                                        notify_pad_up_down();
 363                                }
 364                        }
 365                        else /* just gone up */
 366                        {
 367                                if(recent_transition)
 368                                {
 369                                        /* early bounces are probably part of
 370                                         * a multi-tap gesture, so process
 371                                         * immediately
 372                                         */
 373                                        debounced_down=new_down;
 374                                        notify_pad_up_down();
 375                                }
 376                                else
 377                                {
 378                                        /* don't trust it yet */
 379                                        bounce=JUST_GONE_UP;
 380                                        mod_timer(&bounce_timer,
 381                                                jiffies+current_params.bounce_interval);
 382                                }
 383                        }
 384                }
 385                wake_readers();
 386                raw_data_count=0;
 387        }
 388}
 389
 390/**
 391 *      read_raw_pad:
 392 *      @down: set if the pen is down
 393 *      @debounced: set if the debounced pen position is down
 394 *      @x: X position
 395 *      @y: Y position
 396 *
 397 *      Retrieve the data saved by the interrupt handler and indicate we
 398 *      have no more pending XY to do. 
 399 *
 400 *      FIXME: We should switch to a spinlock for this.
 401 */
 402
 403static void read_raw_pad(int *down, int *debounced, int *x, int *y)
 404{
 405        disable_irq(current_params.irq);
 406        {
 407                *down=raw_down;
 408                *debounced=debounced_down;
 409                *x=raw_x;
 410                *y=raw_y;
 411                xy_pending = 0;
 412        }
 413        enable_irq(current_params.irq);
 414}
 415
 416/*****************************************************************************/
 417/*
 418 * Filesystem interface
 419 */
 420
 421/* 
 422 * Read returns byte triples, so we need to keep track of
 423 * how much of a triple has been read.  This is shared across
 424 * all processes which have this device open---not that anything
 425 * will make much sense in that case.
 426 */
 427static int read_bytes[3];
 428static int read_byte_count;
 429
 430/**
 431 *      sample_raw:
 432 *      @d: sample buffer
 433 *
 434 *      Retrieve a triple of sample data. 
 435 */
 436
 437
 438static void sample_raw(int d[3])
 439{
 440        d[0]=raw_data[0];
 441        d[1]=raw_data[1];
 442        d[2]=raw_data[2];
 443}
 444
 445/**
 446 *      sample_rare:
 447 *      @d: sample buffer
 448 *
 449 *      Retrieve a triple of sample data and sanitize it. We do the needed
 450 *      scaling and masking to get the current status.
 451 */
 452
 453
 454static void sample_rare(int d[3])
 455{
 456        int thisd, thisdd, thisx, thisy;
 457
 458        read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
 459
 460        d[0]=(thisd?0x80:0)
 461           | (thisx/256)<<4
 462           | (thisdd?0x08:0)
 463           | (thisy/256)
 464        ;
 465        d[1]=thisx%256;
 466        d[2]=thisy%256;
 467}
 468
 469/**
 470 *      sample_debug:
 471 *      @d: sample buffer
 472 *
 473 *      Retrieve a triple of sample data and mix it up with the state 
 474 *      information in the gesture parser. Not useful for normal users but
 475 *      handy when debugging
 476 */
 477
 478static void sample_debug(int d[3])
 479{
 480        int thisd, thisdd, thisx, thisy;
 481        int b;
 482        unsigned long flags;
 483        
 484        save_flags(flags);
 485        cli();
 486        read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
 487        d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce;
 488        d[1]=(recent_transition?0x80:0)+transition_count;
 489        read_button(&b);
 490        d[2]=(synthesize_tap<<4) | (b?0x01:0);
 491        restore_flags(flags);
 492}
 493
 494/**
 495 *      sample_ps2:
 496 *      @d: sample buffer
 497 *
 498 *      Retrieve a triple of sample data and turn the debounced tap and
 499 *      stroke information into what appears to be a PS/2 mouse. This means
 500 *      the PC110 pad needs no funny application side support.
 501 */
 502
 503
 504static void sample_ps2(int d[3])
 505{
 506        static int lastx, lasty, lastd;
 507
 508        int thisd, thisdd, thisx, thisy;
 509        int dx, dy, b;
 510
 511        /*
 512         * Obtain the current mouse parameters and limit as appropriate for
 513         * the return data format.  Interrupts are only disabled while 
 514         * obtaining the parameters, NOT during the puts_fs_byte() calls,
 515         * so paging in put_user() does not affect mouse tracking.
 516         */
 517        read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
 518        read_button(&b);
 519
 520        /* Now compare with previous readings.  Note that we use the
 521         * raw down flag rather than the debounced one.
 522         */
 523        if( (thisd && !lastd) /* new stroke */
 524         || (bounce!=NO_BOUNCE) )
 525        {
 526                dx=0;
 527                dy=0;
 528        }
 529        else
 530        {
 531                dx =  (thisx-lastx);
 532                dy = -(thisy-lasty);
 533        }
 534        lastx=thisx;
 535        lasty=thisy;
 536        lastd=thisd;
 537
 538/*
 539        d[0]= ((dy<0)?0x20:0)
 540            | ((dx<0)?0x10:0)
 541            | 0x08
 542            | (b? 0x01:0x00)
 543        ;
 544*/
 545        d[0]= ((dy<0)?0x20:0)
 546            | ((dx<0)?0x10:0)
 547            | (b? 0x00:0x08)
 548        ;
 549        d[1]=dx;
 550        d[2]=dy;
 551}
 552
 553
 554/**
 555 *      fasync_pad:
 556 *      @fd:    file number for the file 
 557 *      @filp:  file handle
 558 *      @on:    1 to add, 0 to remove a notifier
 559 *
 560 *      Update the queue of asynchronous event notifiers. We can use the
 561 *      same helper the mice do and that does almost everything we need.
 562 */
 563 
 564static int fasync_pad(int fd, struct file *filp, int on)
 565{
 566        int retval;
 567
 568        retval = fasync_helper(fd, filp, on, &asyncptr);
 569        if (retval < 0)
 570                return retval;
 571        return 0;
 572}
 573
 574
 575/**
 576 *      close_pad:
 577 *      @inode: inode of pad
 578 *      @file: file handle to pad
 579 *
 580 *      Close access to the pad. We turn the pad power off if this is the
 581 *      last user of the pad. I've not actually measured the power draw but
 582 *      the DOS driver is careful to do this so we follow suit.
 583 */
 584 
 585static int close_pad(struct inode * inode, struct file * file)
 586{
 587        lock_kernel();
 588        fasync_pad(-1, file, 0);
 589        if (!--active)
 590                outb(0x30, current_params.io+2);  /* switch off digitiser */
 591        unlock_kernel();
 592        return 0;
 593}
 594
 595
 596/**
 597 *      open_pad:
 598 *      @inode: inode of pad
 599 *      @file: file handle to pad
 600 *
 601 *      Open access to the pad. We turn the pad off first (we turned it off
 602 *      on close but if this is the first open after a crash the state is
 603 *      indeterminate). The device has a small fifo so we empty that before
 604 *      we kick it back into action.
 605 */
 606 
 607static int open_pad(struct inode * inode, struct file * file)
 608{
 609        unsigned long flags;
 610        
 611        if (active++)
 612                return 0;
 613
 614        save_flags(flags);
 615        cli();
 616        outb(0x30, current_params.io+2);        /* switch off digitiser */
 617        pad_irq(0,0,0);         /* read to flush any pending bytes */
 618        pad_irq(0,0,0);         /* read to flush any pending bytes */
 619        pad_irq(0,0,0);         /* read to flush any pending bytes */
 620        outb(0x38, current_params.io+2);        /* switch on digitiser */
 621        current_params = default_params;
 622        raw_data_count=0;               /* re-sync input byte counter */
 623        read_byte_count=0;              /* re-sync output byte counter */
 624        button_pending=0;
 625        recent_transition=0;
 626        transition_count=0;
 627        synthesize_tap=0;
 628        del_timer(&bounce_timer);
 629        del_timer(&tap_timer);
 630        restore_flags(flags);
 631
 632        return 0;
 633}
 634
 635
 636/**
 637 *      write_pad:
 638 *      @file: File handle to the pad
 639 *      @buffer: Unused
 640 *      @count: Unused
 641 *      @ppos: Unused
 642 *
 643 *      Writes are disallowed. A true PS/2 mouse lets you write stuff. Everyone
 644 *      seems happy with this and not faking the write modes.
 645 */
 646
 647static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos)
 648{
 649        return -EINVAL;
 650}
 651
 652
 653/*
 654 *      new_sample:
 655 *      @d: sample buffer
 656 *
 657 *      Fetch a new sample according the current mouse mode the pad is 
 658 *      using.
 659 */
 660 
 661void new_sample(int d[3])
 662{
 663        switch(current_params.mode)
 664        {
 665                case PC110PAD_RAW:      sample_raw(d);          break;
 666                case PC110PAD_RARE:     sample_rare(d);         break;
 667                case PC110PAD_DEBUG:    sample_debug(d);        break;
 668                case PC110PAD_PS2:      sample_ps2(d);          break;
 669        }
 670}
 671
 672
 673/**
 674 * read_pad:
 675 * @file: File handle to pad
 676 * @buffer: Target for the mouse data
 677 * @count: Buffer length
 678 * @ppos: Offset (unused)
 679 *
 680 * Read data from the pad. We use the reader_lock to avoid mess when there are
 681 * two readers. This shouldnt be happening anyway but we play safe.
 682 */
 683 
 684static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos)
 685{
 686        int r;
 687
 688        down(&reader_lock);
 689        for(r=0; r<count; r++)
 690        {
 691                if(!read_byte_count)
 692                        new_sample(read_bytes);
 693                if(put_user(read_bytes[read_byte_count], buffer+r))
 694                {
 695                        r = -EFAULT;
 696                        break;
 697                }
 698                read_byte_count = (read_byte_count+1)%3;
 699        }
 700        up(&reader_lock);
 701        return r;
 702}
 703
 704
 705/**
 706 * pad_poll:
 707 * @file: File of the pad device
 708 * @wait: Poll table
 709 *
 710 * The pad is ready to read if there is a button or any position change
 711 * pending in the queue. The reading and interrupt routines maintain the
 712 * required state for us and do needed wakeups.
 713 */
 714
 715static unsigned int pad_poll(struct file *file, poll_table * wait)
 716{
 717        poll_wait(file, &queue, wait);
 718        if(button_pending || xy_pending)
 719                return POLLIN | POLLRDNORM;
 720        return 0;
 721}
 722
 723
 724/**
 725 *      pad_ioctl;
 726 *      @inode: Inode of the pad
 727 *      @file: File handle to the pad
 728 *      @cmd: Ioctl command
 729 *      @arg: Argument pointer
 730 *
 731 *      The PC110 pad supports two ioctls both of which use the pc110pad_params
 732 *      structure. GETP queries the current pad status. SETP changes the pad
 733 *      configuration. Changing configuration during normal mouse operations
 734 *      may give momentarily odd results as things like tap gesture state
 735 *      may be lost.
 736 */
 737 
 738static int pad_ioctl(struct inode *inode, struct file * file,
 739        unsigned int cmd, unsigned long arg)
 740{
 741        struct pc110pad_params new;
 742
 743        if (!inode)
 744                return -EINVAL;
 745                
 746        switch (cmd) {
 747        case PC110PADIOCGETP:
 748                new = current_params;
 749                if(copy_to_user((void *)arg, &new, sizeof(new)))
 750                        return -EFAULT;
 751                return 0;
 752
 753        case PC110PADIOCSETP:
 754                if(copy_from_user(&new, (void *)arg, sizeof(new)))
 755                        return -EFAULT;
 756
 757                if( (new.mode<PC110PAD_RAW)
 758                 || (new.mode>PC110PAD_PS2)
 759                 || (new.bounce_interval<0)
 760                 || (new.tap_interval<0)
 761                )
 762                        return -EINVAL;
 763
 764                current_params.mode     = new.mode;
 765                current_params.bounce_interval  = new.bounce_interval;
 766                current_params.tap_interval     = new.tap_interval;
 767                return 0;
 768        }
 769        return -ENOTTY;
 770}
 771
 772
 773static struct file_operations pad_fops = {
 774        owner:          THIS_MODULE,
 775        read:           read_pad,
 776        write:          write_pad,
 777        poll:           pad_poll,
 778        ioctl:          pad_ioctl,
 779        open:           open_pad,
 780        release:        close_pad,
 781        fasync:         fasync_pad,
 782};
 783
 784
 785static struct miscdevice pc110_pad = {
 786        minor:          PC110PAD_MINOR,
 787        name:           "pc110 pad",
 788        fops:           &pad_fops,
 789};
 790
 791
 792/**
 793 *      pc110pad_init_driver:
 794 *
 795 *      We configure the pad with the default parameters (that is PS/2 
 796 *      emulation mode. We then claim the needed I/O and interrupt resources.
 797 *      Finally as a matter of paranoia we turn the pad off until we are
 798 *      asked to open it by an application.
 799 */
 800
 801static char banner[] __initdata = KERN_INFO "PC110 digitizer pad at 0x%X, irq %d.\n";
 802
 803static int __init pc110pad_init_driver(void)
 804{
 805        init_MUTEX(&reader_lock);
 806        current_params = default_params;
 807
 808        if (request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) {
 809                printk(KERN_ERR "pc110pad: Unable to get IRQ.\n");
 810                return -EBUSY;
 811        }
 812        if (!request_region(current_params.io, 4, "pc110pad"))  {
 813                printk(KERN_ERR "pc110pad: I/O area in use.\n");
 814                free_irq(current_params.irq,0);
 815                return -EBUSY;
 816        }
 817        init_waitqueue_head(&queue);
 818        printk(banner, current_params.io, current_params.irq);
 819        misc_register(&pc110_pad);
 820        outb(0x30, current_params.io+2);        /* switch off digitiser */
 821        return 0;
 822}
 823
 824/*
 825 *      pc110pad_exit_driver:
 826 *
 827 *      Free the resources we acquired when the module was loaded. We also
 828 *      turn the pad off to be sure we don't leave it using power.
 829 */
 830
 831static void __exit pc110pad_exit_driver(void)
 832{
 833        outb(0x30, current_params.io+2);        /* switch off digitiser */
 834        if (current_params.irq)
 835                free_irq(current_params.irq, 0);
 836        current_params.irq = 0;
 837        release_region(current_params.io, 4);
 838        misc_deregister(&pc110_pad);
 839}
 840
 841module_init(pc110pad_init_driver);
 842module_exit(pc110pad_exit_driver);
 843
 844MODULE_AUTHOR("Alan Cox, Robin O'Leary");
 845MODULE_DESCRIPTION("Driver for the touchpad on the IBM PC110 palmtop");
 846MODULE_LICENSE("GPL");
 847
 848EXPORT_NO_SYMBOLS;
 849
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.