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

嵌入式Linux

开发平台:

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