三、MTD建立裝置節點
MTD子系統下如何建立裝置節點?
第一步:MTD裝置層。(MTD子系統)
register_chrdev注冊字元型mtd裝置,并添加該裝置到核心,主裝置号為90。但是此時還未在/dev下形成mtd裝置節點。
第二步:MTD原始裝置層。(MTD子系統)
class_register注冊一個mtd類mtd_class,後面再注冊mtd裝置時會用到該class。
第三步:驅動層。(調用接口完成)
在添加MTD裝置同時,在原始裝置層注冊的那個class接口上建立了裝置節點。/dev/mtdXXX出現
核心和驅動中代碼執行的流程:
1)mtdchar.c:
init_mtdchar ---> __register_chrdev();---> cdev_add
2)mtdcore.c:
init_mtd --->class_register(&mtd_class);//建立mtd類
3)jz4780_nand.c:
device_create(); ---> add_mtd_partitions---> add_mtd_device
---> device_create();
其程式代碼分析如下圖所示:
關于裝置節點的建立
一》MTD裝置層
static int __init init_mtdchar(void)
{
int ret;
ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,
"mtd", &mtd_fops);
//申請裝置号并注冊
if (ret < 0) {
pr_notice("Can't allocate major number %d for "
"Memory Technology Devices.\n", MTD_CHAR_MAJOR);
return ret;
}
ret = register_filesystem(&mtd_inodefs_type);
//注冊mtd_inodefs_type檔案系統
if (ret) {
pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret);
goto err_unregister_chdev;
}
return ret;
err_unregister_chdev:
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
//釋放裝置号
return ret;
}
int __register_chrdev(unsigned int major, unsigned int baseminor,
unsigned int count, const char *name,
const struct file_operations *fops)
{
struct char_device_struct *cd;
struct cdev *cdev;
int err = -ENOMEM;
cd = __register_chrdev_region(major, baseminor, count, name);
if (IS_ERR(cd))
return PTR_ERR(cd);
cdev = cdev_alloc();
if (!cdev)
goto out2;
cdev->owner = fops->owner;
cdev->ops = fops;
kobject_set_name(&cdev->kobj, "%s", name);
err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);
if (err)
goto out;
cd->cdev = cdev;
return major ? 0 : cd->major;
out:
kobject_put(&cdev->kobj);
out2:
kfree(__unregister_chrdev_region(cd->major, baseminor, count));
return err;
}
流程:
init_mtdchar ------>__register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS,"mtd", &mtd_fops);------>cdev_add
分析:這是建立裝置節點的第一步:MTD裝置層。
register_chrdev注冊字元型mtd裝置,并添加該裝置到核心,主裝置号為90。但是要注意的是此時還未在/dev下形成mtd裝置節點。
二》MTD原始裝置層
static int __init init_mtd(void)
{
int ret;
ret = class_register(&mtd_class);//建立mtd類
if (ret)
goto err_reg;
ret = mtd_bdi_init(&mtd_bdi_unmappable, "mtd-unmap");//支援無映射裝置
if (ret)
goto err_bdi1;
ret = mtd_bdi_init(&mtd_bdi_ro_mappable, "mtd-romap");//支援R/O映射裝置
if (ret)
goto err_bdi2;
ret = mtd_bdi_init(&mtd_bdi_rw_mappable, "mtd-rwmap");//支援可寫可映射裝置
if (ret)
goto err_bdi3;
#ifdef CONFIG_PROC_FS
proc_mtd = proc_create("mtd", 0, NULL, &mtd_proc_ops);//建立proc檔案體系接口"/proc/mtd"
#endif /* CONFIG_PROC_FS */
return 0;
err_bdi3:
bdi_destroy(&mtd_bdi_ro_mappable);
err_bdi2:
bdi_destroy(&mtd_bdi_unmappable);
err_bdi1:
class_unregister(&mtd_class);
err_reg:
pr_err("Error registering mtd class or bdi: %d\n", ret);
return ret;
}
流程:
init_mtd -> ret = class_register(&mtd_class);
分析:這是建立裝置節點的第二步:MTD原始裝置層。(MTD子系統)
class_register注冊一個mtd類mtd_class,後面再注冊mtd裝置時會用到該class。
三》驅動層
static int jz4780_nand_probe(struct platform_device *pdev)
{
/*
* MTD register
*/
ret = mtd_device_parse_register(mtd, NULL, NULL,
pdata->part_table, pdata->num_part);
if (ret) {
dev_err(&pdev->dev, "Failed to add MTD device\n");
goto err_unreloc_hot;
}
}
int mtd_device_parse_register(struct mtd_info *mtd, const char **types,
struct mtd_part_parser_data *parser_data,
const struct mtd_partition *parts,
int nr_parts)
{
int err;
struct mtd_partition *real_parts;
err = parse_mtd_partitions(mtd, types, &real_parts, parser_data);
if (err <= 0 && nr_parts && parts) {
real_parts = kmemdup(parts, sizeof(*parts) * nr_parts,
GFP_KERNEL);
if (!real_parts)
err = -ENOMEM;
else
err = nr_parts;
}
if (err > 0) {
err = add_mtd_partitions(mtd, real_parts, err);
kfree(real_parts);
} else if (err == 0) {
err = add_mtd_device(mtd);
if (err == 1)
err = -ENODEV;
}
return err;
}
EXPORT_SYMBOL_GPL(mtd_device_parse_register);
int add_mtd_device(struct mtd_info *mtd)
{
struct mtd_notifier *not;
int i, error;
//設定mtd_info結構體資訊
if (!mtd->backing_dev_info) {
switch (mtd->type) {
case MTD_RAM:
mtd->backing_dev_info = &mtd_bdi_rw_mappable;
break;
case MTD_ROM:
mtd->backing_dev_info = &mtd_bdi_ro_mappable;
break;
default:
mtd->backing_dev_info = &mtd_bdi_unmappable;
break;
}
}
BUG_ON(mtd->writesize == 0);//mtd寫操作機關不能為0
mutex_lock(&mtd_table_mutex);//初始化mtd_table_mutex,上鎖
do {
if (!idr_pre_get(&mtd_idr, GFP_KERNEL))//為mtd_idr配置設定記憶體
goto fail_locked;
error = idr_get_new(&mtd_idr, mtd, &i);//将mtd_idr和id關聯起來
} while (error == -EAGAIN);//判斷是否上鎖,否則會報錯
if (error)
goto fail_locked;
mtd->index = i;//索引值設為目前數組項的下标
mtd->usecount = 0;//引用計數設為零
/* default value if not set by driver *///沒有被設定的話将會被設定成預設值
if (mtd->bitflip_threshold == 0)
mtd->bitflip_threshold = mtd->ecc_strength;
if (is_power_of_2(mtd->erasesize))//最小的擦出塊大小
mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
else
mtd->erasesize_shift = 0;
if (is_power_of_2(mtd->writesize))//程式設計塊大小
mtd->writesize_shift = ffs(mtd->writesize) - 1;
else
mtd->writesize_shift = 0;
mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
/* Some chips always power up locked. Unlock them now */
if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
error = mtd_unlock(mtd, 0, mtd->size);
if (error && error != -EOPNOTSUPP)
printk(KERN_WARNING
"%s: unlock failed, writes may not work\n",
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);//設定mtd裝置名
dev_set_drvdata(&mtd->dev, mtd);//設定mtd裝置資訊mtd_info
if (device_register(&mtd->dev) != 0)//注冊裝置
goto fail_added;
if (MTD_DEVT(i))//建立裝置
device_create(&mtd_class, mtd->dev.parent,
MTD_DEVT(i) + 1,
NULL, "mtd%dro", i);
pr_debug("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 */
//周遊list連結清單将每一個mtd_notifier執行add()函數,對新加入的mtd裝置操作,通知所有的MTD user新的MTD裝置的到來
list_for_each_entry(not, &mtd_notifiers, list)
not->add(mtd);
mutex_unlock(&mtd_table_mutex);//解鎖信号量
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
either. :) */
__module_get(THIS_MODULE);
return 0;
fail_added:
idr_remove(&mtd_idr, i);
fail_locked:
mutex_unlock(&mtd_table_mutex);
return 1;
}
流程:
device_create(&mtd_class, mtd->dev.parent,MTD_DEVT(i) + 1,NULL, "mtd%dro", i);->add_mtd_partitions->add_mtd_device-> device_create(&mtd_class, mtd->dev.parent,MTD_DEVT(i) + 1,NULL, "mtd%dro", i);
分析:這是建立裝置節點的第三步:驅動層.
在添加MTD裝置同時,在原始裝置層注冊的那個class接口上建立了裝置節點。/dev/mtdXXX出現