psdev.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:11k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  *       An implementation of a loadable kernel mode driver providing
  3.  * multiple kernel/user space bidirectional communications links.
  4.  *
  5.  *  Author:  Alan Cox <alan@redhat.com>
  6.  *
  7.  * This program is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU General Public License
  9.  * as published by the Free Software Foundation; either version
  10.  * 2 of the License, or (at your option) any later version.
  11.  * 
  12.  *              Adapted to become the Linux 2.0 Coda pseudo device
  13.  *              Peter  Braam  <braam@maths.ox.ac.uk> 
  14.  *              Michael Callahan <mjc@emmy.smith.edu>           
  15.  *
  16.  *              Changes for Linux 2.1
  17.  *              Copyright (c) 1997 Carnegie-Mellon University
  18.  */
  19. #include <linux/module.h>
  20. #include <linux/errno.h>
  21. #include <linux/kernel.h>
  22. #include <linux/major.h>
  23. #include <linux/sched.h>
  24. #include <linux/lp.h>
  25. #include <linux/slab.h>
  26. #include <linux/ioport.h>
  27. #include <linux/fcntl.h>
  28. #include <linux/delay.h>
  29. #include <linux/skbuff.h>
  30. #include <linux/proc_fs.h>
  31. #include <linux/devfs_fs_kernel.h>
  32. #include <linux/vmalloc.h>
  33. #include <linux/fs.h>
  34. #include <linux/file.h>
  35. #include <linux/poll.h>
  36. #include <linux/init.h>
  37. #include <linux/list.h>
  38. #include <linux/smp_lock.h>
  39. #include <asm/io.h>
  40. #include <asm/system.h>
  41. #include <asm/poll.h>
  42. #include <asm/uaccess.h>
  43. #include <linux/coda.h>
  44. #include <linux/coda_linux.h>
  45. #include <linux/coda_fs_i.h>
  46. #include <linux/coda_psdev.h>
  47. #include <linux/coda_proc.h>
  48. #define upc_free(r) kfree(r)
  49. /* 
  50.  * Coda stuff
  51.  */
  52. extern struct file_system_type coda_fs_type;
  53. /* statistics */
  54. int           coda_hard;         /* allows signals during upcalls */
  55. unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
  56. struct venus_comm coda_comms[MAX_CODADEVS];
  57. kmem_cache_t *cii_cache, *cred_cache, *upc_cache;
  58. /*
  59.  * Device operations
  60.  */
  61. static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
  62. {
  63.         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
  64. unsigned int mask = POLLOUT | POLLWRNORM;
  65. poll_wait(file, &vcp->vc_waitq, wait);
  66. if (!list_empty(&vcp->vc_pending))
  67.                 mask |= POLLIN | POLLRDNORM;
  68. return mask;
  69. }
  70. static int coda_psdev_ioctl(struct inode * inode, struct file * filp, 
  71.     unsigned int cmd, unsigned long arg)
  72. {
  73. unsigned int data;
  74. switch(cmd) {
  75. case CIOC_KERNEL_VERSION:
  76. data = CODA_KERNEL_VERSION;
  77. return put_user(data, (int *) arg);
  78. default:
  79. return -ENOTTY;
  80. }
  81. return 0;
  82. }
  83. /*
  84.  * Receive a message written by Venus to the psdev
  85.  */
  86.  
  87. static ssize_t coda_psdev_write(struct file *file, const char *buf, 
  88. size_t nbytes, loff_t *off)
  89. {
  90.         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
  91.         struct upc_req *req = NULL;
  92.         struct upc_req *tmp;
  93. struct list_head *lh;
  94. struct coda_in_hdr hdr;
  95. ssize_t retval = 0, count = 0;
  96. int error;
  97.         /* Peek at the opcode, uniquefier */
  98. if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
  99.         return -EFAULT;
  100. CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), nbytes %ldn", 
  101.        current->pid, hdr.opcode, hdr.unique, (long)nbytes);
  102.         if (DOWNCALL(hdr.opcode)) {
  103. struct super_block *sb = NULL;
  104.                 union outputArgs *dcbuf;
  105. int size = sizeof(*dcbuf);
  106. sb = vcp->vc_sb;
  107. if ( !sb ) {
  108. CDEBUG(D_PSDEV, "coda_psdev_write: downcall, no SB!n");
  109.                         count = nbytes;
  110.                         goto out;
  111. }
  112. CDEBUG(D_PSDEV, "handling downcalln");
  113. if  ( nbytes < sizeof(struct coda_out_hdr) ) {
  114.         printk("coda_downcall opc %ld uniq %ld, not enough!n",
  115.        hdr.opcode, hdr.unique);
  116. count = nbytes;
  117. goto out;
  118. }
  119. if ( nbytes > size ) {
  120.         printk("Coda: downcall opc %ld, uniq %ld, too much!",
  121.        hdr.opcode, hdr.unique);
  122.         nbytes = size;
  123. }
  124. CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
  125. if (copy_from_user(dcbuf, buf, nbytes)) {
  126. CODA_FREE(dcbuf, nbytes);
  127. retval = -EFAULT;
  128. goto out;
  129. }
  130. /* what downcall errors does Venus handle ? */
  131. lock_kernel();
  132. error = coda_downcall(hdr.opcode, dcbuf, sb);
  133. unlock_kernel();
  134. CODA_FREE(dcbuf, nbytes);
  135. if (error) {
  136.         printk("psdev_write: coda_downcall error: %dn", error);
  137. retval = error;
  138. goto out;
  139. }
  140. count = nbytes;
  141. goto out;
  142. }
  143.         
  144. /* Look for the message on the processing queue. */
  145. lock_kernel();
  146. list_for_each(lh, &vcp->vc_processing) {
  147. tmp = list_entry(lh, struct upc_req , uc_chain);
  148. if (tmp->uc_unique == hdr.unique) {
  149. req = tmp;
  150. list_del(&req->uc_chain);
  151. break;
  152. }
  153. }
  154. unlock_kernel();
  155. if (!req) {
  156. printk("psdev_write: msg (%ld, %ld) not foundn", 
  157. hdr.opcode, hdr.unique);
  158. retval = -ESRCH;
  159. goto out;
  160. }
  161. CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!n", hdr.unique);
  162.         /* move data into response buffer. */
  163. if (req->uc_outSize < nbytes) {
  164.                 printk("psdev_write: too much cnt: %d, cnt: %ld, opc: %ld, uniq: %ld.n",
  165.        req->uc_outSize, (long)nbytes, hdr.opcode, hdr.unique);
  166. nbytes = req->uc_outSize; /* don't have more space! */
  167. }
  168.         if (copy_from_user(req->uc_data, buf, nbytes)) {
  169. req->uc_flags |= REQ_ABORT;
  170. wake_up(&req->uc_sleep);
  171. retval = -EFAULT;
  172. goto out;
  173. }
  174. /* adjust outsize. is this useful ?? */
  175.         req->uc_outSize = nbytes;
  176.         req->uc_flags |= REQ_WRITE;
  177. count = nbytes;
  178. /* Convert filedescriptor into a file handle */
  179. if (req->uc_opcode == CODA_OPEN_BY_FD) {
  180. struct coda_open_by_fd_out *outp =
  181. (struct coda_open_by_fd_out *)req->uc_data;
  182. outp->fh = fget(outp->fd);
  183. }
  184. CDEBUG(D_PSDEV, 
  185.        "Found! Count %ld for (opc,uniq)=(%ld,%ld), upc_req at %pn", 
  186.         (long)count, hdr.opcode, hdr.unique, &req);
  187.         wake_up(&req->uc_sleep);
  188. out:
  189.         return(count ? count : retval);  
  190. }
  191. /*
  192.  * Read a message from the kernel to Venus
  193.  */
  194. static ssize_t coda_psdev_read(struct file * file, char * buf, 
  195.        size_t nbytes, loff_t *off)
  196. {
  197. DECLARE_WAITQUEUE(wait, current);
  198.         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
  199.         struct upc_req *req;
  200. ssize_t retval = 0, count = 0;
  201. if (nbytes == 0)
  202. return 0;
  203. lock_kernel();
  204. add_wait_queue(&vcp->vc_waitq, &wait);
  205. set_current_state(TASK_INTERRUPTIBLE);
  206. while (list_empty(&vcp->vc_pending)) {
  207. if (file->f_flags & O_NONBLOCK) {
  208. retval = -EAGAIN;
  209. break;
  210. }
  211. if (signal_pending(current)) {
  212. retval = -ERESTARTSYS;
  213. break;
  214. }
  215. schedule();
  216. }
  217. set_current_state(TASK_RUNNING);
  218. remove_wait_queue(&vcp->vc_waitq, &wait);
  219. if (retval)
  220. goto out;
  221. req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
  222. list_del(&req->uc_chain);
  223. /* Move the input args into userspace */
  224. count = req->uc_inSize;
  225. if (nbytes < req->uc_inSize) {
  226.                 printk ("psdev_read: Venus read %ld bytes of %d in messagen",
  227. (long)nbytes, req->uc_inSize);
  228. count = nbytes;
  229.         }
  230. if (copy_to_user(buf, req->uc_data, count))
  231.         retval = -EFAULT;
  232.         
  233. /* If request was not a signal, enqueue and don't free */
  234. if (!(req->uc_flags & REQ_ASYNC)) {
  235. req->uc_flags |= REQ_READ;
  236. list_add(&(req->uc_chain), vcp->vc_processing.prev);
  237. goto out;
  238. }
  239. CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)n", 
  240. req->uc_opcode, req->uc_unique);
  241. CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
  242. upc_free(req);
  243. out:
  244. unlock_kernel();
  245. return (count ? count : retval);
  246. }
  247. static int coda_psdev_open(struct inode * inode, struct file * file)
  248. {
  249.         struct venus_comm *vcp;
  250. int idx;
  251. lock_kernel();
  252. idx = MINOR(inode->i_rdev);
  253. if(idx >= MAX_CODADEVS) {
  254. unlock_kernel();
  255. return -ENODEV;
  256. }
  257. vcp = &coda_comms[idx];
  258. if(vcp->vc_inuse) {
  259. unlock_kernel();
  260. return -EBUSY;
  261. }
  262. if (!vcp->vc_inuse++) {
  263. INIT_LIST_HEAD(&vcp->vc_pending);
  264. INIT_LIST_HEAD(&vcp->vc_processing);
  265. init_waitqueue_head(&vcp->vc_waitq);
  266. vcp->vc_sb = 0;
  267. vcp->vc_seq = 0;
  268. }
  269. file->private_data = vcp;
  270. CDEBUG(D_PSDEV, "device %i - inuse: %dn", idx, vcp->vc_inuse);
  271. unlock_kernel();
  272.         return 0;
  273. }
  274. static int coda_psdev_release(struct inode * inode, struct file * file)
  275. {
  276.         struct venus_comm *vcp = (struct venus_comm *) file->private_data;
  277.         struct upc_req *req;
  278. struct list_head *lh, *next;
  279. lock_kernel();
  280. if ( !vcp->vc_inuse ) {
  281. unlock_kernel();
  282. printk("psdev_release: Not open.n");
  283. return -1;
  284. }
  285. CDEBUG(D_PSDEV, "psdev_release: inuse %dn", vcp->vc_inuse);
  286. if (--vcp->vc_inuse) {
  287. unlock_kernel();
  288. return 0;
  289. }
  290.         
  291.         /* Wakeup clients so they can return. */
  292. CDEBUG(D_PSDEV, "wake up pending clientsn");
  293. lh = vcp->vc_pending.next;
  294. next = lh;
  295. while ( (lh = next) != &vcp->vc_pending) {
  296. next = lh->next;
  297. req = list_entry(lh, struct upc_req, uc_chain);
  298. /* Async requests need to be freed here */
  299. if (req->uc_flags & REQ_ASYNC) {
  300. CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
  301. upc_free(req);
  302. continue;
  303. }
  304. req->uc_flags |= REQ_ABORT;
  305. wake_up(&req->uc_sleep);
  306.         }
  307.         
  308. lh = &vcp->vc_processing;
  309. CDEBUG(D_PSDEV, "wake up processing clientsn");
  310. while ( (lh = lh->next) != &vcp->vc_processing) {
  311. req = list_entry(lh, struct upc_req, uc_chain);
  312. req->uc_flags |= REQ_ABORT;
  313.         wake_up(&req->uc_sleep);
  314.         }
  315. CDEBUG(D_PSDEV, "Done.n");
  316. unlock_kernel();
  317. return 0;
  318. }
  319. static struct file_operations coda_psdev_fops = {
  320. owner: THIS_MODULE,
  321. read: coda_psdev_read,
  322. write: coda_psdev_write,
  323. poll: coda_psdev_poll,
  324. ioctl: coda_psdev_ioctl,
  325. open: coda_psdev_open,
  326. release: coda_psdev_release,
  327. };
  328. static devfs_handle_t devfs_handle;
  329. static int init_coda_psdev(void)
  330. {
  331. if(devfs_register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev",
  332.  &coda_psdev_fops)) {
  333.               printk(KERN_ERR "coda_psdev: unable to get major %dn", 
  334.      CODA_PSDEV_MAJOR);
  335.               return -EIO;
  336. }
  337. devfs_handle = devfs_mk_dir (NULL, "coda", NULL);
  338. devfs_register_series (devfs_handle, "%u", MAX_CODADEVS, DEVFS_FL_NONE,
  339.        CODA_PSDEV_MAJOR, 0,
  340.        S_IFCHR | S_IRUSR | S_IWUSR,
  341.        &coda_psdev_fops, NULL);
  342. coda_sysctl_init();
  343. return 0;
  344. }
  345. MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
  346. MODULE_LICENSE("GPL");
  347. static int __init init_coda(void)
  348. {
  349. int status;
  350. printk(KERN_INFO "Coda Kernel/Venus communications, v5.3.18, coda@cs.cmu.edun");
  351. status = init_coda_psdev();
  352. if ( status ) {
  353. printk("Problem (%d) in init_coda_psdevn", status);
  354. return status;
  355. }
  356. status = register_filesystem(&coda_fs_type);
  357. if (status) {
  358. printk("coda: failed to register filesystem!n");
  359. devfs_unregister(devfs_handle);
  360. devfs_unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
  361. coda_sysctl_clean();
  362. }
  363. return status;
  364. }
  365. static void __exit exit_coda(void)
  366. {
  367.         int err;
  368. err = unregister_filesystem(&coda_fs_type);
  369.         if ( err != 0 ) {
  370.                 printk("coda: failed to unregister filesystemn");
  371.         }
  372. devfs_unregister(devfs_handle);
  373. devfs_unregister_chrdev(CODA_PSDEV_MAJOR, "coda_psdev");
  374. coda_sysctl_clean();
  375. }
  376. module_init(init_coda);
  377. module_exit(exit_coda);