]> err.no Git - linux-2.6/blobdiff - drivers/mtd/ubi/cdev.c
olpc: sdhci: add quirk for the Marvell CaFe's vdd/powerup issue
[linux-2.6] / drivers / mtd / ubi / cdev.c
index 01978b57e9cb99197e632c53ad98284ba69fae07..9d6aae5449b67010e0ba80e247aab81e3b030ce9 100644 (file)
 #include <asm/div64.h>
 #include "ubi.h"
 
-/*
- * Maximum sequence numbers of UBI and volume character device IOCTLs (direct
- * logical eraseblock erase is a debug-only feature).
- */
-#define UBI_CDEV_IOC_MAX_SEQ 2
-#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO
-#define VOL_CDEV_IOC_MAX_SEQ 1
-#else
-#define VOL_CDEV_IOC_MAX_SEQ 2
-#endif
-
 /**
  * get_exclusive - get exclusive access to an UBI volume.
  * @desc: volume descriptor
@@ -143,8 +132,15 @@ static int vol_cdev_release(struct inode *inode, struct file *file)
        if (vol->updating) {
                ubi_warn("update of volume %d not finished, volume is damaged",
                         vol->vol_id);
+               ubi_assert(!vol->changing_leb);
                vol->updating = 0;
                vfree(vol->upd_buf);
+       } else if (vol->changing_leb) {
+               dbg_msg("only %lld of %lld bytes received for atomic LEB change"
+                       " for volume %d:%d, cancel", vol->upd_received,
+                       vol->upd_bytes, vol->ubi->ubi_num, vol->vol_id);
+               vol->changing_leb = 0;
+               vfree(vol->upd_buf);
        }
 
        ubi_close_volume(desc);
@@ -195,13 +191,13 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
        struct ubi_volume_desc *desc = file->private_data;
        struct ubi_volume *vol = desc->vol;
        struct ubi_device *ubi = vol->ubi;
-       int err, lnum, off, len,  vol_id = desc->vol->vol_id, tbuf_size;
+       int err, lnum, off, len,  tbuf_size;
        size_t count_save = count;
        void *tbuf;
        uint64_t tmp;
 
        dbg_msg("read %zd bytes from offset %lld of volume %d",
-               count, *offp, vol_id);
+               count, *offp, vol->vol_id);
 
        if (vol->updating) {
                dbg_err("updating");
@@ -215,7 +211,7 @@ static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count,
                return 0;
 
        if (vol->corrupted)
-               dbg_msg("read from corrupted volume %d", vol_id);
+               dbg_msg("read from corrupted volume %d", vol->vol_id);
 
        if (*offp + count > vol->used_bytes)
                count_save = count = vol->used_bytes - *offp;
@@ -279,13 +275,13 @@ static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf,
        struct ubi_volume_desc *desc = file->private_data;
        struct ubi_volume *vol = desc->vol;
        struct ubi_device *ubi = vol->ubi;
-       int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0;
+       int lnum, off, len, tbuf_size, err = 0;
        size_t count_save = count;
        char *tbuf;
        uint64_t tmp;
 
        dbg_msg("requested: write %zd bytes to offset %lld of volume %u",
-               count, *offp, desc->vol->vol_id);
+               count, *offp, vol->vol_id);
 
        if (vol->vol_type == UBI_STATIC_VOLUME)
                return -EROFS;
@@ -362,23 +358,32 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
        struct ubi_volume *vol = desc->vol;
        struct ubi_device *ubi = vol->ubi;
 
-       if (!vol->updating)
+       if (!vol->updating && !vol->changing_leb)
                return vol_cdev_direct_write(file, buf, count, offp);
 
-       err = ubi_more_update_data(ubi, vol->vol_id, buf, count);
+       if (vol->updating)
+               err = ubi_more_update_data(ubi, vol, buf, count);
+       else
+               err = ubi_more_leb_change_data(ubi, vol, buf, count);
+
        if (err < 0) {
-               ubi_err("cannot write %zd bytes of update data, error %d",
+               ubi_err("cannot accept more %zd bytes of data, error %d",
                        count, err);
                return err;
        }
 
        if (err) {
                /*
-                * Update is finished, @err contains number of actually written
-                * bytes now.
+                * The operation is finished, @err contains number of actually
+                * written bytes.
                 */
                count = err;
 
+               if (vol->changing_leb) {
+                       revoke_exclusive(desc, UBI_READWRITE);
+                       return count;
+               }
+
                err = ubi_check_volume(ubi, vol->vol_id);
                if (err < 0)
                        return err;
@@ -393,7 +398,6 @@ static ssize_t vol_cdev_write(struct file *file, const char __user *buf,
                revoke_exclusive(desc, UBI_READWRITE);
        }
 
-       *offp += count;
        return count;
 }
 
@@ -438,11 +442,46 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
                if (err < 0)
                        break;
 
-               err = ubi_start_update(ubi, vol->vol_id, bytes);
+               err = ubi_start_update(ubi, vol, bytes);
                if (bytes == 0)
                        revoke_exclusive(desc, UBI_READWRITE);
+               break;
+       }
+
+       /* Atomic logical eraseblock change command */
+       case UBI_IOCEBCH:
+       {
+               struct ubi_leb_change_req req;
+
+               err = copy_from_user(&req, argp,
+                                    sizeof(struct ubi_leb_change_req));
+               if (err) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (desc->mode == UBI_READONLY ||
+                   vol->vol_type == UBI_STATIC_VOLUME) {
+                       err = -EROFS;
+                       break;
+               }
+
+               /* Validate the request */
+               err = -EINVAL;
+               if (req.lnum < 0 || req.lnum >= vol->reserved_pebs ||
+                   req.bytes < 0 || req.lnum >= vol->usable_leb_size)
+                       break;
+               if (req.dtype != UBI_LONGTERM && req.dtype != UBI_SHORTTERM &&
+                   req.dtype != UBI_UNKNOWN)
+                       break;
 
-               file->f_pos = 0;
+               err = get_exclusive(desc);
+               if (err < 0)
+                       break;
+
+               err = ubi_start_leb_change(ubi, vol, &req);
+               if (req.bytes == 0)
+                       revoke_exclusive(desc, UBI_READWRITE);
                break;
        }
 
@@ -458,7 +497,8 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
                        break;
                }
 
-               if (desc->mode == UBI_READONLY) {
+               if (desc->mode == UBI_READONLY ||
+                   vol->vol_type == UBI_STATIC_VOLUME) {
                        err = -EROFS;
                        break;
                }
@@ -468,11 +508,6 @@ static int vol_cdev_ioctl(struct inode *inode, struct file *file,
                        break;
                }
 
-               if (vol->vol_type != UBI_DYNAMIC_VOLUME) {
-                       err = -EROFS;
-                       break;
-               }
-
                dbg_msg("erase LEB %d:%d", vol->vol_id, lnum);
                err = ubi_eba_unmap_leb(ubi, vol, lnum);
                if (err)
@@ -582,8 +617,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
                struct ubi_mkvol_req req;
 
                dbg_msg("create volume");
-               err = copy_from_user(&req, argp,
-                                      sizeof(struct ubi_mkvol_req));
+               err = copy_from_user(&req, argp, sizeof(struct ubi_mkvol_req));
                if (err) {
                        err = -EFAULT;
                        break;
@@ -647,8 +681,7 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
                struct ubi_rsvol_req req;
 
                dbg_msg("re-size volume");
-               err = copy_from_user(&req, argp,
-                                      sizeof(struct ubi_rsvol_req));
+               err = copy_from_user(&req, argp, sizeof(struct ubi_rsvol_req));
                if (err) {
                        err = -EFAULT;
                        break;
@@ -684,8 +717,86 @@ static int ubi_cdev_ioctl(struct inode *inode, struct file *file,
        return err;
 }
 
+static int ctrl_cdev_ioctl(struct inode *inode, struct file *file,
+                          unsigned int cmd, unsigned long arg)
+{
+       int err = 0;
+       void __user *argp = (void __user *)arg;
+
+       if (!capable(CAP_SYS_RESOURCE))
+               return -EPERM;
+
+       switch (cmd) {
+       /* Attach an MTD device command */
+       case UBI_IOCATT:
+       {
+               struct ubi_attach_req req;
+               struct mtd_info *mtd;
+
+               dbg_msg("attach MTD device");
+               err = copy_from_user(&req, argp, sizeof(struct ubi_attach_req));
+               if (err) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               if (req.mtd_num < 0 ||
+                   (req.ubi_num < 0 && req.ubi_num != UBI_DEV_NUM_AUTO)) {
+                       err = -EINVAL;
+                       break;
+               }
+
+               mtd = get_mtd_device(NULL, req.mtd_num);
+               if (IS_ERR(mtd)) {
+                       err = PTR_ERR(mtd);
+                       break;
+               }
+
+               /*
+                * Note, further request verification is done by
+                * 'ubi_attach_mtd_dev()'.
+                */
+               mutex_lock(&ubi_devices_mutex);
+               err = ubi_attach_mtd_dev(mtd, req.ubi_num, req.vid_hdr_offset);
+               mutex_unlock(&ubi_devices_mutex);
+               if (err < 0)
+                       put_mtd_device(mtd);
+               else
+                       /* @err contains UBI device number */
+                       err = put_user(err, (__user int32_t *)argp);
+
+               break;
+       }
+
+       /* Detach an MTD device command */
+       case UBI_IOCDET:
+       {
+               int ubi_num;
+
+               dbg_msg("dettach MTD device");
+               err = get_user(ubi_num, (__user int32_t *)argp);
+               if (err) {
+                       err = -EFAULT;
+                       break;
+               }
+
+               mutex_lock(&ubi_devices_mutex);
+               err = ubi_detach_mtd_dev(ubi_num, 0);
+               mutex_unlock(&ubi_devices_mutex);
+               break;
+       }
+
+       default:
+               err = -ENOTTY;
+               break;
+       }
+
+       return err;
+}
+
 /* UBI control character device operations */
 struct file_operations ubi_ctrl_cdev_operations = {
+       .ioctl = ctrl_cdev_ioctl,
        .owner = THIS_MODULE,
 };