ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / char / busmouse.c
1 /*
2  * linux/drivers/char/busmouse.c
3  *
4  * Copyright (C) 1995 - 1998 Russell King <linux@arm.linux.org.uk>
5  *  Protocol taken from original busmouse.c
6  *  read() waiting taken from psaux.c
7  *
8  * Medium-level interface for quadrature or bus mice.
9  */
10
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/sched.h>
14 #include <linux/signal.h>
15 #include <linux/slab.h>
16 #include <linux/errno.h>
17 #include <linux/mm.h>
18 #include <linux/poll.h>
19 #include <linux/miscdevice.h>
20 #include <linux/random.h>
21 #include <linux/init.h>
22 #include <linux/smp_lock.h>
23
24 #include <asm/uaccess.h>
25 #include <asm/system.h>
26 #include <asm/io.h>
27
28 #include "busmouse.h"
29
30 /* Uncomment this if your mouse drivers expect the kernel to
31  * return with EAGAIN if the mouse does not have any events
32  * available, even if the mouse is opened in blocking mode.
33  * Please report use of this "feature" to the author using the
34  * above address.
35  */
36 /*#define BROKEN_MOUSE*/
37
38 struct busmouse_data {
39         struct miscdevice       miscdev;
40         struct busmouse         *ops;
41         spinlock_t              lock;
42
43         wait_queue_head_t       wait;
44         struct fasync_struct    *fasyncptr;
45         char                    active;
46         char                    buttons;
47         char                    ready;
48         int                     dxpos;
49         int                     dypos;
50 };
51
52 #define NR_MICE                 15
53 #define FIRST_MOUSE             0
54 #define DEV_TO_MOUSE(inode)     MINOR_TO_MOUSE(iminor(inode))
55 #define MINOR_TO_MOUSE(minor)   ((minor) - FIRST_MOUSE)
56
57 /*
58  *      List of mice and guarding semaphore. You must take the semaphore
59  *      before you take the misc device semaphore if you need both
60  */
61  
62 static struct busmouse_data *busmouse_data[NR_MICE];
63 static DECLARE_MUTEX(mouse_sem);
64
65 /**
66  *      busmouse_add_movement - notification of a change of mouse position
67  *      @mousedev: mouse number
68  *      @dx: delta X movement
69  *      @dy: delta Y movement
70  *      @buttons: new button state
71  *
72  *      Updates the mouse position and button information. The mousedev
73  *      parameter is the value returned from register_busmouse. The
74  *      movement information is updated, and the new button state is
75  *      saved.  A waiting user thread is woken.
76  */
77  
78 void busmouse_add_movementbuttons(int mousedev, int dx, int dy, int buttons)
79 {
80         struct busmouse_data *mse = busmouse_data[mousedev];
81         int changed;
82
83         spin_lock(&mse->lock);
84         changed = (dx != 0 || dy != 0 || mse->buttons != buttons);
85
86         if (changed) {
87                 add_mouse_randomness((buttons << 16) + (dy << 8) + dx);
88
89                 mse->buttons = buttons;
90                 mse->dxpos += dx;
91                 mse->dypos += dy;
92                 mse->ready = 1;
93
94                 /*
95                  * keep dx/dy reasonable, but still able to track when X (or
96                  * whatever) must page or is busy (i.e. long waits between
97                  * reads)
98                  */
99                 if (mse->dxpos < -2048)
100                         mse->dxpos = -2048;
101                 if (mse->dxpos > 2048)
102                         mse->dxpos = 2048;
103                 if (mse->dypos < -2048)
104                         mse->dypos = -2048;
105                 if (mse->dypos > 2048)
106                         mse->dypos = 2048;
107         }
108
109         spin_unlock(&mse->lock);
110
111         if (changed) {
112                 wake_up(&mse->wait);
113
114                 kill_fasync(&mse->fasyncptr, SIGIO, POLL_IN);
115         }
116 }
117
118 /**
119  *      busmouse_add_movement - notification of a change of mouse position
120  *      @mousedev: mouse number
121  *      @dx: delta X movement
122  *      @dy: delta Y movement
123  *
124  *      Updates the mouse position. The mousedev parameter is the value
125  *      returned from register_busmouse. The movement information is
126  *      updated, and a waiting user thread is woken.
127  */
128  
129 void busmouse_add_movement(int mousedev, int dx, int dy)
130 {
131         struct busmouse_data *mse = busmouse_data[mousedev];
132
133         busmouse_add_movementbuttons(mousedev, dx, dy, mse->buttons);
134 }
135
136 /**
137  *      busmouse_add_buttons - notification of a change of button state
138  *      @mousedev: mouse number
139  *      @clear: mask of buttons to clear
140  *      @eor: mask of buttons to change
141  *
142  *      Updates the button state. The mousedev parameter is the value
143  *      returned from register_busmouse. The buttons are updated by:
144  *              new_state = (old_state & ~clear) ^ eor
145  *      A waiting user thread is woken up.
146  */
147  
148 void busmouse_add_buttons(int mousedev, int clear, int eor)
149 {
150         struct busmouse_data *mse = busmouse_data[mousedev];
151
152         busmouse_add_movementbuttons(mousedev, 0, 0, (mse->buttons & ~clear) ^ eor);
153 }
154
155 static int busmouse_fasync(int fd, struct file *filp, int on)
156 {
157         struct busmouse_data *mse = (struct busmouse_data *)filp->private_data;
158         int retval;
159
160         retval = fasync_helper(fd, filp, on, &mse->fasyncptr);
161         if (retval < 0)
162                 return retval;
163         return 0;
164 }
165
166 static int busmouse_release(struct inode *inode, struct file *file)
167 {
168         struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
169         int ret = 0;
170
171         lock_kernel();
172         busmouse_fasync(-1, file, 0);
173
174         down(&mouse_sem); /* to protect mse->active */
175         if (--mse->active == 0) {
176                 if (mse->ops->release)
177                         ret = mse->ops->release(inode, file);
178                 module_put(mse->ops->owner);
179                 mse->ready = 0;
180         }
181         unlock_kernel();
182         up( &mouse_sem);
183         
184         return ret;
185 }
186
187 static int busmouse_open(struct inode *inode, struct file *file)
188 {
189         struct busmouse_data *mse;
190         unsigned int mousedev;
191         int ret;
192
193         mousedev = DEV_TO_MOUSE(inode);
194         if (mousedev >= NR_MICE)
195                 return -EINVAL;
196
197         down(&mouse_sem);
198         mse = busmouse_data[mousedev];
199         ret = -ENODEV;
200         if (!mse || !mse->ops)  /* shouldn't happen, but... */
201                 goto end;
202
203         if (!try_module_get(mse->ops->owner))
204                 goto end;
205
206         ret = 0;
207         if (mse->ops->open) {
208                 ret = mse->ops->open(inode, file);
209                 if (ret)
210                         module_put(mse->ops->owner);
211         }
212
213         if (ret)
214                 goto end;
215
216         file->private_data = mse;
217
218         if (mse->active++)
219                 goto end;
220
221         spin_lock_irq(&mse->lock);
222
223         mse->ready   = 0;
224         mse->dxpos   = 0;
225         mse->dypos   = 0;
226         mse->buttons = mse->ops->init_button_state;
227
228         spin_unlock_irq(&mse->lock);
229 end:
230         up(&mouse_sem);
231         return ret;
232 }
233
234 static ssize_t busmouse_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
235 {
236         return -EINVAL;
237 }
238
239 static ssize_t busmouse_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
240 {
241         struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
242         DECLARE_WAITQUEUE(wait, current);
243         int dxpos, dypos, buttons;
244
245         if (count < 3)
246                 return -EINVAL;
247
248         spin_lock_irq(&mse->lock);
249
250         if (!mse->ready) {
251 #ifdef BROKEN_MOUSE
252                 spin_unlock_irq(&mse->lock);
253                 return -EAGAIN;
254 #else
255                 if (file->f_flags & O_NONBLOCK) {
256                         spin_unlock_irq(&mse->lock);
257                         return -EAGAIN;
258                 }
259
260                 add_wait_queue(&mse->wait, &wait);
261 repeat:
262                 set_current_state(TASK_INTERRUPTIBLE);
263                 if (!mse->ready && !signal_pending(current)) {
264                         spin_unlock_irq(&mse->lock);
265                         schedule();
266                         spin_lock_irq(&mse->lock);
267                         goto repeat;
268                 }
269
270                 current->state = TASK_RUNNING;
271                 remove_wait_queue(&mse->wait, &wait);
272
273                 if (signal_pending(current)) {
274                         spin_unlock_irq(&mse->lock);
275                         return -ERESTARTSYS;
276                 }
277 #endif
278         }
279
280         dxpos = mse->dxpos;
281         dypos = mse->dypos;
282         buttons = mse->buttons;
283
284         if (dxpos < -127)
285                 dxpos =- 127;
286         if (dxpos > 127)
287                 dxpos = 127;
288         if (dypos < -127)
289                 dypos =- 127;
290         if (dypos > 127)
291                 dypos = 127;
292
293         mse->dxpos -= dxpos;
294         mse->dypos -= dypos;
295
296         /* This is something that many drivers have apparantly
297          * forgotten...  If the X and Y positions still contain
298          * information, we still have some info ready for the
299          * user program...
300          */
301         mse->ready = mse->dxpos || mse->dypos;
302
303         spin_unlock_irq(&mse->lock);
304
305         /* Write out data to the user.  Format is:
306          *   byte 0 - identifer (0x80) and (inverted) mouse buttons
307          *   byte 1 - X delta position +/- 127
308          *   byte 2 - Y delta position +/- 127
309          */
310         if (put_user((char)buttons | 128, buffer) ||
311             put_user((char)dxpos, buffer + 1) ||
312             put_user((char)dypos, buffer + 2))
313                 return -EFAULT;
314
315         if (count > 3 && clear_user(buffer + 3, count - 3))
316                 return -EFAULT;
317
318         file->f_dentry->d_inode->i_atime = CURRENT_TIME;
319
320         return count;
321 }
322
323 /* No kernel lock held - fine */
324 static unsigned int busmouse_poll(struct file *file, poll_table *wait)
325 {
326         struct busmouse_data *mse = (struct busmouse_data *)file->private_data;
327
328         poll_wait(file, &mse->wait, wait);
329
330         if (mse->ready)
331                 return POLLIN | POLLRDNORM;
332
333         return 0;
334 }
335
336 struct file_operations busmouse_fops=
337 {
338         .owner          = THIS_MODULE,
339         .read           = busmouse_read,
340         .write          = busmouse_write,
341         .poll           = busmouse_poll,
342         .open           = busmouse_open,
343         .release        = busmouse_release,
344         .fasync         = busmouse_fasync,
345 };
346
347 /**
348  *      register_busmouse - register a bus mouse interface
349  *      @ops: busmouse structure for the mouse
350  *
351  *      Registers a mouse with the driver. The return is mouse number on
352  *      success and a negative errno code on an error. The passed ops
353  *      structure most not be freed until the mouser is unregistered
354  */
355  
356 int register_busmouse(struct busmouse *ops)
357 {
358         unsigned int msedev = MINOR_TO_MOUSE(ops->minor);
359         struct busmouse_data *mse;
360         int ret = -EINVAL;
361
362         if (msedev >= NR_MICE) {
363                 printk(KERN_ERR "busmouse: trying to allocate mouse on minor %d\n",
364                        ops->minor);
365                 goto out;
366         }
367
368         ret = -ENOMEM;
369         mse = kmalloc(sizeof(*mse), GFP_KERNEL);
370         if (!mse)
371                 goto out;
372
373         down(&mouse_sem);
374         ret = -EBUSY;
375         if (busmouse_data[msedev])
376                 goto freemem;
377
378         memset(mse, 0, sizeof(*mse));
379
380         mse->miscdev.minor = ops->minor;
381         mse->miscdev.name = ops->name;
382         mse->miscdev.fops = &busmouse_fops;
383         mse->ops = ops;
384         mse->lock = (spinlock_t)SPIN_LOCK_UNLOCKED;
385         init_waitqueue_head(&mse->wait);
386
387
388         ret = misc_register(&mse->miscdev);
389
390         if (ret < 0) 
391                 goto freemem;
392
393         busmouse_data[msedev] = mse;
394         ret = msedev;
395 out:
396         up(&mouse_sem);
397         return ret;
398
399
400 freemem:
401         kfree(mse);
402         goto out;
403 }
404
405 /**
406  *      unregister_busmouse - unregister a bus mouse interface
407  *      @mousedev: Mouse number to release
408  *
409  *      Unregister a previously installed mouse handler. The mousedev
410  *      passed is the return code from a previous call to register_busmouse
411  */
412  
413
414 int unregister_busmouse(int mousedev)
415 {
416         int err = -EINVAL;
417
418         if (mousedev < 0)
419                 return 0;
420         if (mousedev >= NR_MICE) {
421                 printk(KERN_ERR "busmouse: trying to free mouse on"
422                        " mousedev %d\n", mousedev);
423                 return -EINVAL;
424         }
425
426         down(&mouse_sem);
427         
428         if (!busmouse_data[mousedev]) {
429                 printk(KERN_WARNING "busmouse: trying to free free mouse"
430                        " on mousedev %d\n", mousedev);
431                 goto fail;
432         }
433
434         if (busmouse_data[mousedev]->active) {
435                 printk(KERN_ERR "busmouse: trying to free active mouse"
436                        " on mousedev %d\n", mousedev);
437                 goto fail;
438         }
439
440         err = misc_deregister(&busmouse_data[mousedev]->miscdev);
441
442         kfree(busmouse_data[mousedev]);
443         busmouse_data[mousedev] = NULL;
444 fail:
445         up(&mouse_sem);
446         return err;
447 }
448
449 EXPORT_SYMBOL(busmouse_add_movementbuttons);
450 EXPORT_SYMBOL(busmouse_add_movement);
451 EXPORT_SYMBOL(busmouse_add_buttons);
452 EXPORT_SYMBOL(register_busmouse);
453 EXPORT_SYMBOL(unregister_busmouse);
454
455 MODULE_ALIAS_MISCDEV(BUSMOUSE_MINOR);
456 MODULE_LICENSE("GPL");