mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-01 08:04:22 +08:00
[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>
This commit is contained in:
parent
9d63287a46
commit
1f24b5a8ec
@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
|
|||||||
gd->private_data = new;
|
gd->private_data = new;
|
||||||
new->blkcore_priv = gd;
|
new->blkcore_priv = gd;
|
||||||
gd->queue = tr->blkcore_priv->rq;
|
gd->queue = tr->blkcore_priv->rq;
|
||||||
|
gd->driverfs_dev = new->mtd->dev.parent;
|
||||||
|
|
||||||
if (new->readonly)
|
if (new->readonly)
|
||||||
set_disk_ro(gd, 1);
|
set_disk_ro(gd, 1);
|
||||||
|
@ -20,33 +20,6 @@
|
|||||||
|
|
||||||
#include <asm/uaccess.h>
|
#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
|
* 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)
|
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",
|
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
|
||||||
MTD_CHAR_MAJOR);
|
MTD_CHAR_MAJOR);
|
||||||
return -EAGAIN;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mtd_class = class_create(THIS_MODULE, "mtd");
|
return status;
|
||||||
|
|
||||||
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(¬ifier);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit cleanup_mtdchar(void)
|
static void __exit cleanup_mtdchar(void)
|
||||||
{
|
{
|
||||||
unregister_mtd_user(¬ifier);
|
|
||||||
class_destroy(mtd_class);
|
|
||||||
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
|
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(init_mtdchar);
|
module_init(init_mtdchar);
|
||||||
module_exit(cleanup_mtdchar);
|
module_exit(cleanup_mtdchar);
|
||||||
|
|
||||||
|
MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
|
||||||
|
@ -23,6 +23,9 @@
|
|||||||
|
|
||||||
#include "mtdcore.h"
|
#include "mtdcore.h"
|
||||||
|
|
||||||
|
|
||||||
|
static struct class *mtd_class;
|
||||||
|
|
||||||
/* These are exported solely for the purpose of mtd_blkdevs.c. You
|
/* These are exported solely for the purpose of mtd_blkdevs.c. You
|
||||||
should not use them for _anything_ else */
|
should not use them for _anything_ else */
|
||||||
DEFINE_MUTEX(mtd_table_mutex);
|
DEFINE_MUTEX(mtd_table_mutex);
|
||||||
@ -33,6 +36,82 @@ EXPORT_SYMBOL_GPL(mtd_table);
|
|||||||
|
|
||||||
static LIST_HEAD(mtd_notifiers);
|
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
|
* add_mtd_device - register an MTD device
|
||||||
* @mtd: pointer to new MTD device info structure
|
* @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
|
* notify each currently active MTD 'user' of its arrival. Returns
|
||||||
* zero on success or 1 on failure, which currently will only happen
|
* 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)
|
* 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)
|
int add_mtd_device(struct mtd_info *mtd)
|
||||||
@ -95,6 +175,23 @@ int add_mtd_device(struct mtd_info *mtd)
|
|||||||
mtd->name);
|
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);
|
DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
|
||||||
/* No need to get a refcount on the module containing
|
/* No need to get a refcount on the module containing
|
||||||
the notifier, since we hold the mtd_table_mutex */
|
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(unregister_mtd_user);
|
||||||
EXPORT_SYMBOL_GPL(default_mtd_writev);
|
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
|
#ifdef CONFIG_PROC_FS
|
||||||
|
|
||||||
/*====================================================================*/
|
/*====================================================================*/
|
||||||
|
@ -356,6 +356,11 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
|
|||||||
slave->mtd.owner = master->owner;
|
slave->mtd.owner = master->owner;
|
||||||
slave->mtd.backing_dev_info = master->backing_dev_info;
|
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.read = part_read;
|
||||||
slave->mtd.write = part_write;
|
slave->mtd.write = part_write;
|
||||||
|
|
||||||
@ -508,7 +513,9 @@ out_register:
|
|||||||
* This function, given a master MTD object and a partition table, creates
|
* 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
|
* and registers slave MTD objects which are bound to the master according to
|
||||||
* the partition definitions.
|
* 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,
|
int add_mtd_partitions(struct mtd_info *master,
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/uio.h>
|
#include <linux/uio.h>
|
||||||
#include <linux/notifier.h>
|
#include <linux/notifier.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
|
||||||
#include <linux/mtd/compatmac.h>
|
#include <linux/mtd/compatmac.h>
|
||||||
#include <mtd/mtd-abi.h>
|
#include <mtd/mtd-abi.h>
|
||||||
@ -237,6 +238,7 @@ struct mtd_info {
|
|||||||
void *priv;
|
void *priv;
|
||||||
|
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
struct device dev;
|
||||||
int usecount;
|
int usecount;
|
||||||
|
|
||||||
/* If the driver is something smart, like UBI, it may need to maintain
|
/* 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);
|
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)
|
static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
|
||||||
{
|
{
|
||||||
if (mtd->erasesize_shift)
|
if (mtd->erasesize_shift)
|
||||||
|
Loading…
Reference in New Issue
Block a user