patch-2.1.123 linux/drivers/char/qpmouse.c

Next file: linux/drivers/char/selection.c
Previous file: linux/drivers/char/psaux.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.122/linux/drivers/char/qpmouse.c linux/drivers/char/qpmouse.c
@@ -0,0 +1,374 @@
+/*
+ * linux/drivers/char/qpmouse.c
+ *
+ * Driver for a 82C710 C&T mouse interface chip.
+ *
+ * Based on the PS/2 driver by Johan Myreen.
+ *
+ * Corrections in device setup for some laptop mice & trackballs.
+ * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
+ *
+ * Modified by Johan Myreen (jem@pandora.pp.fi) 04Aug93
+ *   to include support for QuickPort mouse.
+ *
+ * Changed references to "QuickPort" with "82C710" since "QuickPort"
+ * is not what this driver is all about -- QuickPort is just a
+ * connector type, and this driver is for the mouse port on the Chips
+ * & Technologies 82C710 interface chip. 15Nov93 jem@pandora.pp.fi
+ *
+ * Added support for SIGIO. 28Jul95 jem@pandora.pp.fi
+ *
+ * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
+ *
+ * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
+ */
+
+#include <linux/module.h>
+ 
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/fcntl.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/miscdevice.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include <asm/semaphore.h>
+
+#include "pc_keyb.h"		/* mouse enable command.. */
+
+
+/*
+ * We use the same minor number as the PS/2 mouse for (bad) historical
+ * reasons..
+ */
+#define PSMOUSE_MINOR      1	       		/* Minor device # for this mouse */
+#define QP_BUF_SIZE	2048
+
+struct qp_queue {
+	unsigned long head;
+	unsigned long tail;
+	struct wait_queue *proc_list;
+	struct fasync_struct *fasync;
+	unsigned char buf[QP_BUF_SIZE];
+};
+
+static struct qp_queue *queue;
+
+static unsigned int get_from_queue(void)
+{
+	unsigned int result;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	result = queue->buf[queue->tail];
+	queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1);
+	restore_flags(flags);
+	return result;
+}
+
+
+static inline int queue_empty(void)
+{
+	return queue->head == queue->tail;
+}
+
+static int fasync_qp(int fd, struct file *filp, int on)
+{
+	int retval;
+
+	retval = fasync_helper(fd, filp, on, &queue->fasync);
+	if (retval < 0)
+		return retval;
+	return 0;
+}
+
+/*
+ *	82C710 Interface
+ */
+
+#define QP_DATA         0x310		/* Data Port I/O Address */
+#define QP_STATUS       0x311		/* Status Port I/O Address */
+
+#define QP_DEV_IDLE     0x01		/* Device Idle */
+#define QP_RX_FULL      0x02		/* Device Char received */
+#define QP_TX_IDLE      0x04		/* Device XMIT Idle */
+#define QP_RESET        0x08		/* Device Reset */
+#define QP_INTS_ON      0x10		/* Device Interrupt On */
+#define QP_ERROR_FLAG   0x20		/* Device Error */
+#define QP_CLEAR        0x40		/* Device Clear */
+#define QP_ENABLE       0x80		/* Device Enable */
+
+#define QP_IRQ          12
+
+static int qp_present = 0;
+static int qp_count = 0;
+static int qp_data = QP_DATA;
+static int qp_status = QP_STATUS;
+
+static int poll_qp_status(void);
+static int probe_qp(void);
+
+/*
+ * Interrupt handler for the 82C710 mouse port. A character
+ * is waiting in the 82C710.
+ */
+
+static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
+{
+	int head = queue->head;
+	int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1);
+
+	add_mouse_randomness(queue->buf[head] = inb(qp_data));
+	if (head != maxhead) {
+		head++;
+		head &= QP_BUF_SIZE-1;
+	}
+	queue->head = head;
+	if (queue->fasync)
+		kill_fasync(queue->fasync, SIGIO);
+	wake_up_interruptible(&queue->proc_list);
+}
+
+static int release_qp(struct inode * inode, struct file * file)
+{
+	unsigned char status;
+
+	fasync_qp(-1, file, 0);
+	if (!--qp_count) {
+		if (!poll_qp_status())
+			printk("Warning: Mouse device busy in release_qp()\n");
+		status = inb_p(qp_status);
+		outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
+		if (!poll_qp_status())
+			printk("Warning: Mouse device busy in release_qp()\n");
+		free_irq(QP_IRQ, NULL);
+		MOD_DEC_USE_COUNT;
+	}
+	return 0;
+}
+
+/*
+ * Install interrupt handler.
+ * Enable the device, enable interrupts. 
+ */
+
+static int open_qp(struct inode * inode, struct file * file)
+{
+	unsigned char status;
+
+	if (!qp_present)
+		return -EINVAL;
+
+	if (qp_count++)
+		return 0;
+
+	if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
+		qp_count--;
+		return -EBUSY;
+	}
+
+	status = inb_p(qp_status);
+	status |= (QP_ENABLE|QP_RESET);
+	outb_p(status, qp_status);
+	status &= ~(QP_RESET);
+	outb_p(status, qp_status);
+
+	queue->head = queue->tail = 0;          /* Flush input queue */
+	status |= QP_INTS_ON;
+	outb_p(status, qp_status);              /* Enable interrupts */
+
+	while (!poll_qp_status()) {
+		printk("Error: Mouse device busy in open_qp()\n");
+		qp_count--;
+		status &= ~(QP_ENABLE|QP_INTS_ON);
+		outb_p(status, qp_status);
+		free_irq(QP_IRQ, NULL);
+		return -EBUSY;
+	}
+
+	outb_p(AUX_ENABLE_DEV, qp_data);	/* Wake up mouse */
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+/*
+ * Write to the 82C710 mouse device.
+ */
+
+static ssize_t write_qp(struct file * file, const char * buffer,
+			size_t count, loff_t *ppos)
+{
+	ssize_t i = count;
+
+	while (i--) {
+		char c;
+		if (!poll_qp_status())
+			return -EIO;
+		get_user(c, buffer++);
+		outb_p(c, qp_data);
+	}
+	file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
+	return count;
+}
+
+static unsigned int poll_qp(struct file *file, poll_table * wait)
+{
+	poll_wait(file, &queue->proc_list, wait);
+	if (!queue_empty())
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+/*
+ * Wait for device to send output char and flush any input char.
+ */
+
+#define MAX_RETRIES (60)
+
+static int poll_qp_status(void)
+{
+	int retries=0;
+
+	while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
+		       != (QP_DEV_IDLE|QP_TX_IDLE)
+		       && retries < MAX_RETRIES) {
+
+		if (inb_p(qp_status)&(QP_RX_FULL))
+			inb_p(qp_data);
+		current->state = TASK_INTERRUPTIBLE;
+		current->timeout = jiffies + (5*HZ + 99) / 100;
+		schedule();
+		retries++;
+	}
+	return !(retries==MAX_RETRIES);
+}
+
+/*
+ * Put bytes from input queue to buffer.
+ */
+
+static ssize_t read_qp(struct file * file, char * buffer,
+			size_t count, loff_t *ppos)
+{
+	struct wait_queue wait = { current, NULL };
+	ssize_t i = count;
+	unsigned char c;
+
+	if (queue_empty()) {
+		if (file->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+		add_wait_queue(&queue->proc_list, &wait);
+repeat:
+		current->state = TASK_INTERRUPTIBLE;
+		if (queue_empty() && !signal_pending(current)) {
+			schedule();
+			goto repeat;
+		}
+		current->state = TASK_RUNNING;
+		remove_wait_queue(&queue->proc_list, &wait);
+	}
+	while (i > 0 && !queue_empty()) {
+		c = get_from_queue();
+		put_user(c, 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;
+}
+
+struct file_operations qp_fops = {
+	NULL,		/* seek */
+	read_qp,
+	write_qp,
+	NULL, 		/* readdir */
+	poll_qp,
+	NULL, 		/* ioctl */
+	NULL,		/* mmap */
+	open_qp,
+	NULL,		/* flush */
+	release_qp,
+	NULL,
+	fasync_qp,
+};
+
+/*
+ * Initialize driver.
+ */
+static struct miscdevice qp_mouse = {
+	PSMOUSE_MINOR, "QPmouse", &qp_fops
+};
+
+/*
+ * Function to read register in 82C710.
+ */
+
+static inline unsigned char read_710(unsigned char index)
+{
+	outb_p(index, 0x390);			/* Write index */
+	return inb_p(0x391);			/* Read the data */
+}
+
+
+/*
+ * See if we can find a 82C710 device. Read mouse address.
+ */
+
+static int __init probe_qp(void)
+{
+	outb_p(0x55, 0x2fa);			/* Any value except 9, ff or 36 */
+	outb_p(0xaa, 0x3fa);			/* Inverse of 55 */
+	outb_p(0x36, 0x3fa);			/* Address the chip */
+	outb_p(0xe4, 0x3fa);			/* 390/4; 390 = config address */
+	outb_p(0x1b, 0x2fa);			/* Inverse of e4 */
+	if (read_710(0x0f) != 0xe4)		/* Config address found? */
+	  return 0;				/* No: no 82C710 here */
+	qp_data = read_710(0x0d)*4;		/* Get mouse I/O address */
+	qp_status = qp_data+1;
+	outb_p(0x0f, 0x390);
+	outb_p(0x0f, 0x391);			/* Close config mode */
+	return 1;
+}
+
+int __init qpmouse_init(void)
+{
+	if (!probe_qp())
+		return -EIO;
+
+	printk(KERN_INFO "82C710 type pointing device detected -- driver installed.\n");
+/*	printk("82C710 address = %x (should be 0x310)\n", qp_data); */
+	qp_present = 1;
+	misc_register(&qp_mouse);
+	queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
+	memset(queue, 0, sizeof(*queue));
+	queue->head = queue->tail = 0;
+	queue->proc_list = NULL;
+
+	return 0;
+}
+
+#ifdef MODULE
+int init_module(void)
+{
+	return qpmouse_init();
+}
+
+void cleanup_module(void)
+{
+	misc_deregister(&qp_mouse);
+	kfree(queue);
+}
+#endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov