shmiq.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:11k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  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. #include <asm/shmiq.h>
  57. #include <asm/gfx.h>
  58. #include <asm/mman.h>
  59. #include <asm/uaccess.h>
  60. #include <asm/poll.h>
  61. #include "graphics.h"
  62. /* we are not really getting any more than a few files in the shmiq */
  63. #define MAX_SHMIQ_DEVS 10
  64. /*
  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.
  68.  */
  69. #define MAX_SHMI_QUEUES 4
  70. static struct {
  71. int                 used;
  72. struct file         *filp;
  73. struct shmiqsetcpos cpos;
  74. } shmiq_pushed_devices [MAX_SHMIQ_DEVS];
  75. /* /dev/qcntlN attached memory regions, location and size of the event queue */
  76. static struct {
  77. int    opened; /* if this device has been opened */
  78. void   *shmiq_vaddr; /* mapping in kernel-land */
  79. int    tail; /* our copy of the shmiq->tail */
  80. int    events;
  81. int    mapped;
  82. wait_queue_head_t    proc_list;
  83. struct fasync_struct *fasync;
  84. } shmiqs [MAX_SHMI_QUEUES];
  85. void
  86. shmiq_push_event (struct shmqevent *e)
  87. {
  88. struct sharedMemoryInputQueue *s;
  89. int    device = 0; /* FIXME: here is the assumption /dev/shmiq == /dev/qcntl0 */
  90. int    tail_next;
  91. if (!shmiqs [device].mapped)
  92. return;
  93. s = shmiqs [device].shmiq_vaddr;
  94. s->flags = 0;
  95. if (s->tail != shmiqs [device].tail){
  96. s->flags |= SHMIQ_CORRUPTED;
  97. return;
  98. }
  99. tail_next = (s->tail + 1) % (shmiqs [device].events);
  100. if (tail_next == s->head){
  101. s->flags |= SHMIQ_OVERFLOW;
  102. return;
  103. }
  104. e->un.time = jiffies;
  105. s->events [s->tail] = *e;
  106. printk ("KERNEL: dev=%d which=%d type=%d flags=%dn",
  107. e->data.device, e->data.which, e->data.type, e->data.flags);
  108. s->tail = tail_next;
  109. shmiqs [device].tail = tail_next;
  110. kill_fasync (&shmiqs [device].fasync, SIGIO, POLL_IN);
  111. wake_up_interruptible (&shmiqs [device].proc_list);
  112. }
  113. static int
  114. shmiq_manage_file (struct file *filp)
  115. {
  116. int i;
  117. if (!filp->f_op || !filp->f_op->ioctl)
  118. return -ENOSR;
  119. for (i = 0; i < MAX_SHMIQ_DEVS; i++){
  120. if (shmiq_pushed_devices [i].used)
  121. continue;
  122. if ((*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_ON, i) != 0)
  123. return -ENOSR;
  124. shmiq_pushed_devices [i].used = 1;
  125. shmiq_pushed_devices [i].filp = filp;
  126. shmiq_pushed_devices [i].cpos.x = 0;
  127. shmiq_pushed_devices [i].cpos.y = 0;
  128. return i;
  129. }
  130. return -ENOSR;
  131. }
  132. static int
  133. shmiq_forget_file (unsigned long fdes)
  134. {
  135. struct file *filp;
  136. if (fdes > MAX_SHMIQ_DEVS)
  137. return -EINVAL;
  138. if (!shmiq_pushed_devices [fdes].used)
  139. return -EINVAL;
  140. filp = shmiq_pushed_devices [fdes].filp;
  141. if (filp){
  142. (*filp->f_op->ioctl)(filp->f_dentry->d_inode, filp, SHMIQ_OFF, 0);
  143. shmiq_pushed_devices [fdes].filp = 0;
  144. fput (filp);
  145. }
  146. shmiq_pushed_devices [fdes].used = 0;
  147. return 0;
  148. }
  149. static int
  150. shmiq_sioc (int device, int cmd, struct strioctl *s)
  151. {
  152. switch (cmd){
  153. case QIOCGETINDX:
  154. /*
  155.  * Ok, we just return the index they are providing us
  156.  */
  157. printk ("QIOCGETINDX: returning %dn", *(int *)s->ic_dp);
  158. return 0;
  159. case QIOCIISTR: {
  160. struct muxioctl *mux = (struct muxioctl *) s->ic_dp;
  161. printk ("Double indirect ioctl: [%d, %xn", mux->index, mux->realcmd);
  162. return -EINVAL;
  163. }
  164. case QIOCSETCPOS: {
  165. if (copy_from_user (&shmiq_pushed_devices [device].cpos, s->ic_dp,
  166.     sizeof (struct shmiqsetcpos)))
  167. return -EFAULT;
  168. return 0;
  169. }
  170. }
  171. printk ("Unknown I_STR request for shmiq device: 0x%xn", cmd);
  172. return -EINVAL;
  173. }
  174. static int
  175. shmiq_ioctl (struct inode *inode, struct file *f, unsigned int cmd, unsigned long arg)
  176. {
  177. struct file *file;
  178. struct strioctl sioc;
  179. int v;
  180. switch (cmd){
  181. /*
  182.  * They are giving us the file descriptor for one
  183.  * of their streams devices
  184.  */
  185. case I_LINK:
  186. file = fget (arg);
  187. if (!file)
  188. goto bad_file;
  189. v = shmiq_manage_file (file);
  190. if (v<0)
  191. fput(file);
  192. return v;
  193. /*
  194.  * Remove a device from our list of managed
  195.  * stream devices
  196.  */
  197. case I_UNLINK:
  198. v = shmiq_forget_file (arg);
  199. return v;
  200. case I_STR:
  201. v = get_sioc (&sioc, arg);
  202. if (v)
  203. return v;
  204. /* FIXME: This forces device = 0 */
  205. return shmiq_sioc (0, sioc.ic_cmd, &sioc);
  206. }
  207. return -EINVAL;
  208. bad_file:
  209. return -EBADF;
  210. }
  211. extern long sys_munmap(unsigned long addr, size_t len);
  212. static int
  213. qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg, int minor)
  214. {
  215. struct shmiqreq req;
  216. struct vm_area_struct *vma;
  217. int v;
  218. switch (cmd) {
  219. /*
  220.  * The address space is already mapped as a /dev/zero
  221.  * mapping.  FIXME: check that /dev/zero is what the user
  222.  * had mapped before :-)
  223.  */
  224. case QIOCATTACH: {
  225. unsigned long vaddr;
  226. int s;
  227. v = verify_area (VERIFY_READ, (void *) arg,
  228.                  sizeof (struct shmiqreq));
  229. if (v)
  230. return v;
  231. if (copy_from_user(&req, (void *) arg, sizeof (req)))
  232. return -EFAULT;
  233. /*
  234.  * Do not allow to attach to another region if it has
  235.  * already been attached
  236.  */
  237. if (shmiqs [minor].mapped) {
  238. printk("SHMIQ:The thingie is already mappedn");
  239. return -EINVAL;
  240. }
  241. vaddr = (unsigned long) req.user_vaddr;
  242. vma = find_vma (current->mm, vaddr);
  243. if (!vma) {
  244. printk ("SHMIQ: could not find %lx the vman",
  245.         vaddr);
  246. return -EINVAL;
  247. }
  248. s = req.arg * sizeof (struct shmqevent) +
  249.     sizeof (struct sharedMemoryInputQueue);
  250. v = sys_munmap (vaddr, s);
  251. down_write(&current->mm->mmap_sem);
  252. do_munmap(current->mm, vaddr, s);
  253. do_mmap(filp, vaddr, s, PROT_READ | PROT_WRITE,
  254.         MAP_PRIVATE|MAP_FIXED, 0);
  255. up_write(&current->mm->mmap_sem);
  256. shmiqs[minor].events = req.arg;
  257. shmiqs[minor].mapped = 1;
  258. return 0;
  259. }
  260. }
  261. return -EINVAL;
  262. }
  263. struct page *
  264. shmiq_nopage (struct vm_area_struct *vma, unsigned long address,
  265.               int write_access)
  266. {
  267. /* Do not allow for mremap to expand us */
  268. return NULL;
  269. }
  270. static struct vm_operations_struct qcntl_mmap = {
  271. nopage: shmiq_nopage, /* our magic no-page fault handler */
  272. };
  273. static int
  274. shmiq_qcntl_mmap (struct file *file, struct vm_area_struct *vma)
  275. {
  276. int           minor = MINOR (file->f_dentry->d_inode->i_rdev), error;
  277. unsigned int  size;
  278. unsigned long mem, start;
  279. /* mmap is only supported on the qcntl devices */
  280. if (minor-- == 0)
  281. return -EINVAL;
  282. if (vma->vm_pgoff != 0)
  283. return -EINVAL;
  284. size  = vma->vm_end - vma->vm_start;
  285. start = vma->vm_start; 
  286. lock_kernel();
  287. mem = (unsigned long) shmiqs [minor].shmiq_vaddr =  vmalloc_uncached (size);
  288. if (!mem) {
  289. unlock_kernel();
  290. return -EINVAL;
  291. }
  292. /* Prevent the swapper from considering these pages for swap and touching them */
  293. vma->vm_flags    |= (VM_SHM  | VM_LOCKED | VM_IO);
  294. vma->vm_ops = &qcntl_mmap;
  295. /* Uncache the pages */
  296. vma->vm_page_prot = PAGE_USERIO;
  297. error = vmap_page_range (vma->vm_start, size, mem);
  298. shmiqs [minor].tail = 0;
  299. /* Init the shared memory input queue */
  300. memset (shmiqs [minor].shmiq_vaddr, 0, size);
  301. unlock_kernel();
  302. return error;
  303. }
  304. static int
  305. shmiq_qcntl_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
  306. {
  307. int minor = MINOR (inode->i_rdev);
  308. if (minor-- == 0)
  309. return shmiq_ioctl (inode, filp, cmd, arg);
  310. return qcntl_ioctl (inode, filp, cmd, arg, minor);
  311. }
  312. static unsigned int
  313. shmiq_qcntl_poll (struct file *filp, poll_table *wait)
  314. {
  315. struct sharedMemoryInputQueue *s;
  316. int minor = MINOR (filp->f_dentry->d_inode->i_rdev);
  317. if (minor-- == 0)
  318. return 0;
  319. if (!shmiqs [minor].mapped)
  320. return 0;
  321. poll_wait (filp, &shmiqs [minor].proc_list, wait);
  322. s = shmiqs [minor].shmiq_vaddr;
  323. if (s->head != s->tail)
  324. return POLLIN | POLLRDNORM;
  325. return 0;
  326. }
  327. static int
  328. shmiq_qcntl_open (struct inode *inode, struct file *filp)
  329. {
  330. int minor = MINOR (inode->i_rdev);
  331. if (minor == 0)
  332. return 0;
  333. minor--;
  334. if (minor > MAX_SHMI_QUEUES)
  335. return -EINVAL;
  336. if (shmiqs [minor].opened)
  337. return -EBUSY;
  338. lock_kernel ();
  339. shmiqs [minor].opened      = 1;
  340. shmiqs [minor].shmiq_vaddr = 0;
  341. unlock_kernel ();
  342. return 0;
  343. }
  344. static int
  345. shmiq_qcntl_fasync (int fd, struct file *file, int on)
  346. {
  347. int retval;
  348. int minor = MINOR (file->f_dentry->d_inode->i_rdev);
  349. retval = fasync_helper (fd, file, on, &shmiqs [minor].fasync);
  350. if (retval < 0)
  351. return retval;
  352. return 0;
  353. }
  354. static int
  355. shmiq_qcntl_close (struct inode *inode, struct file *filp)
  356. {
  357. int minor = MINOR (inode->i_rdev);
  358. int j;
  359. if (minor-- == 0){
  360. for (j = 0; j < MAX_SHMIQ_DEVS; j++)
  361. shmiq_forget_file (j);
  362. }
  363. if (minor > MAX_SHMI_QUEUES)
  364. return -EINVAL;
  365. if (shmiqs [minor].opened == 0)
  366. return -EINVAL;
  367. lock_kernel ();
  368. shmiq_qcntl_fasync (-1, filp, 0);
  369. shmiqs [minor].opened      = 0;
  370. shmiqs [minor].mapped      = 0;
  371. shmiqs [minor].events      = 0;
  372. shmiqs [minor].fasync      = 0;
  373. vfree (shmiqs [minor].shmiq_vaddr);
  374. shmiqs [minor].shmiq_vaddr = 0;
  375. unlock_kernel ();
  376. return 0;
  377. }
  378. static struct file_operations shmiq_fops =
  379. {
  380. poll: shmiq_qcntl_poll,
  381. ioctl: shmiq_qcntl_ioctl,
  382. mmap: shmiq_qcntl_mmap,
  383. open: shmiq_qcntl_open,
  384. release: shmiq_qcntl_close,
  385. fasync: shmiq_qcntl_fasync,
  386. };
  387. void
  388. shmiq_init (void)
  389. {
  390. printk ("SHMIQ setupn");
  391. devfs_register_chrdev(SHMIQ_MAJOR, "shmiq", &shmiq_fops);
  392. devfs_register (NULL, "shmiq", DEVFS_FL_DEFAULT,
  393. SHMIQ_MAJOR, 0, S_IFCHR | S_IRUSR | S_IWUSR,
  394. &shmiq_fops, NULL);
  395. devfs_register_series (NULL, "qcntl%u", 2, DEVFS_FL_DEFAULT,
  396.        SHMIQ_MAJOR, 1,
  397.        S_IFCHR | S_IRUSR | S_IWUSR,
  398.        &shmiq_fops, NULL);
  399. }
  400. EXPORT_SYMBOL(shmiq_init);