blob: 5dc47fe5de5e67ebc89624c0c52a9c37521bae83 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * bin.c - binary file operations for sysfs.
3 *
4 * Copyright (c) 2003 Patrick Mochel
5 * Copyright (c) 2003 Matthew Wilcox
6 * Copyright (c) 2004 Silicon Graphics, Inc.
7 */
8
9#undef DEBUG
10
11#include <linux/errno.h>
12#include <linux/fs.h>
Randy.Dunlap995982c2006-07-10 23:05:25 -070013#include <linux/kernel.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/kobject.h>
15#include <linux/module.h>
16#include <linux/slab.h>
17
18#include <asm/uaccess.h>
Oliver Neukum94bebf42006-12-20 10:52:44 +010019#include <asm/semaphore.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020
21#include "sysfs.h"
22
Tejun Heoeb361652007-06-14 03:45:16 +090023struct bin_buffer {
24 struct mutex mutex;
25 void *buffer;
26};
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028static int
29fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
30{
Tejun Heo3e519032007-06-14 03:45:15 +090031 struct sysfs_dirent *attr_sd = dentry->d_fsdata;
32 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 struct kobject * kobj = to_kobj(dentry->d_parent);
34
35 if (!attr->read)
Dmitry Torokhovc76d0ab2005-04-29 01:22:00 -050036 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38 return attr->read(kobj, buffer, off, count);
39}
40
41static ssize_t
Tejun Heo93e3cd82007-06-14 03:45:13 +090042read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
Linus Torvalds1da177e2005-04-16 15:20:36 -070043{
Tejun Heoeb361652007-06-14 03:45:16 +090044 struct bin_buffer *bb = file->private_data;
Josef "Jeff" Sipekf427f5d2006-12-08 02:36:36 -080045 struct dentry *dentry = file->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -070046 int size = dentry->d_inode->i_size;
47 loff_t offs = *off;
Tejun Heo93e3cd82007-06-14 03:45:13 +090048 int count = min_t(size_t, bytes, PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
50 if (size) {
51 if (offs > size)
52 return 0;
53 if (offs + count > size)
54 count = size - offs;
55 }
56
Tejun Heoeb361652007-06-14 03:45:16 +090057 mutex_lock(&bb->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070058
Tejun Heoeb361652007-06-14 03:45:16 +090059 count = fill_read(dentry, bb->buffer, offs, count);
60 if (count < 0)
61 goto out_unlock;
62
63 if (copy_to_user(userbuf, bb->buffer, count)) {
64 count = -EFAULT;
65 goto out_unlock;
66 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Tejun Heo93e3cd82007-06-14 03:45:13 +090068 pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069
70 *off = offs + count;
71
Tejun Heoeb361652007-06-14 03:45:16 +090072 out_unlock:
73 mutex_unlock(&bb->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070074 return count;
75}
76
77static int
78flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
79{
Tejun Heo3e519032007-06-14 03:45:15 +090080 struct sysfs_dirent *attr_sd = dentry->d_fsdata;
81 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 struct kobject *kobj = to_kobj(dentry->d_parent);
83
84 if (!attr->write)
Dmitry Torokhovc76d0ab2005-04-29 01:22:00 -050085 return -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070086
87 return attr->write(kobj, buffer, offset, count);
88}
89
Tejun Heo93e3cd82007-06-14 03:45:13 +090090static ssize_t write(struct file *file, const char __user *userbuf,
91 size_t bytes, loff_t *off)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092{
Tejun Heoeb361652007-06-14 03:45:16 +090093 struct bin_buffer *bb = file->private_data;
Josef "Jeff" Sipekf427f5d2006-12-08 02:36:36 -080094 struct dentry *dentry = file->f_path.dentry;
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 int size = dentry->d_inode->i_size;
96 loff_t offs = *off;
Tejun Heo93e3cd82007-06-14 03:45:13 +090097 int count = min_t(size_t, bytes, PAGE_SIZE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070098
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 if (size) {
100 if (offs > size)
101 return 0;
102 if (offs + count > size)
103 count = size - offs;
104 }
105
Tejun Heoeb361652007-06-14 03:45:16 +0900106 mutex_lock(&bb->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
Tejun Heoeb361652007-06-14 03:45:16 +0900108 if (copy_from_user(bb->buffer, userbuf, count)) {
109 count = -EFAULT;
110 goto out_unlock;
111 }
112
113 count = flush_write(dentry, bb->buffer, offs, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 if (count > 0)
115 *off = offs + count;
Tejun Heoeb361652007-06-14 03:45:16 +0900116
117 out_unlock:
118 mutex_unlock(&bb->mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 return count;
120}
121
122static int mmap(struct file *file, struct vm_area_struct *vma)
123{
Tejun Heoeb361652007-06-14 03:45:16 +0900124 struct bin_buffer *bb = file->private_data;
Tejun Heo3e519032007-06-14 03:45:15 +0900125 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
126 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
127 struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent);
Tejun Heoeb361652007-06-14 03:45:16 +0900128 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129
130 if (!attr->mmap)
131 return -EINVAL;
132
Tejun Heoeb361652007-06-14 03:45:16 +0900133 mutex_lock(&bb->mutex);
134 rc = attr->mmap(kobj, attr, vma);
135 mutex_unlock(&bb->mutex);
136
137 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138}
139
140static int open(struct inode * inode, struct file * file)
141{
Josef "Jeff" Sipekf427f5d2006-12-08 02:36:36 -0800142 struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
Tejun Heo3e519032007-06-14 03:45:15 +0900143 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
144 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
Tejun Heoeb361652007-06-14 03:45:16 +0900145 struct bin_buffer *bb = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 int error = -EINVAL;
147
148 if (!kobj || !attr)
149 goto Done;
150
151 /* Grab the module reference for this attribute if we have one */
152 error = -ENODEV;
153 if (!try_module_get(attr->attr.owner))
154 goto Done;
155
156 error = -EACCES;
157 if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
158 goto Error;
159 if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
160 goto Error;
161
162 error = -ENOMEM;
Tejun Heoeb361652007-06-14 03:45:16 +0900163 bb = kzalloc(sizeof(*bb), GFP_KERNEL);
164 if (!bb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 goto Error;
166
Tejun Heoeb361652007-06-14 03:45:16 +0900167 bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
168 if (!bb->buffer)
169 goto Error;
170
171 mutex_init(&bb->mutex);
172 file->private_data = bb;
173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 error = 0;
Tejun Heoeb361652007-06-14 03:45:16 +0900175 goto Done;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176
177 Error:
Tejun Heoeb361652007-06-14 03:45:16 +0900178 kfree(bb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 module_put(attr->attr.owner);
180 Done:
Mariusz Kozlowskif7506532007-01-02 13:41:10 +0100181 if (error)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 kobject_put(kobj);
183 return error;
184}
185
186static int release(struct inode * inode, struct file * file)
187{
Josef "Jeff" Sipekf427f5d2006-12-08 02:36:36 -0800188 struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent);
Tejun Heo3e519032007-06-14 03:45:15 +0900189 struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
190 struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
Tejun Heoeb361652007-06-14 03:45:16 +0900191 struct bin_buffer *bb = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192
Mariusz Kozlowskif7506532007-01-02 13:41:10 +0100193 kobject_put(kobj);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 module_put(attr->attr.owner);
Tejun Heoeb361652007-06-14 03:45:16 +0900195 kfree(bb->buffer);
196 kfree(bb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 return 0;
198}
199
Arjan van de Ven4b6f5d22006-03-28 01:56:42 -0800200const struct file_operations bin_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 .read = read,
202 .write = write,
203 .mmap = mmap,
204 .llseek = generic_file_llseek,
205 .open = open,
206 .release = release,
207};
208
209/**
210 * sysfs_create_bin_file - create binary file for object.
211 * @kobj: object.
212 * @attr: attribute descriptor.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 */
214
215int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
216{
217 BUG_ON(!kobj || !kobj->dentry || !attr);
218
219 return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
220}
221
222
223/**
224 * sysfs_remove_bin_file - remove binary file for object.
225 * @kobj: object.
226 * @attr: attribute descriptor.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 */
228
Randy.Dunlap995982c2006-07-10 23:05:25 -0700229void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230{
Randy.Dunlap995982c2006-07-10 23:05:25 -0700231 if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
232 printk(KERN_ERR "%s: "
233 "bad dentry or inode or no such file: \"%s\"\n",
234 __FUNCTION__, attr->attr.name);
235 dump_stack();
236 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237}
238
239EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
240EXPORT_SYMBOL_GPL(sysfs_remove_bin_file);