File: /usr/src/linux/drivers/char/qpmouse.c

1     /*
2      * linux/drivers/char/qpmouse.c
3      *
4      * Driver for a 82C710 C&T mouse interface chip.
5      *
6      * Based on the PS/2 driver by Johan Myreen.
7      *
8      * Corrections in device setup for some laptop mice & trackballs.
9      * 02Feb93  (troyer@saifr00.cfsat.Honeywell.COM,mch@wimsey.bc.ca)
10      *
11      * Modified by Johan Myreen (jem@iki.fi) 04Aug93
12      *   to include support for QuickPort mouse.
13      *
14      * Changed references to "QuickPort" with "82C710" since "QuickPort"
15      * is not what this driver is all about -- QuickPort is just a
16      * connector type, and this driver is for the mouse port on the Chips
17      * & Technologies 82C710 interface chip. 15Nov93 jem@iki.fi
18      *
19      * Added support for SIGIO. 28Jul95 jem@iki.fi
20      *
21      * Rearranged SIGIO support to use code from tty_io.  9Sept95 ctm@ardi.com
22      *
23      * Modularised 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
24      */
25     
26     #include <linux/module.h>
27     #include <linux/kernel.h>
28     #include <linux/sched.h>
29     #include <linux/interrupt.h>
30     #include <linux/fcntl.h>
31     #include <linux/errno.h>
32     #include <linux/timer.h>
33     #include <linux/slab.h>
34     #include <linux/miscdevice.h>
35     #include <linux/random.h>
36     #include <linux/poll.h>
37     #include <linux/init.h>
38     #include <linux/smp_lock.h>
39     
40     #include <asm/io.h>
41     #include <asm/uaccess.h>
42     #include <asm/system.h>
43     #include <asm/semaphore.h>
44     
45     #include <linux/pc_keyb.h>		/* mouse enable command.. */
46     
47     
48     /*
49      * We use the same minor number as the PS/2 mouse for (bad) historical
50      * reasons..
51      */
52     #define PSMOUSE_MINOR      1	       		/* Minor device # for this mouse */
53     #define QP_BUF_SIZE	2048
54     
55     struct qp_queue {
56     	unsigned long head;
57     	unsigned long tail;
58     	wait_queue_head_t proc_list;
59     	struct fasync_struct *fasync;
60     	unsigned char buf[QP_BUF_SIZE];
61     };
62     
63     static struct qp_queue *queue;
64     
65     static unsigned int get_from_queue(void)
66     {
67     	unsigned int result;
68     	unsigned long flags;
69     
70     	save_flags(flags);
71     	cli();
72     	result = queue->buf[queue->tail];
73     	queue->tail = (queue->tail + 1) & (QP_BUF_SIZE-1);
74     	restore_flags(flags);
75     	return result;
76     }
77     
78     
79     static inline int queue_empty(void)
80     {
81     	return queue->head == queue->tail;
82     }
83     
84     static int fasync_qp(int fd, struct file *filp, int on)
85     {
86     	int retval;
87     
88     	retval = fasync_helper(fd, filp, on, &queue->fasync);
89     	if (retval < 0)
90     		return retval;
91     	return 0;
92     }
93     
94     /*
95      *	82C710 Interface
96      */
97     
98     #define QP_DATA         0x310		/* Data Port I/O Address */
99     #define QP_STATUS       0x311		/* Status Port I/O Address */
100     
101     #define QP_DEV_IDLE     0x01		/* Device Idle */
102     #define QP_RX_FULL      0x02		/* Device Char received */
103     #define QP_TX_IDLE      0x04		/* Device XMIT Idle */
104     #define QP_RESET        0x08		/* Device Reset */
105     #define QP_INTS_ON      0x10		/* Device Interrupt On */
106     #define QP_ERROR_FLAG   0x20		/* Device Error */
107     #define QP_CLEAR        0x40		/* Device Clear */
108     #define QP_ENABLE       0x80		/* Device Enable */
109     
110     #define QP_IRQ          12
111     
112     static int qp_present;
113     static int qp_count;
114     static int qp_data = QP_DATA;
115     static int qp_status = QP_STATUS;
116     
117     static int poll_qp_status(void);
118     static int probe_qp(void);
119     
120     /*
121      * Interrupt handler for the 82C710 mouse port. A character
122      * is waiting in the 82C710.
123      */
124     
125     static void qp_interrupt(int cpl, void *dev_id, struct pt_regs * regs)
126     {
127     	int head = queue->head;
128     	int maxhead = (queue->tail-1) & (QP_BUF_SIZE-1);
129     
130     	add_mouse_randomness(queue->buf[head] = inb(qp_data));
131     	if (head != maxhead) {
132     		head++;
133     		head &= QP_BUF_SIZE-1;
134     	}
135     	queue->head = head;
136     	kill_fasync(&queue->fasync, SIGIO, POLL_IN);
137     	wake_up_interruptible(&queue->proc_list);
138     }
139     
140     static int release_qp(struct inode * inode, struct file * file)
141     {
142     	unsigned char status;
143     
144     	lock_kernel();
145     	fasync_qp(-1, file, 0);
146     	if (!--qp_count) {
147     		if (!poll_qp_status())
148     			printk(KERN_WARNING "Warning: Mouse device busy in release_qp()\n");
149     		status = inb_p(qp_status);
150     		outb_p(status & ~(QP_ENABLE|QP_INTS_ON), qp_status);
151     		if (!poll_qp_status())
152     			printk(KERN_WARNING "Warning: Mouse device busy in release_qp()\n");
153     		free_irq(QP_IRQ, NULL);
154     	}
155     	unlock_kernel();
156     	return 0;
157     }
158     
159     /*
160      * Install interrupt handler.
161      * Enable the device, enable interrupts. 
162      */
163     
164     static int open_qp(struct inode * inode, struct file * file)
165     {
166     	unsigned char status;
167     
168     	if (!qp_present)
169     		return -EINVAL;
170     
171     	if (qp_count++)
172     		return 0;
173     
174     	if (request_irq(QP_IRQ, qp_interrupt, 0, "PS/2 Mouse", NULL)) {
175     		qp_count--;
176     		return -EBUSY;
177     	}
178     
179     	status = inb_p(qp_status);
180     	status |= (QP_ENABLE|QP_RESET);
181     	outb_p(status, qp_status);
182     	status &= ~(QP_RESET);
183     	outb_p(status, qp_status);
184     
185     	queue->head = queue->tail = 0;          /* Flush input queue */
186     	status |= QP_INTS_ON;
187     	outb_p(status, qp_status);              /* Enable interrupts */
188     
189     	while (!poll_qp_status()) {
190     		printk(KERN_ERR "Error: Mouse device busy in open_qp()\n");
191     		qp_count--;
192     		status &= ~(QP_ENABLE|QP_INTS_ON);
193     		outb_p(status, qp_status);
194     		free_irq(QP_IRQ, NULL);
195     		return -EBUSY;
196     	}
197     
198     	outb_p(AUX_ENABLE_DEV, qp_data);	/* Wake up mouse */
199     	return 0;
200     }
201     
202     /*
203      * Write to the 82C710 mouse device.
204      */
205     
206     static ssize_t write_qp(struct file * file, const char * buffer,
207     			size_t count, loff_t *ppos)
208     {
209     	ssize_t i = count;
210     
211     	while (i--) {
212     		char c;
213     		if (!poll_qp_status())
214     			return -EIO;
215     		get_user(c, buffer++);
216     		outb_p(c, qp_data);
217     	}
218     	file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
219     	return count;
220     }
221     
222     static unsigned int poll_qp(struct file *file, poll_table * wait)
223     {
224     	poll_wait(file, &queue->proc_list, wait);
225     	if (!queue_empty())
226     		return POLLIN | POLLRDNORM;
227     	return 0;
228     }
229     
230     /*
231      * Wait for device to send output char and flush any input char.
232      */
233     
234     #define MAX_RETRIES (60)
235     
236     static int poll_qp_status(void)
237     {
238     	int retries=0;
239     
240     	while ((inb(qp_status)&(QP_RX_FULL|QP_TX_IDLE|QP_DEV_IDLE))
241     		       != (QP_DEV_IDLE|QP_TX_IDLE)
242     		       && retries < MAX_RETRIES) {
243     
244     		if (inb_p(qp_status)&(QP_RX_FULL))
245     			inb_p(qp_data);
246     		current->state = TASK_INTERRUPTIBLE;
247     		schedule_timeout((5*HZ + 99) / 100);
248     		retries++;
249     	}
250     	return !(retries==MAX_RETRIES);
251     }
252     
253     /*
254      * Put bytes from input queue to buffer.
255      */
256     
257     static ssize_t read_qp(struct file * file, char * buffer,
258     			size_t count, loff_t *ppos)
259     {
260     	DECLARE_WAITQUEUE(wait, current);
261     	ssize_t i = count;
262     	unsigned char c;
263     
264     	if (queue_empty()) {
265     		if (file->f_flags & O_NONBLOCK)
266     			return -EAGAIN;
267     		add_wait_queue(&queue->proc_list, &wait);
268     repeat:
269     		set_current_state(TASK_INTERRUPTIBLE);
270     		if (queue_empty() && !signal_pending(current)) {
271     			schedule();
272     			goto repeat;
273     		}
274     		current->state = TASK_RUNNING;
275     		remove_wait_queue(&queue->proc_list, &wait);
276     	}
277     	while (i > 0 && !queue_empty()) {
278     		c = get_from_queue();
279     		put_user(c, buffer++);
280     		i--;
281     	}
282     	if (count-i) {
283     		file->f_dentry->d_inode->i_atime = CURRENT_TIME;
284     		return count-i;
285     	}
286     	if (signal_pending(current))
287     		return -ERESTARTSYS;
288     	return 0;
289     }
290     
291     struct file_operations qp_fops = {
292     	owner:		THIS_MODULE,
293     	read:		read_qp,
294     	write:		write_qp,
295     	poll:		poll_qp,
296     	open:		open_qp,
297     	release:	release_qp,
298     	fasync:		fasync_qp,
299     };
300     
301     /*
302      * Initialize driver.
303      */
304     static struct miscdevice qp_mouse = {
305     	minor:		PSMOUSE_MINOR,
306     	name:		"QPmouse",
307     	fops:		&qp_fops,
308     };
309     
310     /*
311      * Function to read register in 82C710.
312      */
313     
314     static inline unsigned char read_710(unsigned char index)
315     {
316     	outb_p(index, 0x390);			/* Write index */
317     	return inb_p(0x391);			/* Read the data */
318     }
319     
320     
321     /*
322      * See if we can find a 82C710 device. Read mouse address.
323      */
324     
325     static int __init probe_qp(void)
326     {
327     	outb_p(0x55, 0x2fa);			/* Any value except 9, ff or 36 */
328     	outb_p(0xaa, 0x3fa);			/* Inverse of 55 */
329     	outb_p(0x36, 0x3fa);			/* Address the chip */
330     	outb_p(0xe4, 0x3fa);			/* 390/4; 390 = config address */
331     	outb_p(0x1b, 0x2fa);			/* Inverse of e4 */
332     	if (read_710(0x0f) != 0xe4)		/* Config address found? */
333     	  return 0;				/* No: no 82C710 here */
334     	qp_data = read_710(0x0d)*4;		/* Get mouse I/O address */
335     	qp_status = qp_data+1;
336     	outb_p(0x0f, 0x390);
337     	outb_p(0x0f, 0x391);			/* Close config mode */
338     	return 1;
339     }
340     
341     static char msg_banner[] __initdata = KERN_INFO "82C710 type pointing device detected -- driver installed.\n";
342     static char msg_nomem[]  __initdata = KERN_ERR "qpmouse: no queue memory.\n";
343     
344     static int __init qpmouse_init_driver(void)
345     {
346     	if (!probe_qp())
347     		return -EIO;
348     
349     	printk(msg_banner);
350     
351     /*	printk("82C710 address = %x (should be 0x310)\n", qp_data); */
352     	queue = (struct qp_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
353     	if (queue == NULL) {
354     		printk(msg_nomem);
355     		return -ENOMEM;
356     	}
357     	qp_present = 1;
358     	misc_register(&qp_mouse);
359     	memset(queue, 0, sizeof(*queue));
360     	queue->head = queue->tail = 0;
361     	init_waitqueue_head(&queue->proc_list);
362     	return 0;
363     }
364     
365     static void __exit qpmouse_exit_driver(void)
366     {
367     	misc_deregister(&qp_mouse);
368     	kfree(queue);
369     }
370     
371     module_init(qpmouse_init_driver);
372     module_exit(qpmouse_exit_driver);
373     
374     
375     MODULE_LICENSE("GPL");
376     EXPORT_NO_SYMBOLS;
377