[MTD] driver model updates
authorDavid Brownell <dbrownell@users.sourceforge.net>
Thu, 26 Mar 2009 07:42:41 +0000 (00:42 -0700)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Sat, 4 Apr 2009 13:29:07 +0000 (14:29 +0100)
Update driver model support in the MTD framework, so it fits
better into the current udev-based hotplug framework:

 - Each mtd_info now has a device node.  MTD drivers should set
   the dev.parent field to point to the physical device, before
   setting up partitions or otherwise declaring MTDs.

 - Those device nodes always map to /sys/class/mtdX device nodes,
   which no longer depend on MTD_CHARDEV.

 - Those mtdX sysfs nodes have a "starter set" of attributes;
   it's not yet sufficient to replace /proc/mtd.

 - Enabling MTD_CHARDEV provides /sys/class/mtdXro/ nodes and the
   /sys/class/mtd*/dev attributes (for udev, mdev, etc).

 - Include a MODULE_ALIAS_CHARDEV_MAJOR macro.  It'll work with
   udev creating the /dev/mtd* nodes, not just a static rootfs.

So the sysfs structure is pretty much what you'd expect, except
that readonly chardev nodes are a bit quirky.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdchar.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdpart.c
include/linux/mtd/mtd.h

index 4109e0b..a49a9c8 100644 (file)
@@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
        gd->private_data = new;
        new->blkcore_priv = gd;
        gd->queue = tr->blkcore_priv->rq;
+       gd->driverfs_dev = new->mtd->dev.parent;
 
        if (new->readonly)
                set_disk_ro(gd, 1);
index f478f1f..763d3f0 100644 (file)
 
 #include <asm/uaccess.h>
 
-static struct class *mtd_class;
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
-                     NULL, "mtd%d", mtd->index);
-
-       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
-                     NULL, "mtd%dro", mtd->index);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
-}
-
-static struct mtd_notifier notifier = {
-       .add    = mtd_notify_add,
-       .remove = mtd_notify_remove,
-};
 
 /*
  * Data structure to hold the pointer to the mtd device as well
@@ -854,34 +827,26 @@ static const struct file_operations mtd_fops = {
 
 static int __init init_mtdchar(void)
 {
-       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
+       int status;
+
+       status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);
+       if (status < 0) {
                printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
                       MTD_CHAR_MAJOR);
-               return -EAGAIN;
-       }
-
-       mtd_class = class_create(THIS_MODULE, "mtd");
-
-       if (IS_ERR(mtd_class)) {
-               printk(KERN_ERR "Error creating mtd class.\n");
-               unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-               return PTR_ERR(mtd_class);
        }
 
-       register_mtd_user(&notifier);
-       return 0;
+       return status;
 }
 
 static void __exit cleanup_mtdchar(void)
 {
-       unregister_mtd_user(&notifier);
-       class_destroy(mtd_class);
        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 }
 
 module_init(init_mtdchar);
 module_exit(cleanup_mtdchar);
 
+MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
index 65a7f37..a88f8bc 100644 (file)
@@ -23,6 +23,9 @@
 
 #include "mtdcore.h"
 
+
+static struct class *mtd_class;
+
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
 DEFINE_MUTEX(mtd_table_mutex);
@@ -33,6 +36,82 @@ EXPORT_SYMBOL_GPL(mtd_table);
 
 static LIST_HEAD(mtd_notifiers);
 
+
+#if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE)
+#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
+#else
+#define MTD_DEVT(index) 0
+#endif
+
+/* REVISIT once MTD uses the driver model better, whoever allocates
+ * the mtd_info will probably want to use the release() hook...
+ */
+static void mtd_release(struct device *dev)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       /* remove /dev/mtdXro node if needed */
+       if (MTD_DEVT(mtd->index))
+               device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1);
+}
+
+static ssize_t mtd_type_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+       char *type;
+
+       switch (mtd->type) {
+       case MTD_ABSENT:
+               type = "absent";
+               break;
+       case MTD_RAM:
+               type = "ram";
+               break;
+       case MTD_ROM:
+               type = "rom";
+               break;
+       case MTD_NORFLASH:
+               type = "nor";
+               break;
+       case MTD_NANDFLASH:
+               type = "nand";
+               break;
+       case MTD_DATAFLASH:
+               type = "dataflash";
+               break;
+       case MTD_UBIVOLUME:
+               type = "ubi";
+               break;
+       default:
+               type = "unknown";
+       }
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", type);
+}
+static DEVICE_ATTR(mtd_type, S_IRUGO, mtd_type_show, NULL);
+
+static struct attribute *mtd_attrs[] = {
+       &dev_attr_mtd_type.attr,
+       /* FIXME provide a /proc/mtd superset */
+       NULL,
+};
+
+struct attribute_group mtd_group = {
+       .attrs          = mtd_attrs,
+};
+
+struct attribute_group *mtd_groups[] = {
+       &mtd_group,
+       NULL,
+};
+
+static struct device_type mtd_devtype = {
+       .name           = "mtd",
+       .groups         = mtd_groups,
+       .release        = mtd_release,
+};
+
 /**
  *     add_mtd_device - register an MTD device
  *     @mtd: pointer to new MTD device info structure
@@ -41,6 +120,7 @@ static LIST_HEAD(mtd_notifiers);
  *     notify each currently active MTD 'user' of its arrival. Returns
  *     zero on success or 1 on failure, which currently will only happen
  *     if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16)
+ *     or there's a sysfs error.
  */
 
 int add_mtd_device(struct mtd_info *mtd)
@@ -95,6 +175,23 @@ int add_mtd_device(struct mtd_info *mtd)
                                               mtd->name);
                        }
 
+                       /* Caller should have set dev.parent to match the
+                        * physical device.
+                        */
+                       mtd->dev.type = &mtd_devtype;
+                       mtd->dev.class = mtd_class;
+                       mtd->dev.devt = MTD_DEVT(i);
+                       dev_set_name(&mtd->dev, "mtd%d", i);
+                       if (device_register(&mtd->dev) != 0) {
+                               mtd_table[i] = NULL;
+                               break;
+                       }
+
+                       if (MTD_DEVT(i))
+                               device_create(mtd_class, mtd->dev.parent,
+                                               MTD_DEVT(i) + 1,
+                                               NULL, "mtd%dro", i);
+
                        DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
                        /* No need to get a refcount on the module containing
                           the notifier, since we hold the mtd_table_mutex */
@@ -358,6 +455,24 @@ EXPORT_SYMBOL_GPL(register_mtd_user);
 EXPORT_SYMBOL_GPL(unregister_mtd_user);
 EXPORT_SYMBOL_GPL(default_mtd_writev);
 
+static int __init mtd_setup(void)
+{
+       mtd_class = class_create(THIS_MODULE, "mtd");
+
+       if (IS_ERR(mtd_class)) {
+               pr_err("Error creating mtd class.\n");
+               return PTR_ERR(mtd_class);
+       }
+       return 0;
+}
+core_initcall(mtd_setup);
+
+static void __exit mtd_teardown(void)
+{
+       class_destroy(mtd_class);
+}
+__exitcall(mtd_teardown);
+
 #ifdef CONFIG_PROC_FS
 
 /*====================================================================*/
index 06d5b9d..02ce38f 100644 (file)
@@ -356,6 +356,11 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
        slave->mtd.owner = master->owner;
        slave->mtd.backing_dev_info = master->backing_dev_info;
 
+       /* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
+        * to have the same data be in two different partitions.
+        */
+       slave->mtd.dev.parent = master->dev.parent;
+
        slave->mtd.read = part_read;
        slave->mtd.write = part_write;
 
@@ -508,7 +513,9 @@ out_register:
  * This function, given a master MTD object and a partition table, creates
  * and registers slave MTD objects which are bound to the master according to
  * the partition definitions.
- * (Q: should we register the master MTD object as well?)
+ *
+ * We don't register the master, or expect the caller to have done so,
+ * for reasons of data integrity.
  */
 
 int add_mtd_partitions(struct mtd_info *master,
index 0c079fd..5675b63 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/uio.h>
 #include <linux/notifier.h>
+#include <linux/device.h>
 
 #include <linux/mtd/compatmac.h>
 #include <mtd/mtd-abi.h>
@@ -237,6 +238,7 @@ struct mtd_info {
        void *priv;
 
        struct module *owner;
+       struct device dev;
        int usecount;
 
        /* If the driver is something smart, like UBI, it may need to maintain
@@ -247,6 +249,11 @@ struct mtd_info {
        void (*put_device) (struct mtd_info *mtd);
 };
 
+static inline struct mtd_info *dev_to_mtd(struct device *dev)
+{
+       return dev ? container_of(dev, struct mtd_info, dev) : NULL;
+}
+
 static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
 {
        if (mtd->erasesize_shift)