~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/char/qpmouse.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.