diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/char/raw.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/char/raw.c')
-rw-r--r-- | drivers/char/raw.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/drivers/char/raw.c b/drivers/char/raw.c new file mode 100644 index 000000000000..a2e33ec79615 --- /dev/null +++ b/drivers/char/raw.c | |||
@@ -0,0 +1,342 @@ | |||
1 | /* | ||
2 | * linux/drivers/char/raw.c | ||
3 | * | ||
4 | * Front-end raw character devices. These can be bound to any block | ||
5 | * devices to provide genuine Unix raw character device semantics. | ||
6 | * | ||
7 | * We reserve minor number 0 for a control interface. ioctl()s on this | ||
8 | * device are used to bind the other minor numbers to block devices. | ||
9 | */ | ||
10 | |||
11 | #include <linux/init.h> | ||
12 | #include <linux/fs.h> | ||
13 | #include <linux/devfs_fs_kernel.h> | ||
14 | #include <linux/major.h> | ||
15 | #include <linux/blkdev.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/raw.h> | ||
18 | #include <linux/capability.h> | ||
19 | #include <linux/uio.h> | ||
20 | #include <linux/cdev.h> | ||
21 | #include <linux/device.h> | ||
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | |||
25 | struct raw_device_data { | ||
26 | struct block_device *binding; | ||
27 | int inuse; | ||
28 | }; | ||
29 | |||
30 | static struct class_simple *raw_class; | ||
31 | static struct raw_device_data raw_devices[MAX_RAW_MINORS]; | ||
32 | static DECLARE_MUTEX(raw_mutex); | ||
33 | static struct file_operations raw_ctl_fops; /* forward declaration */ | ||
34 | |||
35 | /* | ||
36 | * Open/close code for raw IO. | ||
37 | * | ||
38 | * We just rewrite the i_mapping for the /dev/raw/rawN file descriptor to | ||
39 | * point at the blockdev's address_space and set the file handle to use | ||
40 | * O_DIRECT. | ||
41 | * | ||
42 | * Set the device's soft blocksize to the minimum possible. This gives the | ||
43 | * finest possible alignment and has no adverse impact on performance. | ||
44 | */ | ||
45 | static int raw_open(struct inode *inode, struct file *filp) | ||
46 | { | ||
47 | const int minor = iminor(inode); | ||
48 | struct block_device *bdev; | ||
49 | int err; | ||
50 | |||
51 | if (minor == 0) { /* It is the control device */ | ||
52 | filp->f_op = &raw_ctl_fops; | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | down(&raw_mutex); | ||
57 | |||
58 | /* | ||
59 | * All we need to do on open is check that the device is bound. | ||
60 | */ | ||
61 | bdev = raw_devices[minor].binding; | ||
62 | err = -ENODEV; | ||
63 | if (!bdev) | ||
64 | goto out; | ||
65 | igrab(bdev->bd_inode); | ||
66 | err = blkdev_get(bdev, filp->f_mode, 0); | ||
67 | if (err) | ||
68 | goto out; | ||
69 | err = bd_claim(bdev, raw_open); | ||
70 | if (err) | ||
71 | goto out1; | ||
72 | err = set_blocksize(bdev, bdev_hardsect_size(bdev)); | ||
73 | if (err) | ||
74 | goto out2; | ||
75 | filp->f_flags |= O_DIRECT; | ||
76 | filp->f_mapping = bdev->bd_inode->i_mapping; | ||
77 | if (++raw_devices[minor].inuse == 1) | ||
78 | filp->f_dentry->d_inode->i_mapping = | ||
79 | bdev->bd_inode->i_mapping; | ||
80 | filp->private_data = bdev; | ||
81 | up(&raw_mutex); | ||
82 | return 0; | ||
83 | |||
84 | out2: | ||
85 | bd_release(bdev); | ||
86 | out1: | ||
87 | blkdev_put(bdev); | ||
88 | out: | ||
89 | up(&raw_mutex); | ||
90 | return err; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * When the final fd which refers to this character-special node is closed, we | ||
95 | * make its ->mapping point back at its own i_data. | ||
96 | */ | ||
97 | static int raw_release(struct inode *inode, struct file *filp) | ||
98 | { | ||
99 | const int minor= iminor(inode); | ||
100 | struct block_device *bdev; | ||
101 | |||
102 | down(&raw_mutex); | ||
103 | bdev = raw_devices[minor].binding; | ||
104 | if (--raw_devices[minor].inuse == 0) { | ||
105 | /* Here inode->i_mapping == bdev->bd_inode->i_mapping */ | ||
106 | inode->i_mapping = &inode->i_data; | ||
107 | inode->i_mapping->backing_dev_info = &default_backing_dev_info; | ||
108 | } | ||
109 | up(&raw_mutex); | ||
110 | |||
111 | bd_release(bdev); | ||
112 | blkdev_put(bdev); | ||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | /* | ||
117 | * Forward ioctls to the underlying block device. | ||
118 | */ | ||
119 | static int | ||
120 | raw_ioctl(struct inode *inode, struct file *filp, | ||
121 | unsigned int command, unsigned long arg) | ||
122 | { | ||
123 | struct block_device *bdev = filp->private_data; | ||
124 | |||
125 | return ioctl_by_bdev(bdev, command, arg); | ||
126 | } | ||
127 | |||
128 | static void bind_device(struct raw_config_request *rq) | ||
129 | { | ||
130 | class_simple_device_remove(MKDEV(RAW_MAJOR, rq->raw_minor)); | ||
131 | class_simple_device_add(raw_class, MKDEV(RAW_MAJOR, rq->raw_minor), | ||
132 | NULL, "raw%d", rq->raw_minor); | ||
133 | } | ||
134 | |||
135 | /* | ||
136 | * Deal with ioctls against the raw-device control interface, to bind | ||
137 | * and unbind other raw devices. | ||
138 | */ | ||
139 | static int raw_ctl_ioctl(struct inode *inode, struct file *filp, | ||
140 | unsigned int command, unsigned long arg) | ||
141 | { | ||
142 | struct raw_config_request rq; | ||
143 | struct raw_device_data *rawdev; | ||
144 | int err = 0; | ||
145 | |||
146 | switch (command) { | ||
147 | case RAW_SETBIND: | ||
148 | case RAW_GETBIND: | ||
149 | |||
150 | /* First, find out which raw minor we want */ | ||
151 | |||
152 | if (copy_from_user(&rq, (void __user *) arg, sizeof(rq))) { | ||
153 | err = -EFAULT; | ||
154 | goto out; | ||
155 | } | ||
156 | |||
157 | if (rq.raw_minor < 0 || rq.raw_minor >= MAX_RAW_MINORS) { | ||
158 | err = -EINVAL; | ||
159 | goto out; | ||
160 | } | ||
161 | rawdev = &raw_devices[rq.raw_minor]; | ||
162 | |||
163 | if (command == RAW_SETBIND) { | ||
164 | dev_t dev; | ||
165 | |||
166 | /* | ||
167 | * This is like making block devices, so demand the | ||
168 | * same capability | ||
169 | */ | ||
170 | if (!capable(CAP_SYS_ADMIN)) { | ||
171 | err = -EPERM; | ||
172 | goto out; | ||
173 | } | ||
174 | |||
175 | /* | ||
176 | * For now, we don't need to check that the underlying | ||
177 | * block device is present or not: we can do that when | ||
178 | * the raw device is opened. Just check that the | ||
179 | * major/minor numbers make sense. | ||
180 | */ | ||
181 | |||
182 | dev = MKDEV(rq.block_major, rq.block_minor); | ||
183 | if ((rq.block_major == 0 && rq.block_minor != 0) || | ||
184 | MAJOR(dev) != rq.block_major || | ||
185 | MINOR(dev) != rq.block_minor) { | ||
186 | err = -EINVAL; | ||
187 | goto out; | ||
188 | } | ||
189 | |||
190 | down(&raw_mutex); | ||
191 | if (rawdev->inuse) { | ||
192 | up(&raw_mutex); | ||
193 | err = -EBUSY; | ||
194 | goto out; | ||
195 | } | ||
196 | if (rawdev->binding) { | ||
197 | bdput(rawdev->binding); | ||
198 | module_put(THIS_MODULE); | ||
199 | } | ||
200 | if (rq.block_major == 0 && rq.block_minor == 0) { | ||
201 | /* unbind */ | ||
202 | rawdev->binding = NULL; | ||
203 | class_simple_device_remove(MKDEV(RAW_MAJOR, | ||
204 | rq.raw_minor)); | ||
205 | } else { | ||
206 | rawdev->binding = bdget(dev); | ||
207 | if (rawdev->binding == NULL) | ||
208 | err = -ENOMEM; | ||
209 | else { | ||
210 | __module_get(THIS_MODULE); | ||
211 | bind_device(&rq); | ||
212 | } | ||
213 | } | ||
214 | up(&raw_mutex); | ||
215 | } else { | ||
216 | struct block_device *bdev; | ||
217 | |||
218 | down(&raw_mutex); | ||
219 | bdev = rawdev->binding; | ||
220 | if (bdev) { | ||
221 | rq.block_major = MAJOR(bdev->bd_dev); | ||
222 | rq.block_minor = MINOR(bdev->bd_dev); | ||
223 | } else { | ||
224 | rq.block_major = rq.block_minor = 0; | ||
225 | } | ||
226 | up(&raw_mutex); | ||
227 | if (copy_to_user((void __user *)arg, &rq, sizeof(rq))) { | ||
228 | err = -EFAULT; | ||
229 | goto out; | ||
230 | } | ||
231 | } | ||
232 | break; | ||
233 | default: | ||
234 | err = -EINVAL; | ||
235 | break; | ||
236 | } | ||
237 | out: | ||
238 | return err; | ||
239 | } | ||
240 | |||
241 | static ssize_t raw_file_write(struct file *file, const char __user *buf, | ||
242 | size_t count, loff_t *ppos) | ||
243 | { | ||
244 | struct iovec local_iov = { | ||
245 | .iov_base = (char __user *)buf, | ||
246 | .iov_len = count | ||
247 | }; | ||
248 | |||
249 | return generic_file_write_nolock(file, &local_iov, 1, ppos); | ||
250 | } | ||
251 | |||
252 | static ssize_t raw_file_aio_write(struct kiocb *iocb, const char __user *buf, | ||
253 | size_t count, loff_t pos) | ||
254 | { | ||
255 | struct iovec local_iov = { | ||
256 | .iov_base = (char __user *)buf, | ||
257 | .iov_len = count | ||
258 | }; | ||
259 | |||
260 | return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos); | ||
261 | } | ||
262 | |||
263 | |||
264 | static struct file_operations raw_fops = { | ||
265 | .read = generic_file_read, | ||
266 | .aio_read = generic_file_aio_read, | ||
267 | .write = raw_file_write, | ||
268 | .aio_write = raw_file_aio_write, | ||
269 | .open = raw_open, | ||
270 | .release= raw_release, | ||
271 | .ioctl = raw_ioctl, | ||
272 | .readv = generic_file_readv, | ||
273 | .writev = generic_file_writev, | ||
274 | .owner = THIS_MODULE, | ||
275 | }; | ||
276 | |||
277 | static struct file_operations raw_ctl_fops = { | ||
278 | .ioctl = raw_ctl_ioctl, | ||
279 | .open = raw_open, | ||
280 | .owner = THIS_MODULE, | ||
281 | }; | ||
282 | |||
283 | static struct cdev raw_cdev = { | ||
284 | .kobj = {.name = "raw", }, | ||
285 | .owner = THIS_MODULE, | ||
286 | }; | ||
287 | |||
288 | static int __init raw_init(void) | ||
289 | { | ||
290 | int i; | ||
291 | dev_t dev = MKDEV(RAW_MAJOR, 0); | ||
292 | |||
293 | if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw")) | ||
294 | goto error; | ||
295 | |||
296 | cdev_init(&raw_cdev, &raw_fops); | ||
297 | if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) { | ||
298 | kobject_put(&raw_cdev.kobj); | ||
299 | unregister_chrdev_region(dev, MAX_RAW_MINORS); | ||
300 | goto error; | ||
301 | } | ||
302 | |||
303 | raw_class = class_simple_create(THIS_MODULE, "raw"); | ||
304 | if (IS_ERR(raw_class)) { | ||
305 | printk(KERN_ERR "Error creating raw class.\n"); | ||
306 | cdev_del(&raw_cdev); | ||
307 | unregister_chrdev_region(dev, MAX_RAW_MINORS); | ||
308 | goto error; | ||
309 | } | ||
310 | class_simple_device_add(raw_class, MKDEV(RAW_MAJOR, 0), NULL, "rawctl"); | ||
311 | |||
312 | devfs_mk_cdev(MKDEV(RAW_MAJOR, 0), | ||
313 | S_IFCHR | S_IRUGO | S_IWUGO, | ||
314 | "raw/rawctl"); | ||
315 | for (i = 1; i < MAX_RAW_MINORS; i++) | ||
316 | devfs_mk_cdev(MKDEV(RAW_MAJOR, i), | ||
317 | S_IFCHR | S_IRUGO | S_IWUGO, | ||
318 | "raw/raw%d", i); | ||
319 | return 0; | ||
320 | |||
321 | error: | ||
322 | printk(KERN_ERR "error register raw device\n"); | ||
323 | return 1; | ||
324 | } | ||
325 | |||
326 | static void __exit raw_exit(void) | ||
327 | { | ||
328 | int i; | ||
329 | |||
330 | for (i = 1; i < MAX_RAW_MINORS; i++) | ||
331 | devfs_remove("raw/raw%d", i); | ||
332 | devfs_remove("raw/rawctl"); | ||
333 | devfs_remove("raw"); | ||
334 | class_simple_device_remove(MKDEV(RAW_MAJOR, 0)); | ||
335 | class_simple_destroy(raw_class); | ||
336 | cdev_del(&raw_cdev); | ||
337 | unregister_chrdev_region(MKDEV(RAW_MAJOR, 0), MAX_RAW_MINORS); | ||
338 | } | ||
339 | |||
340 | module_init(raw_init); | ||
341 | module_exit(raw_exit); | ||
342 | MODULE_LICENSE("GPL"); | ||