Module: xenomai-rpm
Branch: queue/rtipc
Commit: 21b1f8870c1669612cdc70fb58ed9fac529d70e0
URL:    
http://git.xenomai.org/?p=xenomai-rpm.git;a=commit;h=21b1f8870c1669612cdc70fb58ed9fac529d70e0

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 |  682 ++++++++++++++++++++++++++++++++++
 ksrc/nucleus/vfile.c    |  944 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1626 insertions(+), 0 deletions(-)

diff --git a/include/nucleus/vfile.h b/include/nucleus/vfile.h
new file mode 100644
index 0000000..bda3542
--- /dev/null
+++ b/include/nucleus/vfile.h
@@ -0,0 +1,682 @@
+/**
+ * @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
+
+#if defined(CONFIG_XENO_OPT_VFILE) || defined(DOXYGEN_CPP)
+
+/** @addtogroup vfile
+ *...@{*/
+
+#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;
+};
+
+/**
+ * @brief Vfile locking operations
+ * @anchor vfile_lockops
+ *
+ * This structure describes the operations to be provided for
+ * implementing locking support on vfiles. They apply to both
+ * snapshot-driven and regular vfiles.
+ */
+struct xnvfile_lock_ops {
+       /**
+        * @anchor lockops_get
+        * This handler should grab the desired lock.
+        *
+        * @param vfile A pointer to the virtual file which needs
+        * locking.
+        *
+        * @return zero should be returned if the call
+        * succeeds. Otherwise, a negative error code can be returned;
+        * upon error, the current vfile operation is aborted, and the
+        * user-space caller is passed back the error value.
+        */
+       int (*get)(struct xnvfile *vfile);
+       /**
+        * @anchor lockops_put This handler should release the lock
+        * previously grabbed by the @ref lockops_get "get() handler".
+        *
+        * @param vfile A pointer to the virtual file which currently
+        * holds the lock to release.
+        */
+       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;
+};
+
+/**
+ * @brief Regular vfile operation descriptor
+ * @anchor regular_ops
+ *
+ * This structure describes the operations available with a regular
+ * vfile. It defines handlers for sending back formatted kernel data
+ * upon a user-space read request, and for obtaining user data upon a
+ * user-space write request.
+ */
+struct xnvfile_regular_ops {
+       /**
+        * @anchor regular_begin
+        * This handler should prepare for iterating over the records
+        * upon a read request, according to the iterator data.
+        *
+        * @param it A pointer to the current vfile iterator. On
+        * entry, it->pos is set to the (0-based) position of the
+        * first record to output.
+        *
+        * @return A pointer to the first record to format and output,
+        * to be passed to the @ref regular_show "show() handler" as
+        * its @a data parameter, if the call succeeds. Otherwise:
+        *
+        * - NULL in case no record is available, in which case the
+        * read operation will terminate immediately with no output.
+        *
+        * - VFILE_SEQ_START, a special value indicating that @ref
+        * regular_show "the show() handler" should receive a NULL
+        * data pointer first, in order to output a header.
+        *
+        * - ERR_PTR(errno), where errno is a negative error code;
+        * upon error, the current operation will be aborted
+        * immediately.
+        *
+        * @note This handler is optional; if none is given in the
+        * operation descriptor (i.e. NULL value), the @ref
+        * regular_show "show() handler()" will be called only once
+        * for a read operation, with a NULL @a data parameter. This
+        * particular setting is convenient for simple regular vfiles
+        * having a single, fixed record to output.
+        */
+       void *(*begin)(struct xnvfile_regular_iterator *it);
+       /**
+        * @anchor regular_next
+        * This handler should return the address of the next record
+        * to format and output by the @ref regular_show "show()
+        * handler".
+        *
+        * @param it A pointer to the current vfile iterator. On
+        * entry, it->pos is set to the (0-based) position of the
+        * first record to output.
+        *
+        * @return A pointer to the next record to format and output,
+        * to be passed to the @ref regular_show "show() handler" as
+        * its @a data parameter, if the call succeeds. Otherwise:
+        *
+        * - NULL in case no record is available, in which case the
+        * read operation will terminate immediately with no output.
+        *
+        * - VFILE_SEQ_START, a special value indicating that @ref
+        * regular_show "the show() handler" should receive a NULL
+        * data pointer first, in order to output a header.
+        *
+        * - ERR_PTR(errno), where errno is a negative error code;
+        * upon error, the current operation will be aborted
+        * immediately.
+        *
+        * @note This handler is optional; if none is given in the
+        * operation descriptor (i.e. NULL value), the read operation
+        * will stop after the first invocation of the @ref regular_show
+        * "show() handler".
+        */
+       void *(*next)(struct xnvfile_regular_iterator *it);
+       /**
+        * @anchor regular_end
+        * This handler is called after all records have been output.
+        *
+        * @param it A pointer to the current vfile iterator.
+        *
+        * @note This handler is optional and the pointer may be NULL.
+        */
+       void (*end)(struct xnvfile_regular_iterator *it);
+       /**
+        * @anchor regular_show
+        * This handler should format and output a record.
+        *
+        * xnvfile_printf(), xnvfile_write(), xnvfile_puts() and
+        * xnvfile_putc() are available to format and/or emit the
+        * output. All routines take the iterator argument @a it as
+        * their first parameter.
+        *
+        * @param it A pointer to the current vfile iterator.
+        *
+        * @param data A pointer to the record to format then
+        * output. The first call to the handler may receive a NULL @a
+        * data pointer, depending on the presence and/or return of a
+        * @ref regular_begin "hander"; the show handler should test
+        * this special value to output any header that fits, prior to
+        * receiving more calls with actual records.
+        *
+        * @return zero if the call succeeds, also indicating that the
+        * handler should be called for the next record if
+        * any. Otherwise:
+        *
+        * - A negative error code. This will abort the output phase,
+        * and return this status to the reader.
+        *
+        * - VFILE_SEQ_SKIP, a special value indicating that the
+        * current record should be skipped and will not be output.
+        */
+       int (*show)(struct xnvfile_regular_iterator *it, void *data);
+       /**
+        * @anchor regular_store
+        * This handler receives data written to the vfile, likely for
+        * updating some kernel setting, or triggering any other
+        * action which fits. This is the only handler which deals
+        * with the write-side of a vfile.  It is called when writing
+        * to the /proc entry of the vfile from a user-space process.
+        *
+        * The input data is described by a descriptor passed to the
+        * handler, which may be subsequently passed to parsing helper
+        * routines.  For instance, xnvfile_get_string() will accept
+        * the input descriptor for returning the written data as a
+        * null-terminated character string. On the other hand,
+        * xnvfile_get_integer() will attempt to return a long integer
+        * from the input data.
+        *
+        * @param input A pointer to an input descriptor. It refers to
+        * an opaque data from the handler's standpoint.
+        *
+        * @return the number of bytes read from the input descriptor
+        * if the call succeeds. Otherwise, a negative error code.
+        * Return values from parsing helper routines are commonly
+        * passed back to the caller by the @ref show_store
+        * "store() handler".
+        *
+        * @note This handler is optional, and may be omitted for
+        * read-only vfiles.
+        */
+       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;
+};
+
+/**
+ * @brief Regular vfile iterator
+ * @anchor regular_iterator
+ *
+ * This structure defines an iterator over a regular vfile.
+ */
+struct xnvfile_regular_iterator {
+       /** Current record position while iterating. */
+       loff_t pos;
+       /** Highest record position found. */
+       loff_t maxpos;
+       /** Backlink to the host sequential file supporting the vfile. */
+       struct seq_file *seq;
+       /** Backlink to the vfile being read. */
+       struct xnvfile_regular *vfile;
+       /**
+        * Start of private area. Use xnvfile_iterator_priv() to
+        * address it.
+        */
+       char private[0];
+};
+
+/**
+ * @brief Snapshot vfile operation descriptor
+ * @anchor snapshot_ops
+ *
+ * This structure describes the operations available with a
+ * snapshot-driven vfile. It defines handlers for returning a
+ * printable snapshot of some Xenomai object contents upon a
+ * user-space read request, and for updating this object upon a
+ * user-space write request.
+ */
+struct xnvfile_snapshot_ops {
+       /**
+        * @anchor snapshot_rewind
+        * This handler (re-)initializes the data collection, moving
+        * the seek pointer at the first record. When the file
+        * revision tag is touched while collecting data, the current
+        * reading is aborted, all collected data dropped, and the
+        * vfile is eventually rewound.
+        *
+        * @param it A pointer to the current snapshot iterator. Two
+        * useful information can be retrieved from this iterator in
+        * this context:
+        *
+        * - it->vfile is a pointer to the descriptor of the virtual
+        * file being rewound.
+        *
+        * - xnvfile_iterator_priv(it) returns a pointer to the
+        * private data area, available from the descriptor, which
+        * size is vfile->privsz. If the latter size is zero, the
+        * returned pointer is meaningless and should not be used.
+        *
+        * @return A negative error code aborts the data collection,
+        * and is passed back to the reader. Otherwise:
+        *
+        * - a strictly positive value is interpreted as the total
+        * number of records which will be returned by the @ref
+        * snapshot_next "next() handler" during the data collection
+        * phase. If no @ref snapshot_begin "begin() handler" is
+        * provided in the @ref snapshot_ops "operation descriptor",
+        * this value is used to allocate the snapshot buffer
+        * internally. The size of this buffer would then be
+        * vfile->datasz * value.
+        *
+        * - zero leaves the allocation to the @ref snapshot_begin
+        * "begin() handler" if present, or indicates that no record
+        * is to be output in case such handler is not given.
+        *
+        * @note This handler is optional; a NULL value indicates that
+        * nothing needs to be done for rewinding the vfile.  It is
+        * called with the vfile lock held.
+        */
+       int (*rewind)(struct xnvfile_snapshot_iterator *it);
+       /**
+        * @anchor snapshot_begin
+        * This handler should allocate the snapshot buffer to hold
+        * records during the data collection phase.  When specified,
+        * all records collected via the @ref snapshot_next "next()
+        * handler" will be written to a cell from the memory area
+        * returned by begin().
+        *
+        * @param it A pointer to the current snapshot iterator.
+        *
+        * @return A pointer to the record buffer, if the call
+        * succeeds. Otherwise:
+        *
+        * - NULL in case of allocation error. This will abort the data
+        * collection, and return -ENOMEM to the reader.
+        *
+        * - VFILE_SEQ_EMPTY, a special value indicating that no
+        * record will be output. In such a case, the @ref
+        * snapshot_next "next() handler" will not be called, and the
+        * data collection will stop immediately. However, the @ref
+        * snapshot_show "show() handler" will still be called once,
+        * with a NULL data pointer (i.e. header display request).
+        *
+        * @note This handler is optional; if none is given, an
+        * internal allocation depending on the value returned by the
+        * @ref snapshot_rewind "rewind() handler" can be obtained.
+        */
+       void *(*begin)(struct xnvfile_snapshot_iterator *it);
+       /**
+        * @anchor snapshot_end
+        * This handler releases the memory buffer previously obtained
+        * from begin(). It is usually called after the snapshot data
+        * has been output by show(), but it may also be called before
+        * rewinding the vfile after a revision change, to release the
+        * dropped buffer.
+        *
+        * @param it A pointer to the current snapshot iterator.
+        *
+        * @param buf A pointer to the buffer to release.
+        *
+        * @note This routine is optional and the pointer may be
+        * NULL. It is not needed upon internal buffer allocation;
+        * see the description of the @ref snapshot_rewind "rewind()
+        * handler".
+        */
+       void (*end)(struct xnvfile_snapshot_iterator *it, void *buf);
+       /**
+        * @anchor snapshot_next
+        * This handler fetches the next record, as part of the
+        * snapshot data to be sent back to the reader via the
+        * show().
+        *
+        * @param it A pointer to the current snapshot iterator.
+        *
+        * @param data A pointer to the record to fill in.
+        *
+        * @return a strictly positive value, if the call succeeds and
+        * leaves a valid record into @a data, which should be passed
+        * to the @ref snapshot_show "show() handler()" during the
+        * formatting and output phase. Otherwise:
+        *
+        * - A negative error code. This will abort the data
+        * collection, and return this status to the reader.
+        *
+        * - VFILE_SEQ_SKIP, a special value indicating that the
+        * current record should be skipped. In such a case, the @a
+        * data pointer is not advanced to the next position before
+        * the @ref snapshot_next "next() handler" is called anew.
+        *
+        * @note This handler is called with the vfile lock
+        * held. Before each invocation of this handler, the vfile
+        * core checks whether the revision tag has been touched, in
+        * which case the data collection is restarted from scratch. A
+        * data collection phase succeeds whenever all records can be
+        * fetched via the @ref snapshot_next "next() handler", while
+        * the revision tag remains unchanged, which indicates that a
+        * consistent snapshot of the object state was taken.
+        */
+       int (*next)(struct xnvfile_snapshot_iterator *it, void *data);
+       /**
+        * @anchor snapshot_show
+        * This handler should format and output a record from the
+        * collected data.
+        *
+        * xnvfile_printf(), xnvfile_write(), xnvfile_puts() and
+        * xnvfile_putc() are available to format and/or emit the
+        * output. All routines take the iterator argument @a it as
+        * their first parameter.
+        *
+        * @param it A pointer to the current snapshot iterator.
+        *
+        * @param data A pointer to the record to format then
+        * output. The first call to the handler is always passed a
+        * NULL @a data pointer; the show handler should test this
+        * special value to output any header that fits, prior to
+        * receiving more calls with actual records.
+        *
+        * @return zero if the call succeeds, also indicating that the
+        * handler should be called for the next record if
+        * any. Otherwise:
+        *
+        * - A negative error code. This will abort the output phase,
+        * and return this status to the reader.
+        *
+        * - VFILE_SEQ_SKIP, a special value indicating that the
+        * current record should be skipped and will not be output.
+        */
+       int (*show)(struct xnvfile_snapshot_iterator *it, void *data);
+       /**
+        * @anchor snapshot_store
+        * This handler receives data written to the vfile, likely for
+        * updating the associated Xenomai object's state, or
+        * triggering any other action which fits. This is the only
+        * handler which deals with the write-side of a vfile.  It is
+        * called when writing to the /proc entry of the vfile
+        * from a user-space process.
+        *
+        * The input data is described by a descriptor passed to the
+        * handler, which may be subsequently passed to parsing helper
+        * routines.  For instance, xnvfile_get_string() will accept
+        * the input descriptor for returning the written data as a
+        * null-terminated character string. On the other hand,
+        * xnvfile_get_integer() will attempt to return a long integer
+        * from the input data.
+        *
+        * @param input A pointer to an input descriptor. It refers to
+        * an opaque data from the handler's standpoint.
+        *
+        * @return the number of bytes read from the input descriptor
+        * if the call succeeds. Otherwise, a negative error code.
+        * Return values from parsing helper routines are commonly
+        * passed back to the caller by the @ref snapshot_store
+        * "store() handler".
+        *
+        * @note This handler is optional, and may be omitted for
+        * read-only vfiles.
+        */
+       ssize_t (*store)(struct xnvfile_input *input);
+};
+
+/**
+ * @brief Snapshot revision tag
+ * @anchor revision_tag
+ *
+ * This structure defines a revision tag to be used with @ref
+ * snapshot_vfile "snapshot-driven vfiles".
+ */
+struct xnvfile_rev_tag {
+       /** Current revision number. */
+       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;
+};
+
+/**
+ * @brief Snapshot vfile descriptor
+ * @anchor snapshot_vfile
+ *
+ * This structure describes a snapshot-driven vfile.  Reading from
+ * such a vfile involves a preliminary data collection phase under
+ * lock protection, and a subsequent formatting and output phase of
+ * the collected data records. Locking is done in a way that does not
+ * increase worst-case latency, regardless of the number of records to
+ * be collected for output.
+ */
+struct xnvfile_snapshot {
+       struct xnvfile entry;
+       size_t privsz;
+       size_t datasz;
+       struct xnvfile_rev_tag *tag;
+       struct xnvfile_snapshot_ops *ops;
+};
+
+/**
+ * @brief Snapshot-driven vfile iterator
+ * @anchor snapshot_iterator
+ *
+ * This structure defines an iterator over a snapshot-driven vfile.
+ */
+struct xnvfile_snapshot_iterator {
+       /** Number of collected records. */
+       int nrdata;
+       /** Address of record buffer. */
+       caddr_t databuf;
+       /** Backlink to the host sequential file supporting the vfile. */
+       struct seq_file *seq;
+       /** Backlink to the vfile being read. */
+       struct xnvfile_snapshot *vfile;
+       /** Buffer release handler. */
+       void (*endfn)(struct xnvfile_snapshot_iterator *it, void *buf);
+       /**
+        * Start of private area. Use xnvfile_iterator_priv() to
+        * address it.
+        */
+       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 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)
+#define xnvfile_iterator_priv(it)      ((void *)(&(it)->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_string(struct xnvfile_input *input,
+                          char *s, size_t maxlen);
+
+ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp);
+
+int __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..3e14633
--- /dev/null
+++ b/ksrc/nucleus/vfile.c
@@ -0,0 +1,944 @@
+/**
+ * @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 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.
+ *
+ * The vfile support exposes four filesystem object types:
+ *
+ * - snapshot-driven file (struct xnvfile_snapshot). This is commonly
+ * used to export real-time object states via the /proc filesystem. To
+ * minimize the latency involved in protecting the vfile routines from
+ * changes applied by real-time code on such objects, a snapshot of
+ * the data to output is first taken under proper locking, before the
+ * collected data is formatted and sent out in a lockless manner.
+ *
+ * Because a large number of records may have to be output, the data
+ * collection phase is not strictly atomic as a whole, but only
+ * protected at record level. The vfile implementation can be notified
+ * of updates to the underlying data set, and restart the collection
+ * from scratch until the snapshot is fully consistent.
+ *
+ * - regular sequential file (struct xnvfile_regular). This is
+ * basically an encapsulated sequential file object as available from
+ * the host kernel (i.e. seq_file), with a few additional features to
+ * make it more handy in a Xenomai environment, like implicit locking
+ * support and shortened declaration for simplest, single-record
+ * output.
+ *
+ * - virtual link (struct xnvfile_link). This is a symbolic link
+ * feature integrated with the vfile semantics. The link target is
+ * computed dynamically at creation time from a user-given helper
+ * routine.
+ *
+ * - virtual directory (struct xnvfile_directory). A directory object,
+ * which can be used to create a hierarchy for ordering a set of vfile
+ * objects.
+ *
+ *...@{*/
+
+#include <stdarg.h>
+#include <linux/ctype.h>
+#include <nucleus/pod.h>
+#include <nucleus/assert.h>
+#include <nucleus/vfile.h>
+
+/**
+ * @var struct xnvfile_directory nkvfroot
+ * @brief Xenomai vfile root directory
+ *
+ * This vdir maps the /proc/xenomai directory. It can be used to
+ * create a hierarchy of Xenomai-related vfiles under this root.
+ */
+struct xnvfile_directory nkvfroot;
+EXPORT_SYMBOL_GPL(nkvfroot);
+
+static struct xnvfile_directory sysroot;
+
+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;
+
+       ret = vfile->entry.lockops->get(&vfile->entry);
+       if (ret)
+               goto fail;
+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 && vfile->datasz > 0) {
+               /* We have a 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 (;;) {
+               ret = vfile->entry.lockops->get(&vfile->entry);
+               if (ret)
+                       break;
+               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,
+};
+
+/**
+ * @fn int xnvfile_init_snapshot(const char *name, struct xnvfile_snapshot 
*vfile, struct xnvfile_directory *parent)
+ * @brief Initialize a snapshot-driven vfile.
+ *
+ * @param name The name which should appear in the pseudo-filesystem,
+ * identifying the vfile entry.
+ *
+ * @param vfile A pointer to a vfile descriptor to initialize
+ * from. The following fields in this structure should be filled in
+ * prior to call this routine:
+ *
+ * - .privsz is the size (in bytes) of the private data area to be
+ * reserved in the @ref snapshot_iterator "vfile iterator". A NULL
+ * value indicates that no private area should be reserved.
+ *
+ * - .datasz is the size (in bytes) of a single record to be collected
+ * by the @ref snapshot_next "next() handler" from the @ref
+ * snapshot_ops "operation descriptor".
+ *
+ * - .tag is a pointer to a mandatory vfile revision tag structure
+ * (struct xnvfile_rev_tag). This tag will be monitored for changes by
+ * the vfile core while collecting data to output, so that any update
+ * detected will cause the current snapshot data to be dropped, and
+ * the collection to restart from the beginning. To this end, any
+ * change to the data which may be part of the collected records,
+ * should also invoke xnvfile_touch() on the associated tag.
+ *
+ * - entry.lockops is a pointer to a @ref vfile_lockops "locking
+ * descriptor", defining the lock and unlock operations for the
+ * vfile. This pointer may be left to NULL, in which case the
+ * operations on the nucleus lock (i.e. nklock) will be used
+ * internally around calls to data collection handlers (see @ref
+ * snapshot_ops "operation descriptor").
+ *
+ * - .ops is a pointer to an @ref snapshot_ops "operation descriptor".
+ *
+ * @param parent A pointer to a virtual directory descriptor; the
+ * vfile entry will be created into this directory. If NULL, the /proc
+ * root directory will be used. /proc/xenomai is mapped on the
+ * globally available @a nkvfroot vdir.
+ *
+ * @return 0 is returned on success. Otherwise:
+ *
+ * - -ENOMEM is returned if the virtual file entry cannot be created
+ * in the /proc hierarchy.
+ */
+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 = &sysroot;
+
+       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;
+       int ret;
+
+       if (it->pos >= it->maxpos)
+               return NULL;
+
+       it->pos = *offp;
+
+       if (vfile->entry.lockops) {
+               ret = vfile->entry.lockops->get(&vfile->entry);
+               if (ret)
+                       return ERR_PTR(ret);
+       }
+
+       /*
+        * 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;
+       it->maxpos = 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;
+       it->pos = -1;
+
+       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,
+};
+
+/**
+ * @fn int xnvfile_init_regular(const char *name, struct xnvfile_regular 
*vfile, struct xnvfile_directory *parent)
+ * @brief Initialize a regular vfile.
+ *
+ * @param name The name which should appear in the pseudo-filesystem,
+ * identifying the vfile entry.
+ *
+ * @param vfile A pointer to a vfile descriptor to initialize
+ * from. The following fields in this structure should be filled in
+ * prior to call this routine:
+ *
+ * - .privsz is the size (in bytes) of the private data area to be
+ * reserved in the @ref regular_iterator "vfile iterator". A NULL
+ * value indicates that no private area should be reserved.
+ *
+ * - entry.lockops is a pointer to a @ref vfile_lockops "locking
+ * descriptor", defining the lock and unlock operations for the
+ * vfile. This pointer may be left to NULL, in which case no
+ * locking will be applied.
+ *
+ * - .ops is a pointer to an @ref regular_ops "operation descriptor".
+ *
+ * @param parent A pointer to a virtual directory descriptor; the
+ * vfile entry will be created into this directory. If NULL, the /proc
+ * root directory will be used. /proc/xenomai is mapped on the
+ * globally available @a nkvfroot vdir.
+ *
+ * @return 0 is returned on success. Otherwise:
+ *
+ * - -ENOMEM is returned if the virtual file entry cannot be created
+ * in the /proc hierarchy.
+ */
+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 = &sysroot;
+
+       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);
+
+/**
+ * @fn int xnvfile_init_dir(const char *name, struct xnvfile_directory *vdir, 
struct xnvfile_directory *parent)
+ * @brief Initialize a virtual directory entry.
+ *
+ * @param name The name which should appear in the pseudo-filesystem,
+ * identifying the vdir entry.
+ *
+ * @param vdir A pointer to the virtual directory descriptor to
+ * initialize.
+ *
+ * @param parent A pointer to a virtual directory descriptor standing
+ * for the parent directory of the new vdir.  If NULL, the /proc root
+ * directory will be used. /proc/xenomai is mapped on the globally
+ * available @a nkvfroot vdir.
+ *
+ * @return 0 is returned on success. Otherwise:
+ *
+ * - -ENOMEM is returned if the virtual directory entry cannot be
+ * created in the /proc hierarchy.
+ */
+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 = &sysroot;
+
+       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);
+
+/**
+ * @fn int xnvfile_init_link(const char *from, const char *to, struct 
xnvfile_link *vlink, struct xnvfile_directory *parent)
+ * @brief Initialize a virtual link entry.
+ *
+ * @param from The name which should appear in the pseudo-filesystem,
+ * identifying the vlink entry.
+ *
+ * @param to The target file name which should be referred to
+ * symbolically by @a name.
+ *
+ * @param vlink A pointer to the virtual link descriptor to
+ * initialize.
+ *
+ * @param parent A pointer to a virtual directory descriptor standing
+ * for the parent directory of the new vlink. If NULL, the /proc root
+ * directory will be used. /proc/xenomai is mapped on the globally
+ * available @a nkvfroot vdir.
+ *
+ * @return 0 is returned on success. Otherwise:
+ *
+ * - -ENOMEM is returned if the virtual link entry cannot be created
+ * in the /proc hierarchy.
+ */
+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 = &sysroot;
+
+       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);
+
+/**
+ * @fn void xnvfile_destroy(struct xnvfile *vfile)
+ * @brief Removes a virtual file entry.
+ *
+ * @param vfile A pointer to the virtual file descriptor to
+ * remove.
+ */
+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);
+
+/**
+ * @fn ssize_t xnvfile_get_blob(struct xnvfile_input *input, void *data, 
size_t size)
+ * @brief Read in a data bulk written to the vfile.
+ *
+ * When writing to a vfile, the associated store() handler from the
+ * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store
+ * "regular vfile" is called, with a single argument describing the
+ * input data. xnvfile_get_blob() retrieves this data as an untyped
+ * binary blob, and copies it back to the caller's buffer.
+ *
+ * @param input A pointer to the input descriptor passed to the
+ * store() handler.
+ *
+ * @param data The address of the destination buffer to copy the input
+ * data to.
+ *
+ * @param size The maximum number of bytes to copy to the destination
+ * buffer. If @a size is larger than the actual data size, the input
+ * is truncated to @a size.
+ *
+ * @return The number of bytes read and copied to the destination
+ * buffer upon success. Otherwise, a negative error code is returned:
+ *
+ * - -EFAULT indicates an invalid source buffer address.
+ */
+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);
+
+/**
+ * @fn ssize_t xnvfile_get_string(struct xnvfile_input *input, char *s, size_t 
maxlen)
+ * @brief Read in a C-string written to the vfile.
+ *
+ * When writing to a vfile, the associated store() handler from the
+ * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store
+ * "regular vfile" is called, with a single argument describing the
+ * input data. xnvfile_get_string() retrieves this data as a
+ * null-terminated character string, and copies it back to the
+ * caller's buffer.
+ *
+ * @param input A pointer to the input descriptor passed to the
+ * store() handler.
+ *
+ * @param s The address of the destination string buffer to copy the
+ * input data to.
+ *
+ * @param maxlen The maximum number of bytes to copy to the
+ * destination buffer, including the ending null character. If @a
+ * maxlen is larger than the actual string length, the input is
+ * truncated to @a maxlen.
+ *
+ * @return The number of characters read and copied to the destination
+ * buffer upon success. Otherwise, a negative error code is returned:
+ *
+ * - -EFAULT indicates an invalid source buffer address.
+ */
+ssize_t xnvfile_get_string(struct xnvfile_input *input,
+                          char *s, size_t maxlen)
+{
+       ssize_t nbytes;
+
+       if (maxlen < 1)
+               return -EINVAL;
+
+       nbytes = xnvfile_get_blob(input, s, maxlen - 1);
+       if (nbytes < 0)
+               return nbytes;
+
+       if (nbytes > 0 && s[nbytes - 1] == '\n')
+               nbytes--;
+
+       s[nbytes] = '\0';
+
+       return nbytes;
+}
+EXPORT_SYMBOL_GPL(xnvfile_get_string);
+
+/**
+ * @fn ssize_t xnvfile_get_integer(struct xnvfile_input *input, long *valp)
+ * @brief Evaluate the string written to the vfile as a long integer.
+ *
+ * When writing to a vfile, the associated store() handler from the
+ * @ref snapshot_store "snapshot-driven vfile" or @ref regular_store
+ * "regular vfile" is called, with a single argument describing the
+ * input data. xnvfile_get_integer() retrieves and interprets this
+ * data as a long integer, and copies the resulting value back to @a
+ * valp.
+ *
+ * The long integer can be expressed in decimal, octal or hexadecimal
+ * bases depending on the prefix found.
+ *
+ * @param input A pointer to the input descriptor passed to the
+ * store() handler.
+ *
+ * @param valp The address of a long integer variable to receive the
+ * value.
+ *
+ * @return The number of characters read while evaluating the input as
+ * a long integer upon success. Otherwise, a negative error code is
+ * returned:
+ *
+ * - -EINVAL indicates a parse error on the input stream; the written
+ * text cannot be evaluated as a long integer.
+ *
+ * - -EFAULT indicates an invalid source buffer address.
+ */
+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);
+
+int __vfile_hostlock_get(struct xnvfile *vfile)
+{
+       struct xnvfile_hostlock_class *lc;
+
+       lc = container_of(vfile->lockops, struct xnvfile_hostlock_class, ops);
+       return down_interruptible(&lc->sem) ? -ERESTARTSYS : 0;
+}
+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 int __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);
+
+       return 0;
+}
+
+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 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