Import 2.2.7
[davej-history.git] / fs / coda / psdev.c
bloba252cb46b5e0f1ba5b9a45534468ac447b9baf93
1 /*
2 * An implementation of a loadable kernel mode driver providing
3 * multiple kernel/user space bidirectional communications links.
5 * Author: Alan Cox <alan@cymru.net>
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.
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>
16 * Changes for Linux 2.1
17 * Copyright (c) 1997 Carnegie-Mellon University
20 #include <linux/module.h>
21 #include <linux/errno.h>
22 #include <linux/kernel.h>
23 #include <linux/major.h>
24 #include <linux/sched.h>
25 #include <linux/lp.h>
26 #include <linux/malloc.h>
27 #include <linux/ioport.h>
28 #include <linux/fcntl.h>
29 #include <linux/delay.h>
30 #include <linux/skbuff.h>
31 #include <linux/proc_fs.h>
32 #include <linux/vmalloc.h>
33 #include <linux/fs.h>
34 #include <linux/poll.h>
35 #include <linux/init.h>
36 #include <linux/list.h>
37 #include <asm/io.h>
38 #include <asm/segment.h>
39 #include <asm/system.h>
40 #include <asm/poll.h>
41 #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_cache.h>
48 #include <linux/coda_proc.h>
50 /*
51 * Coda stuff
53 extern struct file_system_type coda_fs_type;
54 extern int init_coda_fs(void);
56 /* statistics */
57 int coda_hard = 0; /* allows signals during upcalls */
58 unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
60 struct coda_sb_info coda_super_info;
61 struct venus_comm coda_upc_comm;
64 * Device operations
67 static unsigned int coda_psdev_poll(struct file *file, poll_table * wait)
69 struct venus_comm *vcp = &coda_upc_comm;
70 unsigned int mask = POLLOUT | POLLWRNORM;
72 poll_wait(file, &(vcp->vc_waitq), wait);
73 if (!list_empty(&vcp->vc_pending))
74 mask |= POLLIN | POLLRDNORM;
76 return mask;
81 * Receive a message written by Venus to the psdev
84 static ssize_t coda_psdev_write(struct file *file, const char *buf,
85 size_t count, loff_t *off)
87 struct venus_comm *vcp = &coda_upc_comm;
88 struct upc_req *req = NULL;
89 struct upc_req *tmp;
90 struct list_head *lh;
91 struct coda_in_hdr hdr;
92 int error;
95 if ( !coda_upc_comm.vc_pid )
96 return -EIO;
97 /* Peek at the opcode, uniquefier */
98 if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
99 return -EFAULT;
101 CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%ld,%ld), count %d\n",
102 current->pid, hdr.opcode, hdr.unique, count);
104 if (DOWNCALL(hdr.opcode)) {
105 struct super_block *sb = NULL;
106 union outputArgs *dcbuf;
107 int size = sizeof(*dcbuf);
109 sb = coda_super_info.sbi_sb;
110 if ( !sb ) {
111 printk("coda_psdev_write: downcall, no SB!\n");
112 return count;
114 CDEBUG(D_PSDEV, "handling downcall\n");
116 if ( count < sizeof(struct coda_out_hdr) ) {
117 printk("coda_downcall opc %ld uniq %ld, not enough!\n",
118 hdr.opcode, hdr.unique);
119 return count;
121 CODA_ALLOC(dcbuf, union outputArgs *, size);
122 if ( count > size ) {
123 printk("Coda: downcall opc %ld, uniq %ld, too much!",
124 hdr.opcode, hdr.unique);
125 count = size;
127 if (copy_from_user(dcbuf, buf, count))
128 return -EFAULT;
130 /* what downcall errors does Venus handle ? */
131 error = coda_downcall(hdr.opcode, dcbuf, sb);
133 if ( error) {
134 printk("psdev_write: coda_downcall error: %d\n",
135 error);
136 return 0;
138 CODA_FREE(dcbuf, size);
139 return count;
143 /* Look for the message on the processing queue. */
144 lh = &vcp->vc_processing;
145 while ( (lh = lh->next) != &vcp->vc_processing ) {
146 tmp = list_entry(lh, struct upc_req , uc_chain);
147 if (tmp->uc_unique == hdr.unique) {
148 req = tmp;
149 list_del(&req->uc_chain);
150 CDEBUG(D_PSDEV,"Eureka: uniq %ld on queue!\n",
151 hdr.unique);
152 break;
155 if (!req) {
156 printk("psdev_write: msg (%ld, %ld) not found\n",
157 hdr.opcode, hdr.unique);
158 return(-ESRCH);
161 /* move data into response buffer. */
162 if (req->uc_outSize < count) {
163 printk("psdev_write: too much cnt: %d, cnt: %d, opc: %ld, uniq: %ld.\n",
164 req->uc_outSize, count, hdr.opcode, hdr.unique);
165 count = req->uc_outSize; /* don't have more space! */
167 if (copy_from_user(req->uc_data, buf, count))
168 return -EFAULT;
170 /* adjust outsize. is this usefull ?? */
171 req->uc_outSize = count;
172 req->uc_flags |= REQ_WRITE;
174 CDEBUG(D_PSDEV,
175 "Found! Count %d for (opc,uniq)=(%ld,%ld), upc_req at %x\n",
176 count, hdr.opcode, hdr.unique, (int)&req);
178 wake_up(&req->uc_sleep);
179 return(count);
183 * Read a message from the kernel to Venus
186 static ssize_t coda_psdev_read(struct file * file, char * buf,
187 size_t count, loff_t *off)
189 struct venus_comm *vcp = &coda_upc_comm;
190 struct upc_req *req;
191 int result = count ;
193 CDEBUG(D_PSDEV, "count %d\n", count);
194 if (list_empty(&(vcp->vc_pending))) {
195 return -1;
198 req = list_entry((vcp->vc_pending.next), struct upc_req, uc_chain);
199 list_del(&(req->uc_chain));
201 /* Move the input args into userspace */
202 if (req->uc_inSize <= count)
203 result = req->uc_inSize;
205 if (count < req->uc_inSize) {
206 printk ("psdev_read: Venus read %d bytes of %d in message\n",
207 count, req->uc_inSize);
210 if ( copy_to_user(buf, req->uc_data, result))
211 return -EFAULT;
213 /* If request was a signal, don't enqueue */
214 if (req->uc_opcode == CODA_SIGNAL) {
215 CDEBUG(D_PSDEV, "vcread: signal msg (%d, %d)\n",
216 req->uc_opcode, req->uc_unique);
217 CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
218 CODA_FREE(req, sizeof(struct upc_req));
219 return count;
222 req->uc_flags |= REQ_READ;
223 list_add(&(req->uc_chain), vcp->vc_processing.prev);
225 return result;
229 static int coda_psdev_open(struct inode * inode, struct file * file)
231 struct venus_comm *vcp = &coda_upc_comm;
232 ENTRY;
234 /* first opener: must be lento. Initialize & take its pid */
235 if ( file->f_flags == O_RDWR ) {
236 if ( vcp->vc_pid ) {
237 printk("Venus pid already set to %d!!\n", vcp->vc_pid);
238 return -1;
240 if ( vcp->vc_inuse ) {
241 printk("psdev_open: Cannot O_RDWR while open.\n");
242 return -1;
246 vcp->vc_inuse++;
247 MOD_INC_USE_COUNT;
250 if ( file->f_flags == O_RDWR ) {
251 vcp->vc_pid = current->pid;
252 vcp->vc_seq = 0;
253 INIT_LIST_HEAD(&vcp->vc_pending);
254 INIT_LIST_HEAD(&vcp->vc_processing);
257 CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
258 vcp->vc_inuse, vcp->vc_pid, current->pid);
260 EXIT;
261 return 0;
266 static int coda_psdev_release(struct inode * inode, struct file * file)
268 struct venus_comm *vcp = &coda_upc_comm;
269 struct upc_req *req;
270 struct list_head *lh, *next;
271 ENTRY;
273 if ( !vcp->vc_inuse ) {
274 printk("psdev_release: Not open.\n");
275 return -1;
278 vcp->vc_inuse--;
279 MOD_DEC_USE_COUNT;
280 CDEBUG(D_PSDEV, "inuse: %d, vc_pid %d, caller %d\n",
281 vcp->vc_inuse, vcp->vc_pid, current->pid);
283 if ( vcp->vc_pid != current->pid ) {
284 return 0;
287 vcp->vc_pid = 0;
288 /* Wakeup clients so they can return. */
289 CDEBUG(D_PSDEV, "wake up pending clients\n");
290 lh = vcp->vc_pending.next;
291 next = lh;
292 while ( (lh = next) != &vcp->vc_pending) {
293 next = lh->next;
294 req = list_entry(lh, struct upc_req, uc_chain);
295 /* Async requests need to be freed here */
296 if (req->uc_flags & REQ_ASYNC) {
297 CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
298 CODA_FREE(req, (u_int)sizeof(struct upc_req));
299 continue;
301 wake_up(&req->uc_sleep);
304 lh = &vcp->vc_processing;
305 CDEBUG(D_PSDEV, "wake up processing clients\n");
306 while ( (lh = lh->next) != &vcp->vc_processing) {
307 req = list_entry(lh, struct upc_req, uc_chain);
308 wake_up(&req->uc_sleep);
310 CDEBUG(D_PSDEV, "Done.\n");
312 EXIT;
313 return 0;
317 static struct file_operations coda_psdev_fops = {
318 NULL, /* llseek */
319 coda_psdev_read, /* read */
320 coda_psdev_write, /* write */
321 NULL, /* coda_psdev_readdir */
322 coda_psdev_poll, /* poll */
323 NULL, /* ioctl */
324 NULL, /* coda_psdev_mmap */
325 coda_psdev_open, /* open */
326 NULL,
327 coda_psdev_release, /* release */
328 NULL, /* fsync */
329 NULL, /* fasync */
330 NULL, /* check_media_change */
331 NULL, /* revalidate */
332 NULL /* lock */
337 __initfunc(int init_coda(void))
339 int status;
340 printk(KERN_INFO "Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n");
342 status = init_coda_psdev();
343 if ( status ) {
344 printk("Problem (%d) in init_coda_psdev\n", status);
345 return status;
348 status = init_coda_fs();
349 if (status) {
350 printk("coda: failed in init_coda_fs!\n");
352 return status;
355 int init_coda_psdev(void)
357 if(register_chrdev(CODA_PSDEV_MAJOR,"coda_psdev", &coda_psdev_fops)) {
358 printk(KERN_ERR "coda_psdev: unable to get major %d\n",
359 CODA_PSDEV_MAJOR);
360 return -EIO;
362 memset(&coda_upc_comm, 0, sizeof(coda_upc_comm));
363 memset(&coda_super_info, 0, sizeof(coda_super_info));
365 coda_sysctl_init();
367 return 0;
371 #ifdef MODULE
373 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
375 int init_module(void)
377 int status;
378 printk(KERN_INFO "Coda Kernel/Venus communications (module), v5.0-pre1, braam@cs.cmu.edu.\n");
380 status = init_coda_psdev();
381 if ( status ) {
382 printk("Problem (%d) in init_coda_psdev\n", status);
383 return status;
386 status = init_coda_fs();
387 if (status) {
388 printk("coda: failed in init_coda_fs!\n");
390 return status;
394 void cleanup_module(void)
396 int err;
398 ENTRY;
400 if ( (err = unregister_filesystem(&coda_fs_type)) != 0 ) {
401 printk("coda: failed to unregister filesystem\n");
403 unregister_chrdev(CODA_PSDEV_MAJOR,"coda_psdev");
404 coda_sysctl_clean();
407 #endif