[PATCH][MTD] CORE: sysfs support for mtd

sid at ics.uci.edu sid at ics.uci.edu
Wed Jul 25 14:08:46 EDT 2007


This patch adds sysfs support for mtd. It exports some of the members of the mtd_info structure
into the user space via sysfs. The idea is to use sysfs as an alternative to using mtd ioctl(). 
The following information from mtd_info struct [mtd.h] is exported as files:

- size              : Partition size
- erasesize         : Size of erase block
- writesize         : Page size
- type              : On of the following -> Absent, RAM, ROM, NOR Flash, NAND Flash, Data flash
- bbt               : List of bad blocks from device specific block_isbad() function
- numeraseregions   : Number of erase regions

The master mtd partition appears as a directory under sysfs as /sys/devices/mtd. For each mtd
partition (i.e., for each slave mtd found in mtdpart.c), a file under /sys/devices/mtd directory
by name mtdN (N = partition number) is generated. A struct kobject kobj is added as member of
struct mtd_info mtd [mtd.h] to enable mtd_info to become part of the kobjects hierarchy. The
attributes to be exported are added as struct mtd_attr [mtdpart.c]. A soft link from
/sys/bus/platform/devices/mtd points to /sys/deevices/mtd. Additional attributes can be added by
adding an entry to the mtd_default_attrs[] structure [mtdpart.c]

It would be nice if the patch could be tested. I have tested it on a MIPS based board with NAND
flash. (Please bear with me if the patch does not appear correctly. This is my first patch to the
list and I have tested by sending the patch to myself as per the FAQs).

Signed-off-by: Siddharth Choudhuri <sid at ics.uci.edu>
________________________________________
diff -urN linux-2.6.18.orig/drivers/mtd/mtdpart.c linux-2.6.18.sysfs/drivers/mtd/mtdpart.c
--- linux-2.6.18.orig/drivers/mtd/mtdpart.c	2006-09-19 20:42:06.000000000 -0700
+++ linux-2.6.18.sysfs/drivers/mtd/mtdpart.c	2007-07-24 14:39:24.000000000 -0700
@@ -9,6 +9,9 @@
  *
  * 	02-21-2002	Thomas Gleixner <gleixner at autronix.de>
  *			added support for read_oob, write_oob
+ *
+ *	07-24-2007	Siddharth Choudhuri <sid at ics.uci.edu>
+ *			added support for sysfs
  */

 #include <linux/module.h>
@@ -20,6 +23,8 @@
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/compatmac.h>
+#include <linux/kobject.h>
+#include <linux/device.h>

 /* Our partition linked list */
 static LIST_HEAD(mtd_partitions);
@@ -40,6 +45,180 @@
  */
 #define PART(x)  ((struct mtd_part *)(x))

+/* kobject related data structures */
+extern struct subsystem devices_subsys;
+extern struct bus_type platform_bus_type;
+
+/* sysfs bindings for mtd */
+struct mtd_attr {
+	struct attribute attr;
+	ssize_t (*show)(struct mtd_info *, char *);
+	ssize_t (*store)(struct mtd_info *, const char *, size_t);
+};
+
+/* Call the appropriate show function depending on attribute */
+static ssize_t
+mtd_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
+{
+	struct mtd_info *m = container_of(kobj, struct mtd_info, kobj);
+	struct mtd_attr *mtd_attribute = container_of(attr, struct mtd_attr, attr);
+	ssize_t ret = 0;
+	if (mtd_attribute->show)
+		ret = mtd_attribute->show(m, page);
+	return ret;
+}
+
+/* There are no write attributes under sysfs for mtd */
+static ssize_t
+mtd_attr_store(struct kobject *kobj, struct attribute *attr,
+		const char *page, size_t count)
+{
+	ssize_t ret = 0;
+	return ret;
+}
+
+static struct sysfs_ops mtd_sysfs_ops = {
+	.show	= mtd_attr_show,
+	.store	= mtd_attr_store,
+};
+
+static ssize_t mtd_sysfs_rd_type(struct mtd_info *mtd, char *page)
+{
+	switch(mtd->type) {
+		case MTD_ABSENT:    return sprintf(page, "Absent\n");
+		case MTD_RAM:       return sprintf(page, "RAM\n");
+		case MTD_ROM:       return sprintf(page, "ROM\n");
+		case MTD_NORFLASH:  return sprintf(page, "NOR flash\n");
+		case MTD_NANDFLASH: return sprintf(page, "NAND flash\n");
+		case MTD_DATAFLASH: return sprintf(page, "Data flash\n");
+		default:
+			return sprintf(page, "Unknown %d\n", (u_char)mtd->type);
+	}
+}
+
+static ssize_t mtd_sysfs_rd_size(struct mtd_info *mtd, char *page)
+{
+	return sprintf(page, "%d\n", (u_int32_t)mtd->size);
+}
+
+static ssize_t mtd_sysfs_rd_erasesize(struct mtd_info *mtd, char *page)
+{
+	return sprintf(page, "%d\n", (u_int32_t)mtd->erasesize);
+}
+
+static ssize_t mtd_sysfs_rd_ecctype(struct mtd_info *mtd, char *page)
+{
+	switch(mtd->ecctype) {
+		case MTD_ECC_NONE:          return sprintf(page, "None\n");
+		case MTD_ECC_RS_DiskOnChip: return sprintf(page, "ECC DiskOnChip\n");
+		case MTD_ECC_SW:            return sprintf(page, "Software\n");
+		default:
+			return sprintf(page, "Unknown %d\n", (u_int32_t)mtd->ecctype);
+	}
+}
+
+static ssize_t mtd_sysfs_rd_eccsize(struct mtd_info *mtd, char *page)
+{
+	return sprintf(page, "%d\n", (u_int32_t)mtd->eccsize);
+}
+
+static ssize_t mtd_sysfs_rd_writesize(struct mtd_info *mtd, char *page)
+{
+	return sprintf(page, "%d\n", (u_int32_t)mtd->writesize);
+}
+
+static ssize_t mtd_sysfs_rd_numeraseregions(struct mtd_info *mtd, char *page)
+{
+	return sprintf(page, "%d\n", mtd->numeraseregions);
+}
+
+/*
+ * Display the list of bad blocks from bad block table, list limited
+ * to whatever fits in one page
+ */
+static ssize_t mtd_sysfs_rd_bbt(struct mtd_info *mtd, char *page)
+{
+	int res = 0, ret = 0, lim;
+	loff_t offset = 0;
+	uint32_t i = 0, numblks;
+
+	lim = PAGE_SIZE/12; /* "0x%08x  " is 12 chars */
+	numblks = mtd->size/mtd->erasesize;
+	if (numblks < lim)
+		lim = numblks;
+	for (i = 0; i < lim; i++) {
+		offset = i*mtd->erasesize;
+		res = mtd->block_isbad(mtd, offset);
+		if (res)
+			ret += sprintf(page+ret,"0x%08x  ", i);
+	}
+	ret += sprintf(page+ret,"\n");
+	return ret;
+}
+
+static struct mtd_attr mtd_attr_type = {
+	.attr = {.name = "type", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_type
+};
+
+static struct mtd_attr mtd_attr_size = {
+	.attr = {.name = "size", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_size
+};
+
+static struct mtd_attr mtd_attr_erasesize = {
+	.attr = {.name = "erasesize", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_erasesize
+};
+
+static struct mtd_attr mtd_attr_ecctype = {
+	.attr = {.name = "ecctype", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_ecctype
+};
+
+static struct mtd_attr mtd_attr_eccsize = {
+	.attr = {.name = "eccsize", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_eccsize
+};
+
+static struct mtd_attr mtd_attr_writesize = {
+	.attr = {.name = "writesize", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_writesize
+};
+
+static struct mtd_attr mtd_attr_numeraseregions = {
+	.attr = {.name = "numeraseregions", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_numeraseregions
+};
+
+static struct mtd_attr mtd_attr_bbt = {
+	.attr = {.name = "bbt", .mode = S_IRUGO },
+	.show = mtd_sysfs_rd_bbt
+};
+
+static struct attribute *mtd_default_attrs[] = {
+	&mtd_attr_type.attr,
+	&mtd_attr_erasesize.attr,
+	&mtd_attr_size.attr,
+	&mtd_attr_ecctype.attr,
+	&mtd_attr_eccsize.attr,
+	&mtd_attr_writesize.attr,
+	&mtd_attr_numeraseregions.attr,
+	&mtd_attr_bbt.attr,
+	NULL
+};
+
+/* Free done inside del_mtd_device() so do nothing */
+static void mtd_release(struct kobject *kobj)
+{
+	return;
+}
+
+static struct kobj_type ktype_mtd = {
+	.release	= mtd_release,
+	.sysfs_ops 	= &mtd_sysfs_ops,
+	.default_attrs	= mtd_default_attrs,
+};

 /*
  * MTD methods which simply translate the effective address and pass through
@@ -293,8 +472,10 @@
 		if (slave->master == master) {
 			struct list_head *prev = node->prev;
 			__list_del(prev, node->next);
-			if(slave->registered)
+			if(slave->registered) {
 				del_mtd_device(&slave->mtd);
+				kobject_del(&slave->mtd.kobj);
+			}
 			kfree(slave);
 			node = prev;
 		}
@@ -320,6 +501,16 @@

 	printk (KERN_NOTICE "Creating %d MTD partitions on \"%s\":\n", nbparts, master->name);

+	memset(&master->kobj, 0, sizeof(master->kobj));
+	kobj_set_kset_s(master, devices_subsys);
+	master->kobj.parent = &devices_subsys.kset.kobj;
+	master->kobj.ktype = &ktype_mtd;
+	if (kobject_set_name(&master->kobj, "mtd") < 0) {
+		return -ENOMEM;
+	}
+	kobject_init(&master->kobj);
+	kobject_add(&master->kobj);
+
 	for (i = 0; i < nbparts; i++) {

 		/* allocate the partition structure */
@@ -480,6 +671,17 @@
 			add_mtd_device(&slave->mtd);
 			slave->registered = 1;
 		}
+		memset(&slave->mtd.kobj, 0, sizeof(slave->mtd.kobj));
+		slave->mtd.kobj.kset = master->kobj.kset;
+		slave->mtd.kobj.parent = &master->kobj;
+		slave->mtd.kobj.ktype = &ktype_mtd;
+		slave->mtd.kobj.kset->ktype = &ktype_mtd;
+		if (kobject_set_name(&slave->mtd.kobj, "mtd%d", i) < 0) {
+			return -ENOMEM;
+		}
+		kobject_init(&slave->mtd.kobj);
+		kobject_add(&slave->mtd.kobj);
+		sysfs_create_link(&platform_bus_type.devices.kobj, &master->kobj, "mtd");
 	}

 	return 0;
diff -urN linux-2.6.18.orig/include/linux/mtd/mtd.h linux-2.6.18.sysfs/include/linux/mtd/mtd.h
--- linux-2.6.18.orig/include/linux/mtd/mtd.h	2006-09-19 20:42:06.000000000 -0700
+++ linux-2.6.18.sysfs/include/linux/mtd/mtd.h	2007-07-23 12:11:29.000000000 -0700
@@ -17,6 +17,7 @@
 #include <linux/module.h>
 #include <linux/uio.h>
 #include <linux/notifier.h>
+#include <linux/kobject.h>

 #include <linux/mtd/compatmac.h>
 #include <mtd/mtd-abi.h>
@@ -122,6 +123,8 @@
 	u_int32_t ecctype;
 	u_int32_t eccsize;

+	struct kobject kobj;	// use kobject to export info to sysfs
+
 	/*
 	 * Reuse some of the above unused fields in the case of NOR flash
 	 * with configurable programming regions to avoid modifying the
______________________________________

regards,
-siddharth






More information about the linux-mtd mailing list