OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [char/] [mc68328digi.c] - Rev 1777

Go to most recent revision | Compare with Previous | Blame | View Log

/* mc68328digi.c
 *
 * Linux driver to make the PalmPilot's touchpad behave
 * like a PS/2 mouse.
 *
 * My apologies, this is currently a complete mess. It'll notice taps
 * on Copilot and a real Pilot, but can't figure out the coordinates on the Pilot.
 *
 * Based on touchpad driver.
 * Copyright (C) 1998  Kenneth Albanowski <kjahds@kjahds.com>,
 *                     The Silver Hammer Group, Ltd.
 *
 * Original History
 * -------
 *	0.0 1997-05-16 Alan Cox <alan@cymru.net> - 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-07-10 Robin O'Leary <robin@acm.org> - sticky drag
 *
 * Distribution
 * ------------
 * Copyright (C) 1997-06-18 Robin O'Leary <robin@acm.org>  All rights reserved.
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * Get the latest version from ftp://swan.ml.org/pub/pc110/
 */
 
#include <linux/config.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/busmouse.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
 
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/irq.h>
 
#include "mc68328digi.h"
 
 
static struct mc68328digi_params default_params = {
	MC68328DIGI_PS2,	/* read mode */
	50 MS,		/* bounce interval */
	800 MS,		/* sticky-drag */
	300 MS,		/* tap interval */
	10,		/* IRQ */
	0x15E0,		/* I/O port */
	0, 		/* Not calibrated */
	1<<16,	0,	/* X calibration values (unity) */
	1<<16,	0	/* Y calibration values (unity) */
};
 
 
static struct mc68328digi_params current_params;
 
 
/* driver/filesystem interface management */
static struct wait_queue *queue;
static struct fasync_struct *asyncptr;
static int active=0;	/* number of concurrent open()s */
 
 
/*
 * Utility to reset a timer to go off some time in the future.
 */
static void set_timer_callback(struct timer_list *timer, int ticks)
{
	cli();
	del_timer(timer);
	timer->expires = jiffies+ticks;
	add_timer(timer);
	sti();
}
 
 
/* 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.
 */
static void wake_readers(void)
{
	wake_up_interruptible(&queue);
	if(asyncptr)
		kill_fasync(asyncptr, SIGIO);
}
 
 
/*****************************************************************************/
/*
 * Deal with the messy business of synthesizing button tap and drag
 * events.
 *
 * Exports:
 *	notify_digi_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=0;
static int recent_transition=0;
static int transition_count=0;
static int synthesize_tap=0;
static void tap_timeout(unsigned long data);
static struct timer_list tap_timer = { NULL, NULL, 0, 0, tap_timeout };
 
 
/* 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)
{
	printk("tap_timeout\n");
	if(!recent_transition)
	{
		printk("mc68328digi: 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;
}
 
 
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_digi()
 *		Returns the most current pad state.
 *	xy_pending
 *		Flag is set whenever read_raw_digi() has new values
 *		to return.
 * Imports:
 *	wake_readers()
 *		Called when movement occurs.
 *	notify_digi_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=0;
static int raw_x=0, raw_y=0;	/* most recent absolute co-ords read */
static int raw_down=0;		/* raw up/down state */
static int debounced_down=0;	/* up/down state after debounce processing */
static int is_new_stroke=0;	/* set when xy are discontinuous */
static enum {
	NO_BOUNCE, JUST_GONE_UP, JUST_GONE_DOWN, STICKY_DRAG_UP
} bounce=NO_BOUNCE;
				/* set just after an up/down transition */
static int xy_pending=0;	/* 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 = { NULL, NULL, 0, 0, bounce_timeout };
 
 
/* Called by the raw pad read routines when a (debounced) up/down
 * transition is detected.
 */
void notify_digi_up_down(void)
{
	if(recent_transition)
	{
		transition_count++;
	}
	else
	{
		transition_count=1;
		recent_transition=1;
	}
	set_timer_callback(&tap_timer, current_params.tap_interval);
 
	/* changes to transition_count can cause reported button to change */
	button_pending = 1;
	is_new_stroke=1;
	wake_readers();
}
 
 
 
static void bounce_timeout(unsigned long data)
{
	/* No further up/down transitions happened within the
	 * bounce period, so treat this as a genuine transition.
	 */
	switch(bounce)
	{
		case NO_BOUNCE:
		{
			/* Strange; the timer callback should only go off if
			 * we were expecting to do bounce processing!
			 */
			printk("mc68328digi, bounce_timeout: bounce flag not set!\n");
			break;
		}
		case JUST_GONE_UP:
		case STICKY_DRAG_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("mc68328digi, bounce_timeout: raw already debounced!\n");
			}
			debounced_down=raw_down;
			notify_digi_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;
		}
	}
}
 
static void handle_raw(int new_x, int new_y, int new_down);
 
static void release_timeout(void);
static struct timer_list release_timer = { NULL, NULL, 0, 0, release_timeout };
static void release_timeout(void)
{
	(*((volatile unsigned long*)0xFFFFF304)) |= (1<<20); 
 
	handle_raw(raw_x, raw_y, 0);
 
	(*((volatile unsigned long*)0xFFFFF304)) &= ~(1<<20);   /* Enable interrupt */
}
 
 
/*
 * Callback when pad's irq goes off; copies values in to raw_* globals;
 * initiates debounce processing.
 */
void digi_interrupt(int irq, void *ptr, struct pt_regs *regs)
{
	unsigned long flags;
	int new_x, new_y, new_down;
	volatile int i;
 
/*	set_timer_callback(&release_timer, 1);*/
 
	save_flags(flags); cli();
 
#define PORTF_DIR (*(volatile unsigned char*)0xFFFFF428)
#define PORTF_DAT (*(volatile unsigned char*)0xFFFFF429)
#define PORTF_PUP (*(volatile unsigned char*)0xFFFFF42A)
#define PORTF_SEL (*(volatile unsigned char*)0xFFFFF42B)
 
#define PORTM_DIR (*(volatile unsigned char*)0xFFFFF448)
#define PORTM_DAT (*(volatile unsigned char*)0xFFFFF449)
#define PORTM_PUP (*(volatile unsigned char*)0xFFFFF44A)
#define PORTM_SEL (*(volatile unsigned char*)0xFFFFF44B)
 
#define SPIMCONT (*(volatile unsigned short*)0xFFFFF802)
#define SPIMDATA (*(volatile unsigned short*)0xFFFFF800)
 
 
#define SPIMCONT_IRQEN	0x40
#define SPIMCONT_IRQ	0x80
#define SPIMCONT_XCH	0x100
#define SPIMCONT_EN	0x200
 
#define PF_DigiYVOff	0x01
#define PF_DigiYGOn	0x02
#define PF_DigiXVOff	0x04
#define PF_DigiXGOn	0x08
#define PF_LCDEnable	0x10
#define PF_LCDVccOff	0x20
#define PF_LCDVeeOn	0x40
#define PF_ADSelect	0x80
 
#define PF_DigiMask	0x0f
#define PF_DigiOff	(PF_DigiYVOff|PF_DigiXVOff)
#define PF_DigiTap	(PF_DigiXGOn|PF_DigiYVOff|PF_DigiXVOff)
#define PF_DigiGetY	(PF_DigiXGOn|PF_DigiYVOff)
#define PF_DigiGetX	(PF_DigiXVOff|PF_DigiYGOn)
 
#define PM_VCCFAIL	0x04
#define PM_DOCKBUTTON	0x20
#define PM_PENIO	0x40
#define PM_DOCKIN	0x80
 
 
#if 0
	PORTM_PUP &= ~PM_PENIO;
	PORTM_SEL |= PM_PENIO;
 
	PORTF_DAT = (PORTF_DAT & ~PF_DigiMask) | PF_DigiOff;
	PORTF_DAT = (PORTF_DAT & ~PF_DigiMask) | PF_DigiGetY;
 
 
	SPIMCONT = 0x427f;
	SPIMDATA = 0xc000;
 
	PORTF_DAT &= ~PF_ADSelect;
 
	/* Tell SPIM to sequence */
	SPIMCONT &= ~SPIMCONT_XCH;
	SPIMCONT |= SPIMCONT_XCH;
 
	/* Wait till sample is retrieved */
	while (!(SPIMCONT & SPIMCONT_IRQ))
		;
	new_y = SPIMDATA;
 
	PORTF_DAT |= PF_ADSelect;
 
	SPIMCONT &= ~SPIMCONT_EN;
 
	PORTF_DAT = (PORTF_DAT & ~PF_DigiMask) | PF_DigiOff;
	PORTF_DAT = (PORTF_DAT & ~PF_DigiMask) | PF_DigiGetX;
 
	SPIMCONT = 0x427F;
	SPIMDATA = 0xffff;
 
	PORTF_DAT &= ~PF_ADSelect;
 
	/* Tell SPIM to sequence */
	SPIMCONT &= ~SPIMCONT_XCH;
	SPIMCONT |= SPIMCONT_XCH;
 
	/* Wait till sample is retrieved */
	while (!(SPIMCONT & SPIMCONT_IRQ))
		;
	new_x = SPIMDATA;
 
	PORTF_DAT |= PF_ADSelect;
 
	SPIMCONT &= ~SPIMCONT_EN;
 
	PORTF_DAT = (PORTF_DAT & PF_DigiMask) | PF_DigiOff;
	PORTF_DAT = (PORTF_DAT & PF_DigiMask) | PF_DigiTap;
 
	PORTM_PUP |= PM_PENIO;
	PORTM_SEL &= ~PM_PENIO;
#endif
 
 
#if 1
 
 
#define PFDATA (*((volatile unsigned char*)0xFFFFF429))	
 
#if 0
	(*(volatile unsigned char*)0xFFFFF44A) &= ~64; /* Switch off Port M Pen IRQ pullup */
	(*(volatile unsigned char*)0xFFFFF44B) |= 64;  /* Turn on Port M Pen IRQ select */
#endif
	(*(volatile unsigned char*)0xFFFFF44A) = 0xad; /* Switch off Port M Pen IRQ pullup */
	(*(volatile unsigned char*)0xFFFFF44B) = 0xd0;  /* Turn on Port M Pen IRQ select */
 
#if 0
	(*(volatile unsigned char*)0xFFFFF429) = 0xD5;    /* Set PFDATA to D5, Turn off digi power */ 
#endif
	PFDATA = (PFDATA & 0xf0) | 5;
	(*(volatile unsigned char*)0xFFFFF429) = 0xD9;    /* Set PFDATA to D9, Measure Y */
	(*(volatile unsigned short*)0xFFFFF802) = 0x427f; /* Set SPIMCONT to 427f */
	(*(volatile unsigned short*)0xFFFFF800) = 0xc000; /* Set SPIMDATA to c000, Preload data*/
#if 0
	(*((volatile unsigned char*)0xFFFFF439)) &= ~2;    /* Enable A/D */
#endif
	PFDATA &= ~0x80;
#if 0
	(*((volatile unsigned char*)0xFFFFF429)) = 0x59;    /* Set PFDATA to 59, Measure Y, A/D select HW2  */
#endif	
	(*((volatile unsigned short*)0xFFFFF802)) &= ~0x0100; /* Set SPIMCONT to 427f */
	(*((volatile unsigned short*)0xFFFFF802)) |= 0x0100; /* Set SPIMCONT to 437f */
 
	while(!((*((volatile unsigned short*)0xFFFFF802)) & 0x80))
		;
 
	new_y = (*((volatile unsigned short*)0xFFFFF800)); /* Read SPIMDATA */
 
	PFDATA |= 0x80;
#if 0
	(*(volatile unsigned char*)0xFFFFF429) = 0xD9;    /* Set PFDATA to D9 */
#endif
#if 0
	(*((volatile unsigned char*)0xFFFFF439)) |= 2;    /* Disable A/D */
#endif
	(*(volatile unsigned short*)0xFFFFF802) = 0x407f;/* Set SPIMCONT to 407f */
 
	(*(volatile unsigned char*)0xFFFFF429) = 0xD5;    /* Set PFDATA to D5 */
	(*(volatile unsigned char*)0xFFFFF429) = 0xD6;    /* Set PFDATA to D6 */
	(*(volatile unsigned short*)0xFFFFF802) = 0x427f;/* Set SPIMCONT to 427f */
	(*(volatile unsigned short*)0xFFFFF800) = 0xffff; /* Set SPIMDATA to ffff */
 
	PFDATA &= ~0x80;
#if 0
	(*((volatile unsigned char*)0xFFFFF429)) = 0x56;    /* Set PFDATA to 56 */
#endif
#if 0
	(*((volatile unsigned char*)0xFFFFF439)) &= ~2;    /* Enable A/D */
#endif
	(*(volatile unsigned short*)0xFFFFF802) = 0x427f;/* Set SPIMCONT to 427f */
	(*((volatile unsigned short*)0xFFFFF802)) = 0x437f; /* Set SPIMCONT to 437f */
 
	while(!((*((volatile unsigned short*)0xFFFFF802)) & 0x80)) 
		;
 
	new_x = (*((volatile unsigned short*)0xFFFFF800)); /* Read SPIMDATA */
 
	PFDATA |= 0x80;
#if 0
	(*(volatile unsigned char*)0xFFFFF429) = 0xD6;    /* Set PFDATA to d6 */
#endif
#if 0
	(*((volatile unsigned char*)0xFFFFF439)) |= 2;    /* Disable A/D */
#endif
	(*(volatile unsigned short*)0xFFFFF802) = 0x407f;/* Set SPIMCONT to 407f */
 
	(*(volatile unsigned char*)0xFFFFF429) = 0xD5;    /* Set PFDATA to d5 */
	(*(volatile unsigned char*)0xFFFFF429) = 0xDD;    /* Set PFDATA to dd */
 
#if 0
	(*(volatile unsigned char*)0xFFFFF44A) |= 64; /* Turn on Port M Pen IRQ pullup */
	(*(volatile unsigned char*)0xFFFFF44B) &= ~64;  /* Switch off Port M Pen IRQ select */
#endif
	(*(volatile unsigned char*)0xFFFFF44A) = 0xed; /* Switch off Port M Pen IRQ pullup */
	(*(volatile unsigned char*)0xFFFFF44B) = 0x90;  /* Turn on Port M Pen IRQ select */
 
 
#if 0	
	new_x = ((long)new_x * current_params.x_a + current_params.x_b) >> 16;
	new_y = ((long)new_y * current_params.y_a + current_params.y_b) >> 16;
 
	/*printk("calibrated x=%d, y=%d\n", new_x, new_y);*/
 
	handle_raw(new_x, new_y, 1);
#endif
 
#endif
 
	printk("raw x=%d, y=%d\n", new_x, new_y);
 
	restore_flags(flags);
 
 
 
}
 
void handle_raw(int new_x, int new_y, int new_down) {
 
	/*printk("Pen: %x,%x %x\n", new_x, new_y, new_down);*/
 
	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
				 */
				is_new_stroke=1;
			}
			else
			{
				bounce=JUST_GONE_DOWN;
				set_timer_callback(&bounce_timer,
					current_params.bounce_interval);
				/* start new stroke/tap */
				debounced_down=new_down;
				notify_digi_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_digi_up_down();
			}
			else if(transition_count==3)
			{
				/* give chance to move */
				bounce=STICKY_DRAG_UP;
				set_timer_callback(&bounce_timer,
					current_params.sticky_drag);
			}
			else
			{
				/* don't trust it yet */
				bounce=JUST_GONE_UP;
				set_timer_callback(&bounce_timer,
					current_params.bounce_interval);
			}
		}
	}
	/*printk("Bounce=%d, x=%d, y=%d, new_stroke=%d, debounced_down=%d\n",
		bounce, raw_x, raw_y, is_new_stroke, debounced_down);*/
 
	wake_readers();
 
 
#if 0	
 
	/* 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)
	{
		/* 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
		 */
		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
					 */
					is_new_stroke=1;
				}
				else
				{
					bounce=JUST_GONE_DOWN;
					set_timer_callback(&bounce_timer,
						current_params.bounce_interval);
					/* start new stroke/tap */
					debounced_down=new_down;
					notify_digi_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_digi_up_down();
				}
				else if(transition_count==3)
				{
					/* give chance to move */
					bounce=STICKY_DRAG_UP;
					set_timer_callback(&bounce_timer,
						current_params.sticky_drag);
				}
				else
				{
					/* don't trust it yet */
					bounce=JUST_GONE_UP;
					set_timer_callback(&bounce_timer,
						current_params.bounce_interval);
				}
			}
		}
		wake_readers();
		raw_data_count=0;
	}
#endif
}
 
 
static void read_raw_digi(int *down, int *debounced, int *stroke, int *x, int *y)
{
 
	/*disable_irq(current_params.irq);*/
	(*((volatile unsigned long*)0xFFFFF304)) |= (1<<20);   /* Enable interrupt */
	{
		*down=raw_down;
		*debounced=debounced_down;
		*stroke=is_new_stroke;
		*x=raw_x;
		*y=raw_y;
		xy_pending = 0;
		is_new_stroke = 0;
	}
	(*((volatile unsigned long*)0xFFFFF304)) &= ~(1<<20);   /* Enable interrupt */
	/*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=0;
 
 
static void sample_rare(int d[3])
{
	int thisd, thisdd, thiss, thisx, thisy;
 
	read_raw_digi(&thisd, &thisdd, &thiss, &thisx, &thisy);
 
	d[0]=(thisd?0x80:0)
	   | (thisx/256)<<4
	   | (thisdd?0x08:0)
	   | (thisy/256)
	;
	d[1]=thisx%256;
	d[2]=thisy%256;
}
 
 
static void sample_debug(int d[3])
{
	int thisd, thisdd, thiss, thisx, thisy;
	int b;
	cli();
	read_raw_digi(&thisd, &thisdd, &thiss, &thisx, &thisy);
	d[0]=(thisd?0x80:0) | (thisdd?0x40:0) | (thiss?0x20:0) | bounce;
	d[1]=(recent_transition?0x80:0)+transition_count;
	read_button(&b);
	d[2]=(synthesize_tap<<4) | (b?0x01:0);
	sti();
}
 
 
static void sample_ps2(int d[3])
{
	static int lastx, lasty;
 
	int thisd, thisdd, thiss, 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_digi(&thisd, &thisdd, &thiss, &thisx, &thisy);
	read_button(&b);
 
	/* Now compare with previous readings. */
	if( thiss /* new stroke */
	 || (bounce!=NO_BOUNCE) )
	{
		dx=0;
		dy=0;
	}
	else
	{
		dx = (thisx-lastx);
		dy = -(thisy-lasty);
	}
	lastx=thisx;
	lasty=thisy;
 
	d[0]= ((dy<0)?0x20:0)
	    | ((dx<0)?0x10:0)
	    | (b? 0x00:0x08)
	;
	d[1]=dx;
	d[2]=dy;
}
 
 
 
static int fasync_digi(struct inode *inode, struct file *filp, int on)
{
	int retval;
 
	retval = fasync_helper(inode, filp, on, &asyncptr);
	if (retval < 0)
		return retval;
	return 0;
}
 
 
/*
 * close access to the pad
 */
static void close_digi(struct inode * inode, struct file * file)
{
	fasync_digi(inode, file, 0);
	if (--active)
		return;
 
	(*((volatile unsigned long*)0xFFFFF304)) |= (1<<20);   /* Enable interrupt */
#if 0
	outb(0x30, current_params.io+2);	/* switch off digitiser */
#endif
	MOD_DEC_USE_COUNT;
}
 
 
/*
 * open access to the pad
 */
static int open_digi(struct inode * inode, struct file * file)
{
	if (active++)
		return 0;
	MOD_INC_USE_COUNT;
 
#if 0
	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);
	sti();
#endif
 
	cli();
	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);
	sti();
 
	return 0;
}
 
 
/*
 * writes are disallowed
 */
static int write_digi(struct inode * inode, struct file * file, const char * buffer, int count)
{
	return -EINVAL;
}
 
 
void new_sample(int d[3])
{
	switch(current_params.mode)
	{
		case MC68328DIGI_RARE:	sample_rare(d);		break;
		case MC68328DIGI_DEBUG:	sample_debug(d);	break;
		case MC68328DIGI_PS2:	sample_ps2(d);		break;
	}
}
 
 
/*
 * Read pad data.  Currently never blocks.
 */
static int read_digi(struct inode * inode, struct file * file, char * buffer, int count)
{
	int r;
 
	if ((r = verify_area(VERIFY_WRITE, buffer, count)))
		return r;
 
	for(r=0; r<count; r++)
	{
		if(!read_byte_count)
			new_sample(read_bytes);
		put_user(read_bytes[read_byte_count], buffer+r);
		read_byte_count = (read_byte_count+1)%3;
	}
	return r;
}
 
 
/*
 * select for pad input
 */
static int pad_select(struct inode *inode, struct file *file, int sel_type, select_table * wait)
{
	if(sel_type == SEL_IN) {
	    	if(button_pending || xy_pending)
			return 1;
		select_wait(&queue, wait);
    	}
	return 0;
}
 
 
static int pad_ioctl(struct inode *inode, struct file * file,
	unsigned int cmd, unsigned long arg)
{
	int i;
	struct mc68328digi_params new;
 
	if (!inode)
		return -EINVAL;
 
	switch (cmd) {
	case MC68328DIGIIOCGETP:
		printk("pad_ioctl: get %d\n",current_params.mode);
		i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(new));
		if(i) return i;
		new = current_params;
		memcpy_tofs((void *)arg, &new, sizeof(new));
		return 0;
 
	case MC68328DIGIIOCSETP:
		printk("pad_ioctl: set, was %d",current_params.mode);
		i = verify_area(VERIFY_READ, (void *)arg, sizeof(new));
		if (i) return i;
		memcpy_fromfs(&new, (void *)arg, sizeof(new));
 
		if( (new.mode<MC68328DIGI_RARE)
		 || (new.mode>MC68328DIGI_PS2)
		 || (new.bounce_interval<0)
		 || (new.sticky_drag<0)
		 || (new.tap_interval<0)
		)
			return -EINVAL;
 
		current_params.mode	= new.mode;
		current_params.bounce_interval	= new.bounce_interval;
		current_params.sticky_drag	= new.sticky_drag;
		current_params.tap_interval	= new.tap_interval;
		printk("now %d\n",current_params.mode);
		return 0;
	}
	return -ENOIOCTLCMD;
}
 
 
static struct file_operations pad_fops = {
	NULL,		/* pad_seek */
	read_digi,
	write_digi,
	NULL, 		/* pad_readdir */
	pad_select,
	pad_ioctl,
	NULL,		/* pad_mmap */
	open_digi,
	close_digi,
	NULL,
	fasync_digi,
};
 
 
static struct miscdevice mc68328_digi = {
	MC68328DIGI_MINOR, "mc68328 digitizer", &pad_fops
};
 
 
int mc68328digi_init(void)
{
	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);
 
 
	/*if(request_irq(current_params.irq, pad_irq, 0, "mc68328digi", 0))
	{
		printk("mc68328digi: Unable to get IRQ.\n");
		return -EBUSY;
	}
	if(check_region(current_params.io, 4))
	{
		printk("mc68328digi: I/O area in use.\n");
		free_irq(current_params.irq,0);
		return -EBUSY;
	}
	request_region(current_params.io, 4, "mc68328digi");
	printk("PC110 digitizer pad at 0x%X, irq %d.\n",
		current_params.io,current_params.irq);*/
 
	if (request_irq(IRQ_MACHSPEC | PEN_IRQ_NUM,
	    		digi_interrupt,
	    		IRQ_FLG_STD,
            		"M68328 Digitizer", NULL))
		panic("Unable to attach 68328 digitizer interrupt\n");
 
	printk("MC68328 digitizer.\n");
	misc_register(&mc68328_digi);
 
	(*(volatile unsigned char*)0xFFFFF42B) = 0xFF; /*Setting PFSEL byte to ff*/
	(*(volatile unsigned char*)0xFFFFF428) = 0xFF; /*Setting PFDIR byte to ff*/
	(*(volatile unsigned char*)0xFFFFF42A) = 0x00; /*Setting PFPUEN byte to 0*/
 
	(*(volatile unsigned char*)0xFFFFF429) = 0xc5; /*Setting PFDATA byte to c5*/
	(*(volatile unsigned char*)0xFFFFF429) = 0xd5; /*Setting PFDATA byte to d5*/
	(*(volatile unsigned char*)0xFFFFF448) = 0x50; /*Setting PMDIR byte to 50*/
	(*(volatile unsigned char*)0xFFFFF449) = 0x00; /*Setting PMDATA byte to 0*/
	(*(volatile unsigned char*)0xFFFFF44B) = 0xd0; /*Setting PMSEL byte to d0*/
	(*(volatile unsigned char*)0xFFFFF429) = 0xd5; /*Setting PFDATA byte to d5*/
#if 0
	(*(volatile unsigned char*)0xFFFFF431) = 0x60; /*Setting PGDATA byte to 60*/
#endif
	(*(volatile unsigned short*)0xFFFFF802) = 0x427f; /*Setting SPIMCONT word to 427f*/
	(*(volatile unsigned short*)0xFFFFF800) = 0xffff; /*Setting SPIMDATA word to ffff*/
	(*(volatile unsigned char*)0xFFFFF429) = 0x55; /*Setting PFDATA byte to 55*/
	(*(volatile unsigned short*)0xFFFFF802) = 0x427f; /*Setting SPIMCONT word to 427f*/
	(*(volatile unsigned short*)0xFFFFF802) = 0x437f; /*Setting SPIMCONT word to 437f*/
	(void)(*(volatile unsigned short*)0xFFFFF800); /*Returning word from SPIMDATA ffff*/
	(*(volatile unsigned char*)0xFFFFF429) = 0xd5; /*Setting PFDATA byte to d5*/
	(*(volatile unsigned short*)0xFFFFF802) = 0x407f; /*Setting SPIMCONT word to 407f*/
#if 0
	(*(volatile unsigned char*)0xFFFFF431) = 0x70; /*Setting PGDATA byte to 70*/
#endif
	(*(volatile unsigned char*)0xFFFFF448) = 0x50; /*Setting PMDIR byte to 50*/
	(*(volatile unsigned char*)0xFFFFF449) = 0x00; /*Setting PMDATA byte to 0*/
	(*(volatile unsigned char*)0xFFFFF44A) = 0xED; /* Port M Pullups (dangerous?) */
	(*(volatile unsigned char*)0xFFFFF44B) = 0xd0; /*Setting PMSEL byte to d0*/
	(*(volatile unsigned char*)0xFFFFF429) = 0xd5; /*Setting PFDATA byte to d5*/
	(*(volatile unsigned char*)0xFFFFF429) = 0xdd; /*Setting PFDATA byte to dd*/
 
 
	(*((volatile unsigned long*)0xFFFFF304)) &= ~(1<<20);   /* Enable interrupt */
	(*(volatile unsigned char*)0xFFFFF44A) |= 64; /* Turn on Port M Pen IRQ pullup */
	(*(volatile unsigned char*)0xFFFFF44B) &= ~64;  /* Switch off Port M Pen IRQ select */
 
	/*outb(0x30, current_params.io+2);*/	/* switch off digitiser */
 
	return 0;
}
 
 
static void mc68328digi_unload(void)
{
#if 0
	outb(0x30, current_params.io+2);	/* switch off digitiser */
#endif
	/*if(current_params.irq)
		free_irq(current_params.irq, 0);
	current_params.irq=0;
	release_region(current_params.io, 4);*/
	misc_deregister(&mc68328_digi);
}
 
 
int init_module(void)
{
	return mc68328digi_init();
}
 
void cleanup_module(void)
{
	mc68328digi_unload();
}
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.