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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [qpmouse.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
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;

powered by: WebSVN 2.1.0

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