diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2009-03-26 03:42:41 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-04-04 09:29:07 -0400 |
commit | 1f24b5a8ecbb2a3c7080f418974d40e3ffedb221 (patch) | |
tree | 07dfc44f62dac78bdf4a8cdb4d91d60f73c04ff4 | |
parent | 9d63287a461c269edb39941744f4ff22223cf349 (diff) |
[MTD] driver model updates
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>
-rw-r--r-- | drivers/mtd/mtd_blkdevs.c | 1 | ||||
-rw-r--r-- | drivers/mtd/mtdchar.c | 47 | ||||
-rw-r--r-- | drivers/mtd/mtdcore.c | 115 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 9 | ||||
-rw-r--r-- | include/linux/mtd/mtd.h | 7 |
5 files changed, 137 insertions, 42 deletions
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 4109e0bb94f5..a49a9c8f2cb1 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c | |||
@@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) | |||
286 | gd->private_data = new; | 286 | gd->private_data = new; |
287 | new->blkcore_priv = gd; | 287 | new->blkcore_priv = gd; |
288 | gd->queue = tr->blkcore_priv->rq; | 288 | gd->queue = tr->blkcore_priv->rq; |
289 | gd->driverfs_dev = new->mtd->dev.parent; | ||
289 | 290 | ||
290 | if (new->readonly) | 291 | if (new->readonly) |
291 | set_disk_ro(gd, 1); | 292 | set_disk_ro(gd, 1); |
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index f478f1fc3949..763d3f0a1f42 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c | |||
@@ -20,33 +20,6 @@ | |||
20 | 20 | ||
21 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
22 | 22 | ||
23 | static struct class *mtd_class; | ||
24 | |||
25 | static void mtd_notify_add(struct mtd_info* mtd) | ||
26 | { | ||
27 | if (!mtd) | ||
28 | return; | ||
29 | |||
30 | device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2), | ||
31 | NULL, "mtd%d", mtd->index); | ||
32 | |||
33 | device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1), | ||
34 | NULL, "mtd%dro", mtd->index); | ||
35 | } | ||
36 | |||
37 | static void mtd_notify_remove(struct mtd_info* mtd) | ||
38 | { | ||
39 | if (!mtd) | ||
40 | return; | ||
41 | |||
42 | device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2)); | ||
43 | device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1)); | ||
44 | } | ||
45 | |||
46 | static struct mtd_notifier notifier = { | ||
47 | .add = mtd_notify_add, | ||
48 | .remove = mtd_notify_remove, | ||
49 | }; | ||
50 | 23 | ||
51 | /* | 24 | /* |
52 | * Data structure to hold the pointer to the mtd device as well | 25 | * Data structure to hold the pointer to the mtd device as well |
@@ -854,34 +827,26 @@ static const struct file_operations mtd_fops = { | |||
854 | 827 | ||
855 | static int __init init_mtdchar(void) | 828 | static int __init init_mtdchar(void) |
856 | { | 829 | { |
857 | if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) { | 830 | int status; |
831 | |||
832 | status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); | ||
833 | if (status < 0) { | ||
858 | printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", | 834 | printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", |
859 | MTD_CHAR_MAJOR); | 835 | MTD_CHAR_MAJOR); |
860 | return -EAGAIN; | ||
861 | } | ||
862 | |||
863 | mtd_class = class_create(THIS_MODULE, "mtd"); | ||
864 | |||
865 | if (IS_ERR(mtd_class)) { | ||
866 | printk(KERN_ERR "Error creating mtd class.\n"); | ||
867 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); | ||
868 | return PTR_ERR(mtd_class); | ||
869 | } | 836 | } |
870 | 837 | ||
871 | register_mtd_user(¬ifier); | 838 | return status; |
872 | return 0; | ||
873 | } | 839 | } |
874 | 840 | ||
875 | static void __exit cleanup_mtdchar(void) | 841 | static void __exit cleanup_mtdchar(void) |
876 | { | 842 | { |
877 | unregister_mtd_user(¬ifier); | ||
878 | class_destroy(mtd_class); | ||
879 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); | 843 | unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); |
880 | } | 844 | } |
881 | 845 | ||
882 | module_init(init_mtdchar); | 846 | module_init(init_mtdchar); |
883 | module_exit(cleanup_mtdchar); | 847 | module_exit(cleanup_mtdchar); |
884 | 848 | ||
849 | MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR); | ||
885 | 850 | ||
886 | MODULE_LICENSE("GPL"); | 851 | MODULE_LICENSE("GPL"); |
887 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 852 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 65a7f3703881..a88f8bc9a534 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
@@ -23,6 +23,9 @@ | |||
23 | 23 | ||
24 | #include "mtdcore.h" | 24 | #include "mtdcore.h" |
25 | 25 | ||
26 | |||
27 | static struct class *mtd_class; | ||
28 | |||
26 | /* These are exported solely for the purpose of mtd_blkdevs.c. You | 29 | /* These are exported solely for the purpose of mtd_blkdevs.c. You |
27 | should not use them for _anything_ else */ | 30 | should not use them for _anything_ else */ |
28 | DEFINE_MUTEX(mtd_table_mutex); | 31 | DEFINE_MUTEX(mtd_table_mutex); |
@@ -33,6 +36,82 @@ EXPORT_SYMBOL_GPL(mtd_table); | |||
33 | 36 | ||
34 | static LIST_HEAD(mtd_notifiers); | 37 | static LIST_HEAD(mtd_notifiers); |
35 | 38 | ||
39 | |||
40 | #if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE) | ||
41 | #define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2) | ||
42 | #else | ||
43 | #define MTD_DEVT(index) 0 | ||
44 | #endif | ||
45 | |||
46 | /* REVISIT once MTD uses the driver model better, whoever allocates | ||
47 | * the mtd_info will probably want to use the release() hook... | ||
48 | */ | ||
49 | static void mtd_release(struct device *dev) | ||
50 | { | ||
51 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
52 | |||
53 | /* remove /dev/mtdXro node if needed */ | ||
54 | if (MTD_DEVT(mtd->index)) | ||
55 | device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1); | ||
56 | } | ||
57 | |||
58 | static ssize_t mtd_type_show(struct device *dev, | ||
59 | struct device_attribute *attr, char *buf) | ||
60 | { | ||
61 | struct mtd_info *mtd = dev_to_mtd(dev); | ||
62 | char *type; | ||
63 | |||
64 | switch (mtd->type) { | ||
65 | case MTD_ABSENT: | ||
66 | type = "absent"; | ||
67 | break; | ||
68 | case MTD_RAM: | ||
69 | type = "ram"; | ||
70 | break; | ||
71 | case MTD_ROM: | ||
72 | type = "rom"; | ||
73 | break; | ||
74 | case MTD_NORFLASH: | ||
75 | type = "nor"; | ||
76 | break; | ||
77 | case MTD_NANDFLASH: | ||
78 | type = "nand"; | ||
79 | break; | ||
80 | case MTD_DATAFLASH: | ||
81 | type = "dataflash"; | ||
82 | break; | ||
83 | case MTD_UBIVOLUME: | ||
84 | type = "ubi"; | ||
85 | break; | ||
86 | default: | ||
87 | type = "unknown"; | ||
88 | } | ||
89 | |||
90 | return snprintf(buf, PAGE_SIZE, "%s\n", type); | ||
91 | } | ||
92 | static DEVICE_ATTR(mtd_type, S_IRUGO, mtd_type_show, NULL); | ||
93 | |||
94 | static struct attribute *mtd_attrs[] = { | ||
95 | &dev_attr_mtd_type.attr, | ||
96 | /* FIXME provide a /proc/mtd superset */ | ||
97 | NULL, | ||
98 | }; | ||
99 | |||
100 | struct attribute_group mtd_group = { | ||
101 | .attrs = mtd_attrs, | ||
102 | }; | ||
103 | |||
104 | struct attribute_group *mtd_groups[] = { | ||
105 | &mtd_group, | ||
106 | NULL, | ||
107 | }; | ||
108 | |||
109 | static struct device_type mtd_devtype = { | ||
110 | .name = "mtd", | ||
111 | .groups = mtd_groups, | ||
112 | .release = mtd_release, | ||
113 | }; | ||
114 | |||
36 | /** | 115 | /** |
37 | * add_mtd_device - register an MTD device | 116 | * add_mtd_device - register an MTD device |
38 | * @mtd: pointer to new MTD device info structure | 117 | * @mtd: pointer to new MTD device info structure |
@@ -41,6 +120,7 @@ static LIST_HEAD(mtd_notifiers); | |||
41 | * notify each currently active MTD 'user' of its arrival. Returns | 120 | * notify each currently active MTD 'user' of its arrival. Returns |
42 | * zero on success or 1 on failure, which currently will only happen | 121 | * zero on success or 1 on failure, which currently will only happen |
43 | * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) | 122 | * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) |
123 | * or there's a sysfs error. | ||
44 | */ | 124 | */ |
45 | 125 | ||
46 | int add_mtd_device(struct mtd_info *mtd) | 126 | int add_mtd_device(struct mtd_info *mtd) |
@@ -95,6 +175,23 @@ int add_mtd_device(struct mtd_info *mtd) | |||
95 | mtd->name); | 175 | mtd->name); |
96 | } | 176 | } |
97 | 177 | ||
178 | /* Caller should have set dev.parent to match the | ||
179 | * physical device. | ||
180 | */ | ||
181 | mtd->dev.type = &mtd_devtype; | ||
182 | mtd->dev.class = mtd_class; | ||
183 | mtd->dev.devt = MTD_DEVT(i); | ||
184 | dev_set_name(&mtd->dev, "mtd%d", i); | ||
185 | if (device_register(&mtd->dev) != 0) { | ||
186 | mtd_table[i] = NULL; | ||
187 | break; | ||
188 | } | ||
189 | |||
190 | if (MTD_DEVT(i)) | ||
191 | device_create(mtd_class, mtd->dev.parent, | ||
192 | MTD_DEVT(i) + 1, | ||
193 | NULL, "mtd%dro", i); | ||
194 | |||
98 | DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); | 195 | DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); |
99 | /* No need to get a refcount on the module containing | 196 | /* No need to get a refcount on the module containing |
100 | the notifier, since we hold the mtd_table_mutex */ | 197 | the notifier, since we hold the mtd_table_mutex */ |
@@ -358,6 +455,24 @@ EXPORT_SYMBOL_GPL(register_mtd_user); | |||
358 | EXPORT_SYMBOL_GPL(unregister_mtd_user); | 455 | EXPORT_SYMBOL_GPL(unregister_mtd_user); |
359 | EXPORT_SYMBOL_GPL(default_mtd_writev); | 456 | EXPORT_SYMBOL_GPL(default_mtd_writev); |
360 | 457 | ||
458 | static int __init mtd_setup(void) | ||
459 | { | ||
460 | mtd_class = class_create(THIS_MODULE, "mtd"); | ||
461 | |||
462 | if (IS_ERR(mtd_class)) { | ||
463 | pr_err("Error creating mtd class.\n"); | ||
464 | return PTR_ERR(mtd_class); | ||
465 | } | ||
466 | return 0; | ||
467 | } | ||
468 | core_initcall(mtd_setup); | ||
469 | |||
470 | static void __exit mtd_teardown(void) | ||
471 | { | ||
472 | class_destroy(mtd_class); | ||
473 | } | ||
474 | __exitcall(mtd_teardown); | ||
475 | |||
361 | #ifdef CONFIG_PROC_FS | 476 | #ifdef CONFIG_PROC_FS |
362 | 477 | ||
363 | /*====================================================================*/ | 478 | /*====================================================================*/ |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 06d5b9d8853a..02ce38fb1fc3 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -356,6 +356,11 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, | |||
356 | slave->mtd.owner = master->owner; | 356 | slave->mtd.owner = master->owner; |
357 | slave->mtd.backing_dev_info = master->backing_dev_info; | 357 | slave->mtd.backing_dev_info = master->backing_dev_info; |
358 | 358 | ||
359 | /* NOTE: we don't arrange MTDs as a tree; it'd be error-prone | ||
360 | * to have the same data be in two different partitions. | ||
361 | */ | ||
362 | slave->mtd.dev.parent = master->dev.parent; | ||
363 | |||
359 | slave->mtd.read = part_read; | 364 | slave->mtd.read = part_read; |
360 | slave->mtd.write = part_write; | 365 | slave->mtd.write = part_write; |
361 | 366 | ||
@@ -508,7 +513,9 @@ out_register: | |||
508 | * This function, given a master MTD object and a partition table, creates | 513 | * This function, given a master MTD object and a partition table, creates |
509 | * and registers slave MTD objects which are bound to the master according to | 514 | * and registers slave MTD objects which are bound to the master according to |
510 | * the partition definitions. | 515 | * the partition definitions. |
511 | * (Q: should we register the master MTD object as well?) | 516 | * |
517 | * We don't register the master, or expect the caller to have done so, | ||
518 | * for reasons of data integrity. | ||
512 | */ | 519 | */ |
513 | 520 | ||
514 | int add_mtd_partitions(struct mtd_info *master, | 521 | int add_mtd_partitions(struct mtd_info *master, |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 0c079fd307a5..5675b63a0631 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/uio.h> | 12 | #include <linux/uio.h> |
13 | #include <linux/notifier.h> | 13 | #include <linux/notifier.h> |
14 | #include <linux/device.h> | ||
14 | 15 | ||
15 | #include <linux/mtd/compatmac.h> | 16 | #include <linux/mtd/compatmac.h> |
16 | #include <mtd/mtd-abi.h> | 17 | #include <mtd/mtd-abi.h> |
@@ -237,6 +238,7 @@ struct mtd_info { | |||
237 | void *priv; | 238 | void *priv; |
238 | 239 | ||
239 | struct module *owner; | 240 | struct module *owner; |
241 | struct device dev; | ||
240 | int usecount; | 242 | int usecount; |
241 | 243 | ||
242 | /* If the driver is something smart, like UBI, it may need to maintain | 244 | /* If the driver is something smart, like UBI, it may need to maintain |
@@ -247,6 +249,11 @@ struct mtd_info { | |||
247 | void (*put_device) (struct mtd_info *mtd); | 249 | void (*put_device) (struct mtd_info *mtd); |
248 | }; | 250 | }; |
249 | 251 | ||
252 | static inline struct mtd_info *dev_to_mtd(struct device *dev) | ||
253 | { | ||
254 | return dev ? container_of(dev, struct mtd_info, dev) : NULL; | ||
255 | } | ||
256 | |||
250 | static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) | 257 | static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd) |
251 | { | 258 | { |
252 | if (mtd->erasesize_shift) | 259 | if (mtd->erasesize_shift) |