Import 2.1.118
[davej-history.git] / drivers / sgi / char / shmiq.c
blob6a41d0ae0c0b8b4634556933f030c6b3589f67ac
1 /*
2 * shmiq.c: shared memory input queue driver
3 * written 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
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.
9 * minor number 0 implements /dev/shmiq,
10 * any other number implements /dev/qcntl${minor-1}
12 * /dev/shmiq is used by the X server for two things:
14 * 1. for I_LINK()ing trough ioctl the file handle of a
15 * STREAMS device.
17 * 2. To send STREAMS-commands to the devices with the
18 * QIO ioctl interface.
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.
25 * /dev/qcntlN is used by the X server for two things:
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).
31 * 2. used by select. I bet it is used for checking for events on
32 * the queue.
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.
41 * Until then, I just allow for 1 qcntl device.
45 #include <linux/fs.h>
46 #include <linux/miscdevice.h>
47 #include <linux/sched.h>
48 #include <linux/file.h>
49 #include <linux/interrupt.h>
50 #include <linux/vmalloc.h>
51 #include <linux/wait.h>
52 #include <linux/major.h>
53 #include <linux/smp_lock.h>
55 #include <asm/shmiq.h>
56 #include <asm/mman.h>
57 #include <asm/uaccess.h>
58 #include <asm/poll.h>
59 #include "graphics.h"
61 /* we are not really getting any more than a few files in the shmiq */
62 #define MAX_SHMIQ_DEVS 10
65 * One per X server running, not going to get very big.
66 * Even if we have this we now assume just 1 /dev/qcntl can be
67 * active, I need to find how this works on multi-headed machines.
69 #define MAX_SHMI_QUEUES 4
71 static struct {
72 int used;
73 struct file *filp;
74 struct shmiqsetcpos cpos;
75 } shmiq_pushed_devices [MAX_SHMIQ_DEVS];
77 /* /dev/qcntlN attached memory regions, location and size of the event queue */
78 static struct {
79 int opened; /* if this device has been opened */
80 void *shmiq_vaddr; /* mapping in kernel-land */
81 int tail; /* our copy of the shmiq->tail */
82 int events;
83 int mapped;
85 struct wait_queue *proc_list;
86 struct fasync_struct *fasync;
87 } shmiqs [MAX_SHMI_QUEUES];
89 void
90 shmiq_push_event (struct shmqevent *e)
92 struct sharedMemoryInputQueue *s;
93 int device = 0; /* FIXME: here is the assumption /dev/shmiq == /dev/qcntl0 */
94 int tail_next;
96 if (!shmiqs [device].mapped)
97 return;
98 s = shmiqs [device].shmiq_vaddr;
100 s->flags = 0;
101 if (s->tail != shmiqs [device].tail){
102 s->flags |= SHMIQ_CORRUPTED;
103 return;
105 tail_next = (s->tail + 1) % (shmiqs [device].events);
107 if (tail_next == s->head){
108 s->flags |= SHMIQ_OVERFLOW;
109 return;
112 e->un.time = jiffies;
113 s->events [s->tail] = *e;
114 printk ("KERNEL: dev=%d which=%d type=%d flags=%d\n",
115 e->data.device, e->data.which, e->data.type, e->data.flags);
116 s->tail = tail_next;
117 shmiqs [device].tail = tail_next;
118 if (shmiqs [device].fasync)
119 kill_fasync (shmiqs [device].fasync, SIGIO);
120 wake_up_interruptible (&shmiqs [device].proc_list);
123 static int
124 shmiq_manage_file (struct file *filp)
126 int i;
128 if (!filp->f_op || !filp->f_op->ioctl)
129 return -ENOSR;
131 for (i = 0; i < MAX_SHMIQ_DEVS; i++){
132 if (shmiq_pushed_devices [i].used)
133 continue;
134 if ((*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_ON, i) != 0)
135 return -ENOSR;
136 shmiq_pushed_devices [i].used = 1;
137 shmiq_pushed_devices [i].filp = filp;
138 shmiq_pushed_devices [i].cpos.x = 0;
139 shmiq_pushed_devices [i].cpos.y = 0;
140 return i;
142 return -ENOSR;
145 static int
146 shmiq_forget_file (unsigned long fdes)
148 struct file *filp;
150 if (fdes > MAX_SHMIQ_DEVS)
151 return -EINVAL;
153 if (!shmiq_pushed_devices [fdes].used)
154 return -EINVAL;
156 filp = shmiq_pushed_devices [fdes].filp;
157 if (filp){
158 (*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_OFF, 0);
159 shmiq_pushed_devices [fdes].filp = 0;
160 fput (filp);
162 shmiq_pushed_devices [fdes].used = 0;
164 return 0;
167 static int
168 shmiq_sioc (int device, int cmd, struct strioctl *s)
170 switch (cmd){
171 case QIOCGETINDX:
173 * Ok, we just return the index they are providing us
175 printk ("QIOCGETINDX: returning %d\n", *(int *)s->ic_dp);
176 return 0;
178 case QIOCIISTR: {
179 struct muxioctl *mux = (struct muxioctl *) s->ic_dp;
181 printk ("Double indirect ioctl: [%d, %x\n", mux->index, mux->realcmd);
182 return -EINVAL;
185 case QIOCSETCPOS: {
186 if (copy_from_user (&shmiq_pushed_devices [device].cpos, s->ic_dp,
187 sizeof (struct shmiqsetcpos)))
188 return -EFAULT;
189 return 0;
192 printk ("Unknown I_STR request for shmiq device: 0x%x\n", cmd);
193 return -EINVAL;
196 static int
197 shmiq_ioctl (struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg)
199 struct file *file;
200 struct strioctl sioc;
201 int v;
203 switch (cmd){
205 * They are giving us the file descriptor for one
206 * of their streams devices
209 case I_LINK:
210 file = fget (arg);
211 if (!file)
212 goto bad_file;
214 v = shmiq_manage_file (file);
215 return v;
218 * Remove a device from our list of managed
219 * stream devices
221 case I_UNLINK:
222 v = shmiq_forget_file (arg);
223 return v;
225 case I_STR:
226 v = get_sioc (&sioc, arg);
227 if (v)
228 return v;
230 /* FIXME: This forces device = 0 */
231 return shmiq_sioc (0, sioc.ic_cmd, &sioc);
234 return -EINVAL;
235 bad_file:
236 unlock_kernel ();
237 return -EBADF;
240 extern sys_munmap(unsigned long addr, size_t len);
242 static int
243 qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg, int minor)
245 struct shmiqreq req;
246 struct vm_area_struct *vma;
247 int v;
249 switch (cmd){
251 * The address space is already mapped as a /dev/zero
252 * mapping. FIXME: check that /dev/zero is what the user
253 * had mapped before :-)
255 case QIOCATTACH: {
256 unsigned long vaddr;
257 int s;
259 v = verify_area (VERIFY_READ, (void *) arg, sizeof (struct shmiqreq));
260 if (v)
261 return v;
262 if (copy_from_user (&req, (void *) arg, sizeof (req)))
263 return -EFAULT;
264 /* Do not allow to attach to another region if it has been already attached */
265 if (shmiqs [minor].mapped){
266 printk ("SHMIQ:The thingie is already mapped\n");
267 return -EINVAL;
270 vaddr = (unsigned long) req.user_vaddr;
271 vma = find_vma (current->mm, vaddr);
272 if (!vma){
273 printk ("SHMIQ: could not find %lx the vma\n", vaddr);
274 return -EINVAL;
276 s = req.arg * sizeof (struct shmqevent) + sizeof (struct sharedMemoryInputQueue);
277 v = sys_munmap (vaddr, s);
278 do_mmap (filp, vaddr, s, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 0);
279 shmiqs [minor].events = req.arg;
280 shmiqs [minor].mapped = 1;
281 return 0;
284 return -EINVAL;
287 unsigned long
288 shmiq_nopage (struct vm_area_struct *vma, unsigned long address, int write_access)
290 /* Do not allow for mremap to expand us */
291 return 0;
294 static struct vm_operations_struct qcntl_mmap = {
295 NULL, /* no special mmap-open */
296 NULL, /* no special mmap-close */
297 NULL, /* no special mmap-unmap */
298 NULL, /* no special mmap-protect */
299 NULL, /* no special mmap-sync */
300 NULL, /* no special mmap-advise */
301 shmiq_nopage, /* our magic no-page fault handler */
302 NULL, /* no special mmap-wppage */
303 NULL, /* no special mmap-swapout */
304 NULL /* no special mmap-swapin */
307 static int
308 shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
310 int minor = MINOR (file->f_dentry->d_inode->i_rdev), error;
311 unsigned int size;
312 unsigned long mem, start;
314 /* mmap is only supported on the qcntl devices */
315 if (minor-- == 0)
316 return -EINVAL;
318 if (vma->vm_offset != 0)
319 return -EINVAL;
321 size = vma->vm_end - vma->vm_start;
322 start = vma->vm_start;
323 mem = (unsigned long) shmiqs [minor].shmiq_vaddr = vmalloc_uncached (size);
324 if (!mem)
325 return -EINVAL;
327 /* Prevent the swapper from considering these pages for swap and touching them */
328 vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
329 vma->vm_ops = &qcntl_mmap;
331 /* Uncache the pages */
332 vma->vm_page_prot = PAGE_USERIO;
334 error = vmap_page_range (vma->vm_start, size, mem);
336 shmiqs [minor].tail = 0;
337 /* Init the shared memory input queue */
338 memset (shmiqs [minor].shmiq_vaddr, 0, size);
340 return error;
343 static int
344 shmiq_qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
346 int minor = MINOR (inode->i_rdev);
348 lock_kernel ();
350 if (minor-- == 0)
351 return shmiq_ioctl (inode, filp, cmd, arg);
353 return qcntl_ioctl (inode, filp, cmd, arg, minor);
356 static unsigned int
357 shmiq_qcntl_poll (struct file *filp, poll_table *wait)
359 struct sharedMemoryInputQueue *s;
360 int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
362 if (minor-- == 0)
363 return 0;
365 if (!shmiqs [minor].mapped)
366 return 0;
368 poll_wait (filp, &shmiqs [minor].proc_list, wait);
369 s = shmiqs [minor].shmiq_vaddr;
370 if (s->head != s->tail)
371 return POLLIN | POLLRDNORM;
372 return 0;
375 static int
376 shmiq_qcntl_open (struct inode *inode, struct file *filp)
378 int minor = MINOR (inode->i_rdev);
380 if (minor == 0)
381 return 0;
383 minor--;
384 if (minor > MAX_SHMI_QUEUES)
385 return -EINVAL;
386 if (shmiqs [minor].opened)
387 return -EBUSY;
389 lock_kernel ();
390 shmiqs [minor].opened = 1;
391 shmiqs [minor].shmiq_vaddr = 0;
392 unlock_kernel ();
393 return 0;
396 static int
397 shmiq_qcntl_fasync (struct file *file, int on)
399 int retval;
400 int minor = MINOR (file->f_dentry->d_inode->i_rdev);
402 retval = fasync_helper (file, on, &shmiqs [minor].fasync);
403 if (retval < 0)
404 return retval;
405 return 0;
408 static int
409 shmiq_qcntl_close (struct inode *inode, struct file *filp)
411 int minor = MINOR (inode->i_rdev);
412 int j;
414 if (minor-- == 0){
415 for (j = 0; j < MAX_SHMIQ_DEVS; j++)
416 shmiq_forget_file (j);
419 if (minor > MAX_SHMI_QUEUES)
420 return -EINVAL;
421 if (shmiqs [minor].opened == 0)
422 return -EINVAL;
424 lock_kernel ();
425 shmiq_qcntl_fasync (filp, 0);
426 shmiqs [minor].opened = 0;
427 shmiqs [minor].mapped = 0;
428 shmiqs [minor].events = 0;
429 shmiqs [minor].fasync = 0;
430 vfree (shmiqs [minor].shmiq_vaddr);
431 shmiqs [minor].shmiq_vaddr = 0;
432 unlock_kernel ();
433 return 0;
437 static struct
438 file_operations shmiq_fops =
440 NULL, /* seek */
441 NULL, /* read */
442 NULL, /* write */
443 NULL, /* readdir */
444 shmiq_qcntl_poll, /* poll */
445 shmiq_qcntl_ioctl, /* ioctl */
446 shmiq_qcntl_mmap, /* mmap */
447 shmiq_qcntl_open, /* open */
448 NULL, /* flush */
449 shmiq_qcntl_close, /* close */
450 NULL, /* fsync */
451 shmiq_qcntl_fasync, /* fasync */
452 NULL, /* check_media_change */
453 NULL, /* revalidate */
456 void
457 shmiq_init (void)
459 printk ("SHMIQ setup\n");
460 register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops);