import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / sgi / char / shmiq.c
1 /*
2  * shmiq.c: shared memory input queue driver
3  * written 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
4  *
5  * We implement /dev/shmiq, /dev/qcntlN here
6  * this is different from IRIX that has shmiq as a misc
7  * streams device and the and qcntl devices as a major device.
8  *
9  * minor number 0 implements /dev/shmiq,
10  * any other number implements /dev/qcntl${minor-1}
11  *
12  * /dev/shmiq is used by the X server for two things:
13  *
14  *    1. for I_LINK()ing trough ioctl the file handle of a
15  *       STREAMS device.
16  *
17  *    2. To send STREAMS-commands to the devices with the
18  *       QIO ioctl interface.
19  *
20  * I have not yet figured how to make multiple X servers share
21  * /dev/shmiq for having different servers running.  So, for now
22  * I keep a kernel-global array of inodes that are pushed into
23  * /dev/shmiq.
24  *
25  * /dev/qcntlN is used by the X server for two things:
26  *
27  *    1. Issuing the QIOCATTACH for mapping the shared input
28  *       queue into the address space of the X server (yeah, yeah,
29  *       I did not invent this interface).
30  *
31  *    2. used by select.  I bet it is used for checking for events on
32  *       the queue.
33  *
34  * Now the problem is that there does not seem anything that
35  * establishes a connection between /dev/shmiq and the qcntlN file.  I
36  * need an strace from an X server that runs on a machine with more
37  * than one keyboard.  And this is a problem since the file handles
38  * are pushed in /dev/shmiq, while the events should be dispatched to
39  * the /dev/qcntlN device.
40  *
41  * Until then, I just allow for 1 qcntl device.
42  *
43  */
44 #include <linux/fs.h>
45 #include <linux/miscdevice.h>
46 #include <linux/sched.h>
47 #include <linux/file.h>
48 #include <linux/interrupt.h>
49 #include <linux/poll.h>
50 #include <linux/vmalloc.h>
51 #include <linux/wait.h>
52 #include <linux/major.h>
53 #include <linux/module.h>
54 #include <linux/smp_lock.h>
55 #include <linux/devfs_fs_kernel.h>
56
57 #include <asm/shmiq.h>
58 #include <asm/gfx.h>
59 #include <asm/mman.h>
60 #include <asm/uaccess.h>
61 #include <asm/poll.h>
62 #include "graphics.h"
63
64 /* we are not really getting any more than a few files in the shmiq */
65 #define MAX_SHMIQ_DEVS 10
66
67 /*
68  * One per X server running, not going to get very big.
69  * Even if we have this we now assume just 1 /dev/qcntl can be
70  * active, I need to find how this works on multi-headed machines.
71  */
72 #define MAX_SHMI_QUEUES 4
73
74 static struct {
75         int                 used;
76         struct file         *filp;
77         struct shmiqsetcpos cpos;
78 } shmiq_pushed_devices [MAX_SHMIQ_DEVS];
79
80 /* /dev/qcntlN attached memory regions, location and size of the event queue */
81 static struct {
82         int    opened;          /* if this device has been opened */
83         void   *shmiq_vaddr;    /* mapping in kernel-land */
84         int    tail;            /* our copy of the shmiq->tail */
85         int    events;
86         int    mapped;
87
88         wait_queue_head_t    proc_list;
89         struct fasync_struct *fasync;
90 } shmiqs [MAX_SHMI_QUEUES];
91
92 void
93 shmiq_push_event (struct shmqevent *e)
94 {
95         struct sharedMemoryInputQueue *s;
96         int    device = 0;      /* FIXME: here is the assumption /dev/shmiq == /dev/qcntl0 */
97         int    tail_next;
98
99         if (!shmiqs [device].mapped)
100                 return;
101         s = shmiqs [device].shmiq_vaddr;
102
103         s->flags = 0;
104         if (s->tail != shmiqs [device].tail){
105                 s->flags |= SHMIQ_CORRUPTED;
106                 return;
107         }
108         tail_next = (s->tail + 1) % (shmiqs [device].events);
109
110         if (tail_next == s->head){
111                 s->flags |= SHMIQ_OVERFLOW;
112                 return;
113         }
114
115         e->un.time = jiffies;
116         s->events [s->tail] = *e;
117         printk ("KERNEL: dev=%d which=%d type=%d flags=%d\n",
118                 e->data.device, e->data.which, e->data.type, e->data.flags);
119         s->tail = tail_next;
120         shmiqs [device].tail = tail_next;
121         kill_fasync (&shmiqs [device].fasync, SIGIO, POLL_IN);
122         wake_up_interruptible (&shmiqs [device].proc_list);
123 }
124
125 static int
126 shmiq_manage_file (struct file *filp)
127 {
128         int i;
129
130         if (!filp->f_op || !filp->f_op->ioctl)
131                 return -ENOSR;
132
133         for (i = 0; i < MAX_SHMIQ_DEVS; i++){
134                 if (shmiq_pushed_devices [i].used)
135                         continue;
136                 if ((*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_ON, i) != 0)
137                         return -ENOSR;
138                 shmiq_pushed_devices [i].used = 1;
139                 shmiq_pushed_devices [i].filp = filp;
140                 shmiq_pushed_devices [i].cpos.x = 0;
141                 shmiq_pushed_devices [i].cpos.y = 0;
142                 return i;
143         }
144         return -ENOSR;
145 }
146
147 static int
148 shmiq_forget_file (unsigned long fdes)
149 {
150         struct file *filp;
151
152         if (fdes > MAX_SHMIQ_DEVS)
153                 return -EINVAL;
154
155         if (!shmiq_pushed_devices [fdes].used)
156                 return -EINVAL;
157
158         filp = shmiq_pushed_devices [fdes].filp;
159         if (filp){
160                 (*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_OFF, 0);
161                 shmiq_pushed_devices [fdes].filp = 0;
162                 fput (filp);
163         }
164         shmiq_pushed_devices [fdes].used = 0;
165
166         return 0;
167 }
168
169 static int
170 shmiq_sioc (int device, int cmd, struct strioctl *s)
171 {
172         switch (cmd){
173         case QIOCGETINDX:
174                 /*
175                  * Ok, we just return the index they are providing us
176                  */
177                 printk ("QIOCGETINDX: returning %d\n", *(int *)s->ic_dp);
178                 return 0;
179
180         case QIOCIISTR: {
181                 struct muxioctl *mux = (struct muxioctl *) s->ic_dp;
182
183                 printk ("Double indirect ioctl: [%d, %x\n", mux->index, mux->realcmd);
184                 return -EINVAL;
185         }
186
187         case QIOCSETCPOS: {
188                 if (copy_from_user (&shmiq_pushed_devices [device].cpos, s->ic_dp,
189                                     sizeof (struct shmiqsetcpos)))
190                         return -EFAULT;
191                 return 0;
192         }
193         }
194         printk ("Unknown I_STR request for shmiq device: 0x%x\n", cmd);
195         return -EINVAL;
196 }
197
198 static int
199 shmiq_ioctl (struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg)
200 {
201         struct file *file;
202         struct strioctl sioc;
203         int v;
204
205         switch (cmd){
206                 /*
207                  * They are giving us the file descriptor for one
208                  * of their streams devices
209                  */
210
211         case I_LINK:
212                 file = fget (arg);
213                 if (!file)
214                         goto bad_file;
215
216                 v = shmiq_manage_file (file);
217                 if (v<0)
218                         fput(file);
219                 return v;
220
221                 /*
222                  * Remove a device from our list of managed
223                  * stream devices
224                  */
225         case I_UNLINK:
226                 v = shmiq_forget_file (arg);
227                 return v;
228
229         case I_STR:
230                 v = get_sioc (&sioc, arg);
231                 if (v)
232                         return v;
233
234                 /* FIXME: This forces device = 0 */
235                 return shmiq_sioc (0, sioc.ic_cmd, &sioc);
236         }
237
238         return -EINVAL;
239
240 bad_file:
241         return -EBADF;
242 }
243
244 extern long sys_munmap(unsigned long addr, size_t len);
245
246 static int
247 qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg, int minor)
248 {
249         struct shmiqreq req;
250         struct vm_area_struct *vma;
251         int v;
252
253         switch (cmd) {
254                 /*
255                  * The address space is already mapped as a /dev/zero
256                  * mapping.  FIXME: check that /dev/zero is what the user
257                  * had mapped before :-)
258                  */
259                 case QIOCATTACH: {
260                         unsigned long vaddr;
261                         int s;
262
263                         v = verify_area (VERIFY_READ, (void *) arg,
264                                          sizeof (struct shmiqreq));
265                         if (v)
266                                 return v;
267                         if (copy_from_user(&req, (void *) arg, sizeof (req)))
268                                 return -EFAULT;
269                         /*
270                          * Do not allow to attach to another region if it has
271                          * already been attached
272                          */
273                         if (shmiqs [minor].mapped) {
274                                 printk("SHMIQ:The thingie is already mapped\n");
275                                 return -EINVAL;
276                         }
277
278                         vaddr = (unsigned long) req.user_vaddr;
279                         vma = find_vma (current->mm, vaddr);
280                         if (!vma) {
281                                 printk ("SHMIQ: could not find %lx the vma\n",
282                                         vaddr);
283                                 return -EINVAL;
284                         }
285                         s = req.arg * sizeof (struct shmqevent) +
286                             sizeof (struct sharedMemoryInputQueue);
287                         v = sys_munmap (vaddr, s);
288                         down_write(&current->mm->mmap_sem);
289                         do_munmap(current->mm, vaddr, s);
290                         do_mmap(filp, vaddr, s, PROT_READ | PROT_WRITE,
291                                 MAP_PRIVATE|MAP_FIXED, 0);
292                         up_write(&current->mm->mmap_sem);
293                         shmiqs[minor].events = req.arg;
294                         shmiqs[minor].mapped = 1;
295
296                         return 0;
297                 }
298         }
299
300         return -EINVAL;
301 }
302
303 struct page *
304 shmiq_nopage (struct vm_area_struct *vma, unsigned long address,
305               int write_access)
306 {
307         /* Do not allow for mremap to expand us */
308         return NULL;
309 }
310
311 static struct vm_operations_struct qcntl_mmap = {
312         nopage: shmiq_nopage,           /* our magic no-page fault handler */
313 };
314
315 static int
316 shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
317 {
318         int           minor = MINOR (file->f_dentry->d_inode->i_rdev), error;
319         unsigned int  size;
320         unsigned long mem, start;
321
322         /* mmap is only supported on the qcntl devices */
323         if (minor-- == 0)
324                 return -EINVAL;
325
326         if (vma->vm_pgoff != 0)
327                 return -EINVAL;
328
329         size  = vma->vm_end - vma->vm_start;
330         start = vma->vm_start;
331         lock_kernel();
332         mem = (unsigned long) shmiqs [minor].shmiq_vaddr =  vmalloc_uncached (size);
333         if (!mem) {
334                 unlock_kernel();
335                 return -EINVAL;
336         }
337
338         /* Prevent the swapper from considering these pages for swap and touching them */
339         vma->vm_flags    |= (VM_SHM  | VM_LOCKED | VM_IO);
340         vma->vm_ops = &qcntl_mmap;
341
342         /* Uncache the pages */
343         vma->vm_page_prot = PAGE_USERIO;
344
345         error = vmap_page_range (vma->vm_start, size, mem);
346
347         shmiqs [minor].tail = 0;
348         /* Init the shared memory input queue */
349         memset (shmiqs [minor].shmiq_vaddr, 0, size);
350         unlock_kernel();
351
352         return error;
353 }
354
355 static int
356 shmiq_qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
357 {
358         int minor = MINOR (inode->i_rdev);
359
360         if (minor-- == 0)
361                 return shmiq_ioctl (inode, filp, cmd, arg);
362
363         return qcntl_ioctl (inode, filp, cmd, arg, minor);
364 }
365
366 static unsigned int
367 shmiq_qcntl_poll (struct file *filp, poll_table *wait)
368 {
369         struct sharedMemoryInputQueue *s;
370         int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
371
372         if (minor-- == 0)
373                 return 0;
374
375         if (!shmiqs [minor].mapped)
376                 return 0;
377
378         poll_wait (filp, &shmiqs [minor].proc_list, wait);
379         s = shmiqs [minor].shmiq_vaddr;
380         if (s->head != s->tail)
381                 return POLLIN | POLLRDNORM;
382         return 0;
383 }
384
385 static int
386 shmiq_qcntl_open (struct inode *inode, struct file *filp)
387 {
388         int minor = MINOR (inode->i_rdev);
389
390         if (minor == 0)
391                 return 0;
392
393         minor--;
394         if (minor > MAX_SHMI_QUEUES)
395                 return -EINVAL;
396         if (shmiqs [minor].opened)
397                 return -EBUSY;
398
399         lock_kernel ();
400         shmiqs [minor].opened      = 1;
401         shmiqs [minor].shmiq_vaddr = 0;
402         unlock_kernel ();
403
404         return 0;
405 }
406
407 static int
408 shmiq_qcntl_fasync (int fd, struct file *file, int on)
409 {
410         int retval;
411         int minor = MINOR (file->f_dentry->d_inode->i_rdev);
412
413         retval = fasync_helper (fd, file, on, &shmiqs [minor].fasync);
414         if (retval < 0)
415                 return retval;
416         return 0;
417 }
418
419 static int
420 shmiq_qcntl_close (struct inode *inode, struct file *filp)
421 {
422         int minor = MINOR (inode->i_rdev);
423         int j;
424
425         if (minor-- == 0){
426                 for (j = 0; j < MAX_SHMIQ_DEVS; j++)
427                         shmiq_forget_file (j);
428         }
429
430         if (minor > MAX_SHMI_QUEUES)
431                 return -EINVAL;
432         if (shmiqs [minor].opened == 0)
433                 return -EINVAL;
434
435         lock_kernel ();
436         shmiq_qcntl_fasync (-1, filp, 0);
437         shmiqs [minor].opened      = 0;
438         shmiqs [minor].mapped      = 0;
439         shmiqs [minor].events      = 0;
440         shmiqs [minor].fasync      = 0;
441         vfree (shmiqs [minor].shmiq_vaddr);
442         shmiqs [minor].shmiq_vaddr = 0;
443         unlock_kernel ();
444
445         return 0;
446 }
447
448
449 static struct file_operations shmiq_fops =
450 {
451         poll:           shmiq_qcntl_poll,
452         ioctl:          shmiq_qcntl_ioctl,
453         mmap:           shmiq_qcntl_mmap,
454         open:           shmiq_qcntl_open,
455         release:        shmiq_qcntl_close,
456         fasync:         shmiq_qcntl_fasync,
457 };
458
459 void
460 shmiq_init (void)
461 {
462         printk ("SHMIQ setup\n");
463         devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops);
464         devfs_register (NULL, "shmiq", DEVFS_FL_DEFAULT,
465                         SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR,
466                         &shmiq_fops, NULL);
467         devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT,
468                                SHMIQ_MAJOR, 1,
469                                S_IFCHR | S_IRUSR | S_IWUSR,
470                                &shmiq_fops, NULL);
471 }
472
473 EXPORT_SYMBOL(shmiq_init);