pc110pad.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:19k
- /*
- * Linux driver for the PC110 pad
- */
-
- /**
- * DOC: PC110 Digitizer Hardware
- *
- * The pad provides triples of data. The first byte has
- * 0x80=bit 8 X, 0x01=bit 7 X, 0x08=bit 8 Y, 0x01=still down
- * The second byte is bits 0-6 X
- * The third is bits 0-6 Y
- *
- * This is read internally and used to synthesize a stream of
- * triples in the form expected from a PS/2 device. Specialist
- * applications can choose to obtain the pad data in other formats
- * including a debugging mode.
- *
- * It would be good to add a joystick driver mode to this pad so
- * that doom and other game playing are better. One possible approach
- * would be to deactive the mouse mode while the joystick port is opened.
- */
-
- /*
- * History
- *
- * 0.0 1997-05-16 Alan Cox <alan@redhat.com> - Pad reader
- * 0.1 1997-05-19 Robin O'Leary <robin@acm.org> - PS/2 emulation
- * 0.2 1997-06-03 Robin O'Leary <robin@acm.org> - tap gesture
- * 0.3 1997-06-27 Alan Cox <alan@redhat.com> - 2.1 commit
- * 0.4 1997-11-09 Alan Cox <alan@redhat.com> - Single Unix VFS API changes
- * 0.5 2000-02-10 Alan Cox <alan@redhat.com> - 2.3.x cleanup, documentation
- */
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/signal.h>
- #include <linux/errno.h>
- #include <linux/mm.h>
- #include <linux/miscdevice.h>
- #include <linux/ptrace.h>
- #include <linux/poll.h>
- #include <linux/ioport.h>
- #include <linux/interrupt.h>
- #include <linux/smp_lock.h>
- #include <linux/init.h>
- #include <asm/signal.h>
- #include <asm/io.h>
- #include <asm/irq.h>
- #include <asm/semaphore.h>
- #include <linux/spinlock.h>
- #include <asm/uaccess.h>
- #include "pc110pad.h"
- static struct pc110pad_params default_params = {
- mode: PC110PAD_PS2,
- bounce_interval: 50 MS,
- tap_interval: 200 MS,
- irq: 10,
- io: 0x15E0,
- };
- static struct pc110pad_params current_params;
- /* driver/filesystem interface management */
- static wait_queue_head_t queue;
- static struct fasync_struct *asyncptr;
- static int active; /* number of concurrent open()s */
- static struct semaphore reader_lock;
- /**
- * wake_readers:
- *
- * Take care of letting any waiting processes know that
- * now would be a good time to do a read(). Called
- * whenever a state transition occurs, real or synthetic. Also
- * issue any SIGIO's to programs that use SIGIO on mice (eg
- * Executor)
- */
-
- static void wake_readers(void)
- {
- wake_up_interruptible(&queue);
- kill_fasync(&asyncptr, SIGIO, POLL_IN);
- }
- /*****************************************************************************/
- /*
- * Deal with the messy business of synthesizing button tap and drag
- * events.
- *
- * Exports:
- * notify_pad_up_down()
- * Must be called whenever debounced pad up/down state changes.
- * button_pending
- * Flag is set whenever read_button() has new values
- * to return.
- * read_button()
- * Obtains the current synthetic mouse button state.
- */
- /*
- * These keep track of up/down transitions needed to generate the
- * synthetic mouse button events. While recent_transition is set,
- * up/down events cause transition_count to increment. tap_timer
- * turns off the recent_transition flag and may cause some synthetic
- * up/down mouse events to be created by incrementing synthesize_tap.
- */
-
- static int button_pending;
- static int recent_transition;
- static int transition_count;
- static int synthesize_tap;
- static void tap_timeout(unsigned long data);
- static struct timer_list tap_timer = { function: tap_timeout };
- /**
- * tap_timeout:
- * @data: Unused
- *
- * This callback goes off a short time after an up/down transition;
- * before it goes off, transitions will be considered part of a
- * single PS/2 event and counted in transition_count. Once the
- * timeout occurs the recent_transition flag is cleared and
- * any synthetic mouse up/down events are generated.
- */
-
- static void tap_timeout(unsigned long data)
- {
- if(!recent_transition)
- {
- printk(KERN_ERR "pc110pad: tap_timeout but no recent transition!n");
- }
- if( transition_count==2 || transition_count==4 || transition_count==6 )
- {
- synthesize_tap+=transition_count;
- button_pending = 1;
- wake_readers();
- }
- recent_transition=0;
- }
- /**
- * notify_pad_up_down:
- *
- * Called by the raw pad read routines when a (debounced) up/down
- * transition is detected.
- */
-
- void notify_pad_up_down(void)
- {
- if(recent_transition)
- {
- transition_count++;
- }
- else
- {
- transition_count=1;
- recent_transition=1;
- }
- mod_timer(&tap_timer, jiffies + current_params.tap_interval);
- /* changes to transition_count can cause reported button to change */
- button_pending = 1;
- wake_readers();
- }
- /**
- * read_button:
- * @b: pointer to the button status.
- *
- * The actual button state depends on what we are seeing. We have to check
- * for the tap gesture and also for dragging.
- */
- static void read_button(int *b)
- {
- if(synthesize_tap)
- {
- *b=--synthesize_tap & 1;
- }
- else
- {
- *b=(!recent_transition && transition_count==3); /* drag */
- }
- button_pending=(synthesize_tap>0);
- }
- /*****************************************************************************/
- /*
- * Read pad absolute co-ordinates and debounced up/down state.
- *
- * Exports:
- * pad_irq()
- * Function to be called whenever the pad signals
- * that it has new data available.
- * read_raw_pad()
- * Returns the most current pad state.
- * xy_pending
- * Flag is set whenever read_raw_pad() has new values
- * to return.
- * Imports:
- * wake_readers()
- * Called when movement occurs.
- * notify_pad_up_down()
- * Called when debounced up/down status changes.
- */
- /*
- * These are up/down state and absolute co-ords read directly from pad
- */
- static int raw_data[3];
- static int raw_data_count;
- static int raw_x, raw_y; /* most recent absolute co-ords read */
- static int raw_down; /* raw up/down state */
- static int debounced_down; /* up/down state after debounce processing */
- static enum { NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN } bounce=NO_BOUNCE;
- /* set just after an up/down transition */
- static int xy_pending; /* set if new data have not yet been read */
- /*
- * Timer goes off a short while after an up/down transition and copies
- * the value of raw_down to debounced_down.
- */
-
- static void bounce_timeout(unsigned long data);
- static struct timer_list bounce_timer = { function: bounce_timeout };
- /**
- * bounce_timeout:
- * @data: Unused
- *
- * No further up/down transitions happened within the
- * bounce period, so treat this as a genuine transition.
- */
- static void bounce_timeout(unsigned long data)
- {
- switch(bounce)
- {
- case NO_BOUNCE:
- {
- /*
- * Strange; the timer callback should only go off if
- * we were expecting to do bounce processing!
- */
- printk(KERN_WARNING "pc110pad, bounce_timeout: bounce flag not set!n");
- break;
- }
- case JUST_GONE_UP:
- {
- /*
- * The last up we spotted really was an up, so set
- * debounced state the same as raw state.
- */
- bounce=NO_BOUNCE;
- if(debounced_down==raw_down)
- {
- printk(KERN_WARNING "pc110pad, bounce_timeout: raw already debounced!n");
- }
- debounced_down=raw_down;
- notify_pad_up_down();
- break;
- }
- case JUST_GONE_DOWN:
- {
- /*
- * We don't debounce down events, but we still time
- * out soon after one occurs so we can avoid the (x,y)
- * skittering that sometimes happens.
- */
- bounce=NO_BOUNCE;
- break;
- }
- }
- }
- /**
- * pad_irq:
- * @irq: Interrupt number
- * @ptr: Unused
- * @regs: Unused
- *
- * Callback when pad's irq goes off; copies values in to raw_* globals;
- * initiates debounce processing. This isn't SMP safe however there are
- * no SMP machines with a PC110 touchpad on them.
- */
-
- static void pad_irq(int irq, void *ptr, struct pt_regs *regs)
- {
- /* Obtain byte from pad and prime for next byte */
- {
- int value=inb_p(current_params.io);
- int handshake=inb_p(current_params.io+2);
- outb_p(handshake | 1, current_params.io+2);
- outb_p(handshake &~1, current_params.io+2);
- inb_p(0x64);
- raw_data[raw_data_count++]=value;
- }
- if(raw_data_count==3)
- {
- int new_down=raw_data[0]&0x01;
- int new_x=raw_data[1];
- int new_y=raw_data[2];
- if(raw_data[0]&0x10) new_x+=128;
- if(raw_data[0]&0x80) new_x+=256;
- if(raw_data[0]&0x08) new_y+=128;
- if( (raw_x!=new_x) || (raw_y!=new_y) )
- {
- raw_x=new_x;
- raw_y=new_y;
- xy_pending=1;
- }
- if(new_down != raw_down)
- {
- /* Down state has changed. raw_down always holds
- * the most recently observed state.
- */
- raw_down=new_down;
- /* Forget any earlier bounce processing */
- if(bounce)
- {
- del_timer(&bounce_timer);
- bounce=NO_BOUNCE;
- }
- if(new_down)
- {
- if(debounced_down)
- {
- /* pad gone down, but we were reporting
- * it down anyway because we suspected
- * (correctly) that the last up was just
- * a bounce
- */
- }
- else
- {
- bounce=JUST_GONE_DOWN;
- mod_timer(&bounce_timer,
- jiffies+current_params.bounce_interval);
- /* start new stroke/tap */
- debounced_down=new_down;
- notify_pad_up_down();
- }
- }
- else /* just gone up */
- {
- if(recent_transition)
- {
- /* early bounces are probably part of
- * a multi-tap gesture, so process
- * immediately
- */
- debounced_down=new_down;
- notify_pad_up_down();
- }
- else
- {
- /* don't trust it yet */
- bounce=JUST_GONE_UP;
- mod_timer(&bounce_timer,
- jiffies+current_params.bounce_interval);
- }
- }
- }
- wake_readers();
- raw_data_count=0;
- }
- }
- /**
- * read_raw_pad:
- * @down: set if the pen is down
- * @debounced: set if the debounced pen position is down
- * @x: X position
- * @y: Y position
- *
- * Retrieve the data saved by the interrupt handler and indicate we
- * have no more pending XY to do.
- *
- * FIXME: We should switch to a spinlock for this.
- */
- static void read_raw_pad(int *down, int *debounced, int *x, int *y)
- {
- disable_irq(current_params.irq);
- {
- *down=raw_down;
- *debounced=debounced_down;
- *x=raw_x;
- *y=raw_y;
- xy_pending = 0;
- }
- enable_irq(current_params.irq);
- }
- /*****************************************************************************/
- /*
- * Filesystem interface
- */
- /*
- * Read returns byte triples, so we need to keep track of
- * how much of a triple has been read. This is shared across
- * all processes which have this device open---not that anything
- * will make much sense in that case.
- */
- static int read_bytes[3];
- static int read_byte_count;
- /**
- * sample_raw:
- * @d: sample buffer
- *
- * Retrieve a triple of sample data.
- */
- static void sample_raw(int d[3])
- {
- d[0]=raw_data[0];
- d[1]=raw_data[1];
- d[2]=raw_data[2];
- }
- /**
- * sample_rare:
- * @d: sample buffer
- *
- * Retrieve a triple of sample data and sanitize it. We do the needed
- * scaling and masking to get the current status.
- */
- static void sample_rare(int d[3])
- {
- int thisd, thisdd, thisx, thisy;
- read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
- d[0]=(thisd?0x80:0)
- | (thisx/256)<<4
- | (thisdd?0x08:0)
- | (thisy/256)
- ;
- d[1]=thisx%256;
- d[2]=thisy%256;
- }
- /**
- * sample_debug:
- * @d: sample buffer
- *
- * Retrieve a triple of sample data and mix it up with the state
- * information in the gesture parser. Not useful for normal users but
- * handy when debugging
- */
- static void sample_debug(int d[3])
- {
- int thisd, thisdd, thisx, thisy;
- int b;
- unsigned long flags;
-
- save_flags(flags);
- cli();
- read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
- d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | bounce;
- d[1]=(recent_transition?0x80:0)+transition_count;
- read_button(&b);
- d[2]=(synthesize_tap<<4) | (b?0x01:0);
- restore_flags(flags);
- }
- /**
- * sample_ps2:
- * @d: sample buffer
- *
- * Retrieve a triple of sample data and turn the debounced tap and
- * stroke information into what appears to be a PS/2 mouse. This means
- * the PC110 pad needs no funny application side support.
- */
- static void sample_ps2(int d[3])
- {
- static int lastx, lasty, lastd;
- int thisd, thisdd, thisx, thisy;
- int dx, dy, b;
- /*
- * Obtain the current mouse parameters and limit as appropriate for
- * the return data format. Interrupts are only disabled while
- * obtaining the parameters, NOT during the puts_fs_byte() calls,
- * so paging in put_user() does not affect mouse tracking.
- */
- read_raw_pad(&thisd, &thisdd, &thisx, &thisy);
- read_button(&b);
- /* Now compare with previous readings. Note that we use the
- * raw down flag rather than the debounced one.
- */
- if( (thisd && !lastd) /* new stroke */
- || (bounce!=NO_BOUNCE) )
- {
- dx=0;
- dy=0;
- }
- else
- {
- dx = (thisx-lastx);
- dy = -(thisy-lasty);
- }
- lastx=thisx;
- lasty=thisy;
- lastd=thisd;
- /*
- d[0]= ((dy<0)?0x20:0)
- | ((dx<0)?0x10:0)
- | 0x08
- | (b? 0x01:0x00)
- ;
- */
- d[0]= ((dy<0)?0x20:0)
- | ((dx<0)?0x10:0)
- | (b? 0x00:0x08)
- ;
- d[1]=dx;
- d[2]=dy;
- }
- /**
- * fasync_pad:
- * @fd: file number for the file
- * @filp: file handle
- * @on: 1 to add, 0 to remove a notifier
- *
- * Update the queue of asynchronous event notifiers. We can use the
- * same helper the mice do and that does almost everything we need.
- */
-
- static int fasync_pad(int fd, struct file *filp, int on)
- {
- int retval;
- retval = fasync_helper(fd, filp, on, &asyncptr);
- if (retval < 0)
- return retval;
- return 0;
- }
- /**
- * close_pad:
- * @inode: inode of pad
- * @file: file handle to pad
- *
- * Close access to the pad. We turn the pad power off if this is the
- * last user of the pad. I've not actually measured the power draw but
- * the DOS driver is careful to do this so we follow suit.
- */
-
- static int close_pad(struct inode * inode, struct file * file)
- {
- lock_kernel();
- fasync_pad(-1, file, 0);
- if (!--active)
- outb(0x30, current_params.io+2); /* switch off digitiser */
- unlock_kernel();
- return 0;
- }
- /**
- * open_pad:
- * @inode: inode of pad
- * @file: file handle to pad
- *
- * Open access to the pad. We turn the pad off first (we turned it off
- * on close but if this is the first open after a crash the state is
- * indeterminate). The device has a small fifo so we empty that before
- * we kick it back into action.
- */
-
- static int open_pad(struct inode * inode, struct file * file)
- {
- unsigned long flags;
-
- if (active++)
- return 0;
- save_flags(flags);
- cli();
- outb(0x30, current_params.io+2); /* switch off digitiser */
- pad_irq(0,0,0); /* read to flush any pending bytes */
- pad_irq(0,0,0); /* read to flush any pending bytes */
- pad_irq(0,0,0); /* read to flush any pending bytes */
- outb(0x38, current_params.io+2); /* switch on digitiser */
- current_params = default_params;
- raw_data_count=0; /* re-sync input byte counter */
- read_byte_count=0; /* re-sync output byte counter */
- button_pending=0;
- recent_transition=0;
- transition_count=0;
- synthesize_tap=0;
- del_timer(&bounce_timer);
- del_timer(&tap_timer);
- restore_flags(flags);
- return 0;
- }
- /**
- * write_pad:
- * @file: File handle to the pad
- * @buffer: Unused
- * @count: Unused
- * @ppos: Unused
- *
- * Writes are disallowed. A true PS/2 mouse lets you write stuff. Everyone
- * seems happy with this and not faking the write modes.
- */
- static ssize_t write_pad(struct file * file, const char * buffer, size_t count, loff_t *ppos)
- {
- return -EINVAL;
- }
- /*
- * new_sample:
- * @d: sample buffer
- *
- * Fetch a new sample according the current mouse mode the pad is
- * using.
- */
-
- void new_sample(int d[3])
- {
- switch(current_params.mode)
- {
- case PC110PAD_RAW: sample_raw(d); break;
- case PC110PAD_RARE: sample_rare(d); break;
- case PC110PAD_DEBUG: sample_debug(d); break;
- case PC110PAD_PS2: sample_ps2(d); break;
- }
- }
- /**
- * read_pad:
- * @file: File handle to pad
- * @buffer: Target for the mouse data
- * @count: Buffer length
- * @ppos: Offset (unused)
- *
- * Read data from the pad. We use the reader_lock to avoid mess when there are
- * two readers. This shouldnt be happening anyway but we play safe.
- */
-
- static ssize_t read_pad(struct file * file, char * buffer, size_t count, loff_t *ppos)
- {
- int r;
- down(&reader_lock);
- for(r=0; r<count; r++)
- {
- if(!read_byte_count)
- new_sample(read_bytes);
- if(put_user(read_bytes[read_byte_count], buffer+r))
- {
- r = -EFAULT;
- break;
- }
- read_byte_count = (read_byte_count+1)%3;
- }
- up(&reader_lock);
- return r;
- }
- /**
- * pad_poll:
- * @file: File of the pad device
- * @wait: Poll table
- *
- * The pad is ready to read if there is a button or any position change
- * pending in the queue. The reading and interrupt routines maintain the
- * required state for us and do needed wakeups.
- */
- static unsigned int pad_poll(struct file *file, poll_table * wait)
- {
- poll_wait(file, &queue, wait);
- if(button_pending || xy_pending)
- return POLLIN | POLLRDNORM;
- return 0;
- }
- /**
- * pad_ioctl;
- * @inode: Inode of the pad
- * @file: File handle to the pad
- * @cmd: Ioctl command
- * @arg: Argument pointer
- *
- * The PC110 pad supports two ioctls both of which use the pc110pad_params
- * structure. GETP queries the current pad status. SETP changes the pad
- * configuration. Changing configuration during normal mouse operations
- * may give momentarily odd results as things like tap gesture state
- * may be lost.
- */
-
- static int pad_ioctl(struct inode *inode, struct file * file,
- unsigned int cmd, unsigned long arg)
- {
- struct pc110pad_params new;
- if (!inode)
- return -EINVAL;
-
- switch (cmd) {
- case PC110PADIOCGETP:
- new = current_params;
- if(copy_to_user((void *)arg, &new, sizeof(new)))
- return -EFAULT;
- return 0;
- case PC110PADIOCSETP:
- if(copy_from_user(&new, (void *)arg, sizeof(new)))
- return -EFAULT;
- if( (new.mode<PC110PAD_RAW)
- || (new.mode>PC110PAD_PS2)
- || (new.bounce_interval<0)
- || (new.tap_interval<0)
- )
- return -EINVAL;
- current_params.mode = new.mode;
- current_params.bounce_interval = new.bounce_interval;
- current_params.tap_interval = new.tap_interval;
- return 0;
- }
- return -ENOTTY;
- }
- static struct file_operations pad_fops = {
- owner: THIS_MODULE,
- read: read_pad,
- write: write_pad,
- poll: pad_poll,
- ioctl: pad_ioctl,
- open: open_pad,
- release: close_pad,
- fasync: fasync_pad,
- };
- static struct miscdevice pc110_pad = {
- minor: PC110PAD_MINOR,
- name: "pc110 pad",
- fops: &pad_fops,
- };
- /**
- * pc110pad_init_driver:
- *
- * We configure the pad with the default parameters (that is PS/2
- * emulation mode. We then claim the needed I/O and interrupt resources.
- * Finally as a matter of paranoia we turn the pad off until we are
- * asked to open it by an application.
- */
- static char banner[] __initdata = KERN_INFO "PC110 digitizer pad at 0x%X, irq %d.n";
- static int __init pc110pad_init_driver(void)
- {
- init_MUTEX(&reader_lock);
- current_params = default_params;
- if (request_irq(current_params.irq, pad_irq, 0, "pc110pad", 0)) {
- printk(KERN_ERR "pc110pad: Unable to get IRQ.n");
- return -EBUSY;
- }
- if (!request_region(current_params.io, 4, "pc110pad")) {
- printk(KERN_ERR "pc110pad: I/O area in use.n");
- free_irq(current_params.irq,0);
- return -EBUSY;
- }
- init_waitqueue_head(&queue);
- printk(banner, current_params.io, current_params.irq);
- misc_register(&pc110_pad);
- outb(0x30, current_params.io+2); /* switch off digitiser */
- return 0;
- }
- /*
- * pc110pad_exit_driver:
- *
- * Free the resources we acquired when the module was loaded. We also
- * turn the pad off to be sure we don't leave it using power.
- */
- static void __exit pc110pad_exit_driver(void)
- {
- outb(0x30, current_params.io+2); /* switch off digitiser */
- if (current_params.irq)
- free_irq(current_params.irq, 0);
- current_params.irq = 0;
- release_region(current_params.io, 4);
- misc_deregister(&pc110_pad);
- }
- module_init(pc110pad_init_driver);
- module_exit(pc110pad_exit_driver);
- MODULE_AUTHOR("Alan Cox, Robin O'Leary");
- MODULE_DESCRIPTION("Driver for the touchpad on the IBM PC110 palmtop");
- MODULE_LICENSE("GPL");
- EXPORT_NO_SYMBOLS;