天天看點

NAND FLASH學習筆記之MTD下nand flash驅動(三)

三、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出現
           

繼續閱讀