📄 pc110pad.c
字号:
/* * 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -