diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/ssi/adc7843.c linux-2.4.19-rmk2/drivers/ssi/adc7843.c
diff -urN -X /home/arm/dontdiff_tml_arm /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/ssi/adc7843.c linux-2.4.19-rmk2/drivers/ssi/adc7843.c
--- /home/download/kernels/linux-2.4.19-rmk2-vanilla/drivers/ssi/adc7843.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.19-rmk2/drivers/ssi/adc7843.c	2002-10-02 22:07:30.000000000 -0700
@@ -0,0 +1,297 @@
+/* drivers/ssi/adc7843.c
+ *
+ * Driver for the 7843 ADC found on the Psion 5mx 
+ * Copyright 2001 Shane R. Nay <shane@minirl.com>
+ *
+ * Polling support by Thomas A. de Ruiter <thomas@de-ruiter.cx>
+ *
+ * Some code is borrowed from other parts of the kernel,
+ * the largest body of such code was grabbed from:
+ * drivers/char/qpmouse.c
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+
+#include <asm/mach-types.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+
+#include "ssi_dev.h"
+#include "ssi_bus.h"
+
+
+#define AD_QUEUE_SIZE	32
+#define PEN_DOWN        1<<31
+
+struct ad_queue {
+	u_int head;
+	u_int tail;
+	wait_queue_head_t proc_list;
+	struct fasync_struct *fasync;
+	u_int buf[AD_QUEUE_SIZE];
+};
+
+static struct ad_queue * queue;
+static struct timer_list adtimer;
+static int ad_active;
+void ad_bottomhalf(unsigned long);
+DECLARE_TASKLET(adtasklet, ad_bottomhalf, NULL);
+
+/* Each buf u_int composes three pieces of information.
+ * The first is located at bit 31, or 1<<31.  That is whether
+ * or not the pen is down.  The x coord is 12bit and shifted up
+ * in the upper 2 bytes, while the y coord is in the lower 2 bytes
+ * and is also 12bit.
+ *
+ * Every read from this driver should produce *3* shorts, first is
+ * pen status, either 1 or 0.  The other in order are x coord, and
+ * then y coord.  If less than 3 shorts are requested, then it will
+ * give the requested amount of information starting at the top, and
+ * will abandon the rest, and move it's pointer up to the next set
+ * of coordinates.
+ *
+ * It will only store one pen up info set, after that it will just
+ * dump the info into never-never land.
+ */
+
+
+/* Always happens in ISR, no need to deal with concurrency */
+static void add_adqueue(u_int dat) {
+	int head=queue->head;
+	queue->buf[head]=dat;
+	if(head != ((queue->tail-1)&(AD_QUEUE_SIZE-1))) {
+		head++;
+		head &= (AD_QUEUE_SIZE-1);
+	}
+	queue->head=head;
+}
+
+static u_int pop_adqueue(void) {
+	u_int flags;
+	u_int ret;
+	save_flags(flags);
+	cli();
+	ret=queue->buf[queue->tail];
+	queue->tail=(queue->tail+1) & (AD_QUEUE_SIZE-1);
+	restore_flags(flags);
+	return ret;
+}
+
+#define IS_EMPTY(queue) (queue->head==queue->tail)
+
+
+static struct ssi_dev ads7843_dev = {
+	name:		"ADS7843",
+	id:		1,
+	proto:		SSI_MICROWIRE,
+	cfglen:		8,
+	framelen:	12,
+	clkpol:		0,
+	clkfreq:	2500000,
+};
+
+/* Send a request for an x,y pair */
+static void ad_sendreq(unsigned long ptr) {
+	struct ssi_dev* dev=(struct ssi_dev*)ptr;
+	ssi_transmit_data(dev,0xd000);
+	ssi_transmit_data(dev,0x9000);
+}
+
+/* Do the time consuming ISR stuff */
+void ad_bottomhalf(unsigned long private) {
+	/* Wake up userland process, allow that process to
+	 *   restart timer */
+	del_timer(&adtimer);
+
+	if(!(IS_EMPTY(queue)) && ad_active) {
+		kill_fasync(&queue->fasync, SIGIO, POLL_IN);
+		wake_up_interruptible(&queue->proc_list);
+	}
+	if(ad_active) {
+		init_timer(&adtimer);
+		adtimer.function=ad_sendreq;
+		adtimer.data=private;
+		adtimer.expires=jiffies + 10;
+		add_timer(&adtimer);
+	}
+}
+
+/* Recieve each byte off the serial return, called by the SSI bus */
+void ad_rcv(struct ssi_dev* dev, u_int dat) {
+	static u_int x=0x80001000;
+	/* Bit 12 of static x acts as a selector switch between
+	 * whether we're recieving x, or y.  Statics are bad, so
+	 * I cooked up this goofy scheme so as not to need another
+	 * one.  Also bit 31 acts inverse of normal, if it's set,
+	 * then the prior was a pen up event.  We start off the
+	 * action with it being up, and recieving x.
+	 */
+	if(!(x&0x1000)) {
+		if(x==PEN_DOWN) {
+			/* Pen was up on previous, so we
+			 * don't really care.  Most frequent
+			 * case so it lies at the top.
+			 */
+		} else if(x==0) { /* Pen was just released */
+			x|=PEN_DOWN;
+			add_adqueue(0);
+		} else { /* We've got data. */
+			add_adqueue((x<<16)|((~(dat))&0xfff)|(PEN_DOWN));
+			/* You are probably wondering why I inverted y.  Well...,
+			 * don't blame me, Psion inverted in it hardware,
+			 * and I don't want to deal with it in every application
+			 * I write, so I'm doing it at driver level.
+			 */
+			x=0;
+		}
+		x|=0x1000;
+		tasklet_schedule(&adtasklet);
+	} else {
+		x|=dat & 0xfff;
+		x &= ~(0x1000);
+	}
+}
+
+/* Read routine.  Documented in more detail above. */
+static ssize_t read_ad(struct file * file, char * buffer,
+                       size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	ssize_t i = count;
+	unsigned char c[6];
+	if (IS_EMPTY(queue)) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&queue->proc_list, &wait);
+repeat:
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (IS_EMPTY(queue) && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&queue->proc_list, &wait);
+	}
+	while (i > 0 && !(IS_EMPTY(queue))) {
+		u_int coord=pop_adqueue();
+		c[0]=0;
+		if(coord & PEN_DOWN)
+			c[1]=1;
+		else
+			c[1]=0;
+		c[2]=(coord>>24)&0xf;
+		c[3]=(coord>>16)&0xff;
+		c[4]=(coord>>8)&0xff;
+		c[5]=coord&0xff;
+		for(coord=0;coord<6 && i>0; coord++) {
+			put_user(c[coord],buffer++);
+			i--;
+		}
+	}
+	if (count-i) {
+		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
+		return count-i;
+	}
+	if (signal_pending(current))
+		return -ERESTARTSYS;
+	return 0;
+}
+
+/* Open routine, kick starts the timer, and sets active
+ * to true so timer reset routine will flip the timer on
+ * again for the next go round.
+ */
+static int open_ad (struct inode* inode, struct file* file) {
+	if(ad_active)
+		return -EBUSY;
+	ad_active++;
+	init_timer(&adtimer);
+	adtimer.function=ad_sendreq;
+	adtimer.data=(unsigned long)&ads7843_dev;
+	adtimer.expires=jiffies + 10;
+	add_timer(&adtimer);
+	return 0;
+}
+
+/* Asynchronous anyone? :) */
+static int fasync_ad(int fd, struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(fd, filp, on, &queue->fasync);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+/* Polling :-) */
+static unsigned int poll_ad(struct file * filp, poll_table * wait)
+{
+//    poll_wait(filp, &queue, wait);
+    if (IS_EMPTY(queue))
+        return 0;
+    else
+        return POLLIN | POLLRDNORM;
+}
+
+
+
+/* Close routine. By setting ad_active to false, basically when
+ * the timer pings around the next time, it will not activate a
+ * new timer next go round.  This allows for a nice "soft landing",
+ * and for all the x/y variables to be saved for next reader, and in
+ * good order.
+ */
+static int release_ad(struct inode * inode, struct file* file) {
+	if(ad_active) {
+		ad_active=0;
+		fasync_ad(-1,file,0);
+	}
+	return 0;
+}
+
+static struct file_operations adc7843_fops = {
+        owner:    THIS_MODULE,
+        read:     read_ad,
+        open:     open_ad,
+        poll:     poll_ad,
+        release:  release_ad,
+        fasync:   fasync_ad,
+        };
+
+static struct miscdevice adc7843_tpanel = {
+	        PS5MX_TPANEL_MINOR, "adc7843", &adc7843_fops
+        };
+
+/* Initialization routine called by bus when registered */
+int ad_init(struct ssi_dev* dev) {
+	if((queue = kmalloc(sizeof(*queue), GFP_KERNEL))==NULL) {
+		printk(KERN_ERR "adc7843: no queue memory.\n");
+		return -ENOMEM;
+	}
+	memset(queue,0,sizeof(*queue));
+	init_waitqueue_head(&queue->proc_list);
+	ssi_select_device(dev->bus,dev);
+	adtasklet.data=(unsigned long)dev;
+	misc_register(&adc7843_tpanel);
+	return 0;
+}
+
+/* Initialization routine called from initialization routine
+ * in device specific bus.
+ */
+void init_adc7843(struct ssi_bus* bus) {
+	ads7843_dev.init=ad_init;
+	ads7843_dev.rcv=ad_rcv;
+	ssi_register_device(bus,&ads7843_dev);
+}
