转载请注明出处:http://blog.csdn.net/wang_zheng_kai

四、Register到MTD子系统

两种注册方式:(我们需要完成的)

1)直接注册整个flash设备(MTD Device)到MTD。

ret = add_mtd_device(mtd);

2)分partion添加到mtd_table,并将每个partion当成

一个mtd设备注册到MTD。

ret = add_mtd_partitions(mtd, parts,num_part);

其中:

add_mtd_partitions()->add_one_partition()

slave->mtd.xxx= master->mtd.xxx;

add_mtd_device(&slave->mtd);

从master中获得type,read,write等参数。

并将slave作为一个mtd设备添加到mtd_table。

最后注册设备及创建dev下面的设备节点。

五、NAND FLASH驱动层

Linux内核在MTD的下层实现了通用的NAND驱动( 主要通过drivers/mtd/nand/nand_base.c文件实现),因此芯片级的NAND 驱动不再需要实现mtd_info中的read()、write()、read_oob()、write_oob()等成员函数,而主体转移到了nand_chip数据结构。

MTD使用nand_chip数据结构表示一个NANDFlash芯片,这个结构体中包含了关于NAND Flash的地址信息、读写方法、ECC模式、硬件控制等一些底层机制。

驱动实现层:jz4780_nand.c

1)通过platform注册。(加载驱动)

platform_driver_register(&jz4780_nand_driver);

2)探测函数jz4780_nand_probe(重要,驱动实现的第一步)

在probe函数中申请资源、硬件初始化、实现nand_chip结构体中的函数指针以及与NAND芯片进行交互,添加mtd设备到MTD。

3)实现一些nand_base.c没有实现的以及其他的一些操作一些操作如:read_buf()等。

4) remove函数

当任意一方卸载时,会释放资源,当该驱动函数或者驱动函数正在操作的设备被移除时,内核会调用驱动函数中的remove函数调用,进行一些设备卸载相应的操作

5)注销jz4780_nand驱动(卸载驱动)

platform_driver_unregister(&jz4780_nand_driver);

驱动的加载程序流程如下图:

其驱动加载的代码分析如下图所示:

NAND驱动的代码分析(一)
----------驱动的加载
Joedevice_driver和platform driverPlatform device是一种device自己是不会做事情的,要有人为它做事情,那就是platform driver。platform driver遵循linux系统的driver model。对于device的discovery/enumerate都不是driver自己完成的而是由由系统的driver注册机制完成。 driver编写人员只要将注册必须的数据结构初始化并调用注册driver的kernel API就可以了。
接下来来看platform_driver结构体的原型定义,代码如下:
struct platform_driver {    int (*probe)(struct platform_device *);   int (*remove)(struct platform_device *);    void (*shutdown)(struct platform_device *);    int (*suspend)(struct platform_device *, pm_message_t state);   int (*suspend_late)(struct platform_device *, pm_message_t state);   int (*resume_early)(struct platform_device *);   int (*resume)(struct platform_device *);   struct device_driver driver;
};
可见,它包含了设备操作的几个功能函数,同时包含了一个device_driver结构,说明device_driver是 platform_driver的基类。驱动程序中需要初始化这个变量。下面看一下这个变量的定义
121struct device_driver {
122        const char              *name;
123        struct bus_type         *bus;
124
125        struct module           *owner;
126        const char              *mod_name;      /* used for built-in modules */
127
128        int (*probe) (struct device *dev);
129        int (*remove) (struct device *dev);
130        void (*shutdown) (struct device *dev);
131        int (*suspend) (struct device *dev, pm_message_t state);
132        int (*resume) (struct device *dev);
133        struct attribute_group **groups;
134
135        struct driver_private *p;
136};
device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的 driver都是基于device_driver派生而来的,具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。    需要注意这两个变量:name和owner。其作用主要是为了和相关的platform_device关联起来,owner的作用是说明模块的所有者,驱动程序中一般初始化为THIS_MODULE。   device_driver结构中也有一个name变量。platform_driver从字面上来看就知道是设备驱动。设备驱动是为谁服务的呢?当然是设备了。内核正是通过这个一致性来为驱动程序找到资源,即 platform_device中的resource。《一》 jz4780_nand_init
static int __init jz4780_nand_init(void)
{
//创建DebugFS,是一种用于内核调试的虚拟文件系统,内核开发者通过debugfs和用户空间交换数据。
#ifdef CONFIG_DEBUG_FSdebugfs_root = debugfs_create_dir(DRVNAME, NULL);if (IS_ERR(debugfs_root))return PTR_ERR(debugfs_root);
#endif          return platform_driver_register(&jz4780_nand_driver);}《二》platform_driver_registe
int platform_driver_register(struct platform_driver *drv)
{ /*设置成platform_bus_type(当前驱动所在的总线,平台设备和平台驱动处在同一条总线上 )这个很重要,因为driver和device是通过bus联系在一起的,具体在本例中是通过platform_bus_type中注册的回调例程和属性来是实现的, driver与device的匹配就是通过 platform_bus_type注册的回调例程platform_match ()来完成的。*/
表明该 dev 是 platform_bus_type 类型的,并且该 dev
也会被添加到 platform_bus_type 的 devices_kset 容器中和 klist_devices 列表中。drv->driver.bus = &platform_bus_type; //在really_probe函数中,回调了platform_drv_probe函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。if (drv->probe) drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;//把驱动注册到总线上return driver_register(&drv->driver);}
代码中:
1. 设置成 platform_bus_type 这个很重要,因为 driver 和 device 是通过 bus 联系在一起的,具体在本例中是通过 platform_bus_type 中注册的回调例程和属性来是实现的, driver 与 device 的匹配就是通过 platform_bus_type 注册的回到例程mach()来完成的。下面就分析 driver_register()。
《三》 driver_register
不要被上面的platform_drv_XXX吓倒了,它们其实很简单,就是将struct device转换为struct  platform_device和struct platform_driver,然后调用platform_driver中的相应接口函数。那为什么不直接调用platform_drv_XXX等接口呢?这就是Linux内核中面向对象的设计思想。device_driver提供了一些操作接口,但其并没有实现,相当于一些虚函数,由派生类platform_driver进行重载,无论何种类型的 driver都是基于device_driver派生而来的,device_driver中具体的各种操作都是基于统一的基类接口的,这样就实现了面向对象的设计。
在此函数中,初始化结构体struct device_driver中的klist_device和unloaded字段,通过klist_device字段, 可以保存此驱动支持的设备链表,通过“完成”接口机制,完成线程间的同步。
int driver_register(struct device_driver *drv)
{int ret;struct device_driver *other;BUG_ON(!drv->bus->p);// 如果总线的方法和设备自己的方法同时存在,将打印告警信息,对于platform bus,其没有probe等接口if ((drv->bus->probe && drv->probe) ||(drv->bus->remove && drv->remove) ||(drv->bus->shutdown && drv->shutdown))printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods\n", drv->name);
/*通过驱动的名字查找 driver,如果找到了,说明已经注册过,返回错误代码,后面会
分析*/ other = driver_find(drv->name, drv->bus);if (other) {put_driver(other);printk(KERN_ERR "Error: Driver '%s' is already registered, ""aborting...\n", drv->name);return -EBUSY;}/* 将 driver 加入到 bus 的 kset,并生成些文件夹和链接文件,将程序挂在到总线上,通过总线来驱动程序ret = bus_add_driver(drv); //将本driver驱动注册登记到drv->bus所在的总线上。if (ret)return ret;/* 添加 attribute_group,本例中没有设置 drv->groups */ret = driver_add_groups(drv, drv->groups);if (ret)bus_remove_driver(drv);return ret;
}
代码中,
1.正如该例程的英文注释所言,大部分工作在 bus_add_driver()例程中完成。
2. driver_find()例程通过驱动所属 bus 的 driver 容器 drivers_kset 来查找struct device_driver *driver_find(const char *name, struct bus_type *bus)
{// 在 drivers_kset 容器中查找struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);struct driver_private *priv;if (k) {priv = to_driver(k);return priv->driver;
//返回找到的driver}return NULL;
}代码中,
通过 kset_find_obj(bus->p->drivers_kset, name)查找该 driver 的 kobj,其代码如下,struct kobject *kset_find_obj_hinted(struct kset *kset, const char *name,struct kobject *hint)
{struct kobject *k;struct kobject *ret = NULL;//锁定自旋锁spin_lock(&kset->list_lock);if (!hint)goto slow_search;/* end of list detection */if (hint->entry.next == kset->list.next)goto slow_search;k = container_of(hint->entry.next, struct kobject, entry);if (!kobject_name(k) || strcmp(kobject_name(k), name))goto slow_search;ret = kobject_get(k);goto unlock_exit;slow_search:list_for_each_entry(k, &kset->list, entry) {// 遍历 kset->list 列表获取 kobjif (kobject_name(k) && !strcmp(kobject_name(k), name)) { // 比较 name 字符ret = kobject_get(k); // 如果找到就增加引用并返回break;}}//释放自旋锁
unlock_exit:spin_unlock(&kset->list_lock);if (hint)     //这里hint为NULLkobject_put(hint);return ret;}
《四》bus_add_driver(struct device_driver *drv)
显然,所有同类型的 driver 都注册到了 一个 bus->p->drivers_kset->list 中,所以可通过其查找已经注册的 driver。下面分析 bus_add_driver()例程,该代码主要用来将驱动设备注册到bus总线中。int bus_add_driver(struct device_driver *drv) //将本driver驱动注册登记到drv->bus所在的总线上。
{       struct bus_type *bus;struct driver_private *priv;int error = 0;      bus = bus_get(drv->bus);//获取总线内容,即前面定义的  if (!bus)return -EINVAL;pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);priv = kzalloc(sizeof(*priv), GFP_KERNEL);
//为驱动私有成员申请内存且清0, 这个结构体中存放着 kobj 相关的数据,主要存放的是私有的数据。if (!priv) {error = -ENOMEM;goto out_put_bus;}klist_init(&priv->klist_devices, NULL, NULL);//初始化驱动所支持的设备链表priv->driver = drv;         // 将匹配的 driver 指针关联到 dev,以便后续使用drv->p = priv;                // 将 priv 保存到 device_driver,
/* 指向 bus 的 drivers_kset 容器,该容器的作用与 device_kset 容器相同,前者是包含所有注册到该 bus 的 driver,后者是包含所有注册到该 bus 的 device。*/priv->kobj.kset = bus->p->drivers_kset;/*初始化 priv->kobj,并将其添加到 bus->p->drivers_kset 中,在本例中生成/sys/bus/platform/drivers/jz4780_nand 目录,后面会分析 drivers_kset 的初始化及/sys/bus/platform/drivers/目录的生成 */error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,"%s", drv->name);
//初始化并添加驱动kobject对象到总线驱动中if (error)goto out_unregister;  if (drv->bus->p->drivers_autoprobe) {// 在 bus_register()例程中已经设置为 1 了error = driver_attach(drv); // 所以会寻找匹配的 device,后面分析if (error)goto out_unregister;}// 将 driver 链接到 klist_drivers,方便后续快速查找 driver//将驱动节点加入到总线链表中klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);// 将 driver 添加到 module 模块,后面会分析module_add_driver(drv->owner, drv);/* 生成/sys//sys/bus/platform/drivers/属性文件,其作用与 device 中的
uevent 类似*/error = driver_create_file(drv, &driver_attr_uevent);if (error) {printk(KERN_ERR "%s: uevent attr (%s) failed\n",__func__, drv->name);}/* 添加 bus 的公有属性文件到/sys//sys/bus/platform/drivers/目录,所有的 driver
都添加 */error = driver_add_attrs(bus, drv);if (error) {/* How the hell do we get out of this pickle? Give up */printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",__func__, drv->name);}if (!drv->suppress_bind_attrs) {
/* 如果配置了”CONFIG_HOTPLUG",则生成“bind”和"unbind”属性文件,可用于手动匹
配和移除 device 与 driver 之间的关联 */error = add_bind_files(drv);if (error) {/* Ditto */printk(KERN_ERR "%s: add_bind_files(%s) failed\n",__func__, drv->name);}}// 通过 uevent 设置几个环境变量并通知用户空间,以便调用程序来完成相关设置kobject_uevent(&priv->kobj, KOBJ_ADD);return 0;out_unregister:kobject_put(&priv->kobj);kfree(drv->p);drv->p = NULL;
out_put_bus:bus_put(bus);return error;
}
代码中,
struct driver_private 来存放 kobj 相关的数据,将 struct driver_private 嵌入在 struct
device_driver 中。struct driver_private 定义如下:
struct driver_private {struct kobject kobj;
//代表该bus,里面的kobj是该bus的主kobj,也就是最顶层struct klist klist_devices;
//其作用与devices_kset->list作用相同struct klist_node knode_bus;struct module_kobject *mkobj;struct device_driver *driver;};
《五》driver_attach(struct device_driver *drv)
//如果总线上的driver是自动probe的话,则将该总线上的driver和device绑定起来。
/*** driver_attach - try to bind driver to devices.* @drv: driver.* Walk the list of devices that the bus has on it and try to* match the driver with each one.  If driver_probe_device()* returns 0 and the @dev->driver is set, we've found a* compatible pair.*/
int driver_attach(struct device_driver *drv)
{
/* 遍历 bus 的 klist_devices 列表,对每个 device 使用回调函数__driver_attach()来鉴别是否
和 driver 匹配 */return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}EXPORT_SYMBOL_GPL(driver_attach);int bus_for_each_dev(struct bus_type *bus, struct device *start,void *data, int (*fn)(struct device *, void *)){       struct klist_iter i;struct device *dev;int error = 0;if (!bus)return -EINVAL;//设置iklist_iter_init_node(&bus->p->klist_devices, &i,(start ? &start->p->knode_bus : NULL));//使用i进行遍历while ((dev = next_device(&i)) && !error)error = fn(dev, data);//
使用回调例程,处理klist_iter_exit(&i);return error;}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
//扫描该总线上的每一个设备,将当前driver和总线上的设备进行match,如果匹配成功,则将设备和driver绑定起来。
回调例程__driver_attach()
函数__driver_attach()在调用driver_probe_device()函数前,需要进行线程间的互斥处理。《六》__driver_attach(struct device *dev, void *data)
static int __driver_attach(struct device *dev, void *data)
{struct device_driver *drv = data;/** Lock device and try to bind to it. We drop the error* here and always return 0, because we need to keep trying* to bind to devices and some drivers will return an error* simply if it didn't support the device.* driver_probe_device() will spit a warning if there* is an error.*/
/* 调用 bus 的 match (),在这里是 platform_bus_type 的 mach(),即 platform_match()例
程 */if (!driver_match_device(drv, dev))return 0;if (dev->parent)        /* Needed for USB */device_lock(dev->parent);device_lock(dev);//如果该设备尚没有匹配的driver,则尝试匹配。if (!dev->driver)driver_probe_device(drv, dev);
// 这里开始 probedevice_unlock(dev);if (dev->parent)device_unlock(dev->parent);return 0;}《七》driver_probe_device(struct device_driver *drv, struct device *dev)
在driver_probe_device()函数中,调用了match函数platform_match(),如果它返回0,
表示驱动与设备不一致,函数返回;否则,调用really_probe()函数。
/** * driver_probe_device - attempt to bind device & driver together
* @drv: driver to bind a device to * @dev: device to try to bind to the driver * * First,
/*** driver_probe_device - attempt to bind device & driver together* @drv: driver to bind a device to* @dev: device to try to bind to the driver* This function returns -ENODEV if the device is not registered,* 1 if the device is bound successfully and 0 otherwise.* This function must be called with @dev lock held.  When called for a* USB interface, @dev->parent lock must be held as well.*/
int driver_probe_device(struct device_driver *drv, struct device *dev)
{int ret = 0;if (!device_is_registered(dev))//判断dev是否已经注册return -ENODEV;pr_debug("bus: '%s': %s: matched device %s with driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);pm_runtime_get_noresume(dev);pm_runtime_barrier(dev);ret = really_probe(dev, drv);pm_runtime_put_sync(dev);return ret;}
《八》really_probe(struct device *dev, struct device_driver *drv)
//在此函数中,回调了我们在jz4780.c文件中实现的探测函数jz4780_nand_probe(),至此,平台驱动的注册过程结束。
static int really_probe(struct device *dev, struct device_driver *drv)
{int ret = 0;atomic_inc(&probe_count);//原子锁上锁pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));WARN_ON(!list_empty(&dev->devres_head));dev->driver = drv;
// 将匹配的 driver 指针关联到 dev,以便后续使用if (driver_sysfs_add(dev)) {printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",__func__, dev_name(dev));goto probe_failed;}// 如果设置了 dev->bus->probe,则调用,在 platform_bus_type 没有设置if (dev->bus->probe) {ret = dev->bus->probe(dev);if (ret)goto probe_failed;} else if (drv->probe) {ret = drv->probe(dev);if (ret)goto probe_failed;}
// 将 device 添加到 driver 列表中,并通知 bus 上的设备,表明 BOUND_DRIVER。
//设备与驱动绑定后,对系统中已注册的组件进行事件通知。driver_bound(dev);ret = 1;pr_debug("bus: '%s': %s: bound device %s to driver %s\n",drv->bus->name, __func__, dev_name(dev), drv->name);goto done;probe_failed:devres_release_all(dev);driver_sysfs_remove(dev);dev->driver = NULL;if (ret != -ENODEV && ret != -ENXIO) {/* driver matched but the probe failed */printk(KERN_WARNING"%s: probe of %s failed with error %d\n",drv->name, dev_name(dev), ret);}/** Ignore errors returned by ->probe so that the next driver can try* its luck.*/ret = 0;
done:atomic_dec(&probe_count);wake_up(&probe_waitqueue);return ret;
}

NAND FLASH学习笔记之MTD下nand flash驱动(四)相关推荐

  1. NAND FLASH学习笔记之MTD下nand flash驱动(一)

    转载请注明:http://blog.csdn.net/wang_zheng_kai/article/details/18988521 有了前面的基础,我们就可以研究MTD下的nand驱动了,我这里用的 ...

  2. NAND FLASH学习笔记之MTD下nand flash驱动(二)

    在这里补充这一片是为了更好的理解上一篇,补充的内容包括:MTD下NAND的布局中几个重要文件的诠释和MTD涉及的几个重要的结构体(更好的理解接口) 一.内核中的NAND代码布局 在Linux 内核中, ...

  3. NAND FLASH学习笔记之MTD下nand flash驱动(六)

    六.驱动层之Flash读操作 MTD对NAND芯片的读写 主要分三部分: A.struct mtd_info中的读写函数,如read,write_oob等,这是MTD原始设备层与FLASH硬件层之间的 ...

  4. NAND FLASH学习笔记之MTD下nand flash驱动(三)

    三.MTD创建设备节点 MTD子系统下如何创建设备节点? 第一步:MTD设备层.(MTD子系统) register_chrdev注册字符型mtd设备,并添加该设备到内核,主设备号为90.但是此时还未在 ...

  5. 学习笔记(1)centos7 下安装nginx

    学习笔记(1)centos7 下安装nginx 这里我是通过来自nginx.org的nginx软件包进行安装的. 1.首先为centos设置添加nginx的yum存储库 1.通过vi命令创建一个rep ...

  6. Python学习笔记17:实操案例十四(模拟高铁售票系统,推算几天后的日期)

    Python学习笔记17:实操案例十四(模拟高铁售票系统,推算几天后的日期) 1.模拟高铁售票系统 使用漂亮的表格模块PrettyTable 这个模块需要预先安装,不然直接导入会报错: 安装办法: h ...

  7. NAND FLASH学习笔记之nand flash基础(一)

    我入职以来接触的第一个实践内容就是MTD下的NAND FLASH的驱动,下面我将从nand flash的基础和驱动程序两个方面来探讨该知识点,同时最后我会把自己的 动手实验也展示出来,我学习是基于jz ...

  8. 嵌入式 linux nand flash 容量查看,Linux MTD下获取Nand flash 各个参数的过程的详细解析...

    下面是Linux MTD中,获取nand flash型号,各个参数,以及硬件特性的函数,其实也就是nand_get_flash_type,下面对其详细解析:1.1. Program(编程)此处的编程, ...

  9. GD32学习笔记(3)NAND Flash管理

    目录 NAND Flash介绍 ECC算法 FTL 参考代码 ECC 写入数据后获取ECC并写入相应区域 读出数据.获取ECC并重新计算ECC,校验 ECC校正 FTL 标记某一个块为坏块 标记某一个 ...

最新文章

  1. python协程框架_[记录]python的简单协程框架(回调+时间循环+select)
  2. [k8s] 第一章 十分钟带你理解Kubernetes核心概念
  3. 日志模块-logging模块
  4. 从云服务器上拷贝文件,从云服务器上拷贝文件
  5. Wget漏洞(CVE-2016-4971)利用方式解析
  6. 佳能c3020维修模式 白电平调整_天禹/TY流量计指示针不动维修附近厂家
  7. 解决cef加载flash时弹出黑框的问题
  8. 计算机网络常见面试题
  9. 贪心算法c语言部分背包,c语言背包问题_背包问题贪心算法_背包问题 贪心算法(13)...
  10. java在线编译器_java在线编译器-youjavait.com
  11. 双月数据生成及其常见算法(二)
  12. 新鲜出炉的点菜系统(附源码)
  13. QOpenGLWidget运行时闪退
  14. Ubuntu无法ping通百度
  15. 【Flutter实战 BLoC模式 RxDart Provider模式】
  16. css 图片切换模版
  17. 精炼口语短语446句
  18. 基于DOA联合TDOA时间积累的二维GDOP仿真分析
  19. delphi java 类似_【Java二十周年】Delphi转行java的一些小感触
  20. 巧妙解决window系统盘不足 而又不想全盘格式分区 增加C盘空间大小的问题

热门文章

  1. 集线器,路由器,交换机,网关设备
  2. Android开发笔记: 解决View宽高为0的问题
  3. PIL获取图像尺寸size 以及与 numpy中size()函数的区别
  4. 续航能力最强的蓝牙耳机推荐,续航好的蓝牙耳机盘点
  5. STM32F407控制WH-NB73发送数据
  6. 如何做一个好的产品!
  7. iOS进阶课程-iCloud编程-关东升-专题视频课程
  8. woe分析_用IV和WOE来做特征筛选
  9. iOS 小组件 widget group id, app group, 数据共享
  10. 特斯拉「红利」结束?一场新的智能普及+升级战上演