/*
 *              An implementation of a loadable kernel mode driver providing
 *              multiple kernel/user space bidirectional communications links.
 *
 *              Author:         Alan Cox <alan@cymru.net>
 *
 *              This program is free software; you can redistribute it and/or
 *              modify it under the terms of the GNU General Public License
 *              as published by the Free Software Foundation; either version
 *              2 of the License, or (at your option) any later version.
 *
 *              Adapted to become the Linux 2.0 Coda pseudo device
 *              Peter  Braam  <braam@maths.ox.ac.uk>
 *              Michael Callahan <mjc@emmy.smith.edu>
 *
 *              Changes for Linux 2.1
 *              Copyright (c) 1997 Carnegie-Mellon University
 *
 *              Redone again for InterMezzo
 *              Copyright (c) 1998 Peter J. Braam
 *              Copyright (c) 2000 Mountain View Data, Inc.
 *              Copyright (c) 2000 Tacitus Systems, Inc.
 *              Copyright (c) 2001 Cluster File Systems, Inc.
 *
 *		Extended attribute support
 *		Copyright (c) 2001 Shirish. H. Phatak
 *		Copyright (c) 2001 Tacit Networks, Inc.
 */


#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/list.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/poll.h>
#include <asm/uaccess.h>

#include <linux/intermezzo_fs.h>
#include <linux/intermezzo_upcall.h>
#include <linux/intermezzo_psdev.h>
#include <linux/intermezzo_kml.h>


#ifdef PRESTO_DEVEL
int  presto_print_entry = 1;
int  presto_debug = 4095;
#else
int  presto_print_entry = 0;
int  presto_debug = 0;
#endif

/* Like inode.c (presto_sym_iops), the initializer is just to prevent
   upc_comms from appearing as a COMMON symbol (and therefore
   interfering with other modules that use the same variable name. */
struct upc_comm upc_comms[MAX_PRESTODEV] = {{0}};

/*
 * Device operations: map file to upcall structure
 */
static inline struct upc_comm *presto_psdev_f2u(struct file *file)
{
        int minor;

        if ( MAJOR(file->f_dentry->d_inode->i_rdev) != PRESTO_PSDEV_MAJOR ) {
                EXIT;
                return NULL;
        }

        minor = MINOR(file->f_dentry->d_inode->i_rdev);
        if ( minor < 0 || minor >= MAX_PRESTODEV ) {
                EXIT;
                return NULL;
        }

        return &(upc_comms[minor]);
}

inline int presto_lento_up(int minor) 
{
        return upc_comms[minor].uc_pid;
}


static unsigned int presto_psdev_poll(struct file *file, poll_table * wait)
{
        struct upc_comm *upccom;
        unsigned int mask = POLLOUT | POLLWRNORM;
        /* ENTRY; this will flood you */

        if ( ! (upccom = presto_psdev_f2u(file)) ) {
                kdev_t dev = file->f_dentry->d_inode->i_rdev;
                printk("InterMezzo: %s, bad device %s\n",
                       __FUNCTION__, kdevname(dev));
        }

        poll_wait(file, &(upccom->uc_waitq), wait);

        if (!list_empty(&upccom->uc_pending)) {
                CDEBUG(D_PSDEV, "Non-empty pending list.\n");
                mask |= POLLIN | POLLRDNORM;
        }

        /* EXIT; will flood you */
        return mask;
}



/*
 *      Receive a message written by Lento to the psdev
 */
static ssize_t presto_psdev_write(struct file *file, const char *buf,
                                  size_t count, loff_t *off)
{
        struct upc_comm *upccom;
        struct upc_req *req = NULL;
        struct upc_req *tmp;
        struct list_head *lh;
        struct lento_down_hdr hdr;
        int error;

        if ( ! (upccom = presto_psdev_f2u(file)) ) {
                kdev_t dev = file->f_dentry->d_inode->i_rdev;
                printk("InterMezzo: %s, bad device %s\n",
                       __FUNCTION__, kdevname(dev));
        }

        /* Peek at the opcode, uniquefier */
        if ( count < sizeof(hdr) ) {
              printk("presto_psdev_write: Lento didn't write full hdr.\n");
                return -EINVAL;
        }

        error = copy_from_user(&hdr, buf, sizeof(hdr));
        if ( error )
                return error;

        CDEBUG(D_PSDEV, "(process,opc,uniq)=(%d,%d,%d)\n",
               current->pid, hdr.opcode, hdr.unique);

        /* Look for the message on the processing queue. */
        lh  = &upccom->uc_processing;
        while ( (lh = lh->next) != &upccom->uc_processing ) {
                tmp = list_entry(lh, struct upc_req , rq_chain);
                if (tmp->rq_unique == hdr.unique) {
                        req = tmp;
                      /* unlink here: keeps search length minimal */
                        list_del(&req->rq_chain);
                      INIT_LIST_HEAD(&req->rq_chain);
                        CDEBUG(D_PSDEV,"Eureka opc %d uniq %d!\n",
                               hdr.opcode, hdr.unique);
                        break;
                }
        }
        if (!req) {
                printk("psdev_write: msg (%d, %d) not found\n",
                       hdr.opcode, hdr.unique);
                return(-ESRCH);
        }

        /* move data into response buffer. */
        if (req->rq_bufsize < count) {
                printk("psdev_write: too much cnt: %d, cnt: %Zd, "
                       "opc: %d, uniq: %d.\n",
                       req->rq_bufsize, count, hdr.opcode, hdr.unique);
                count = req->rq_bufsize; /* don't have more space! */
        }
        error = copy_from_user(req->rq_data, buf, count);
        if ( error )
                return error;

        /* adjust outsize: good upcalls can be aware of this */
        req->rq_rep_size = count;
        req->rq_flags |= REQ_WRITE;

        wake_up(&req->rq_sleep);
        return(count);
}

/*
 *      Read a message from the kernel to Lento
 */
static ssize_t presto_psdev_read(struct file * file, char * buf,
                                 size_t count, loff_t *off)
{
        struct upc_comm *upccom;
        struct upc_req *req;
        int result = count;

        if ( ! (upccom = presto_psdev_f2u(file)) ) {
                kdev_t dev = file->f_dentry->d_inode->i_rdev;
                printk("InterMezzo: %s, bad device %s\n",
                       __FUNCTION__, kdevname(dev));
        }

        CDEBUG(D_PSDEV, "count %Zd\n", count);
        if (list_empty(&(upccom->uc_pending))) {
                CDEBUG(D_UPCALL, "Empty pending list in read, not good\n");
                return -EINVAL;
        }

        req = list_entry((upccom->uc_pending.next), struct upc_req, rq_chain);
        list_del(&(req->rq_chain));
      if (! (req->rq_flags & REQ_ASYNC) ) {
              list_add(&(req->rq_chain), upccom->uc_processing.prev);
      }
      req->rq_flags |= REQ_READ;

        /* Move the input args into userspace */
        if (req->rq_bufsize <= count) {
                result = req->rq_bufsize;
        }

        if (count < req->rq_bufsize) {
                printk ("psdev_read: buffer too small, read %Zd of %d bytes\n",
                        count, req->rq_bufsize);
        }

        if ( copy_to_user(buf, req->rq_data, result) ) {
                return -EFAULT;
        }

        /* If request was asynchronous don't enqueue, but free */
        if (req->rq_flags & REQ_ASYNC) {
                CDEBUG(D_PSDEV, "psdev_read: async msg (%d, %d), result %d\n",
                       req->rq_opcode, req->rq_unique, result);
                PRESTO_FREE(req->rq_data, req->rq_bufsize);
                PRESTO_FREE(req, sizeof(*req));
                return result;
        }

        return result;
}

static int presto_psdev_ioctl(struct inode *inode, struct file *file,
                              unsigned int cmd, unsigned long arg)
{
        struct upc_comm *upccom;
        /* XXX is this rdev or dev? */
        kdev_t dev = inode->i_rdev;

        ENTRY;
        upccom = presto_psdev_f2u(file);
        if ( !upccom) {
                printk("InterMezzo: %s, bad device %s\n",
                       __FUNCTION__, kdevname(dev));
                EXIT;
                return -ENODEV;
        }

        switch(cmd) {

        case TCGETS:
                return -EINVAL;

        case PRESTO_GETMOUNT: {
                /* return all the mounts for this device.  */
                int minor = 0;
                int len, outlen;
                struct readmount readmount;
                struct readmount *user_readmount = (struct readmount *) arg;
                char * tmp;
                int error;

                error = copy_from_user(&readmount, (void *)arg,
                                       sizeof(readmount));
                if ( error )  {
                        printk("psdev: can't copy %Zd bytes from %p to %p\n",
                                sizeof(readmount), (struct readmount *) arg,
                                &readmount);
                        EXIT;
                        return error;
                }

                len = readmount.io_len;
                minor = MINOR(dev);
                PRESTO_ALLOC(tmp, char *, len);
                if (!tmp) {
                        EXIT;
                        return -ENOMEM;
                }

                outlen = presto_sprint_mounts(tmp, len, minor);
                CDEBUG(D_PSDEV, "presto_sprint_mounts returns %d bytes\n",
                                outlen);

                /* as this came out on 1/3/2000, it could NEVER work.
                 * So fix it ... RGM
                 * I mean, let's let the compiler do a little work ...
                 * gcc suggested the extra ()
                 */
                error = copy_to_user(readmount.io_string, tmp, outlen);
                if ( error ) {
                        CDEBUG(D_PSDEV, "Copy_to_user string 0x%p failed\n",
                               readmount.io_string);
                }
                if ((!error) && (error = copy_to_user(&(user_readmount->io_len),
                                                      &outlen, sizeof(int))) ) {
                        CDEBUG(D_PSDEV, "Copy_to_user len @0x%p failed\n",
                               &(user_readmount->io_len));
                }

                PRESTO_FREE(tmp, len);
                EXIT;
                return error;
        }

        case PRESTO_SETPID: {
                /*
                 * This ioctl is performed by each Lento that starts up
                 * and wants to do further communication with presto.
                 */
                CDEBUG(D_PSDEV, "Setting current pid to %d\n", current->pid);
                upccom->uc_pid = current->pid;
                if ( !list_empty(&upccom->uc_processing) ) {
                        struct list_head *lh;
                        struct upc_req *req;
                        printk("WARNING: setpid & processing not empty!\n");
                        lh = &upccom->uc_processing;
                        while ( (lh = lh->next) != &upccom->uc_processing) {
                                req = list_entry(lh, struct upc_req, rq_chain);
                                /* freeing of req and data is done by the sleeper */
                                wake_up(&req->rq_sleep);
                        }
                }
                if ( !list_empty(&upccom->uc_processing) ) {
                        printk("BAD: FAILDED TO CLEAN PROCESSING LIST!\n");
                }
                EXIT;
                return 0;
        }

        case PRESTO_CLEAR_FSETROOT: {
                /*
                 * Close KML files.
                 */
                int error;
                int saved_pid = upccom->uc_pid;
                char *path;
                struct {
                        char *path;
                        int   path_len;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                PRESTO_ALLOC(path, char *, input.path_len + 1);
                if ( !path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(path, input.path, input.path_len);
                if ( error ) {
                        PRESTO_FREE(path, input.path_len + 1);
                        EXIT;
                        return error;
                }
                path[input.path_len] = '\0';
                CDEBUG(D_PSDEV, "clear_fsetroot: path %s\n", path);

                upccom->uc_pid = current->pid;
                error = presto_clear_fsetroot(path);
                upccom->uc_pid = saved_pid;
                PRESTO_FREE(path, input.path_len + 1);
                EXIT;
                return error;
        }


        case PRESTO_CLEAR_ALL_FSETROOTS: {
                /*
                 * Close KML files.
                 */
                int error;
                int saved_pid = upccom->uc_pid;
                char *path;
                struct {
                        char *path;
                        int   path_len;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                PRESTO_ALLOC(path, char *, input.path_len + 1);
                if ( !path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(path, input.path, input.path_len);
                if ( error ) {
                        PRESTO_FREE(path, input.path_len + 1);
                        EXIT;
                        return error;
                }
                path[input.path_len] = '\0';
                CDEBUG(D_PSDEV, "clear_all_fsetroot: path %s\n", path);

                upccom->uc_pid = current->pid;
                error = presto_clear_all_fsetroots(path);
                upccom->uc_pid = saved_pid;
                PRESTO_FREE(path, input.path_len + 1);
                EXIT;
                return error;
        }

        case PRESTO_GET_KMLSIZE: {
                int error;
                int saved_pid = upccom->uc_pid;
                char *path;
                size_t size = 0;
                struct {
                        __u64 size;
                        char *path;
                        int   path_len;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                PRESTO_ALLOC(path, char *, input.path_len + 1);
                if ( !path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(path, input.path, input.path_len);
                if ( error ) {
                        PRESTO_FREE(path, input.path_len + 1);
                        EXIT;
                        return error;
                }
                path[input.path_len] = '\0';
                CDEBUG(D_PSDEV, "get_kmlsize: len %d path %s\n", 
                       input.path_len, path);

                upccom->uc_pid = current->pid;
                error = presto_get_kmlsize(path, &size);
                PRESTO_FREE(path, input.path_len + 1);
                if (error) {
                        EXIT;
                        return error;
                }
                input.size = size;
                upccom->uc_pid = saved_pid;

                CDEBUG(D_PSDEV, "get_kmlsize: size = %Zd\n", size);

                EXIT;
                return copy_to_user((char *)arg, &input, sizeof(input));
        }

        case PRESTO_GET_RECNO: {
                int error;
                int saved_pid = upccom->uc_pid;
                char *path;
                off_t recno = 0;
                struct {
                        __u64 recno;
                        char *path;
                        int   path_len;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                PRESTO_ALLOC(path, char *, input.path_len + 1);
                if ( !path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(path, input.path, input.path_len);
                if ( error ) {
                        PRESTO_FREE(path, input.path_len + 1);
                        EXIT;
                        return error;
                }
                path[input.path_len] = '\0';
                CDEBUG(D_PSDEV, "get_recno: len %d path %s\n", 
                       input.path_len, path);

                upccom->uc_pid = current->pid;
                error = presto_get_lastrecno(path, &recno);
                PRESTO_FREE(path, input.path_len + 1);
                if (error) {
                        EXIT;
                        return error;
                }
                input.recno = recno;
                upccom->uc_pid = saved_pid;

                CDEBUG(D_PSDEV, "get_recno: recno = %d\n", (int) recno);

                EXIT;
                return copy_to_user((char *)arg, &input, sizeof(input));
        }

        case PRESTO_SET_FSETROOT: {
                /*
                 * Save information about the cache, and initialize "special"
                 * cache files (KML, etc).
                 */
                int error;
                int saved_pid = upccom->uc_pid;
                char *fsetname;
                char *path;
                struct {
                        char *path;
                        int   path_len;
                        char *name;
                        int   name_len;
                        int   id;
                        int   flags;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                PRESTO_ALLOC(path, char *, input.path_len + 1);
                if ( !path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(path, input.path, input.path_len);
                if ( error ) {
                        EXIT;
                        goto exit_free_path;
                }
                path[input.path_len] = '\0';

                PRESTO_ALLOC(fsetname, char *, input.name_len + 1);
                if ( !fsetname ) {
                        error = -ENOMEM;
                        EXIT;
                        goto exit_free_path;
                }
                error = copy_from_user(fsetname, input.name, input.name_len);
                if ( error ) {
                        EXIT;
                        goto exit_free_fsetname;
                }
                fsetname[input.name_len] = '\0';

                CDEBUG(D_PSDEV,
                       "set_fsetroot: path %s name %s, id %d, flags %x\n",
                       path, fsetname, input.id, input.flags);
                upccom->uc_pid = current->pid;
                error = presto_set_fsetroot(path, fsetname, input.id,input.flags);
                upccom->uc_pid = saved_pid;
                if ( error ) {
                        EXIT;
                        goto exit_free_fsetname;
                }
                /* fsetname is kept in the fset, so don't free it now */
                PRESTO_FREE(path, input.path_len + 1);
                EXIT;
                return 0;

        exit_free_fsetname:
                PRESTO_FREE(fsetname, input.name_len + 1);
        exit_free_path:
                PRESTO_FREE(path, input.path_len + 1);
                return error;
        }

        case PRESTO_CLOSE_JOURNALF: {
                int saved_pid = upccom->uc_pid;
                int error;

                CDEBUG(D_SUPER, "HELLO\n");

                /* pretend we are lento: we should lock something */
                upccom->uc_pid = current->pid;
                error = presto_close_journal_file(NULL);
                CDEBUG(D_PSDEV, "error is %d\n", error);
                upccom->uc_pid = saved_pid;
                EXIT;
                return error;
        }

        case PRESTO_GETOPT:
        case PRESTO_SETOPT: {
                /* return all the mounts for this device.  */
                int dosetopt(int, struct psdev_opt *);
                int dogetopt(int, struct psdev_opt *);
                int minor = 0;
                struct psdev_opt kopt;
                struct psdev_opt *user_opt = (struct psdev_opt *) arg;
                int error;

                error = copy_from_user(&kopt, (void *)arg, sizeof(kopt));
                if ( error )  {
                        printk("psdev: can't copyin %Zd bytes from %p to %p\n",
                               sizeof(kopt), (struct kopt *) arg, &kopt);
                        EXIT;
                        return error;
                }
                minor = MINOR(dev);
                if (cmd == PRESTO_SETOPT)
                        error = dosetopt(minor, &kopt);

                if ( error ) {
                        CDEBUG(D_PSDEV,
                               "dosetopt failed minor %d, opt %d, val %d\n",
                               minor, kopt.optname, kopt.optval);
                        EXIT;
                        return error;
                }

                error = dogetopt(minor, &kopt);

                if ( error ) {
                        CDEBUG(D_PSDEV,
                               "dogetopt failed minor %d, opt %d, val %d\n",
                               minor, kopt.optname, kopt.optval);
                        EXIT;
                        return error;
                }

                error = copy_to_user(user_opt, &kopt, sizeof(kopt));
                if ( error ) {
                        CDEBUG(D_PSDEV, "Copy_to_user opt 0x%p failed\n",
                               user_opt);
                        EXIT;
                        return error;
                }
                CDEBUG(D_PSDEV, "dosetopt minor %d, opt %d, val %d return %d\n",
                         minor, kopt.optname, kopt.optval, error);
                EXIT;
                return 0;
        }

        case PRESTO_VFS_SETATTR: {
                int error;
                struct lento_input_attr input;
                struct iattr iattr;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                iattr.ia_valid = input.valid;
                iattr.ia_mode  = (umode_t)input.mode;
                iattr.ia_uid   = (uid_t)input.uid;
                iattr.ia_gid   = (gid_t)input.gid;
                iattr.ia_size  = (off_t)input.size;
                iattr.ia_atime = (time_t)input.atime;
                iattr.ia_mtime = (time_t)input.mtime;
                iattr.ia_ctime = (time_t)input.ctime;
                iattr.ia_attr_flags = input.attr_flags;

                error = lento_setattr(input.name, &iattr, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_CREATE: {
                int error;
                struct lento_input_mode input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_create(input.name, input.mode, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_LINK: {
                int error;
                struct lento_input_old_new input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_link(input.oldname, input.newname, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_UNLINK: {
                int error;
                struct lento_input input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_unlink(input.name, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_SYMLINK: {
                int error;
                struct lento_input_old_new input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_symlink(input.oldname, input.newname,&input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_MKDIR: {
                int error;
                struct lento_input_mode input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_mkdir(input.name, input.mode, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_RMDIR: {
                int error;
                struct lento_input input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_rmdir(input.name, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_MKNOD: {
                int error;
                struct lento_input_dev input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_mknod(input.name, input.mode,
                                    MKDEV(input.major,input.minor),&input.info);
                EXIT;
                return error;
        }

        case PRESTO_VFS_RENAME: {
                int error;
                struct lento_input_old_new input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                error = lento_rename(input.oldname, input.newname, &input.info);
                EXIT;
                return error;
        }

#ifdef CONFIG_FS_EXT_ATTR
        /* IOCTL to create/modify an extended attribute */
        case PRESTO_VFS_SETEXTATTR: {
                int error;
                struct lento_input_ext_attr input;
                char *name;
                char *buffer;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) { 
                    EXIT;
                    return error;
                }

                /* Now setup the input parameters */
                PRESTO_ALLOC(name, char *, input.name_len+1);
                /* We need null terminated strings for attr names */
                name[input.name_len] = '\0';
                error=copy_from_user(name, input.name, input.name_len);
                if ( error ) { 
                    EXIT;
                    PRESTO_FREE(name,input.name_len+1);
                    return error;
                }

                PRESTO_ALLOC(buffer, char *, input.buffer_len+1);
                error=copy_from_user(buffer, input.buffer, input.buffer_len);
                if ( error ) { 
                    EXIT;
                    PRESTO_FREE(name,input.name_len+1);
                    PRESTO_FREE(buffer,input.buffer_len+1);
                    return error;
                }
                /* Make null terminated for easy printing */
                buffer[input.buffer_len]='\0';
 
                CDEBUG(D_PSDEV," setextattr params: name %s, valuelen %d,"
                       " value %s, attr flags %x, mode %o, slot offset %d,"
                       " recno %d, kml offset %lu, flags %x, time %d\n", 
                       name, input.buffer_len, buffer, input.flags, input.mode,
                       input.info.slot_offset, input.info.recno,
                       (unsigned long) input.info.kml_offset, input.info.flags,
                       input.info.updated_time);

                error=lento_set_ext_attr
                      (input.path,name,buffer,input.buffer_len,
                       input.flags, input.mode, &input.info);

                PRESTO_FREE(name,input.name_len+1);
                PRESTO_FREE(buffer,input.buffer_len+1);
                EXIT;
                return error;
        }

        /* IOCTL to delete an extended attribute */
        case PRESTO_VFS_DELEXTATTR: {
                int error;
                struct lento_input_ext_attr input;
                char *name;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) { 
                    EXIT;
                    return error;
                }

                /* Now setup the input parameters */
                PRESTO_ALLOC(name, char *, input.name_len+1);
                /* We need null terminated strings for attr names */
                name[input.name_len] = '\0';
                error=copy_from_user(name, input.name, input.name_len);
                if ( error ) { 
                    EXIT;
                    PRESTO_FREE(name,input.name_len+1);
                    return error;
                }

                CDEBUG(D_PSDEV," delextattr params: name %s,"
                       " attr flags %x, mode %o, slot offset %d, recno %d,"
                       " kml offset %lu, flags %x, time %d\n", 
                       name, input.flags, input.mode,
                       input.info.slot_offset, input.info.recno,
                       (unsigned long) input.info.kml_offset, input.info.flags,
                       input.info.updated_time);

                error=lento_set_ext_attr
                      (input.path,name,NULL,0,input.flags,
                       input.mode,&input.info);
                PRESTO_FREE(name,input.name_len+1);
                EXIT;
                return error;
        }
#endif

        case PRESTO_VFS_IOPEN: {
                struct lento_input_iopen input;
                int error;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                input.fd = lento_iopen(input.name, (ino_t)input.ino,
                                       input.generation, input.flags);
                CDEBUG(D_PIOCTL, "lento_iopen file descriptor: %d\n", input.fd);
                if (input.fd < 0) {
                        EXIT;
                        return input.fd;
                }
                EXIT;
                return copy_to_user((char *)arg, &input, sizeof(input));
        }

        case PRESTO_VFS_CLOSE: {
                int error;
                struct lento_input_close input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }

                CDEBUG(D_PIOCTL, "lento_close file descriptor: %d\n", input.fd);
                error = lento_close(input.fd, &input.info);
                EXIT;
                return error;
        }

        case PRESTO_BACKFETCH_LML: {
                char *user_path;
                int error;
                struct lml_arg {
                        char *path;
                        __u32 path_len;
                        __u64 remote_ino;
                        __u32 remote_generation;
                        __u32 remote_version;
                        struct presto_version remote_file_version;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                user_path = input.path;

                PRESTO_ALLOC(input.path, char *, input.path_len + 1);
                if ( !input.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(input.path, user_path, input.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(input.path, input.path_len + 1);
                        return error;
                }
                input.path[input.path_len] = '\0';

                CDEBUG(D_DOWNCALL, "lml name: %s\n", input.path);
                
                return lento_write_lml(input.path, 
                                       input.remote_ino, 
                                       input.remote_generation,
                                       input.remote_version,
                                       &input.remote_file_version); 

        }
                

        case PRESTO_CANCEL_LML: {
                char *user_path;
                int error;
                struct lml_arg {
                        char *path;
                        __u64 lml_offset; 
                        __u32 path_len;
                        __u64 remote_ino;
                        __u32 remote_generation;
                        __u32 remote_version;
                        struct lento_vfs_context info;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                user_path = input.path;

                PRESTO_ALLOC(input.path, char *, input.path_len + 1);
                if ( !input.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(input.path, user_path, input.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(input.path, input.path_len + 1);
                        return error;
                }
                input.path[input.path_len] = '\0';

                CDEBUG(D_DOWNCALL, "lml name: %s\n", input.path);
                
                return lento_cancel_lml(input.path, 
                                        input.lml_offset, 
                                        input.remote_ino, 
                                        input.remote_generation,
                                        input.remote_version,
                                        &input.info); 

        }

        case PRESTO_COMPLETE_CLOSES: {
                char *user_path;
                int error;
                struct lml_arg {
                        char *path;
                        __u32 path_len;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                user_path = input.path;

                PRESTO_ALLOC(input.path, char *, input.path_len + 1);
                if ( !input.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(input.path, user_path, input.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(input.path, input.path_len + 1);
                        return error;
                }
                input.path[input.path_len] = '\0';

                CDEBUG(D_DOWNCALL, "lml name: %s\n", input.path);
                
                error = lento_complete_closes(input.path);
                PRESTO_FREE(input.path, input.path_len + 1);
                return error;
        }

        case PRESTO_RESET_FSET: {
                char *user_path;
                int error;
                struct lml_arg {
                        char *path;
                        __u32 path_len;
                        __u64 offset;
                        __u32 recno;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                user_path = input.path;

                PRESTO_ALLOC(input.path, char *, input.path_len + 1);
                if ( !input.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(input.path, user_path, input.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(input.path, input.path_len + 1);
                        return error;
                }
                input.path[input.path_len] = '\0';

                CDEBUG(D_DOWNCALL, "lml name: %s\n", input.path);
                
                return lento_reset_fset(input.path, input.offset, input.recno); 

        }
                

        case PRESTO_MARK: {
                char *user_path;
                int res = 0;  /* resulting flags - returned to user */
                int error;
                struct {
                        int  mark_what;
                        int  and_flag;
                        int  or_flag;
                        int path_len;
                        char *path;
                } input;

                error = copy_from_user(&input, (char *)arg, sizeof(input));
                if ( error ) {
                        EXIT;
                        return error;
                }
                user_path = input.path;

                PRESTO_ALLOC(input.path, char *, input.path_len + 1);
                if ( !input.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(input.path, user_path, input.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(input.path, input.path_len + 1);
                        return error;
                }
                input.path[input.path_len] = '\0';

                CDEBUG(D_DOWNCALL, "mark name: %s, and: %x, or: %x, what %d\n",
                       input.path, input.and_flag, input.or_flag, 
                       input.mark_what);

                switch (input.mark_what) {
                case MARK_DENTRY:               
                        error = presto_mark_dentry(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &res);
                        break;
                case MARK_FSET:
                        error = presto_mark_fset(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &res);
                        break;
                case MARK_CACHE:
                        error = presto_mark_cache(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &res);
                        break;
                case MARK_GETFL: {
                        int fflags, cflags;
                        input.and_flag = 0xffffffff;
                        input.or_flag = 0; 
                        error = presto_mark_dentry(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &res);
                        if (error) 
                                break;
                        error = presto_mark_fset(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &fflags);
                        if (error) 
                                break;
                        error = presto_mark_cache(input.path,
                                                   input.and_flag,
                                                   input.or_flag, &cflags);

                        if (error) 
                                break;
                        input.and_flag = fflags;
                        input.or_flag = cflags;
                	break;
                }
                default:
                        error = -EINVAL;
                }

                PRESTO_FREE(input.path, input.path_len + 1);
                if (error == -EBUSY) {
                        input.and_flag = error;
                        error = 0;
                }
                if (error) { 
                        EXIT;
                        return error;
                }
                /* return the correct cookie to wait for */
                input.mark_what = res;
                return copy_to_user((char *)arg, &input, sizeof(input));
        }

#ifdef  CONFIG_KREINT
        case PRESTO_REINT_BEGIN:
                return begin_kml_reint (file, arg);
        case PRESTO_DO_REINT:
                return do_kml_reint (file, arg);
        case PRESTO_REINT_END:
                return end_kml_reint (file, arg);
#endif

        case PRESTO_RELEASE_PERMIT: {
                int error;
                char *user_path;
                struct {
                        int  cookie;
                        int path_len;
                        char *path;
                } permit;
                
                error = copy_from_user(&permit, (char *)arg, sizeof(permit));
                if ( error ) {
                        EXIT;
                        return error;
                        }
                user_path = permit.path;
                
                PRESTO_ALLOC(permit.path, char *, permit.path_len + 1);
                if ( !permit.path ) {
                        EXIT;
                        return -ENOMEM;
                }
                error = copy_from_user(permit.path, user_path, permit.path_len);
                if ( error ) {
                        EXIT;
                        PRESTO_FREE(permit.path, permit.path_len + 1);
                        return error;
                }
                permit.path[permit.path_len] = '\0';
                
                CDEBUG(D_DOWNCALL, "release permit: %s, in cookie=%d\n",
                       permit.path, permit.cookie);
                error = presto_permit_downcall(permit.path, &permit.cookie);
                
                PRESTO_FREE(permit.path, permit.path_len + 1);
                if (error) {
                        EXIT;
                        return error;
                }
                /* return the correct cookie to wait for */
                return copy_to_user((char *)arg, &permit, sizeof(permit));
        }
        
        default:
                CDEBUG(D_PSDEV, "bad ioctl 0x%x, \n", cmd);
                CDEBUG(D_PSDEV, "valid are 0x%Zx - 0x%Zx, 0x%Zx - 0x%Zx \n",
                        PRESTO_GETMOUNT, PRESTO_GET_KMLSIZE,
                        PRESTO_VFS_SETATTR, PRESTO_VFS_IOPEN);
                EXIT;
        }

        return -EINVAL;
}


static int presto_psdev_open(struct inode * inode, struct file * file)
{
         struct upc_comm *upccom;
         ENTRY;

         if ( ! (upccom = presto_psdev_f2u(file)) ) {
                 kdev_t dev = file->f_dentry->d_inode->i_rdev;
                 printk("InterMezzo: %s, bad device %s\n",
                        __FUNCTION__, kdevname(dev));
                 EXIT;
                 return -EINVAL;
         }

        MOD_INC_USE_COUNT;

        CDEBUG(D_PSDEV, "Psdev_open: uc_pid: %d, caller: %d, flags: %d\n",
               upccom->uc_pid, current->pid, file->f_flags);

        EXIT;
        return 0;
}



static int presto_psdev_release(struct inode * inode, struct file * file)
{
        struct upc_comm *upccom;
        struct upc_req *req;
        struct list_head *lh;
        ENTRY;


        if ( ! (upccom = presto_psdev_f2u(file)) ) {
                kdev_t dev = file->f_dentry->d_inode->i_rdev;
                printk("InterMezzo: %s, bad device %s\n",
                       __FUNCTION__, kdevname(dev));
        }

        if ( upccom->uc_pid != current->pid ) {
                printk("psdev_release: Not lento.\n");
                MOD_DEC_USE_COUNT;
                return 0;
        }

        MOD_DEC_USE_COUNT;
        CDEBUG(D_PSDEV, "Lento: pid %d\n", current->pid);
        upccom->uc_pid = 0;

        /* Wake up clients so they can return. */
        CDEBUG(D_PSDEV, "Wake up clients sleeping for pending.\n");
        lh = &upccom->uc_pending;
        while ( (lh = lh->next) != &upccom->uc_pending) {
                req = list_entry(lh, struct upc_req, rq_chain);

                /* Async requests stay around for a new lento */
                if (req->rq_flags & REQ_ASYNC) {
                        continue;
                }
                /* the sleeper will free the req and data */
                req->rq_flags |= REQ_DEAD; 
                wake_up(&req->rq_sleep);
        }

        CDEBUG(D_PSDEV, "Wake up clients sleeping for processing\n");
        lh = &upccom->uc_processing;
        while ( (lh = lh->next) != &upccom->uc_processing) {
                req = list_entry(lh, struct upc_req, rq_chain);
                /* freeing of req and data is done by the sleeper */
                req->rq_flags |= REQ_DEAD; 
                wake_up(&req->rq_sleep);
        }
        CDEBUG(D_PSDEV, "Done.\n");

        EXIT;
        return 0;
}

static struct file_operations presto_psdev_fops = {
        read:    presto_psdev_read,
        write:   presto_psdev_write,
        poll:    presto_psdev_poll,
        ioctl:   presto_psdev_ioctl,
        open:    presto_psdev_open,
        release: presto_psdev_release
};


int  presto_psdev_init(void)
{
        int i;

#ifdef PRESTO_DEVEL
        if (register_chrdev(PRESTO_PSDEV_MAJOR, "intermezzo_psdev_devel",
                           &presto_psdev_fops)) {
                printk(KERN_ERR "presto_psdev: unable to get major %d\n",
                       PRESTO_PSDEV_MAJOR);
                return -EIO;
        }
#else
        if (register_chrdev(PRESTO_PSDEV_MAJOR, "intermezzo_psdev",
                           &presto_psdev_fops)) {
                printk("presto_psdev: unable to get major %d\n",
                       PRESTO_PSDEV_MAJOR);
                return -EIO;
        }
#endif

        memset(&upc_comms, 0, sizeof(upc_comms));
        for ( i = 0 ; i < MAX_PRESTODEV ; i++ ) {
                char *name;
                struct upc_comm *psdev = &upc_comms[i];
                INIT_LIST_HEAD(&psdev->uc_pending);
                INIT_LIST_HEAD(&psdev->uc_processing);
                INIT_LIST_HEAD(&psdev->uc_cache_list);
                init_waitqueue_head(&psdev->uc_waitq);
                psdev->uc_hard = 0;
                psdev->uc_no_filter = 0;
                psdev->uc_no_journal = 0;
                psdev->uc_no_upcall = 0;
                psdev->uc_timeout = 30;
                psdev->uc_errorval = 0;
                psdev->uc_minor = i;
                PRESTO_ALLOC(name, char *, strlen(PRESTO_PSDEV_NAME "256")+1);
                if (!name) { 
                        printk("Unable to allocate memory for device name\n");
                        continue;
                }
                sprintf(name, PRESTO_PSDEV_NAME "%d", i); 
                psdev->uc_devname = name;
        }
        return 0;
}

void presto_psdev_cleanup(void)
{
        int i;

        for ( i = 0 ; i < MAX_PRESTODEV ; i++ ) {
                struct upc_comm *psdev = &upc_comms[i];
                struct list_head *lh;

                if ( ! list_empty(&psdev->uc_pending)) { 
                        printk("Weird, tell Peter: module cleanup and pending list not empty dev %d\n", i);
                }
                if ( ! list_empty(&psdev->uc_processing)) { 
                        printk("Weird, tell Peter: module cleanup and processing list not empty dev %d\n", i);
                }
                if ( ! list_empty(&psdev->uc_cache_list)) { 
                        printk("Weird, tell Peter: module cleanup and cache listnot empty dev %d\n", i);
                }
                if (psdev->uc_devname) {
                        PRESTO_FREE(psdev->uc_devname,
                                    strlen(PRESTO_PSDEV_NAME "256")+1);
                }
                lh = psdev->uc_pending.next;
                while ( lh != &psdev->uc_pending) {
                        struct upc_req *req;

                        req = list_entry(lh, struct upc_req, rq_chain);
                        lh = lh->next;
                        if ( req->rq_flags & REQ_ASYNC ) {
                                list_del(&(req->rq_chain));
                                CDEBUG(D_UPCALL, "free pending upcall type %d\n",
                                       req->rq_opcode);
                                PRESTO_FREE(req->rq_data, req->rq_bufsize);
                                PRESTO_FREE(req, sizeof(struct upc_req));
                        } else {
                                req->rq_flags |= REQ_DEAD; 
                                wake_up(&req->rq_sleep);
                        }
                }
                lh = &psdev->uc_processing;
                while ( (lh = lh->next) != &psdev->uc_processing ) {
                        struct upc_req *req;
                        req = list_entry(lh, struct upc_req, rq_chain);
                        list_del(&(req->rq_chain));
                        req->rq_flags |= REQ_DEAD; 
                        wake_up(&req->rq_sleep);
                }
        }
}

/*
 * lento_upcall and lento_downcall routines
 */
static inline unsigned long lento_waitfor_upcall(struct upc_req *req,
                                                 int minor)
{
        DECLARE_WAITQUEUE(wait, current);
        unsigned long posttime;

        req->rq_posttime = posttime = jiffies;

        add_wait_queue(&req->rq_sleep, &wait);
        for (;;) {
                if ( upc_comms[minor].uc_hard == 0 )
                        current->state = TASK_INTERRUPTIBLE;
                else
                        current->state = TASK_UNINTERRUPTIBLE;

                /* got a reply */
                if ( req->rq_flags & (REQ_WRITE | REQ_DEAD) )
                        break;

                if ( !upc_comms[minor].uc_hard && signal_pending(current) ) {
                        /* if this process really wants to die, let it go */
                        if (sigismember(&(current->pending.signal), SIGKILL)||
                            sigismember(&(current->pending.signal), SIGINT) )
                                break;
                        /* signal is present: after timeout always return
                           really smart idea, probably useless ... */
                        if ( time_after(jiffies, req->rq_posttime +
                             upc_comms[minor].uc_timeout * HZ) )
                                break;
                }
                schedule();

        }
      list_del(&req->rq_chain); 
      INIT_LIST_HEAD(&req->rq_chain); 
        remove_wait_queue(&req->rq_sleep, &wait);
        current->state = TASK_RUNNING;

        CDEBUG(D_SPECIAL, "posttime: %ld, returned: %ld\n",
               posttime, jiffies-posttime);
        return  (jiffies - posttime);

}

/*
 * lento_upcall will return an error in the case of
 * failed communication with Lento _or_ will peek at Lento
 * reply and return Lento's error.
 *
 * As lento has 2 types of errors, normal errors (positive) and internal
 * errors (negative), normal errors are negated, while internal errors
 * are all mapped to -EINTR, while showing a nice warning message. (jh)
 *
 * lento_upcall will always free buffer, either directly, when an upcall
 * is read (in presto_psdev_read), when the filesystem is unmounted, or
 * when the module is unloaded.
 */
int lento_upcall(int minor, int bufsize, int *rep_size, union up_args *buffer,
                 int async, struct upc_req *rq)
{
        unsigned long runtime;
        struct upc_comm *upc_commp;
        union down_args *out;
        struct upc_req *req;
        int error = 0;

        ENTRY;
        upc_commp = &(upc_comms[minor]);

        if (upc_commp->uc_no_upcall) {
                EXIT;
                goto exit_buf;
        }
        if (!upc_commp->uc_pid && !async) {
                EXIT;
                error = -ENXIO;
                goto exit_buf;
        }

        /* Format the request message. */
        CDEBUG(D_UPCALL, "buffer at %p, size %d\n", buffer, bufsize);
        PRESTO_ALLOC(req, struct upc_req *, sizeof(struct upc_req));
        if ( !req ) {
                EXIT;
                error = -ENOMEM;
                goto exit_buf;
        }
        req->rq_data = (void *)buffer;
        req->rq_flags = 0;
        req->rq_bufsize = bufsize;
        req->rq_rep_size = 0;
        req->rq_opcode = ((union up_args *)buffer)->uh.opcode;
        req->rq_unique = ++upc_commp->uc_seq;
        init_waitqueue_head(&req->rq_sleep);

        /* Fill in the common input args. */
        ((union up_args *)buffer)->uh.unique = req->rq_unique;
        /* Append msg to pending queue and poke Lento. */
        list_add(&req->rq_chain, upc_commp->uc_pending.prev);
        CDEBUG(D_UPCALL,
               "Proc %d waking Lento %d for(opc,uniq) =(%d,%d) msg at %p.\n",
               current->pid, upc_commp->uc_pid, req->rq_opcode,
               req->rq_unique, req);

        wake_up_interruptible(&upc_commp->uc_waitq);

        if ( async ) {
                req->rq_flags = REQ_ASYNC;
                if( rq != NULL ) {
                        *rq = *req; /* struct copying */
                }
                /* req, rq_data are freed in presto_psdev_read for async */
                EXIT;
                return 0;
        }

        /* We can be interrupted while we wait for Lento to process
         * our request.  If the interrupt occurs before Lento has read
         * the request, we dequeue and return. If it occurs after the
         * read but before the reply, we dequeue, send a signal
         * message, and return. If it occurs after the reply we ignore
         * it. In no case do we want to restart the syscall.  If it
         * was interrupted by a lento shutdown (psdev_close), return
         * ENODEV.  */

        /* Go to sleep.  Wake up on signals only after the timeout. */
        runtime = lento_waitfor_upcall(req, minor);

        CDEBUG(D_TIMING, "opc: %d time: %ld uniq: %d size: %d\n",
               req->rq_opcode, jiffies - req->rq_posttime,
               req->rq_unique, req->rq_rep_size);
        CDEBUG(D_UPCALL,
               "..process %d woken up by Lento for req at 0x%p, data at %p\n",
               current->pid, req, req->rq_data);

        if (upc_commp->uc_pid) {      /* i.e. Lento is still alive */
          /* Op went through, interrupt or not we go on */
            if (req->rq_flags & REQ_WRITE) {
                    out = (union down_args *)req->rq_data;
                    /* here we map positive Lento errors to kernel errors */
                    if ( out->dh.result < 0 ) {
                            printk("Tell Peter: Lento returns negative error %d, for oc %d!\n",
                                   out->dh.result, out->dh.opcode);
                          out->dh.result = EINVAL;
                    }
                    error = -out->dh.result;
                    CDEBUG(D_UPCALL, "upcall: (u,o,r) (%d, %d, %d) out at %p\n",
                           out->dh.unique, out->dh.opcode, out->dh.result, out);
                    *rep_size = req->rq_rep_size;
                    EXIT;
                    goto exit_req;
            }
            /* Interrupted before lento read it. */
            if ( !(req->rq_flags & REQ_READ) && signal_pending(current)) {
                    CDEBUG(D_UPCALL,
                           "Interrupt before read: (op,un)=(%d,%d), flags %x\n",
                           req->rq_opcode, req->rq_unique, req->rq_flags);
                    /* perhaps the best way to convince the app to give up? */
                    error = -EINTR;
                    EXIT;
                    goto exit_req;
            }

            /* interrupted after Lento did its read, send signal */
            if ( (req->rq_flags & REQ_READ) && signal_pending(current) ) {
                    union up_args *sigargs;
                    struct upc_req *sigreq;

                    CDEBUG(D_UPCALL,"Sending for: op = %d.%d, flags = %x\n",
                           req->rq_opcode, req->rq_unique, req->rq_flags);

                    error = -EINTR;

                    /* req, rq_data are freed in presto_psdev_read for async */
                    PRESTO_ALLOC(sigreq, struct upc_req *,
                                 sizeof (struct upc_req));
                    if (!sigreq) {
                            error = -ENOMEM;
                            EXIT;
                            goto exit_req;
                    }
                    PRESTO_ALLOC((sigreq->rq_data), char *,
                                 sizeof(struct lento_up_hdr));
                    if (!(sigreq->rq_data)) {
                            PRESTO_FREE(sigreq, sizeof (struct upc_req));
                            error = -ENOMEM;
                            EXIT;
                            goto exit_req;
                    }

                    sigargs = (union up_args *)sigreq->rq_data;
                    sigargs->uh.opcode = LENTO_SIGNAL;
                    sigargs->uh.unique = req->rq_unique;

                    sigreq->rq_flags = REQ_ASYNC;
                    sigreq->rq_opcode = sigargs->uh.opcode;
                    sigreq->rq_unique = sigargs->uh.unique;
                    sigreq->rq_bufsize = sizeof(struct lento_up_hdr);
                    sigreq->rq_rep_size = 0;
                    CDEBUG(D_UPCALL,
                           "presto_upcall: enqueing signal msg (%d, %d)\n",
                           sigreq->rq_opcode, sigreq->rq_unique);

                    /* insert at head of queue! */
                    list_add(&sigreq->rq_chain, &upc_commp->uc_pending);
                    wake_up_interruptible(&upc_commp->uc_waitq);
            } else {
                  printk("Lento: Strange interruption - tell Peter.\n");
                    error = -EINTR;
            }
        } else {        /* If lento died i.e. !UC_OPEN(upc_commp) */
                printk("presto_upcall: Lento dead on (op,un) (%d.%d) flags %d\n",
                       req->rq_opcode, req->rq_unique, req->rq_flags);
                error = -ENODEV;
        }

exit_req:
        PRESTO_FREE(req, sizeof(struct upc_req));
exit_buf:
        PRESTO_FREE(buffer, bufsize);
        return error;
}


