Import 2.3.9pre5
[davej-history.git] / drivers / sgi / char / shmiq.c
blob6b93fdd2850838f22ce37031f11067fbf28caf2c
1 /* $Id: shmiq.c,v 1.12 1999/06/17 13:29:04 ralf Exp $
3 * shmiq.c: shared memory input queue driver
4 * written 1997 Miguel de Icaza (miguel@nuclecu.unam.mx)
6 * We implement /dev/shmiq, /dev/qcntlN here
7 * this is different from IRIX that has shmiq as a misc
8 * streams device and the and qcntl devices as a major device.
10 * minor number 0 implements /dev/shmiq,
11 * any other number implements /dev/qcntl${minor-1}
13 * /dev/shmiq is used by the X server for two things:
15 * 1. for I_LINK()ing trough ioctl the file handle of a
16 * STREAMS device.
18 * 2. To send STREAMS-commands to the devices with the
19 * QIO ioctl interface.
21 * I have not yet figured how to make multiple X servers share
22 * /dev/shmiq for having different servers running. So, for now
23 * I keep a kernel-global array of inodes that are pushed into
24 * /dev/shmiq.
26 * /dev/qcntlN is used by the X server for two things:
28 * 1. Issuing the QIOCATTACH for mapping the shared input
29 * queue into the address space of the X server (yeah, yeah,
30 * I did not invent this interface).
32 * 2. used by select. I bet it is used for checking for events on
33 * the queue.
35 * Now the problem is that there does not seem anything that
36 * establishes a connection between /dev/shmiq and the qcntlN file. I
37 * need an strace from an X server that runs on a machine with more
38 * than one keyboard. And this is a problem since the file handles
39 * are pushed in /dev/shmiq, while the events should be dispatched to
40 * the /dev/qcntlN device.
42 * Until then, I just allow for 1 qcntl device.
46 #include <linux/fs.h>
47 #include <linux/miscdevice.h>
48 #include <linux/sched.h>
49 #include <linux/file.h>
50 #include <linux/interrupt.h>
51 #include <linux/poll.h>
52 #include <linux/vmalloc.h>
53 #include <linux/wait.h>
54 #include <linux/major.h>
55 #include <linux/smp_lock.h>
57 #include <asm/shmiq.h>
58 #include <asm/mman.h>
59 #include <asm/uaccess.h>
60 #include <asm/poll.h>
61 #include "graphics.h"
63 /* we are not really getting any more than a few files in the shmiq */
64 #define MAX_SHMIQ_DEVS 10
67 * One per X server running, not going to get very big.
68 * Even if we have this we now assume just 1 /dev/qcntl can be
69 * active, I need to find how this works on multi-headed machines.
71 #define MAX_SHMI_QUEUES 4
73 static struct {
74 int used;
75 struct file *filp;
76 struct shmiqsetcpos cpos;
77 } shmiq_pushed_devices [MAX_SHMIQ_DEVS];
79 /* /dev/qcntlN attached memory regions, location and size of the event queue */
80 static struct {
81 int opened; /* if this device has been opened */
82 void *shmiq_vaddr; /* mapping in kernel-land */
83 int tail; /* our copy of the shmiq->tail */
84 int events;
85 int mapped;
87 wait_queue_head_t proc_list;
88 struct fasync_struct *fasync;
89 } shmiqs [MAX_SHMI_QUEUES];
91 void
92 shmiq_push_event (struct shmqevent *e)
94 struct sharedMemoryInputQueue *s;
95 int device = 0; /* FIXME: here is the assumption /dev/shmiq == /dev/qcntl0 */
96 int tail_next;
98 if (!shmiqs [device].mapped)
99 return;
100 s = shmiqs [device].shmiq_vaddr;
102 s->flags = 0;
103 if (s->tail != shmiqs [device].tail){
104 s->flags |= SHMIQ_CORRUPTED;
105 return;
107 tail_next = (s->tail + 1) % (shmiqs [device].events);
109 if (tail_next == s->head){
110 s->flags |= SHMIQ_OVERFLOW;
111 return;
114 e->un.time = jiffies;
115 s->events [s->tail] = *e;
116 printk ("KERNEL: dev=%d which=%d type=%d flags=%d\n",
117 e->data.device, e->data.which, e->data.type, e->data.flags);
118 s->tail = tail_next;
119 shmiqs [device].tail = tail_next;
120 if (shmiqs [device].fasync)
121 kill_fasync (shmiqs [device].fasync, SIGIO);
122 wake_up_interruptible (&shmiqs [device].proc_list);
125 static int
126 shmiq_manage_file (struct file *filp)
128 int i;
130 if (!filp->f_op || !filp->f_op->ioctl)
131 return -ENOSR;
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;
144 return -ENOSR;
147 static int
148 shmiq_forget_file (unsigned long fdes)
150 struct file *filp;
152 if (fdes > MAX_SHMIQ_DEVS)
153 return -EINVAL;
155 if (!shmiq_pushed_devices [fdes].used)
156 return -EINVAL;
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);
164 shmiq_pushed_devices [fdes].used = 0;
166 return 0;
169 static int
170 shmiq_sioc (int device, int cmd, struct strioctl *s)
172 switch (cmd){
173 case QIOCGETINDX:
175 * Ok, we just return the index they are providing us
177 printk ("QIOCGETINDX: returning %d\n", *(int *)s->ic_dp);
178 return 0;
180 case QIOCIISTR: {
181 struct muxioctl *mux = (struct muxioctl *) s->ic_dp;
183 printk ("Double indirect ioctl: [%d, %x\n", mux->index, mux->realcmd);
184 return -EINVAL;
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;
194 printk ("Unknown I_STR request for shmiq device: 0x%x\n", cmd);
195 return -EINVAL;
198 static int
199 shmiq_ioctl (struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg)
201 struct file *file;
202 struct strioctl sioc;
203 int v;
205 switch (cmd){
207 * They are giving us the file descriptor for one
208 * of their streams devices
211 case I_LINK:
212 file = fget (arg);
213 if (!file)
214 goto bad_file;
216 v = shmiq_manage_file (file);
217 return v;
220 * Remove a device from our list of managed
221 * stream devices
223 case I_UNLINK:
224 v = shmiq_forget_file (arg);
225 return v;
227 case I_STR:
228 v = get_sioc (&sioc, arg);
229 if (v)
230 return v;
232 /* FIXME: This forces device = 0 */
233 return shmiq_sioc (0, sioc.ic_cmd, &sioc);
236 return -EINVAL;
237 bad_file:
238 unlock_kernel ();
239 return -EBADF;
242 extern int sys_munmap(unsigned long addr, size_t len);
244 static int
245 qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg, int minor)
247 struct shmiqreq req;
248 struct vm_area_struct *vma;
249 int v;
251 switch (cmd){
253 * The address space is already mapped as a /dev/zero
254 * mapping. FIXME: check that /dev/zero is what the user
255 * had mapped before :-)
257 case QIOCATTACH: {
258 unsigned long vaddr;
259 int s;
261 v = verify_area (VERIFY_READ, (void *) arg, sizeof (struct shmiqreq));
262 if (v)
263 return v;
264 if (copy_from_user (&req, (void *) arg, sizeof (req)))
265 return -EFAULT;
266 /* Do not allow to attach to another region if it has been already attached */
267 if (shmiqs [minor].mapped){
268 printk ("SHMIQ:The thingie is already mapped\n");
269 return -EINVAL;
272 vaddr = (unsigned long) req.user_vaddr;
273 vma = find_vma (current->mm, vaddr);
274 if (!vma){
275 printk ("SHMIQ: could not find %lx the vma\n", vaddr);
276 return -EINVAL;
278 s = req.arg * sizeof (struct shmqevent) + sizeof (struct sharedMemoryInputQueue);
279 v = sys_munmap (vaddr, s);
280 do_mmap (filp, vaddr, s, PROT_READ | PROT_WRITE, MAP_PRIVATE|MAP_FIXED, 0);
281 shmiqs [minor].events = req.arg;
282 shmiqs [minor].mapped = 1;
283 return 0;
286 return -EINVAL;
289 unsigned long
290 shmiq_nopage (struct vm_area_struct *vma, unsigned long address, int write_access)
292 /* Do not allow for mremap to expand us */
293 return 0;
296 static struct vm_operations_struct qcntl_mmap = {
297 NULL, /* no special mmap-open */
298 NULL, /* no special mmap-close */
299 NULL, /* no special mmap-unmap */
300 NULL, /* no special mmap-protect */
301 NULL, /* no special mmap-sync */
302 NULL, /* no special mmap-advise */
303 shmiq_nopage, /* our magic no-page fault handler */
304 NULL, /* no special mmap-wppage */
305 NULL, /* no special mmap-swapout */
306 NULL /* no special mmap-swapin */
309 static int
310 shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
312 int minor = MINOR (file->f_dentry->d_inode->i_rdev), error;
313 unsigned int size;
314 unsigned long mem, start;
316 /* mmap is only supported on the qcntl devices */
317 if (minor-- == 0)
318 return -EINVAL;
320 if (vma->vm_offset != 0)
321 return -EINVAL;
323 size = vma->vm_end - vma->vm_start;
324 start = vma->vm_start;
325 mem = (unsigned long) shmiqs [minor].shmiq_vaddr = vmalloc_uncached (size);
326 if (!mem)
327 return -EINVAL;
329 /* Prevent the swapper from considering these pages for swap and touching them */
330 vma->vm_flags |= (VM_SHM | VM_LOCKED | VM_IO);
331 vma->vm_ops = &qcntl_mmap;
333 /* Uncache the pages */
334 vma->vm_page_prot = PAGE_USERIO;
336 error = vmap_page_range (vma->vm_start, size, mem);
338 shmiqs [minor].tail = 0;
339 /* Init the shared memory input queue */
340 memset (shmiqs [minor].shmiq_vaddr, 0, size);
342 return error;
345 static int
346 shmiq_qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
348 int minor = MINOR (inode->i_rdev);
350 lock_kernel ();
352 if (minor-- == 0)
353 return shmiq_ioctl (inode, filp, cmd, arg);
355 return qcntl_ioctl (inode, filp, cmd, arg, minor);
358 static unsigned int
359 shmiq_qcntl_poll (struct file *filp, poll_table *wait)
361 struct sharedMemoryInputQueue *s;
362 int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
364 if (minor-- == 0)
365 return 0;
367 if (!shmiqs [minor].mapped)
368 return 0;
370 poll_wait (filp, &shmiqs [minor].proc_list, wait);
371 s = shmiqs [minor].shmiq_vaddr;
372 if (s->head != s->tail)
373 return POLLIN | POLLRDNORM;
374 return 0;
377 static int
378 shmiq_qcntl_open (struct inode *inode, struct file *filp)
380 int minor = MINOR (inode->i_rdev);
382 if (minor == 0)
383 return 0;
385 minor--;
386 if (minor > MAX_SHMI_QUEUES)
387 return -EINVAL;
388 if (shmiqs [minor].opened)
389 return -EBUSY;
391 lock_kernel ();
392 shmiqs [minor].opened = 1;
393 shmiqs [minor].shmiq_vaddr = 0;
394 unlock_kernel ();
395 return 0;
398 static int
399 shmiq_qcntl_fasync (int fd, struct file *file, int on)
401 int retval;
402 int minor = MINOR (file->f_dentry->d_inode->i_rdev);
404 retval = fasync_helper (fd, file, on, &shmiqs [minor].fasync);
405 if (retval < 0)
406 return retval;
407 return 0;
410 static int
411 shmiq_qcntl_close (struct inode *inode, struct file *filp)
413 int minor = MINOR (inode->i_rdev);
414 int j;
416 if (minor-- == 0){
417 for (j = 0; j < MAX_SHMIQ_DEVS; j++)
418 shmiq_forget_file (j);
421 if (minor > MAX_SHMI_QUEUES)
422 return -EINVAL;
423 if (shmiqs [minor].opened == 0)
424 return -EINVAL;
426 lock_kernel ();
427 shmiq_qcntl_fasync (-1, filp, 0);
428 shmiqs [minor].opened = 0;
429 shmiqs [minor].mapped = 0;
430 shmiqs [minor].events = 0;
431 shmiqs [minor].fasync = 0;
432 vfree (shmiqs [minor].shmiq_vaddr);
433 shmiqs [minor].shmiq_vaddr = 0;
434 unlock_kernel ();
435 return 0;
439 static struct
440 file_operations shmiq_fops =
442 NULL, /* seek */
443 NULL, /* read */
444 NULL, /* write */
445 NULL, /* readdir */
446 shmiq_qcntl_poll, /* poll */
447 shmiq_qcntl_ioctl, /* ioctl */
448 shmiq_qcntl_mmap, /* mmap */
449 shmiq_qcntl_open, /* open */
450 NULL, /* flush */
451 shmiq_qcntl_close, /* close */
452 NULL, /* fsync */
453 shmiq_qcntl_fasync, /* fasync */
454 NULL, /* check_media_change */
455 NULL, /* revalidate */
458 void
459 shmiq_init (void)
461 printk ("SHMIQ setup\n");
462 register_chrdev (SHMIQ_MAJOR, "shmiq", &shmiq_fops);