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>
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>
34 #include <linux/poll.h>
35 #include <linux/init.h>
36 #include <linux/list.h>
38 #include <asm/segment.h>
39 #include <asm/system.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>
53 extern struct file_system_type coda_fs_type
;
54 extern int init_coda_fs(void);
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
;
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
;
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
;
91 struct coda_in_hdr hdr
;
95 if ( !coda_upc_comm
.vc_pid
)
97 /* Peek at the opcode, uniquefier */
98 if (copy_from_user(&hdr
, buf
, 2 * sizeof(u_long
)))
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
;
111 printk("coda_psdev_write: downcall, no SB!\n");
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
);
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
);
127 if (copy_from_user(dcbuf
, buf
, count
))
130 /* what downcall errors does Venus handle ? */
131 error
= coda_downcall(hdr
.opcode
, dcbuf
, sb
);
134 printk("psdev_write: coda_downcall error: %d\n",
138 CODA_FREE(dcbuf
, size
);
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
) {
149 list_del(&req
->uc_chain
);
150 CDEBUG(D_PSDEV
,"Eureka: uniq %ld on queue!\n",
156 printk("psdev_write: msg (%ld, %ld) not found\n",
157 hdr
.opcode
, hdr
.unique
);
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
))
170 /* adjust outsize. is this usefull ?? */
171 req
->uc_outSize
= count
;
172 req
->uc_flags
|= REQ_WRITE
;
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
);
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
;
193 CDEBUG(D_PSDEV
, "count %d\n", count
);
194 if (list_empty(&(vcp
->vc_pending
))) {
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
))
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
));
222 req
->uc_flags
|= REQ_READ
;
223 list_add(&(req
->uc_chain
), vcp
->vc_processing
.prev
);
229 static int coda_psdev_open(struct inode
* inode
, struct file
* file
)
231 struct venus_comm
*vcp
= &coda_upc_comm
;
234 /* first opener: must be lento. Initialize & take its pid */
235 if ( file
->f_flags
== O_RDWR
) {
237 printk("Venus pid already set to %d!!\n", vcp
->vc_pid
);
240 if ( vcp
->vc_inuse
) {
241 printk("psdev_open: Cannot O_RDWR while open.\n");
250 if ( file
->f_flags
== O_RDWR
) {
251 vcp
->vc_pid
= current
->pid
;
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
);
266 static int coda_psdev_release(struct inode
* inode
, struct file
* file
)
268 struct venus_comm
*vcp
= &coda_upc_comm
;
270 struct list_head
*lh
, *next
;
273 if ( !vcp
->vc_inuse
) {
274 printk("psdev_release: Not open.\n");
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
) {
288 /* Wakeup clients so they can return. */
289 CDEBUG(D_PSDEV
, "wake up pending clients\n");
290 lh
= vcp
->vc_pending
.next
;
292 while ( (lh
= next
) != &vcp
->vc_pending
) {
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
));
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");
317 static struct file_operations coda_psdev_fops
= {
319 coda_psdev_read
, /* read */
320 coda_psdev_write
, /* write */
321 NULL
, /* coda_psdev_readdir */
322 coda_psdev_poll
, /* poll */
324 NULL
, /* coda_psdev_mmap */
325 coda_psdev_open
, /* open */
327 coda_psdev_release
, /* release */
330 NULL
, /* check_media_change */
331 NULL
, /* revalidate */
337 __initfunc(int init_coda(void))
340 printk(KERN_INFO
"Coda Kernel/Venus communications, v4.6.0, braam@cs.cmu.edu\n");
342 status
= init_coda_psdev();
344 printk("Problem (%d) in init_coda_psdev\n", status
);
348 status
= init_coda_fs();
350 printk("coda: failed in init_coda_fs!\n");
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",
362 memset(&coda_upc_comm
, 0, sizeof(coda_upc_comm
));
363 memset(&coda_super_info
, 0, sizeof(coda_super_info
));
373 MODULE_AUTHOR("Peter J. Braam <braam@cs.cmu.edu>");
375 int init_module(void)
378 printk(KERN_INFO
"Coda Kernel/Venus communications (module), v5.0-pre1, braam@cs.cmu.edu.\n");
380 status
= init_coda_psdev();
382 printk("Problem (%d) in init_coda_psdev\n", status
);
386 status
= init_coda_fs();
388 printk("coda: failed in init_coda_fs!\n");
394 void cleanup_module(void)
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");