Module: xenomai-rpm
Branch: queue/vfile
Commit: 2ca77b4777a3b8a9171ed2db4c8df5aedec39d2b
URL:    
http://git.xenomai.org/?p=xenomai-rpm.git;a=commit;h=2ca77b4777a3b8a9171ed2db4c8df5aedec39d2b

Author: Philippe Gerum <r...@xenomai.org>
Date:   Tue May 25 17:36:56 2010 +0200

nucleus: introduce virtual file support

Virtual files provide a mean to export Xenomai object states to
user-space, based on common kernel interfaces.  This encapsulation is
aimed at:

- supporting consistent collection of very large record-based output,
without encurring latency peaks for undergoing real-time activities.

- in the future, hiding discrepancies between linux kernel releases,
regarding the proper way to export kernel object states to userland,
either via the /proc interface or by any other mean.

This virtual file implementation offers record-based read support
based on seq_files, single-buffer write support, directory and link
handling, all visible from the /proc namespace.

---

 include/nucleus/vfile.h |  285 ++++++++++++++++++++
 ksrc/nucleus/vfile.c    |  658 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 943 insertions(+), 0 deletions(-)

diff --git a/include/nucleus/vfile.h b/include/nucleus/vfile.h
new file mode 100644
index 0000000..ab1fd91
--- /dev/null
+++ b/include/nucleus/vfile.h
@@ -0,0 +1,285 @@
+/**
+ * @file
+ * This file is part of the Xenomai project.
+ *
+ * @note Copyright (C) 2010 Philippe Gerum <r...@xenomai.org> 
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * \ingroup vfile
+ */
+
+#ifndef _XENO_NUCLEUS_VFILE_H
+#define _XENO_NUCLEUS_VFILE_H
+
+#ifdef CONFIG_PROC_FS
+
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <nucleus/types.h>
+
+struct xnvfile_directory;
+struct xnvfile_regular_iterator;
+struct xnvfile_snapshot_iterator;
+struct xnvfile_lock_ops;
+
+struct xnvfile {
+       struct proc_dir_entry *pde;
+       struct xnvfile_directory *parent;
+       struct xnvfile_lock_ops *lockops;
+       void *private;
+};
+
+struct xnvfile_lock_ops {
+       void (*get)(struct xnvfile *vfile);
+       void (*put)(struct xnvfile *vfile);
+};
+
+/*
+ * XXX: struct semaphore is legacy for mutual exclusion, but supported
+ * on both 2.4 and 2.6 kernels. Will be changed to mutex when 2.4
+ * support is dropped from Xenomai.
+ */
+struct xnvfile_hostlock_class {
+       struct xnvfile_lock_ops ops;
+       struct semaphore sem;
+};
+
+struct xnvfile_nklock_class {
+       struct xnvfile_lock_ops ops;
+       spl_t s;
+};
+
+struct xnvfile_input {
+       const char __user *u_buf;
+       size_t size;
+       struct xnvfile *vfile;
+};
+
+struct xnvfile_regular_ops {
+       void *(*begin)(struct xnvfile_regular_iterator *it);
+       void *(*next)(struct xnvfile_regular_iterator *it);
+       void (*end)(struct xnvfile_regular_iterator *it);
+       int (*show)(struct xnvfile_regular_iterator *it, void *data);
+       ssize_t (*store)(struct xnvfile_input *input);
+};
+
+struct xnvfile_regular {
+       struct xnvfile entry;
+       size_t privsz;
+       struct xnvfile_regular_ops *ops;
+};
+
+struct xnvfile_regular_template {
+       size_t privsz;
+       struct xnvfile_regular_ops *ops;
+       struct xnvfile_lock_ops *lockops;
+};
+
+struct xnvfile_regular_iterator {
+       loff_t pos;
+       struct seq_file *seq;
+       struct xnvfile_regular *vfile;
+       char private[0];
+};
+
+struct xnvfile_snapshot_ops {
+       int (*rewind)(struct xnvfile_snapshot_iterator *it);
+       void *(*begin)(struct xnvfile_snapshot_iterator *it);
+       int (*next)(struct xnvfile_snapshot_iterator *it, void *data);
+       void (*end)(struct xnvfile_snapshot_iterator *it, void *buf);
+       int (*show)(struct xnvfile_snapshot_iterator *it, void *data);
+       ssize_t (*store)(struct xnvfile_input *input);
+};
+
+struct xnvfile_rev_tag {
+       int rev;
+};
+
+struct xnvfile_snapshot_template {
+       size_t privsz;
+       size_t datasz;
+       struct xnvfile_rev_tag *tag;
+       struct xnvfile_snapshot_ops *ops;
+       struct xnvfile_lock_ops *lockops;
+};
+
+struct xnvfile_snapshot {
+       struct xnvfile entry;
+       size_t privsz;
+       size_t datasz;
+       struct xnvfile_rev_tag *tag;
+       struct xnvfile_snapshot_ops *ops;
+};
+
+struct xnvfile_snapshot_iterator {
+       int nrdata;
+       caddr_t databuf;
+       struct seq_file *seq;
+       struct xnvfile_snapshot *vfile;
+       void (*endfn)(struct xnvfile_snapshot_iterator *it, void *buf);
+       char private[0];
+};
+
+struct xnvfile_directory {
+       struct xnvfile entry;
+};
+
+struct xnvfile_link {
+       struct xnvfile entry;
+};
+
+/* vfile.begin()=> */
+#define VFILE_SEQ_EMPTY                        ((void *)-1)
+/* =>vfile.show() */
+#define VFILE_SEQ_START                        SEQ_START_TOKEN
+/* vfile.next/show()=> */
+#define VFILE_SEQ_SKIP                 2
+
+#define xnvfile_printf(it, args...)    seq_printf((it)->seq, ##args)
+#define xnvfile_write(it, data, len)   seq_write((it)->seq, (data),(len))
+#define xnvfile_puts(it, s)            seq_puts((it)->seq, (s))
+#define xnvfile_putc(it, c)            seq_putc((it)->seq, (c))
+
+static inline void xnvfile_touch_tag(struct xnvfile_rev_tag *tag)
+{
+       tag->rev++;
+}
+
+static inline void xnvfile_touch(struct xnvfile_snapshot *vfile)
+{
+       xnvfile_touch_tag(vfile->tag);
+}
+
+static inline void *xnvfile_snapshot_iterator_priv(struct 
xnvfile_snapshot_iterator *it)
+{
+       return &it->private;
+}
+
+static inline int xnvfile_reg_p(struct xnvfile *entry)
+{
+       return S_ISREG(entry->pde->mode);
+}
+
+static inline int xnvfile_dir_p(struct xnvfile *entry)
+{
+       return S_ISDIR(entry->pde->mode);
+}
+
+static inline int xnvfile_link_p(struct xnvfile *entry)
+{
+       return S_ISLNK(entry->pde->mode);
+}
+
+#define xnvfile_noentry                        \
+       {                               \
+               .pde = NULL,            \
+               .parent = NULL,         \
+               .private = NULL,        \
+       }
+
+#define xnvfile_nodir  { .entry = xnvfile_noentry }
+#define xnvfile_nolink { .entry = xnvfile_noentry }
+#define xnvfile_nofile { .entry = xnvfile_noentry }
+
+#define xnvfile_parent(e)      ((e)->entry.parent)
+#define xnvfile_priv(e)                ((e)->entry.private)
+
+extern struct xnvfile_nklock_class xnvfile_nucleus_lock;
+
+extern struct xnvfile_directory nkvfroot;
+
+int xnvfile_init_root(void);
+
+void xnvfile_destroy_root(void);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int xnvfile_init_snapshot(const char *name,
+                         struct xnvfile_snapshot *vfile,
+                         struct xnvfile_directory *parent);
+
+int xnvfile_init_regular(const char *name,
+                        struct xnvfile_regular *vfile,
+                        struct xnvfile_directory *parent);
+
+int xnvfile_init_dir(const char *name,
+                    struct xnvfile_directory *vdir,
+                    struct xnvfile_directory *parent);
+
+int xnvfile_init_link(const char *from,
+                     const char *to,
+                     struct xnvfile_link *vlink,
+                     struct xnvfile_directory *parent);
+
+void xnvfile_destroy(struct xnvfile *vfile);
+
+ssize_t xnvfile_get_blob(struct xnvfile_input *input,
+                        void *data, size_t size);
+
+ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp);
+
+void __vfile_hostlock_get(struct xnvfile *vfile);
+
+void __vfile_hostlock_put(struct xnvfile *vfile);
+
+#ifdef __cplusplus
+}
+#endif
+
+static inline
+void xnvfile_destroy_snapshot(struct xnvfile_snapshot *vfile)
+{
+       xnvfile_destroy(&vfile->entry);
+}
+
+static inline
+void xnvfile_destroy_regular(struct xnvfile_regular *vfile)
+{
+       xnvfile_destroy(&vfile->entry);
+}
+
+static inline
+void xnvfile_destroy_dir(struct xnvfile_directory *vdir)
+{
+       xnvfile_destroy(&vdir->entry);
+}
+
+static inline
+void xnvfile_destroy_link(struct xnvfile_link *vlink)
+{
+       xnvfile_destroy(&vlink->entry);
+}
+
+#define DEFINE_VFILE_HOSTLOCK(name)                                    \
+       struct xnvfile_hostlock_class name = {                          \
+               .ops = {                                                \
+                       .get = __vfile_hostlock_get,                    \
+                       .put = __vfile_hostlock_put,                    \
+               },                                                      \
+               .sem = __SEMAPHORE_INITIALIZER(name.sem, 1),            \
+       }
+
+#else /* !CONFIG_PROC_FS */
+
+#define xnvfile_touch_tag(tag) do { } while (0)
+
+#define xnvfile_touch(vfile)   do { } while (0)
+
+#endif /* !CONFIG_PROC_FS */
+
+#endif /* !_XENO_NUCLEUS_VFILE_H */
diff --git a/ksrc/nucleus/vfile.c b/ksrc/nucleus/vfile.c
new file mode 100644
index 0000000..f7649a0
--- /dev/null
+++ b/ksrc/nucleus/vfile.c
@@ -0,0 +1,658 @@
+/**
+ * @file
+ * This file is part of the Xenomai project.
+ *
+ * @note Copyright (C) 2010 Philippe Gerum <r...@xenomai.org> 
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * \ingroup vfile
+ */
+
+/*!
+ * \ingroup nucleus
+ * \defgroup vfile Virtual file services.
+ *
+ * Virtual files provide a mean to export Xenomai object states to
+ * user-space, based on common kernel interfaces.  This encapsulation
+ * is aimed at:
+ *
+ * - supporting consistent collection of very large record-based
+ * output, without encurring latency peaks for undergoing real-time
+ * activities.
+ *
+ * - in the future, hiding discrepancies between linux kernel
+ * releases, regarding the proper way to export kernel object states
+ * to userland, either via the /proc interface or by any other mean.
+ *
+ * This virtual file implementation offers record-based read support
+ * based on seq_files, single-buffer write support, directory and link
+ * handling, all visible from the /proc namespace.
+ *
+ *...@{*/
+
+#include <stdarg.h>
+#include <linux/ctype.h>
+#include <nucleus/pod.h>
+#include <nucleus/assert.h>
+#include <nucleus/vfile.h>
+
+struct xnvfile_directory nkvfroot;
+EXPORT_SYMBOL_GPL(nkvfroot);
+
+static void *vfile_snapshot_start(struct seq_file *seq, loff_t *offp)
+{
+       struct xnvfile_snapshot_iterator *it = seq->private;
+       loff_t pos = *offp;
+
+       if (pos > it->nrdata)
+               return NULL;
+
+       if (pos == 0)
+               return SEQ_START_TOKEN;
+
+       return it->databuf + (pos - 1) * it->vfile->datasz;
+}
+
+static void *vfile_snapshot_next(struct seq_file *seq, void *v, loff_t *offp)
+{
+       struct xnvfile_snapshot_iterator *it = seq->private;
+       loff_t pos = ++*offp;
+
+       if (pos > it->nrdata)
+               return NULL;
+
+       return it->databuf + (pos - 1) * it->vfile->datasz;
+}
+
+static void vfile_snapshot_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int vfile_snapshot_show(struct seq_file *seq, void *v)
+{
+       struct xnvfile_snapshot_iterator *it = seq->private;
+       void *data = v == SEQ_START_TOKEN ? NULL : v;
+       int ret;
+
+       ret = it->vfile->ops->show(it, data);
+
+       return ret == VFILE_SEQ_SKIP ? SEQ_SKIP : ret;
+}
+
+static struct seq_operations vfile_snapshot_ops = {
+       .start = vfile_snapshot_start,
+       .next = vfile_snapshot_next,
+       .stop = vfile_snapshot_stop,
+       .show = vfile_snapshot_show
+};
+
+static void vfile_snapshot_free(struct xnvfile_snapshot_iterator *it, void 
*buf)
+{
+       kfree(buf);
+}
+
+static int vfile_snapshot_open(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *pde = PDE(inode);
+       struct xnvfile_snapshot *vfile = pde->data;
+       struct xnvfile_snapshot_ops *ops = vfile->ops;
+       struct xnvfile_snapshot_iterator *it;
+       int revtag, ret, nrdata;
+       struct seq_file *seq;
+       caddr_t data;
+
+       /*
+        * There is no point in reading/writing to v-files that must
+        * be connected to Xenomai resources if the system has not
+        * been initialized yet (i.e. xnpod_init() called).
+        */
+       if (!xnpod_active_p())
+               return -ESRCH;
+
+       if ((file->f_mode & FMODE_WRITE) != 0 && ops->store == NULL)
+               return -EACCES;
+
+       /*
+        * Make sure to create the seq_file backend only when reading
+        * from the v-file is possible.
+        */
+       if ((file->f_mode & FMODE_READ) == 0) {
+               file->private_data = NULL;
+               return 0;
+       }
+
+       it = kzalloc(sizeof(*it) + vfile->privsz, GFP_KERNEL);
+       if (it == NULL)
+               return -ENOMEM;
+
+       it->vfile = vfile;
+
+       vfile->entry.lockops->get(&vfile->entry);
+redo:
+       /*
+        * The ->rewind() method is optional; there may be cases where
+        * we don't have to take an atomic snapshot of the v-file
+        * contents before proceeding. In case ->rewind() detects a
+        * stale backend object, it can force us to bail out.
+        *
+        * If present, ->rewind() may return a strictly positive
+        * value, indicating how many records at most may be returned
+        * by ->next(). We use this hint to allocate the snapshot
+        * buffer, in case ->begin() is not provided. The size of this
+        * buffer would then be vfile->datasz * hint value.
+        *
+        * If ->begin() is given, we always expect the latter do the
+        * allocation for us regardless of the hint value. Otherwise,
+        * a null return from ->rewind() tells us that the vfile won't
+        * output any snapshot data via ->show().
+        */
+       it->endfn = ops->end;
+       nrdata = 0;
+       if (ops->rewind) {
+               nrdata = ops->rewind(it);
+               if (nrdata < 0) {
+                       ret = nrdata;
+                       vfile->entry.lockops->put(&vfile->entry);
+                       goto fail;
+               }
+       }
+       revtag = vfile->tag->rev;
+
+       vfile->entry.lockops->put(&vfile->entry);
+
+       /* Release the data buffer, in case we had to restart. */
+       if (it->databuf) {
+               it->endfn(it, it->databuf);
+               it->databuf = NULL;
+       }
+
+       /*
+        * Having no record to output is fine, in which case ->begin()
+        * shall return VFILE_SEQ_EMPTY if present. ->begin() may be
+        * absent, meaning that no allocation is even required to
+        * collect the records to output. NULL is kept for allocation
+        * errors in all other cases.
+        */
+       if (ops->begin) {
+               XENO_BUGON(NUCLEUS, ops->end == NULL);
+               data = ops->begin(it);
+               if (data == NULL) {
+                       kfree(it);
+                       return -ENOMEM;
+               }
+               if (data != VFILE_SEQ_EMPTY)
+                       it->databuf = data;
+       } else if (nrdata > 0) { /* Any hint for auto-allocation? */
+               data = kmalloc(vfile->datasz * nrdata, GFP_KERNEL);
+               if (data == NULL) {
+                       kfree(it);
+                       return -ENOMEM;
+               }
+               it->databuf = data;
+               it->endfn = vfile_snapshot_free;
+       }
+
+       ret = seq_open(file, &vfile_snapshot_ops);
+       if (ret)
+               goto fail;
+
+       it->nrdata = 0;
+       data = it->databuf;
+       if (data == NULL)
+               goto finish;
+
+       /*
+        * Take a snapshot of the vfile contents, redo if the revision
+        * tag of the scanned data set changed concurrently.
+        */
+       for (;;) {
+               vfile->entry.lockops->get(&vfile->entry);
+               if (vfile->tag->rev != revtag)
+                       goto redo;
+               ret = ops->next(it, data);
+               vfile->entry.lockops->put(&vfile->entry);
+               if (ret <= 0)
+                       break;
+               if (ret != VFILE_SEQ_SKIP) {
+                       data += vfile->datasz;
+                       it->nrdata++;
+               }
+       }
+
+       if (ret < 0) {
+               seq_release(inode, file);
+       fail:
+               if (it->databuf)
+                       it->endfn(it, it->databuf);
+               kfree(it);
+               return ret;
+       }
+
+finish:
+       seq = file->private_data;
+       it->seq = seq;
+       seq->private = it;
+
+       return 0;
+}
+
+static int vfile_snapshot_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct xnvfile_snapshot_iterator *it;
+
+       if (seq) {
+               it = seq->private;
+               if (it) {
+                       if (it->databuf)
+                               it->endfn(it, it->databuf);
+                       kfree(it);
+               }
+
+               return seq_release(inode, file);
+       }
+
+       return 0;
+}
+
+ssize_t vfile_snapshot_write(struct file *file, const char __user *buf,
+                            size_t size, loff_t *ppos)
+{
+       struct proc_dir_entry *pde = PDE(wrap_f_inode(file));
+       struct xnvfile_snapshot *vfile = pde->data;
+       struct xnvfile_input input;
+
+       input.u_buf = buf;
+       input.size = size;
+       input.vfile = &vfile->entry;
+
+       return vfile->ops->store(&input);
+}
+
+static struct file_operations vfile_snapshot_fops = {
+       .owner = THIS_MODULE,
+       .open = vfile_snapshot_open,
+       .read = seq_read,
+       .write = vfile_snapshot_write,
+       .llseek = seq_lseek,
+       .release = vfile_snapshot_release,
+};
+
+int xnvfile_init_snapshot(const char *name,
+                         struct xnvfile_snapshot *vfile,
+                         struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+       int mode;
+
+       XENO_BUGON(NUCLEUS, vfile->tag == NULL);
+
+       if (vfile->entry.lockops == NULL)
+               /* Defaults to nucleus lock */
+               vfile->entry.lockops = &xnvfile_nucleus_lock.ops;
+
+       if (parent == NULL)
+               parent = &nkvfroot;
+
+       mode = vfile->ops->store ? 0644 : 0444;
+       ppde = parent->entry.pde;
+       pde = create_proc_entry(name, mode, ppde);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       pde->proc_fops = &vfile_snapshot_fops;
+       wrap_proc_dir_entry_owner(pde);
+
+       vfile->entry.parent = parent;
+       vfile->entry.pde = pde;
+       pde->data = vfile;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_snapshot);
+
+static void *vfile_regular_start(struct seq_file *seq, loff_t *offp)
+{
+       struct xnvfile_regular_iterator *it = seq->private;
+       struct xnvfile_regular *vfile = it->vfile;
+
+       it->pos = *offp;
+
+       if (vfile->entry.lockops)
+               vfile->entry.lockops->get(&vfile->entry);
+
+       /*
+        * If we have no begin() op, then we allow a single call only
+        * to ->show(), by returning the start token once. Otherwise,
+        * we are done.
+        */
+       if (vfile->ops->begin == NULL)
+               return it->pos > 0 ? NULL : SEQ_START_TOKEN;
+
+       return vfile->ops->begin(it);
+}
+
+static void *vfile_regular_next(struct seq_file *seq, void *v, loff_t *offp)
+{
+       struct xnvfile_regular_iterator *it = seq->private;
+       struct xnvfile_regular *vfile = it->vfile;
+       loff_t pos = ++*offp;
+
+       if (vfile->ops->next == NULL)
+               return NULL;
+
+       it->pos = pos;
+
+       return vfile->ops->next(it);
+}
+
+static void vfile_regular_stop(struct seq_file *seq, void *v)
+{
+       struct xnvfile_regular_iterator *it = seq->private;
+       struct xnvfile_regular *vfile = it->vfile;
+
+       if (vfile->entry.lockops)
+               vfile->entry.lockops->put(&vfile->entry);
+
+       if (vfile->ops->end)
+               vfile->ops->end(it);
+}
+
+static int vfile_regular_show(struct seq_file *seq, void *v)
+{
+       struct xnvfile_regular_iterator *it = seq->private;
+       struct xnvfile_regular *vfile = it->vfile;
+       void *data = v == SEQ_START_TOKEN ? NULL : v;
+       int ret;
+
+       ret = vfile->ops->show(it, data);
+
+       return ret == VFILE_SEQ_SKIP ? SEQ_SKIP : ret;
+}
+
+static struct seq_operations vfile_regular_ops = {
+       .start = vfile_regular_start,
+       .next = vfile_regular_next,
+       .stop = vfile_regular_stop,
+       .show = vfile_regular_show
+};
+
+static int vfile_regular_open(struct inode *inode, struct file *file)
+{
+       struct proc_dir_entry *pde = PDE(inode);
+       struct xnvfile_regular *vfile = pde->data;
+       struct xnvfile_regular_ops *ops = vfile->ops;
+       struct xnvfile_regular_iterator *it;
+       struct seq_file *seq;
+       int ret;
+
+       if ((file->f_mode & FMODE_WRITE) != 0 &&
+           ops->store == NULL)
+               return -EACCES;
+
+       if ((file->f_mode & FMODE_READ) == 0) {
+               file->private_data = NULL;
+               return 0;
+       }
+
+       it = kzalloc(sizeof(*it) + vfile->privsz, GFP_KERNEL);
+       if (it == NULL)
+               return -ENOMEM;
+
+       it->vfile = vfile;
+
+       ret = seq_open(file, &vfile_regular_ops);
+       if (ret) {
+               kfree(it);
+               return ret;
+       }
+
+       seq = file->private_data;
+       it->seq = seq;
+       seq->private = it;
+
+       return 0;
+}
+
+static int vfile_regular_release(struct inode *inode, struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct xnvfile_regular_iterator *it;
+
+       if (seq) {
+               it = seq->private;
+               if (it)
+                       kfree(it);
+
+               return seq_release(inode, file);
+       }
+
+       return 0;
+}
+
+ssize_t vfile_regular_write(struct file *file, const char __user *buf,
+                           size_t size, loff_t *ppos)
+{
+       struct proc_dir_entry *pde = PDE(wrap_f_inode(file));
+       struct xnvfile_regular *vfile = pde->data;
+       struct xnvfile_input input;
+
+       input.u_buf = buf;
+       input.size = size;
+       input.vfile = &vfile->entry;
+
+       return vfile->ops->store(&input);
+}
+
+static struct file_operations vfile_regular_fops = {
+       .owner = THIS_MODULE,
+       .open = vfile_regular_open,
+       .read = seq_read,
+       .write = vfile_regular_write,
+       .llseek = seq_lseek,
+       .release = vfile_regular_release,
+};
+
+int xnvfile_init_regular(const char *name,
+                        struct xnvfile_regular *vfile,
+                        struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+       int mode;
+
+       if (parent == NULL)
+               parent = &nkvfroot;
+
+       mode = vfile->ops->store ? 0644 : 0444;
+       ppde = parent->entry.pde;
+       pde = create_proc_entry(name, mode, ppde);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       pde->proc_fops = &vfile_regular_fops;
+       wrap_proc_dir_entry_owner(pde);
+
+       vfile->entry.parent = parent;
+       vfile->entry.pde = pde;
+       pde->data = vfile;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_regular);
+
+int xnvfile_init_dir(const char *name,
+                    struct xnvfile_directory *vdir,
+                    struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+
+       if (parent == NULL)
+               parent = &nkvfroot;
+
+       ppde = parent->entry.pde;
+       pde = create_proc_entry(name, S_IFDIR, ppde);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       vdir->entry.parent = parent;
+       vdir->entry.pde = pde;
+       vdir->entry.lockops = NULL;
+       vdir->entry.private = NULL;
+       wrap_proc_dir_entry_owner(pde);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_dir);
+
+int xnvfile_init_link(const char *from,
+                     const char *to,
+                     struct xnvfile_link *vlink,
+                     struct xnvfile_directory *parent)
+{
+       struct proc_dir_entry *ppde, *pde;
+
+       if (parent == NULL)
+               parent = &nkvfroot;
+
+       ppde = parent->entry.pde;
+       pde = proc_symlink(from, ppde, to);
+       if (vlink->entry.pde == NULL)
+               return -ENOMEM;
+
+       vlink->entry.parent = parent;
+       vlink->entry.pde = pde;
+       vlink->entry.lockops = NULL;
+       vlink->entry.private = NULL;
+       wrap_proc_dir_entry_owner(pde);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(xnvfile_init_link);
+
+void xnvfile_destroy(struct xnvfile *vfile)
+{
+       struct proc_dir_entry *ppde;
+
+       ppde = vfile->parent ? vfile->parent->entry.pde : nkvfroot.entry.pde;
+       remove_proc_entry(vfile->pde->name, ppde);
+}
+EXPORT_SYMBOL_GPL(xnvfile_destroy);
+
+ssize_t xnvfile_get_blob(struct xnvfile_input *input,
+                        void *data, size_t size)
+{
+       ssize_t nbytes = input->size;
+
+       if (nbytes < size)
+               nbytes = size;
+
+       if (nbytes > 0 &&
+           __xn_safe_copy_from_user(data, input->u_buf, nbytes))
+               return -EFAULT;
+
+       return nbytes;
+}
+EXPORT_SYMBOL_GPL(xnvfile_get_blob);
+
+ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp)
+{
+       char *end, buf[32];
+       ssize_t nbytes;
+       long val;
+
+       nbytes = xnvfile_get_blob(input, buf, sizeof(buf));
+       if (nbytes < 0)
+               return nbytes;
+
+       if (nbytes == 0)
+               return -EINVAL;
+
+       buf[nbytes] = '\0';
+       val = simple_strtol(buf, &end, 0);
+
+       if (*end != '\0' && !isspace(*end))
+               return -EINVAL;
+
+       *valp = val;
+
+       return nbytes;
+}
+EXPORT_SYMBOL_GPL(xnvfile_get_integer);
+
+void __vfile_hostlock_get(struct xnvfile *vfile)
+{
+       struct xnvfile_hostlock_class *lc;
+
+       lc = container_of(vfile->lockops, struct xnvfile_hostlock_class, ops);
+       down(&lc->sem);
+}
+EXPORT_SYMBOL_GPL(__vfile_hostlock_get);
+
+void __vfile_hostlock_put(struct xnvfile *vfile)
+{
+       struct xnvfile_hostlock_class *lc;
+
+       lc = container_of(vfile->lockops, struct xnvfile_hostlock_class, ops);
+       up(&lc->sem);
+}
+EXPORT_SYMBOL_GPL(__vfile_hostlock_put);
+
+static void __vfile_nklock_get(struct xnvfile *vfile)
+{
+       struct xnvfile_nklock_class *lc;
+
+       lc = container_of(vfile->lockops, struct xnvfile_nklock_class, ops);
+       xnlock_get_irqsave(&nklock, lc->s);
+}
+
+static void __vfile_nklock_put(struct xnvfile *vfile)
+{
+       struct xnvfile_nklock_class *lc;
+
+       lc = container_of(vfile->lockops, struct xnvfile_nklock_class, ops);
+       xnlock_put_irqrestore(&nklock, lc->s);
+}
+
+struct xnvfile_nklock_class xnvfile_nucleus_lock = {
+       .ops = {
+               .get = __vfile_nklock_get,
+               .put = __vfile_nklock_put,
+       },
+};
+
+int __init xnvfile_init_root(void)
+{
+       struct xnvfile_directory *vdir = &nkvfroot;
+       struct proc_dir_entry *pde;
+
+       pde = create_proc_entry("xenomai", S_IFDIR, NULL);
+       if (pde == NULL)
+               return -ENOMEM;
+
+       vdir->entry.parent = NULL;
+       vdir->entry.pde = pde;
+       vdir->entry.lockops = NULL;
+       vdir->entry.private = NULL;
+       wrap_proc_dir_entry_owner(pde);
+
+       return 0;
+}
+
+void __exit xnvfile_destroy_root(void)
+{
+       nkvfroot.entry.pde = NULL;
+       remove_proc_entry("xenomai", NULL);
+}


_______________________________________________
Xenomai-git mailing list
Xenomai-git@gna.org
https://mail.gna.org/listinfo/xenomai-git

Reply via email to