1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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>
46
47
48
49
50
51
52#define PSMOUSE_MINOR 1
53#define QP_BUF_SIZE 2048
54
55struct 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
63static struct qp_queue *queue;
64
65static 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
79static inline int queue_empty(void)
80{
81 return queue->head == queue->tail;
82}
83
84static 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
96
97
98#define QP_DATA 0x310
99#define QP_STATUS 0x311
100
101#define QP_DEV_IDLE 0x01
102#define QP_RX_FULL 0x02
103#define QP_TX_IDLE 0x04
104#define QP_RESET 0x08
105#define QP_INTS_ON 0x10
106#define QP_ERROR_FLAG 0x20
107#define QP_CLEAR 0x40
108#define QP_ENABLE 0x80
109
110#define QP_IRQ 12
111
112static int qp_present;
113static int qp_count;
114static int qp_data = QP_DATA;
115static int qp_status = QP_STATUS;
116
117static int poll_qp_status(void);
118static int probe_qp(void);
119
120
121
122
123
124
125static 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
140static 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
161
162
163
164static 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;
186 status |= QP_INTS_ON;
187 outb_p(status, qp_status);
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);
199 return 0;
200}
201
202
203
204
205
206static 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
222static 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
232
233
234#define MAX_RETRIES (60)
235
236static 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
255
256
257static 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);
268repeat:
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
291struct 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
303
304static struct miscdevice qp_mouse = {
305 minor: PSMOUSE_MINOR,
306 name: "QPmouse",
307 fops: &qp_fops,
308};
309
310
311
312
313
314static inline unsigned char read_710(unsigned char index)
315{
316 outb_p(index, 0x390);
317 return inb_p(0x391);
318}
319
320
321
322
323
324
325static int __init probe_qp(void)
326{
327 outb_p(0x55, 0x2fa);
328 outb_p(0xaa, 0x3fa);
329 outb_p(0x36, 0x3fa);
330 outb_p(0xe4, 0x3fa);
331 outb_p(0x1b, 0x2fa);
332 if (read_710(0x0f) != 0xe4)
333 return 0;
334 qp_data = read_710(0x0d)*4;
335 qp_status = qp_data+1;
336 outb_p(0x0f, 0x390);
337 outb_p(0x0f, 0x391);
338 return 1;
339}
340
341static char msg_banner[] __initdata = KERN_INFO "82C710 type pointing device detected -- driver installed.\n";
342static char msg_nomem[] __initdata = KERN_ERR "qpmouse: no queue memory.\n";
343
344static int __init qpmouse_init_driver(void)
345{
346 if (!probe_qp())
347 return -EIO;
348
349 printk(msg_banner);
350
351
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
365static void __exit qpmouse_exit_driver(void)
366{
367 misc_deregister(&qp_mouse);
368 kfree(queue);
369}
370
371module_init(qpmouse_init_driver);
372module_exit(qpmouse_exit_driver);
373
374
375MODULE_LICENSE("GPL");
376EXPORT_NO_SYMBOLS;
377