diff options
Diffstat (limited to 'fs/debugfs/file.c')
| -rw-r--r-- | fs/debugfs/file.c | 128 | 
1 files changed, 128 insertions, 0 deletions
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 5dfafdd1dbd..2340f6978d6 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -20,6 +20,7 @@  #include <linux/namei.h>  #include <linux/debugfs.h>  #include <linux/io.h> +#include <linux/slab.h>  static ssize_t default_read_file(struct file *file, char __user *buf,  				 size_t count, loff_t *ppos) @@ -520,6 +521,133 @@ struct dentry *debugfs_create_blob(const char *name, umode_t mode,  }  EXPORT_SYMBOL_GPL(debugfs_create_blob); +struct array_data { +	void *array; +	u32 elements; +}; + +static int u32_array_open(struct inode *inode, struct file *file) +{ +	file->private_data = NULL; +	return nonseekable_open(inode, file); +} + +static size_t format_array(char *buf, size_t bufsize, const char *fmt, +			   u32 *array, u32 array_size) +{ +	size_t ret = 0; +	u32 i; + +	for (i = 0; i < array_size; i++) { +		size_t len; + +		len = snprintf(buf, bufsize, fmt, array[i]); +		len++;	/* ' ' or '\n' */ +		ret += len; + +		if (buf) { +			buf += len; +			bufsize -= len; +			buf[-1] = (i == array_size-1) ? '\n' : ' '; +		} +	} + +	ret++;		/* \0 */ +	if (buf) +		*buf = '\0'; + +	return ret; +} + +static char *format_array_alloc(const char *fmt, u32 *array, +						u32 array_size) +{ +	size_t len = format_array(NULL, 0, fmt, array, array_size); +	char *ret; + +	ret = kmalloc(len, GFP_KERNEL); +	if (ret == NULL) +		return NULL; + +	format_array(ret, len, fmt, array, array_size); +	return ret; +} + +static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len, +			      loff_t *ppos) +{ +	struct inode *inode = file->f_path.dentry->d_inode; +	struct array_data *data = inode->i_private; +	size_t size; + +	if (*ppos == 0) { +		if (file->private_data) { +			kfree(file->private_data); +			file->private_data = NULL; +		} + +		file->private_data = format_array_alloc("%u", data->array, +							      data->elements); +	} + +	size = 0; +	if (file->private_data) +		size = strlen(file->private_data); + +	return simple_read_from_buffer(buf, len, ppos, +					file->private_data, size); +} + +static int u32_array_release(struct inode *inode, struct file *file) +{ +	kfree(file->private_data); + +	return 0; +} + +static const struct file_operations u32_array_fops = { +	.owner	 = THIS_MODULE, +	.open	 = u32_array_open, +	.release = u32_array_release, +	.read	 = u32_array_read, +	.llseek  = no_llseek, +}; + +/** + * debugfs_create_u32_array - create a debugfs file that is used to read u32 + * array. + * @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. + * @array: u32 array that provides data. + * @elements: total number of elements in the array. + * + * This function creates a file in debugfs with the given name that exports + * @array as data. If the @mode variable is so set it can be read from. + * Writing is not supported. Seek within the file is also not supported. + * Once array is created its size can not be changed. + * + * The function returns a pointer to dentry on success. If debugfs is not + * enabled in the kernel, the value -%ENODEV will be returned. + */ +struct dentry *debugfs_create_u32_array(const char *name, umode_t mode, +					    struct dentry *parent, +					    u32 *array, u32 elements) +{ +	struct array_data *data = kmalloc(sizeof(*data), GFP_KERNEL); + +	if (data == NULL) +		return NULL; + +	data->array = array; +	data->elements = elements; + +	return debugfs_create_file(name, mode, parent, data, &u32_array_fops); +} +EXPORT_SYMBOL_GPL(debugfs_create_u32_array); +  #ifdef CONFIG_HAS_IOMEM  /*  |