1
2
3
4#include <linux/sched.h>
5#include <linux/interrupt.h>
6#include <linux/tty.h>
7#include <linux/tty_flip.h>
8#include <linux/mm.h>
9#include <linux/slab.h>
10#include <linux/ptrace.h>
11#include <linux/signal.h>
12#include <linux/timer.h>
13#include <linux/random.h>
14#include <linux/ctype.h>
15#include <linux/kbd_ll.h>
16#include <linux/delay.h>
17#include <linux/init.h>
18#include <linux/poll.h>
19#include <linux/miscdevice.h>
20
21#include <asm/bitops.h>
22#include <asm/irq.h>
23#include <asm/hardware.h>
24#include <asm/io.h>
25#include <asm/hardware/iomd.h>
26#include <asm/system.h>
27#include <asm/uaccess.h>
28
29
30
31
32
33static struct aux_queue *queue;
34static int aux_count = 0;
35
36static unsigned char mouse_reply_expected = 0;
37
38#define MAX_RETRIES 60
39
40
41
42
43
44#define AUX_SET_RES 0xE8
45#define AUX_SET_SCALE11 0xE6
46#define AUX_SET_SCALE21 0xE7
47#define AUX_GET_SCALE 0xE9
48#define AUX_SET_STREAM 0xEA
49#define AUX_SET_SAMPLE 0xF3
50#define AUX_ENABLE_DEV 0xF4
51#define AUX_DISABLE_DEV 0xF5
52#define AUX_RESET 0xFF
53#define AUX_ACK 0xFA
54
55#define AUX_BUF_SIZE 2048
56
57
58
59
60struct aux_queue {
61 unsigned long head;
62 unsigned long tail;
63 wait_queue_head_t proc_list;
64 struct fasync_struct *fasync;
65 unsigned char buf[AUX_BUF_SIZE];
66};
67
68
69
70
71static void aux_write_dev(int val)
72{
73 while (!(iomd_readb(IOMD_MSECTL) & 0x80));
74 iomd_writeb(val, IOMD_MSEDAT);
75}
76
77
78
79
80static void aux_write_ack(int val)
81{
82 while (!(iomd_readb(IOMD_MSECTL) & 0x80));
83 iomd_writeb(val, IOMD_MSEDAT);
84
85
86 mouse_reply_expected++;
87}
88
89static unsigned char get_from_queue(void)
90{
91 unsigned char result;
92
93 result = queue->buf[queue->tail];
94 queue->tail = (queue->tail + 1) & (AUX_BUF_SIZE-1);
95 return result;
96}
97
98static void psaux_interrupt(int irq, void *dev_id, struct pt_regs *regs)
99{
100 int val = iomd_readb(IOMD_MSEDAT);
101
102 if (mouse_reply_expected) {
103 if (val == AUX_ACK) {
104 mouse_reply_expected--;
105 return;
106 }
107 mouse_reply_expected = 0;
108 }
109
110 add_mouse_randomness(val);
111 if (aux_count) {
112 int head = queue->head;
113
114 queue->buf[head] = val;
115 head = (head + 1) & (AUX_BUF_SIZE-1);
116 if (head != queue->tail) {
117 queue->head = head;
118 kill_fasync(&queue->fasync, SIGIO, POLL_IN);
119 wake_up_interruptible(&queue->proc_list);
120 }
121 }
122}
123
124static inline int queue_empty(void)
125{
126 return queue->head == queue->tail;
127}
128
129static int fasync_aux(int fd, struct file *filp, int on)
130{
131 int retval;
132
133 retval = fasync_helper(fd, filp, on, &queue->fasync);
134 if (retval < 0)
135 return retval;
136 return 0;
137}
138
139
140
141
142
143#define AUX_DEV ((void *)queue)
144
145static int release_aux(struct inode * inode, struct file * file)
146{
147 fasync_aux(-1, file, 0);
148 if (--aux_count)
149 return 0;
150 free_irq(IRQ_MOUSERX, AUX_DEV);
151 return 0;
152}
153
154
155
156
157
158
159static int open_aux(struct inode * inode, struct file * file)
160{
161 if (aux_count++)
162 return 0;
163
164 queue->head = queue->tail = 0;
165 if (request_irq(IRQ_MOUSERX, psaux_interrupt, SA_SHIRQ, "ps/2 mouse",
166 AUX_DEV)) {
167 aux_count--;
168 return -EBUSY;
169 }
170
171 aux_write_ack(AUX_ENABLE_DEV);
172
173 return 0;
174}
175
176
177
178
179
180static ssize_t read_aux(struct file * file, char * buffer,
181 size_t count, loff_t *ppos)
182{
183 DECLARE_WAITQUEUE(wait, current);
184 ssize_t i = count;
185 unsigned char c;
186
187 if (queue_empty()) {
188 if (file->f_flags & O_NONBLOCK)
189 return -EAGAIN;
190 add_wait_queue(&queue->proc_list, &wait);
191repeat:
192 current->state = TASK_INTERRUPTIBLE;
193 if (queue_empty() && !signal_pending(current)) {
194 schedule();
195 goto repeat;
196 }
197 current->state = TASK_RUNNING;
198 remove_wait_queue(&queue->proc_list, &wait);
199 }
200 while (i > 0 && !queue_empty()) {
201 c = get_from_queue();
202 put_user(c, buffer++);
203 i--;
204 }
205 if (count-i) {
206 file->f_dentry->d_inode->i_atime = CURRENT_TIME;
207 return count-i;
208 }
209 if (signal_pending(current))
210 return -ERESTARTSYS;
211 return 0;
212}
213
214
215
216
217
218static ssize_t write_aux(struct file * file, const char * buffer,
219 size_t count, loff_t *ppos)
220{
221 ssize_t retval = 0;
222
223 if (count) {
224 ssize_t written = 0;
225
226 if (count > 32)
227 count = 32;
228 do {
229 char c;
230 get_user(c, buffer++);
231 aux_write_dev(c);
232 written++;
233 } while (--count);
234 retval = -EIO;
235 if (written) {
236 retval = written;
237 file->f_dentry->d_inode->i_mtime = CURRENT_TIME;
238 }
239 }
240
241 return retval;
242}
243
244static unsigned int aux_poll(struct file *file, poll_table * wait)
245{
246 poll_wait(file, &queue->proc_list, wait);
247 if (!queue_empty())
248 return POLLIN | POLLRDNORM;
249 return 0;
250}
251
252struct file_operations psaux_fops = {
253 read: read_aux,
254 write: write_aux,
255 poll: aux_poll,
256 open: open_aux,
257 release: release_aux,
258 fasync: fasync_aux,
259};
260
261
262
263
264static struct miscdevice psaux_mouse = {
265 PSMOUSE_MINOR, "psaux", &psaux_fops
266};
267
268int __init psaux_init(void)
269{
270
271 iomd_writeb(0, IOMD_MSECTL);
272 iomd_writeb(8, IOMD_MSECTL);
273
274 queue = (struct aux_queue *) kmalloc(sizeof(*queue), GFP_KERNEL);
275 if (queue == NULL)
276 return -ENOMEM;
277
278 if (misc_register(&psaux_mouse)) {
279 kfree(queue);
280 return -ENODEV;
281 }
282
283 memset(queue, 0, sizeof(*queue));
284 queue->head = queue->tail = 0;
285 init_waitqueue_head(&queue->proc_list);
286
287 aux_write_ack(AUX_SET_SAMPLE);
288 aux_write_ack(100);
289 aux_write_ack(AUX_SET_RES);
290 aux_write_ack(3);
291 aux_write_ack(AUX_SET_SCALE21);
292
293 return 0;
294}
295
296module_init(psaux_init);
297
298