This makes debugfs use its own file_operations for the value accessor files
created by debugfs_create_XXX. Having that, we can also have proper versions
for signed integers.
Signed-off-by: Mattias Nissler <[email protected]>
---
When writing some debugfs code for mac80211 I wanted to have access to
some s32 variables. I thought it's better to make debugfs support signed
integers instead of adding the functionality locally. I assume generic
versions will be useful for other people as well.
Please CC me for replies.
fs/debugfs/file.c | 291 +++++++++++++++++++++++++++++++++++++++--------
include/linux/debugfs.h | 45 +++++++
2 files changed, 287 insertions(+), 49 deletions(-)
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index fa6b7f7..8808bc0 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -56,18 +56,121 @@ const struct inode_operations debugfs_link_operations = {
.follow_link = debugfs_follow_link,
};
-static void debugfs_u8_set(void *data, u64 val)
+/* Simple numeric attributes */
+struct debugfs_num_attr
{
- *(u8 *)data = val;
+ void (*get)(void *, char *);
+ void (*set)(void *, char *);
+ char get_buf[24];
+ char set_buf[24];
+ void *pnum;
+ struct mutex mutex;
+};
+
+static int debugfs_num_attr_open(struct inode *inode, struct file *file,
+ void (*get)(void *, char*),
+ void (*set)(void *, char*))
+{
+ struct debugfs_num_attr *attr;
+
+ attr = kmalloc(sizeof(*attr), GFP_KERNEL);
+ if (!attr)
+ return -ENOMEM;
+
+ attr->get = get;
+ attr->set = set;
+ attr->pnum = inode->i_private;
+ mutex_init(&attr->mutex);
+
+ file->private_data = attr;
+
+ return nonseekable_open(inode, file);
+}
+
+static int debugfs_num_attr_close(struct inode *inode, struct file *file)
+{
+ kfree(file->private_data);
+
+ return 0;
+}
+
+static ssize_t debugfs_num_attr_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ struct debugfs_num_attr *attr;
+ size_t size;
+ ssize_t ret;
+
+ attr = file->private_data;
+
+ mutex_lock(&attr->mutex);
+ if (!*ppos) {
+ /* first read */
+ attr->get(attr->pnum, attr->get_buf);
+ attr->get_buf[sizeof(attr->get_buf) - 1] = '\0';
+ }
+
+ size = strlen(attr->get_buf);
+ ret = simple_read_from_buffer(buf, len, ppos, attr->get_buf, size);
+ mutex_unlock(&attr->mutex);
+
+ return ret;
}
-static u64 debugfs_u8_get(void *data)
+
+static ssize_t debugfs_num_attr_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ppos)
{
- return *(u8 *)data;
+ struct debugfs_num_attr *attr;
+ size_t size;
+ ssize_t ret;
+
+ attr = file->private_data;
+
+ mutex_lock(&attr->mutex);
+ ret = -EFAULT;
+ size = min(sizeof(attr->set_buf) - 1, len);
+ if (copy_from_user(attr->set_buf, buf, size))
+ goto out;
+
+ ret = len; /* claim we got the whole input */
+ attr->set_buf[size] = '\0';
+ attr->set(attr->pnum, attr->set_buf);
+out:
+ mutex_unlock(&attr->mutex);
+ return ret;
}
-DEFINE_SIMPLE_ATTRIBUTE(fops_u8, debugfs_u8_get, debugfs_u8_set, "%llu\n");
+
+#define DEFINE_NUM_ATTR(__fops, __type, __format) \
+static void __fops ## _get(void *data, char *buf) \
+{ \
+ scnprintf(buf, 24, __format, *(__type *) data); \
+} \
+static void __fops ## _set(void *data, char *buf) \
+{ \
+ sscanf(buf, __format, (__type *) data); \
+} \
+static int __fops ## _open(struct inode *inode, struct file *file) \
+{ \
+ return debugfs_num_attr_open(inode, file, __fops ## _get, \
+ __fops ## _set); \
+} \
+static struct file_operations __fops = { \
+ .owner = THIS_MODULE, \
+ .open = __fops ## _open, \
+ .release = debugfs_num_attr_close, \
+ .read = debugfs_num_attr_read, \
+ .write = debugfs_num_attr_write, \
+};
+
+DEFINE_NUM_ATTR(fops_u8, u8, "%hhu\n")
+DEFINE_NUM_ATTR(fops_u16, u16, "%hu\n")
+DEFINE_NUM_ATTR(fops_u32, u32, "%u\n")
+DEFINE_NUM_ATTR(fops_u64, u64, "%llu\n")
/**
- * debugfs_create_u8 - create a debugfs file that is used to read and write an unsigned 8-bit value
+ * debugfs_create_u8 - create a debugfs file that is used to read and write an
+ * unsigned 8-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -97,18 +200,10 @@ struct dentry *debugfs_create_u8(const char *name, mode_t mode,
}
EXPORT_SYMBOL_GPL(debugfs_create_u8);
-static void debugfs_u16_set(void *data, u64 val)
-{
- *(u16 *)data = val;
-}
-static u64 debugfs_u16_get(void *data)
-{
- return *(u16 *)data;
-}
-DEFINE_SIMPLE_ATTRIBUTE(fops_u16, debugfs_u16_get, debugfs_u16_set, "%llu\n");
-
/**
- * debugfs_create_u16 - create a debugfs file that is used to read and write an unsigned 16-bit value
+ * debugfs_create_u16 - create a debugfs file that is used to read and write an
+ * unsigned 16-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -138,18 +233,10 @@ struct dentry *debugfs_create_u16(const char *name, mode_t mode,
}
EXPORT_SYMBOL_GPL(debugfs_create_u16);
-static void debugfs_u32_set(void *data, u64 val)
-{
- *(u32 *)data = val;
-}
-static u64 debugfs_u32_get(void *data)
-{
- return *(u32 *)data;
-}
-DEFINE_SIMPLE_ATTRIBUTE(fops_u32, debugfs_u32_get, debugfs_u32_set, "%llu\n");
-
/**
- * debugfs_create_u32 - create a debugfs file that is used to read and write an unsigned 32-bit value
+ * debugfs_create_u32 - create a debugfs file that is used to read and write an
+ * unsigned 32-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -179,19 +266,10 @@ struct dentry *debugfs_create_u32(const char *name, mode_t mode,
}
EXPORT_SYMBOL_GPL(debugfs_create_u32);
-static void debugfs_u64_set(void *data, u64 val)
-{
- *(u64 *)data = val;
-}
-
-static u64 debugfs_u64_get(void *data)
-{
- return *(u64 *)data;
-}
-DEFINE_SIMPLE_ATTRIBUTE(fops_u64, debugfs_u64_get, debugfs_u64_set, "%llu\n");
-
/**
- * debugfs_create_u64 - create a debugfs file that is used to read and write an unsigned 64-bit value
+ * debugfs_create_u64 - create a debugfs file that is used to read and write an
+ * unsigned 64-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -221,14 +299,104 @@ struct dentry *debugfs_create_u64(const char *name, mode_t mode,
}
EXPORT_SYMBOL_GPL(debugfs_create_u64);
-DEFINE_SIMPLE_ATTRIBUTE(fops_x8, debugfs_u8_get, debugfs_u8_set, "0x%02llx\n");
+DEFINE_NUM_ATTR(fops_s8, s8, "%hhd\n")
+DEFINE_NUM_ATTR(fops_s16, s16, "%hd\n")
+DEFINE_NUM_ATTR(fops_s32, s32, "%d\n")
+DEFINE_NUM_ATTR(fops_s64, s64, "%lld\n")
+
+/*
+ * debugfs_create_s{8,16,32,64} - create a debugfs file that is used to read
+ * and write a signed {8,16,32,64}-bit value
+ *
+ * These functions are exactly the same as the above functions (but use a hex
+ * output for the decimal challenged). For details look at the above unsigned
+ * decimal functions.
+ */
+
+/**
+ * debugfs_create_s8 - create a debugfs file that is used to read and write a
+ * signed 8-bit value
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ */
+struct dentry *debugfs_create_s8(const char *name, mode_t mode,
+ struct dentry *parent, s8 *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_s8);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_s8);
+
+/**
+ * debugfs_create_s16 - create a debugfs file that is used to read and write a
+ * signed 16-bit value
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ */
+struct dentry *debugfs_create_s16(const char *name, mode_t mode,
+ struct dentry *parent, s16 *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_s16);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_s16);
+
+/**
+ * debugfs_create_s32 - create a debugfs file that is used to read and write a
+ * signed 32-bit value
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ */
+struct dentry *debugfs_create_s32(const char *name, mode_t mode,
+ struct dentry *parent, s32 *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_s32);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_s32);
-DEFINE_SIMPLE_ATTRIBUTE(fops_x16, debugfs_u16_get, debugfs_u16_set, "0x%04llx\n");
+/**
+ * debugfs_create_s64 - create a debugfs file that is used to read and write a
+ * signed 32-bit value
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ */
+struct dentry *debugfs_create_s64(const char *name, mode_t mode,
+ struct dentry *parent, s64 *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_s64);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_s64);
-DEFINE_SIMPLE_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%08llx\n");
+DEFINE_NUM_ATTR(fops_x8, u8, "%hhx\n")
+DEFINE_NUM_ATTR(fops_x16, u16, "%hx\n")
+DEFINE_NUM_ATTR(fops_x32, u32, "%x\n")
+DEFINE_NUM_ATTR(fops_x64, u64, "%llx\n")
/*
- * debugfs_create_x{8,16,32} - create a debugfs file that is used to read and write an unsigned {8,16,32}-bit value
+ * debugfs_create_x{8,16,32,64} - create a debugfs file that is used to read
+ * and write an unsigned {8,16,32,64}-bit value
*
* These functions are exactly the same as the above functions (but use a hex
* output for the decimal challenged). For details look at the above unsigned
@@ -236,7 +404,9 @@ DEFINE_SIMPLE_ATTRIBUTE(fops_x32, debugfs_u32_get, debugfs_u32_set, "0x%08llx\n"
*/
/**
- * debugfs_create_x8 - create a debugfs file that is used to read and write an unsigned 8-bit value
+ * debugfs_create_x8 - create a debugfs file that is used to read and write an
+ * unsigned 8-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -253,7 +423,9 @@ struct dentry *debugfs_create_x8(const char *name, mode_t mode,
EXPORT_SYMBOL_GPL(debugfs_create_x8);
/**
- * debugfs_create_x16 - create a debugfs file that is used to read and write an unsigned 16-bit value
+ * debugfs_create_x16 - create a debugfs file that is used to read and write an
+ * unsigned 16-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -263,14 +435,16 @@ EXPORT_SYMBOL_GPL(debugfs_create_x8);
* from.
*/
struct dentry *debugfs_create_x16(const char *name, mode_t mode,
- struct dentry *parent, u16 *value)
+ struct dentry *parent, u16 *value)
{
return debugfs_create_file(name, mode, parent, value, &fops_x16);
}
EXPORT_SYMBOL_GPL(debugfs_create_x16);
/**
- * debugfs_create_x32 - create a debugfs file that is used to read and write an unsigned 32-bit value
+ * debugfs_create_x32 - create a debugfs file that is used to read and write an
+ * unsigned 32-bit value
+ *
* @name: a pointer to a string containing the name of the file to create.
* @mode: the permission that the file should have
* @parent: a pointer to the parent dentry for this file. This should be a
@@ -280,12 +454,31 @@ EXPORT_SYMBOL_GPL(debugfs_create_x16);
* from.
*/
struct dentry *debugfs_create_x32(const char *name, mode_t mode,
- struct dentry *parent, u32 *value)
+ struct dentry *parent, u32 *value)
{
return debugfs_create_file(name, mode, parent, value, &fops_x32);
}
EXPORT_SYMBOL_GPL(debugfs_create_x32);
+/**
+ * debugfs_create_x64 - create a debugfs file that is used to read and write an
+ * unsigned 32-bit value
+ *
+ * @name: a pointer to a string containing the name of the file to create.
+ * @mode: the permission that the file should have
+ * @parent: a pointer to the parent dentry for this file. This should be a
+ * directory dentry if set. If this parameter is %NULL, then the
+ * file will be created in the root of the debugfs filesystem.
+ * @value: a pointer to the variable that the file should read to and write
+ * from.
+ */
+struct dentry *debugfs_create_x64(const char *name, mode_t mode,
+ struct dentry *parent, u64 *value)
+{
+ return debugfs_create_file(name, mode, parent, value, &fops_x64);
+}
+EXPORT_SYMBOL_GPL(debugfs_create_x64);
+
static ssize_t read_file_bool(struct file *file, char __user *user_buf,
size_t count, loff_t *ppos)
{
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index f592d6d..e9991ed 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -49,12 +49,22 @@ struct dentry *debugfs_create_u32(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
struct dentry *debugfs_create_u64(const char *name, mode_t mode,
struct dentry *parent, u64 *value);
+struct dentry *debugfs_create_s8(const char *name, mode_t mode,
+ struct dentry *parent, s8 *value);
+struct dentry *debugfs_create_s16(const char *name, mode_t mode,
+ struct dentry *parent, s16 *value);
+struct dentry *debugfs_create_s32(const char *name, mode_t mode,
+ struct dentry *parent, s32 *value);
+struct dentry *debugfs_create_s64(const char *name, mode_t mode,
+ struct dentry *parent, s64 *value);
struct dentry *debugfs_create_x8(const char *name, mode_t mode,
struct dentry *parent, u8 *value);
struct dentry *debugfs_create_x16(const char *name, mode_t mode,
struct dentry *parent, u16 *value);
struct dentry *debugfs_create_x32(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
+struct dentry *debugfs_create_x64(const char *name, mode_t mode,
+ struct dentry *parent, u64 *value);
struct dentry *debugfs_create_bool(const char *name, mode_t mode,
struct dentry *parent, u32 *value);
@@ -128,6 +138,34 @@ static inline struct dentry *debugfs_create_u64(const char *name, mode_t mode,
return ERR_PTR(-ENODEV);
}
+static inline struct dentry *debugfs_create_s8(const char *name, mode_t mode,
+ struct dentry *parent,
+ s8 *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_s16(const char *name, mode_t mode,
+ struct dentry *parent,
+ s16 *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_s32(const char *name, mode_t mode,
+ struct dentry *parent,
+ s32 *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+static inline struct dentry *debugfs_create_s64(const char *name, mode_t mode,
+ struct dentry *parent,
+ s64 *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline struct dentry *debugfs_create_x8(const char *name, mode_t mode,
struct dentry *parent,
u8 *value)
@@ -149,6 +187,13 @@ static inline struct dentry *debugfs_create_x32(const char *name, mode_t mode,
return ERR_PTR(-ENODEV);
}
+static inline struct dentry *debugfs_create_x64(const char *name, mode_t mode,
+ struct dentry *parent,
+ u64 *value)
+{
+ return ERR_PTR(-ENODEV);
+}
+
static inline struct dentry *debugfs_create_bool(const char *name, mode_t mode,
struct dentry *parent,
u32 *value)
--
1.5.3.7
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[Index of Archives]
[Kernel Newbies]
[Netfilter]
[Bugtraq]
[Photo]
[Stuff]
[Gimp]
[Yosemite News]
[MIPS Linux]
[ARM Linux]
[Linux Security]
[Linux RAID]
[Video 4 Linux]
[Linux for the blind]
[Linux Resources]