blob: 01978b57e9cb99197e632c53ad98284ba69fae07 [file] [log] [blame]
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +04001/*
2 * Copyright (c) International Business Machines Corp., 2006
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
12 * the GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Artem Bityutskiy (Битюцкий Артём)
19 */
20
21/*
22 * This file includes implementation of UBI character device operations.
23 *
24 * There are two kinds of character devices in UBI: UBI character devices and
25 * UBI volume character devices. UBI character devices allow users to
26 * manipulate whole volumes: create, remove, and re-size them. Volume character
27 * devices provide volume I/O capabilities.
28 *
29 * Major and minor numbers are assigned dynamically to both UBI and volume
30 * character devices.
Artem Bityutskiy9f961b52007-12-16 16:59:31 +020031 *
32 * Well, there is the third kind of character devices - the UBI control
33 * character device, which allows to manipulate by UBI devices - create and
34 * delete them. In other words, it is used for attaching and detaching MTD
35 * devices.
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +040036 */
37
38#include <linux/module.h>
39#include <linux/stat.h>
40#include <linux/ioctl.h>
41#include <linux/capability.h>
42#include <mtd/ubi-user.h>
43#include <asm/uaccess.h>
44#include <asm/div64.h>
45#include "ubi.h"
46
47/*
48 * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
49 * logical eraseblock erase is a debug-only feature).
50 */
51#define UBI_CDEV_IOC_MAX_SEQ 2
52#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
53#define VOL_CDEV_IOC_MAX_SEQ 1
54#else
55#define VOL_CDEV_IOC_MAX_SEQ 2
56#endif
57
58/**
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +040059 * get_exclusive - get exclusive access to an UBI volume.
60 * @desc: volume descriptor
61 *
62 * This function changes UBI volume open mode to "exclusive". Returns previous
63 * mode value (positive integer) in case of success and a negative error code
64 * in case of failure.
65 */
66static int get_exclusive(struct ubi_volume_desc *desc)
67{
68 int users, err;
69 struct ubi_volume *vol = desc->vol;
70
71 spin_lock(&vol->ubi->volumes_lock);
72 users = vol->readers + vol->writers + vol->exclusive;
73 ubi_assert(users > 0);
74 if (users > 1) {
75 dbg_err("%d users for volume %d", users, vol->vol_id);
76 err = -EBUSY;
77 } else {
78 vol->readers = vol->writers = 0;
79 vol->exclusive = 1;
80 err = desc->mode;
81 desc->mode = UBI_EXCLUSIVE;
82 }
83 spin_unlock(&vol->ubi->volumes_lock);
84
85 return err;
86}
87
88/**
89 * revoke_exclusive - revoke exclusive mode.
90 * @desc: volume descriptor
91 * @mode: new mode to switch to
92 */
93static void revoke_exclusive(struct ubi_volume_desc *desc, int mode)
94{
95 struct ubi_volume *vol = desc->vol;
96
97 spin_lock(&vol->ubi->volumes_lock);
98 ubi_assert(vol->readers == 0 && vol->writers == 0);
99 ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE);
100 vol->exclusive = 0;
101 if (mode == UBI_READONLY)
102 vol->readers = 1;
103 else if (mode == UBI_READWRITE)
104 vol->writers = 1;
105 else
106 vol->exclusive = 1;
107 spin_unlock(&vol->ubi->volumes_lock);
108
109 desc->mode = mode;
110}
111
112static int vol_cdev_open(struct inode *inode, struct file *file)
113{
114 struct ubi_volume_desc *desc;
Artem Bityutskiye73f4452007-12-17 17:37:26 +0200115 int vol_id = iminor(inode) - 1, mode, ubi_num;
116
117 ubi_num = ubi_major2num(imajor(inode));
118 if (ubi_num < 0)
119 return ubi_num;
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400120
121 if (file->f_mode & FMODE_WRITE)
122 mode = UBI_READWRITE;
123 else
124 mode = UBI_READONLY;
125
126 dbg_msg("open volume %d, mode %d", vol_id, mode);
127
Artem Bityutskiye73f4452007-12-17 17:37:26 +0200128 desc = ubi_open_volume(ubi_num, vol_id, mode);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400129 if (IS_ERR(desc))
130 return PTR_ERR(desc);
131
132 file->private_data = desc;
133 return 0;
134}
135
136static int vol_cdev_release(struct inode *inode, struct file *file)
137{
138 struct ubi_volume_desc *desc = file->private_data;
139 struct ubi_volume *vol = desc->vol;
140
141 dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode);
142
143 if (vol->updating) {
144 ubi_warn("update of volume %d not finished, volume is damaged",
145 vol->vol_id);
146 vol->updating = 0;
Artem Bityutskiy92ad8f32007-05-06 16:12:54 +0300147 vfree(vol->upd_buf);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400148 }
149
150 ubi_close_volume(desc);
151 return 0;
152}
153
154static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin)
155{
156 struct ubi_volume_desc *desc = file->private_data;
157 struct ubi_volume *vol = desc->vol;
158 loff_t new_offset;
159
160 if (vol->updating) {
161 /* Update is in progress, seeking is prohibited */
162 dbg_err("updating");
163 return -EBUSY;
164 }
165
166 switch (origin) {
167 case 0: /* SEEK_SET */
168 new_offset = offset;
169 break;
170 case 1: /* SEEK_CUR */
171 new_offset = file->f_pos + offset;
172 break;
173 case 2: /* SEEK_END */
174 new_offset = vol->used_bytes + offset;
175 break;
176 default:
177 return -EINVAL;
178 }
179
180 if (new_offset < 0 || new_offset > vol->used_bytes) {
181 dbg_err("bad seek %lld", new_offset);
182 return -EINVAL;
183 }
184
185 dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld",
186 vol->vol_id, offset, origin, new_offset);
187
188 file->f_pos = new_offset;
189 return new_offset;
190}
191
192static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
193 loff_t *offp)
194{
195 struct ubi_volume_desc *desc = file->private_data;
196 struct ubi_volume *vol = desc->vol;
197 struct ubi_device *ubi = vol->ubi;
198 int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size;
199 size_t count_save = count;
200 void *tbuf;
201 uint64_t tmp;
202
203 dbg_msg("read %zd bytes from offset %lld of volume %d",
204 count, *offp, vol_id);
205
206 if (vol->updating) {
207 dbg_err("updating");
208 return -EBUSY;
209 }
210 if (vol->upd_marker) {
211 dbg_err("damaged volume, update marker is set");
212 return -EBADF;
213 }
214 if (*offp == vol->used_bytes || count == 0)
215 return 0;
216
217 if (vol->corrupted)
218 dbg_msg("read from corrupted volume %d", vol_id);
219
220 if (*offp + count > vol->used_bytes)
221 count_save = count = vol->used_bytes - *offp;
222
223 tbuf_size = vol->usable_leb_size;
224 if (count < tbuf_size)
225 tbuf_size = ALIGN(count, ubi->min_io_size);
Artem Bityutskiy92ad8f32007-05-06 16:12:54 +0300226 tbuf = vmalloc(tbuf_size);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400227 if (!tbuf)
228 return -ENOMEM;
229
230 len = count > tbuf_size ? tbuf_size : count;
231
232 tmp = *offp;
233 off = do_div(tmp, vol->usable_leb_size);
234 lnum = tmp;
235
236 do {
237 cond_resched();
238
239 if (off + len >= vol->usable_leb_size)
240 len = vol->usable_leb_size - off;
241
Artem Bityutskiy89b96b62007-12-16 20:00:38 +0200242 err = ubi_eba_read_leb(ubi, vol, lnum, tbuf, off, len, 0);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400243 if (err)
244 break;
245
246 off += len;
247 if (off == vol->usable_leb_size) {
248 lnum += 1;
249 off -= vol->usable_leb_size;
250 }
251
252 count -= len;
253 *offp += len;
254
255 err = copy_to_user(buf, tbuf, len);
256 if (err) {
257 err = -EFAULT;
258 break;
259 }
260
261 buf += len;
262 len = count > tbuf_size ? tbuf_size : count;
263 } while (count);
264
Artem Bityutskiy92ad8f32007-05-06 16:12:54 +0300265 vfree(tbuf);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400266 return err ? err : count_save - count;
267}
268
269#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
270
271/*
272 * This function allows to directly write to dynamic UBI volumes, without
273 * issuing the volume update operation. Available only as a debugging feature.
274 * Very useful for testing UBI.
275 */
276static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
277 size_t count, loff_t *offp)
278{
279 struct ubi_volume_desc *desc = file->private_data;
280 struct ubi_volume *vol = desc->vol;
281 struct ubi_device *ubi = vol->ubi;
282 int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0;
283 size_t count_save = count;
284 char *tbuf;
285 uint64_t tmp;
286
287 dbg_msg("requested: write %zd bytes to offset %lld of volume %u",
288 count, *offp, desc->vol->vol_id);
289
290 if (vol->vol_type == UBI_STATIC_VOLUME)
291 return -EROFS;
292
293 tmp = *offp;
294 off = do_div(tmp, vol->usable_leb_size);
295 lnum = tmp;
296
297 if (off % ubi->min_io_size) {
298 dbg_err("unaligned position");
299 return -EINVAL;
300 }
301
302 if (*offp + count > vol->used_bytes)
303 count_save = count = vol->used_bytes - *offp;
304
305 /* We can write only in fractions of the minimum I/O unit */
306 if (count % ubi->min_io_size) {
307 dbg_err("unaligned write length");
308 return -EINVAL;
309 }
310
311 tbuf_size = vol->usable_leb_size;
312 if (count < tbuf_size)
313 tbuf_size = ALIGN(count, ubi->min_io_size);
Artem Bityutskiy92ad8f32007-05-06 16:12:54 +0300314 tbuf = vmalloc(tbuf_size);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400315 if (!tbuf)
316 return -ENOMEM;
317
318 len = count > tbuf_size ? tbuf_size : count;
319
320 while (count) {
321 cond_resched();
322
323 if (off + len >= vol->usable_leb_size)
324 len = vol->usable_leb_size - off;
325
326 err = copy_from_user(tbuf, buf, len);
327 if (err) {
328 err = -EFAULT;
329 break;
330 }
331
Artem Bityutskiy89b96b62007-12-16 20:00:38 +0200332 err = ubi_eba_write_leb(ubi, vol, lnum, tbuf, off, len,
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400333 UBI_UNKNOWN);
334 if (err)
335 break;
336
337 off += len;
338 if (off == vol->usable_leb_size) {
339 lnum += 1;
340 off -= vol->usable_leb_size;
341 }
342
343 count -= len;
344 *offp += len;
345 buf += len;
346 len = count > tbuf_size ? tbuf_size : count;
347 }
348
Artem Bityutskiy92ad8f32007-05-06 16:12:54 +0300349 vfree(tbuf);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400350 return err ? err : count_save - count;
351}
352
353#else
354#define vol_cdev_direct_write(file, buf, count, offp) -EPERM
355#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */
356
357static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
358 size_t count, loff_t *offp)
359{
360 int err = 0;
361 struct ubi_volume_desc *desc = file->private_data;
362 struct ubi_volume *vol = desc->vol;
363 struct ubi_device *ubi = vol->ubi;
364
365 if (!vol->updating)
366 return vol_cdev_direct_write(file, buf, count, offp);
367
368 err = ubi_more_update_data(ubi, vol->vol_id, buf, count);
369 if (err < 0) {
Artem Bityutskiy01f7b302007-12-15 19:56:51 +0200370 ubi_err("cannot write %zd bytes of update data, error %d",
371 count, err);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400372 return err;
373 }
374
375 if (err) {
376 /*
377 * Update is finished, @err contains number of actually written
378 * bytes now.
379 */
380 count = err;
381
382 err = ubi_check_volume(ubi, vol->vol_id);
383 if (err < 0)
384 return err;
385
386 if (err) {
387 ubi_warn("volume %d on UBI device %d is corrupted",
388 vol->vol_id, ubi->ubi_num);
389 vol->corrupted = 1;
390 }
391 vol->checked = 1;
Artem Bityutskiy941dfb02007-05-05 16:33:13 +0300392 ubi_gluebi_updated(vol);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400393 revoke_exclusive(desc, UBI_READWRITE);
394 }
395
396 *offp += count;
397 return count;
398}
399
400static int vol_cdev_ioctl(struct inode *inode, struct file *file,
401 unsigned int cmd, unsigned long arg)
402{
403 int err = 0;
404 struct ubi_volume_desc *desc = file->private_data;
405 struct ubi_volume *vol = desc->vol;
406 struct ubi_device *ubi = vol->ubi;
407 void __user *argp = (void __user *)arg;
408
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400409 switch (cmd) {
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400410 /* Volume update command */
411 case UBI_IOCVOLUP:
412 {
413 int64_t bytes, rsvd_bytes;
414
415 if (!capable(CAP_SYS_RESOURCE)) {
416 err = -EPERM;
417 break;
418 }
419
420 err = copy_from_user(&bytes, argp, sizeof(int64_t));
421 if (err) {
422 err = -EFAULT;
423 break;
424 }
425
426 if (desc->mode == UBI_READONLY) {
427 err = -EROFS;
428 break;
429 }
430
431 rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad);
432 if (bytes < 0 || bytes > rsvd_bytes) {
433 err = -EINVAL;
434 break;
435 }
436
437 err = get_exclusive(desc);
438 if (err < 0)
439 break;
440
441 err = ubi_start_update(ubi, vol->vol_id, bytes);
442 if (bytes == 0)
443 revoke_exclusive(desc, UBI_READWRITE);
444
445 file->f_pos = 0;
446 break;
447 }
448
449#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
450 /* Logical eraseblock erasure command */
451 case UBI_IOCEBER:
452 {
453 int32_t lnum;
454
Christoph Hellwigbf078032007-05-17 16:32:10 +0200455 err = get_user(lnum, (__user int32_t *)argp);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400456 if (err) {
457 err = -EFAULT;
458 break;
459 }
460
461 if (desc->mode == UBI_READONLY) {
462 err = -EROFS;
463 break;
464 }
465
466 if (lnum < 0 || lnum >= vol->reserved_pebs) {
467 err = -EINVAL;
468 break;
469 }
470
471 if (vol->vol_type != UBI_DYNAMIC_VOLUME) {
472 err = -EROFS;
473 break;
474 }
475
476 dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
Artem Bityutskiy89b96b62007-12-16 20:00:38 +0200477 err = ubi_eba_unmap_leb(ubi, vol, lnum);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400478 if (err)
479 break;
480
481 err = ubi_wl_flush(ubi);
482 break;
483 }
484#endif
485
486 default:
487 err = -ENOTTY;
488 break;
489 }
490
491 return err;
492}
493
494/**
495 * verify_mkvol_req - verify volume creation request.
496 * @ubi: UBI device description object
497 * @req: the request to check
498 *
499 * This function zero if the request is correct, and %-EINVAL if not.
500 */
501static int verify_mkvol_req(const struct ubi_device *ubi,
502 const struct ubi_mkvol_req *req)
503{
504 int n, err = -EINVAL;
505
506 if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 ||
507 req->name_len < 0)
508 goto bad;
509
510 if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) &&
511 req->vol_id != UBI_VOL_NUM_AUTO)
512 goto bad;
513
514 if (req->alignment == 0)
515 goto bad;
516
517 if (req->bytes == 0)
518 goto bad;
519
520 if (req->vol_type != UBI_DYNAMIC_VOLUME &&
521 req->vol_type != UBI_STATIC_VOLUME)
522 goto bad;
523
524 if (req->alignment > ubi->leb_size)
525 goto bad;
526
527 n = req->alignment % ubi->min_io_size;
528 if (req->alignment != 1 && n)
529 goto bad;
530
531 if (req->name_len > UBI_VOL_NAME_MAX) {
532 err = -ENAMETOOLONG;
533 goto bad;
534 }
535
536 return 0;
537
538bad:
539 dbg_err("bad volume creation request");
540 ubi_dbg_dump_mkvol_req(req);
541 return err;
542}
543
544/**
545 * verify_rsvol_req - verify volume re-size request.
546 * @ubi: UBI device description object
547 * @req: the request to check
548 *
549 * This function returns zero if the request is correct, and %-EINVAL if not.
550 */
551static int verify_rsvol_req(const struct ubi_device *ubi,
552 const struct ubi_rsvol_req *req)
553{
554 if (req->bytes <= 0)
555 return -EINVAL;
556
557 if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots)
558 return -EINVAL;
559
560 return 0;
561}
562
563static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
564 unsigned int cmd, unsigned long arg)
565{
566 int err = 0;
567 struct ubi_device *ubi;
568 struct ubi_volume_desc *desc;
569 void __user *argp = (void __user *)arg;
570
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400571 if (!capable(CAP_SYS_RESOURCE))
572 return -EPERM;
573
Artem Bityutskiye73f4452007-12-17 17:37:26 +0200574 ubi = ubi_get_by_major(imajor(inode));
575 if (!ubi)
576 return -ENODEV;
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400577
578 switch (cmd) {
579 /* Create volume command */
580 case UBI_IOCMKVOL:
581 {
582 struct ubi_mkvol_req req;
583
584 dbg_msg("create volume");
Christoph Hellwigbf078032007-05-17 16:32:10 +0200585 err = copy_from_user(&req, argp,
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400586 sizeof(struct ubi_mkvol_req));
587 if (err) {
588 err = -EFAULT;
589 break;
590 }
591
592 err = verify_mkvol_req(ubi, &req);
593 if (err)
594 break;
595
596 req.name[req.name_len] = '\0';
597
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200598 mutex_lock(&ubi->volumes_mutex);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400599 err = ubi_create_volume(ubi, &req);
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200600 mutex_unlock(&ubi->volumes_mutex);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400601 if (err)
602 break;
603
Christoph Hellwigbf078032007-05-17 16:32:10 +0200604 err = put_user(req.vol_id, (__user int32_t *)argp);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400605 if (err)
606 err = -EFAULT;
607
608 break;
609 }
610
611 /* Remove volume command */
612 case UBI_IOCRMVOL:
613 {
614 int vol_id;
615
616 dbg_msg("remove volume");
Christoph Hellwigbf078032007-05-17 16:32:10 +0200617 err = get_user(vol_id, (__user int32_t *)argp);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400618 if (err) {
619 err = -EFAULT;
620 break;
621 }
622
623 desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE);
624 if (IS_ERR(desc)) {
625 err = PTR_ERR(desc);
626 break;
627 }
628
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200629 mutex_lock(&ubi->volumes_mutex);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400630 err = ubi_remove_volume(desc);
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200631 mutex_unlock(&ubi->volumes_mutex);
632
Artem Bityutskiy450f8722007-12-17 13:09:09 +0200633 /*
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200634 * The volume is deleted (unless an error occurred), and the
635 * 'struct ubi_volume' object will be freed when
636 * 'ubi_close_volume()' will call 'put_device()'.
Artem Bityutskiy450f8722007-12-17 13:09:09 +0200637 */
638 ubi_close_volume(desc);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400639 break;
640 }
641
642 /* Re-size volume command */
643 case UBI_IOCRSVOL:
644 {
645 int pebs;
646 uint64_t tmp;
647 struct ubi_rsvol_req req;
648
649 dbg_msg("re-size volume");
Christoph Hellwigbf078032007-05-17 16:32:10 +0200650 err = copy_from_user(&req, argp,
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400651 sizeof(struct ubi_rsvol_req));
652 if (err) {
653 err = -EFAULT;
654 break;
655 }
656
657 err = verify_rsvol_req(ubi, &req);
658 if (err)
659 break;
660
661 desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE);
662 if (IS_ERR(desc)) {
663 err = PTR_ERR(desc);
664 break;
665 }
666
667 tmp = req.bytes;
668 pebs = !!do_div(tmp, desc->vol->usable_leb_size);
669 pebs += tmp;
670
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200671 mutex_lock(&ubi->volumes_mutex);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400672 err = ubi_resize_volume(desc, pebs);
Artem Bityutskiy40e4d0c2007-12-17 17:08:55 +0200673 mutex_unlock(&ubi->volumes_mutex);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400674 ubi_close_volume(desc);
675 break;
676 }
677
678 default:
679 err = -ENOTTY;
680 break;
681 }
682
Artem Bityutskiye73f4452007-12-17 17:37:26 +0200683 ubi_put_device(ubi);
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400684 return err;
685}
686
Artem Bityutskiy9f961b52007-12-16 16:59:31 +0200687/* UBI control character device operations */
688struct file_operations ubi_ctrl_cdev_operations = {
689 .owner = THIS_MODULE,
690};
691
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400692/* UBI character device operations */
693struct file_operations ubi_cdev_operations = {
694 .owner = THIS_MODULE,
695 .ioctl = ubi_cdev_ioctl,
Artem Bityutskiyf800f092007-05-06 16:45:43 +0300696 .llseek = no_llseek,
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400697};
698
699/* UBI volume character device operations */
700struct file_operations ubi_vol_cdev_operations = {
701 .owner = THIS_MODULE,
702 .open = vol_cdev_open,
703 .release = vol_cdev_release,
704 .llseek = vol_cdev_llseek,
705 .read = vol_cdev_read,
706 .write = vol_cdev_write,
Artem Bityutskiyf800f092007-05-06 16:45:43 +0300707 .ioctl = vol_cdev_ioctl,
Artem B. Bityutskiy801c1352006-06-27 12:22:22 +0400708};