diff options
Diffstat (limited to 'fs/sysfs/bin.c')
-rw-r--r-- | fs/sysfs/bin.c | 204 |
1 files changed, 204 insertions, 0 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c new file mode 100644 index 000000000000..d4aaa88d0214 --- /dev/null +++ b/fs/sysfs/bin.c @@ -0,0 +1,204 @@ +/* + * bin.c - binary file operations for sysfs. + * + * Copyright (c) 2003 Patrick Mochel + * Copyright (c) 2003 Matthew Wilcox + * Copyright (c) 2004 Silicon Graphics, Inc. + */ + +#undef DEBUG + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kobject.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <asm/uaccess.h> + +#include "sysfs.h" + +static int +fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) +{ + struct bin_attribute * attr = to_bin_attr(dentry); + struct kobject * kobj = to_kobj(dentry->d_parent); + + if (!attr->read) + return -EINVAL; + + return attr->read(kobj, buffer, off, count); +} + +static ssize_t +read(struct file * file, char __user * userbuf, size_t count, loff_t * off) +{ + char *buffer = file->private_data; + struct dentry *dentry = file->f_dentry; + int size = dentry->d_inode->i_size; + loff_t offs = *off; + int ret; + + if (count > PAGE_SIZE) + count = PAGE_SIZE; + + if (size) { + if (offs > size) + return 0; + if (offs + count > size) + count = size - offs; + } + + ret = fill_read(dentry, buffer, offs, count); + if (ret < 0) + return ret; + count = ret; + + if (copy_to_user(userbuf, buffer, count)) + return -EFAULT; + + pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); + + *off = offs + count; + + return count; +} + +static int +flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) +{ + struct bin_attribute *attr = to_bin_attr(dentry); + struct kobject *kobj = to_kobj(dentry->d_parent); + + if (!attr->write) + return -EINVAL; + + return attr->write(kobj, buffer, offset, count); +} + +static ssize_t write(struct file * file, const char __user * userbuf, + size_t count, loff_t * off) +{ + char *buffer = file->private_data; + struct dentry *dentry = file->f_dentry; + int size = dentry->d_inode->i_size; + loff_t offs = *off; + + if (count > PAGE_SIZE) + count = PAGE_SIZE; + if (size) { + if (offs > size) + return 0; + if (offs + count > size) + count = size - offs; + } + + if (copy_from_user(buffer, userbuf, count)) + return -EFAULT; + + count = flush_write(dentry, buffer, offs, count); + if (count > 0) + *off = offs + count; + return count; +} + +static int mmap(struct file *file, struct vm_area_struct *vma) +{ + struct dentry *dentry = file->f_dentry; + struct bin_attribute *attr = to_bin_attr(dentry); + struct kobject *kobj = to_kobj(dentry->d_parent); + + if (!attr->mmap) + return -EINVAL; + + return attr->mmap(kobj, attr, vma); +} + +static int open(struct inode * inode, struct file * file) +{ + struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent); + struct bin_attribute * attr = to_bin_attr(file->f_dentry); + int error = -EINVAL; + + if (!kobj || !attr) + goto Done; + + /* Grab the module reference for this attribute if we have one */ + error = -ENODEV; + if (!try_module_get(attr->attr.owner)) + goto Done; + + error = -EACCES; + if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) + goto Error; + if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) + goto Error; + + error = -ENOMEM; + file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!file->private_data) + goto Error; + + error = 0; + goto Done; + + Error: + module_put(attr->attr.owner); + Done: + if (error && kobj) + kobject_put(kobj); + return error; +} + +static int release(struct inode * inode, struct file * file) +{ + struct kobject * kobj = to_kobj(file->f_dentry->d_parent); + struct bin_attribute * attr = to_bin_attr(file->f_dentry); + u8 * buffer = file->private_data; + + if (kobj) + kobject_put(kobj); + module_put(attr->attr.owner); + kfree(buffer); + return 0; +} + +struct file_operations bin_fops = { + .read = read, + .write = write, + .mmap = mmap, + .llseek = generic_file_llseek, + .open = open, + .release = release, +}; + +/** + * sysfs_create_bin_file - create binary file for object. + * @kobj: object. + * @attr: attribute descriptor. + * + */ + +int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) +{ + BUG_ON(!kobj || !kobj->dentry || !attr); + + return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR); +} + + +/** + * sysfs_remove_bin_file - remove binary file for object. + * @kobj: object. + * @attr: attribute descriptor. + * + */ + +int sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) +{ + sysfs_hash_and_remove(kobj->dentry,attr->attr.name); + return 0; +} + +EXPORT_SYMBOL_GPL(sysfs_create_bin_file); +EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); |