Merge branch 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[pandora-kernel.git] / drivers / mtd / ubi / debug.c
index d4d07e5..ab80c0d 100644 (file)
 #ifdef CONFIG_MTD_UBI_DEBUG
 
 #include "ubi.h"
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-
-unsigned int ubi_msg_flags;
-unsigned int ubi_chk_flags;
-unsigned int ubi_tst_flags;
-
-module_param_named(debug_msgs, ubi_msg_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_chks, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-module_param_named(debug_tsts, ubi_chk_flags, uint, S_IRUGO | S_IWUSR);
-
-MODULE_PARM_DESC(debug_msgs, "Debug message type flags");
-MODULE_PARM_DESC(debug_chks, "Debug check flags");
-MODULE_PARM_DESC(debug_tsts, "Debug special test flags");
 
 /**
  * ubi_dbg_dump_ec_hdr - dump an erase counter header.
@@ -75,15 +64,15 @@ void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr)
 {
        printk(KERN_DEBUG "Volume identifier header dump:\n");
        printk(KERN_DEBUG "\tmagic     %08x\n", be32_to_cpu(vid_hdr->magic));
-       printk(KERN_DEBUG "\tversion   %d\n",   (int)vid_hdr->version);
-       printk(KERN_DEBUG "\tvol_type  %d\n",   (int)vid_hdr->vol_type);
-       printk(KERN_DEBUG "\tcopy_flag %d\n",   (int)vid_hdr->copy_flag);
-       printk(KERN_DEBUG "\tcompat    %d\n",   (int)vid_hdr->compat);
-       printk(KERN_DEBUG "\tvol_id    %d\n",   be32_to_cpu(vid_hdr->vol_id));
-       printk(KERN_DEBUG "\tlnum      %d\n",   be32_to_cpu(vid_hdr->lnum));
-       printk(KERN_DEBUG "\tdata_size %d\n",   be32_to_cpu(vid_hdr->data_size));
-       printk(KERN_DEBUG "\tused_ebs  %d\n",   be32_to_cpu(vid_hdr->used_ebs));
-       printk(KERN_DEBUG "\tdata_pad  %d\n",   be32_to_cpu(vid_hdr->data_pad));
+       printk(KERN_DEBUG "\tversion   %d\n",  (int)vid_hdr->version);
+       printk(KERN_DEBUG "\tvol_type  %d\n",  (int)vid_hdr->vol_type);
+       printk(KERN_DEBUG "\tcopy_flag %d\n",  (int)vid_hdr->copy_flag);
+       printk(KERN_DEBUG "\tcompat    %d\n",  (int)vid_hdr->compat);
+       printk(KERN_DEBUG "\tvol_id    %d\n",  be32_to_cpu(vid_hdr->vol_id));
+       printk(KERN_DEBUG "\tlnum      %d\n",  be32_to_cpu(vid_hdr->lnum));
+       printk(KERN_DEBUG "\tdata_size %d\n",  be32_to_cpu(vid_hdr->data_size));
+       printk(KERN_DEBUG "\tused_ebs  %d\n",  be32_to_cpu(vid_hdr->used_ebs));
+       printk(KERN_DEBUG "\tdata_pad  %d\n",  be32_to_cpu(vid_hdr->data_pad));
        printk(KERN_DEBUG "\tsqnum     %llu\n",
                (unsigned long long)be64_to_cpu(vid_hdr->sqnum));
        printk(KERN_DEBUG "\thdr_crc   %08x\n", be32_to_cpu(vid_hdr->hdr_crc));
@@ -242,4 +231,261 @@ out:
        return;
 }
 
+/**
+ * ubi_debugging_init_dev - initialize debugging for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function initializes debugging-related data for UBI device @ubi.
+ * Returns zero in case of success and a negative error code in case of
+ * failure.
+ */
+int ubi_debugging_init_dev(struct ubi_device *ubi)
+{
+       ubi->dbg = kzalloc(sizeof(struct ubi_debug_info), GFP_KERNEL);
+       if (!ubi->dbg)
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * ubi_debugging_exit_dev - free debugging data for an UBI device.
+ * @ubi: UBI device description object
+ */
+void ubi_debugging_exit_dev(struct ubi_device *ubi)
+{
+       kfree(ubi->dbg);
+}
+
+/*
+ * Root directory for UBI stuff in debugfs. Contains sub-directories which
+ * contain the stuff specific to particular UBI devices.
+ */
+static struct dentry *dfs_rootdir;
+
+/**
+ * ubi_debugfs_init - create UBI debugfs directory.
+ *
+ * Create UBI debugfs directory. Returns zero in case of success and a negative
+ * error code in case of failure.
+ */
+int ubi_debugfs_init(void)
+{
+       dfs_rootdir = debugfs_create_dir("ubi", NULL);
+       if (IS_ERR_OR_NULL(dfs_rootdir)) {
+               int err = dfs_rootdir ? -ENODEV : PTR_ERR(dfs_rootdir);
+
+               ubi_err("cannot create \"ubi\" debugfs directory, error %d\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+
+/**
+ * ubi_debugfs_exit - remove UBI debugfs directory.
+ */
+void ubi_debugfs_exit(void)
+{
+       debugfs_remove(dfs_rootdir);
+}
+
+/* Read an UBI debugfs file */
+static ssize_t dfs_file_read(struct file *file, char __user *user_buf,
+                            size_t count, loff_t *ppos)
+{
+       unsigned long ubi_num = (unsigned long)file->private_data;
+       struct dentry *dent = file->f_path.dentry;
+       struct ubi_device *ubi;
+       struct ubi_debug_info *d;
+       char buf[3];
+       int val;
+
+       ubi = ubi_get_device(ubi_num);
+       if (!ubi)
+               return -ENODEV;
+       d = ubi->dbg;
+
+       if (dent == d->dfs_chk_gen)
+               val = d->chk_gen;
+       else if (dent == d->dfs_chk_io)
+               val = d->chk_io;
+       else if (dent == d->dfs_disable_bgt)
+               val = d->disable_bgt;
+       else if (dent == d->dfs_emulate_bitflips)
+               val = d->emulate_bitflips;
+       else if (dent == d->dfs_emulate_io_failures)
+               val = d->emulate_io_failures;
+       else {
+               count = -EINVAL;
+               goto out;
+       }
+
+       if (val)
+               buf[0] = '1';
+       else
+               buf[0] = '0';
+       buf[1] = '\n';
+       buf[2] = 0x00;
+
+       count = simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+
+out:
+       ubi_put_device(ubi);
+       return count;
+}
+
+/* Write an UBI debugfs file */
+static ssize_t dfs_file_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       unsigned long ubi_num = (unsigned long)file->private_data;
+       struct dentry *dent = file->f_path.dentry;
+       struct ubi_device *ubi;
+       struct ubi_debug_info *d;
+       size_t buf_size;
+       char buf[8];
+       int val;
+
+       ubi = ubi_get_device(ubi_num);
+       if (!ubi)
+               return -ENODEV;
+       d = ubi->dbg;
+
+       buf_size = min_t(size_t, count, (sizeof(buf) - 1));
+       if (copy_from_user(buf, user_buf, buf_size)) {
+               count = -EFAULT;
+               goto out;
+       }
+
+       if (buf[0] == '1')
+               val = 1;
+       else if (buf[0] == '0')
+               val = 0;
+       else {
+               count = -EINVAL;
+               goto out;
+       }
+
+       if (dent == d->dfs_chk_gen)
+               d->chk_gen = val;
+       else if (dent == d->dfs_chk_io)
+               d->chk_io = val;
+       else if (dent == d->dfs_disable_bgt)
+               d->disable_bgt = val;
+       else if (dent == d->dfs_emulate_bitflips)
+               d->emulate_bitflips = val;
+       else if (dent == d->dfs_emulate_io_failures)
+               d->emulate_io_failures = val;
+       else
+               count = -EINVAL;
+
+out:
+       ubi_put_device(ubi);
+       return count;
+}
+
+static int default_open(struct inode *inode, struct file *file)
+{
+       if (inode->i_private)
+               file->private_data = inode->i_private;
+
+       return 0;
+}
+
+/* File operations for all UBI debugfs files */
+static const struct file_operations dfs_fops = {
+       .read   = dfs_file_read,
+       .write  = dfs_file_write,
+       .open   = default_open,
+       .llseek = no_llseek,
+       .owner  = THIS_MODULE,
+};
+
+/**
+ * ubi_debugfs_init_dev - initialize debugfs for an UBI device.
+ * @ubi: UBI device description object
+ *
+ * This function creates all debugfs files for UBI device @ubi. Returns zero in
+ * case of success and a negative error code in case of failure.
+ */
+int ubi_debugfs_init_dev(struct ubi_device *ubi)
+{
+       int err, n;
+       unsigned long ubi_num = ubi->ubi_num;
+       const char *fname;
+       struct dentry *dent;
+       struct ubi_debug_info *d = ubi->dbg;
+
+       n = snprintf(d->dfs_dir_name, UBI_DFS_DIR_LEN + 1, UBI_DFS_DIR_NAME,
+                    ubi->ubi_num);
+       if (n == UBI_DFS_DIR_LEN) {
+               /* The array size is too small */
+               fname = UBI_DFS_DIR_NAME;
+               dent = ERR_PTR(-EINVAL);
+               goto out;
+       }
+
+       fname = d->dfs_dir_name;
+       dent = debugfs_create_dir(fname, dfs_rootdir);
+       if (IS_ERR_OR_NULL(dent))
+               goto out;
+       d->dfs_dir = dent;
+
+       fname = "chk_gen";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_gen = dent;
+
+       fname = "chk_io";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_chk_io = dent;
+
+       fname = "tst_disable_bgt";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_disable_bgt = dent;
+
+       fname = "tst_emulate_bitflips";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_emulate_bitflips = dent;
+
+       fname = "tst_emulate_io_failures";
+       dent = debugfs_create_file(fname, S_IWUSR, d->dfs_dir, (void *)ubi_num,
+                                  &dfs_fops);
+       if (IS_ERR_OR_NULL(dent))
+               goto out_remove;
+       d->dfs_emulate_io_failures = dent;
+
+       return 0;
+
+out_remove:
+       debugfs_remove_recursive(d->dfs_dir);
+out:
+       err = dent ? PTR_ERR(dent) : -ENODEV;
+       ubi_err("cannot create \"%s\" debugfs file or directory, error %d\n",
+               fname, err);
+       return err;
+}
+
+/**
+ * dbg_debug_exit_dev - free all debugfs files corresponding to device @ubi
+ * @ubi: UBI device description object
+ */
+void ubi_debugfs_exit_dev(struct ubi_device *ubi)
+{
+       debugfs_remove_recursive(ubi->dbg->dfs_dir);
+}
+
 #endif /* CONFIG_MTD_UBI_DEBUG */