MTD 设备驱动 和 NAND Flash 驱动程序分析
MTD 设备驱动 和 NAND Flash 驱动程序分析
硬件环境: 飞凌OK6410,256MB DDR,2GB NAND Flash、 NAND Flash 型号:K9G8G08U9A 、 分析源码:Linux 2.6.36.2 内核源码。
一、 MTD 设备驱动。
1、先来简单介绍一下MTD
在Linux 系统中, 提供了MTD(Memory Technology Device , 内存技术设备)系统来建立 Flash 针对 Linux 的系统、抽象的接口, MTD 将文件系统 与 底层的Flash
存储器进行了隔离, 使 Flash 驱动工程师 无需关心Flash 作为字符设备和 块 设备与 LInux内核的接口。
2、在引入MTD 后Linux 系统中的Flash 设备及接口可分为4层, 从上到下依次是:设备节点、MTD 设备层、MTD原始设备层 和 硬件驱动层。 这 4 层的作用定义如下:
1-> 硬件驱动层: Flash 硬件驱动层负责 Flash 硬件设备的读、写、擦除, LInux MTD 设备的 NOR Flash 芯片驱动位于 drivers/mtd/chips 子目录下, NAND Flash
的驱动程序则 位于 drivers/mtd/nand 子目录下。
2->MTD 原始设备层: MTD原始设备层由两部分组成, 一部分是MTD 原始设备的通用代码, 另一部分是各个特定 Flash 的数据,例如分区。
3->MTD设备层: 基于MTD 原始设备,Linux 系统可以定义出 MTD 的块设备的结构(主设备号 31) 和 字符设备 (设备号 90) ,构成MTD 设备层, MTD 字符设备定义
在mtdchar.c 中实现,MTD 块设备则是定义在一个描述MTD 块设备的结构 mtdblk_dev ,并声明了一个名为 mtdblks 的指针数组,这个数组 中的每个mtdblk_dev
和 mtd_table 中的每一个mtd_info 一一对应。
4->设备节点: 通过mknod 在/dev 子目录下建立MTD字符设备节点 和 块设备节点,用户通过访问此此设备节点即可访问 MTD 字符设备和块设备。
3、分析Linux MTD 系统接口 mtd_info 结构体代码分析 此结构体定义在 ./include/linux/mtd/mtd.h 中
关键词词解析:
XIP :XIP eXecute In Place,即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行
是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.
OOB :Out Of Brower 传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据
快速地发送到对方.为了发送这些数据
iovec-base : iovec 结构体基础。struct iovec定义了一个向量元素。通常,这个结构用作一个多元素的数组。对于每一个传输的元素,指针成员iov_base指向
一个缓冲区,这个缓冲区是存放的是readv所接收的数据或是writev将要发送的数据。成员iov_len在各种情况下分别确定了接收的最大长度以及实际写入的长度。
Sync : 函数, 函数说明:此函数负责将系统缓冲区的内容写回磁盘,以确保数据同步。
- struct mtd_info {
- u_char type; // 内存技术的类型
- uint32_t flags; // 标志位
- uint64_t size; // Total size of the MTD 、mtd 设备的大小
- /* "Major" erase size for the device. Na茂ve users may take this
- * to be the only erase size available, or may use the more detailed
- * information below if they desire
- */
- uint32_t erasesize; // 主要的擦除块大小 erase size of main block
- /* Minimal writable flash unit size. In case of NOR flash it is 1 (even
- * though individual bits can be cleared), in case of NAND flash it is
- * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
- * it is of ECC block size, etc. It is illegal to have writesize = 0.
- * Any driver registering a struct mtd_info must ensure a writesize of
- * 1 or larger.
- */
- uint32_t writesize; // 最小的可写单元的字节数
- uint32_t oobsize; // Amount of OOB data per block (e.g. 16) OOB 字节数
- uint32_t oobavail; // Available OOB bytes per block 可用OBB 字节数
- /*
- * If erasesize is a power of 2 then the shift is stored in
- * erasesize_shift otherwise erasesize_shift is zero. Ditto writesize.
- */
- unsigned int erasesize_shift;
- unsigned int writesize_shift;
- /* Masks based on erasesize_shift and writesize_shift */
- unsigned int erasesize_mask;
- unsigned int writesize_mask;
- // Kernel-only stuff starts here.
- const char *name;
- int index;
- /* ecc layout structure pointer - read only ! */
- struct nand_ecclayout *ecclayout; // ECC 布局结构体指针
- /* Data for variable erase regions. If numeraseregions is zero,
- * it means that the whole device has erasesize as given above.
- */
- int numeraseregions; // 不同的erasesize 的区域 数目通常是1
- struct mtd_erase_region_info *eraseregions;
- /*
- * Erase is an asynchronous operation. Device drivers are supposed
- * to call instr->callback() whenever the operation completes, even
- * if it completes with a failure.
- * Callers are supposed to pass a callback function and wait for it
- * to be called before writing to the block.
- */
- int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
- /* This stuff for eXecute-In-Place */
- /* phys is optional and may be set to NULL */
- int (*point) (struct mtd_info *mtd, loff_t from, size_t len, // 针对 eXecute-In- Place
- size_t *retlen, void **virt, resource_size_t *phys);
- /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
- void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); // 如果unpoint 为空,不允许 XIP
- /* Allow NOMMU mmap() to directly map the device (if not NULL)
- * - return the address to which the offset maps
- * - return -ENOSYS to indicate refusal to do the mapping
- */
- unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
- unsigned long len,
- unsigned long offset,
- unsigned long flags);
- /* Backing device capabilities for this device
- * - provides mmap capabilities
- */
- struct backing_dev_info *backing_dev_info;
- int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); // 读 flash
- int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); // 写 flash
- /* In blackbox flight recorder like scenarios we want to make successful
- writes in interrupt context. panic_write() is only intended to be
- called when its known the kernel is about to panic and we need the
- write to succeed. Since the kernel is not going to be running for much
- longer, this function can break locks and delay to ensure the write
- succeeds (but not sleep). */
- int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); // Kernel panic 时序读写
- int (*read_oob) (struct mtd_info *mtd, loff_t from, // 读 out-of-band
- struct mtd_oob_ops *ops);
- int (*write_oob) (struct mtd_info *mtd, loff_t to, // 写 out-of-band
- struct mtd_oob_ops *ops);
- /*
- * Methods to access the protection register area, present in some
- * flash devices. The user data is one time programmable but the
- * factory data is read only.
- */
- int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
- int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
- int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
- /* kvec-based read/write methods.
- NB: The 'count' parameter is the number of _vectors_, each of
- which contains an (ofs, len) tuple.
- */
- int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); // iovec-based 读写函数
- /* Sync */
- void (*sync) (struct mtd_info *mtd); // Sync
- /* Chip-supported device locking */
- int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); // 设备锁
- int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- int (*is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
- /* Power Management functions */
- int (*suspend) (struct mtd_info *mtd); // 电源管理函数
- void (*resume) (struct mtd_info *mtd);
- /* Bad block management functions */
- int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); // 坏块管理函数
- int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
- struct notifier_block reboot_notifier; /* default mode before reboot */
- /* ECC status information */
- struct mtd_ecc_stats ecc_stats;
- /* Subpage shift (NAND) */
- int subpage_sft;
- void *priv; // 私有函数
- struct module *owner;
- struct device dev;
- int usecount;
- /* If the driver is something smart, like UBI, it may need to maintain
- * its own reference counting. The below functions are only for driver.
- * The driver may register its callbacks. These callbacks are not
- * supposed to be called by MTD users */
- int (*get_device) (struct mtd_info *mtd);
- void (*put_device) (struct mtd_info *mtd);
- };
mtd_info 中的 read(). write(). read_oob(). write_oob(). erase() 是 MTD 设备驱动主要实现的函数。在在后面我将要介绍的nand flahs 驱动中几乎看不到mtd_info
的成员函数(也就是说这些函数对于Flash 芯片来说是透明的),这是因为在Linux MTD 下层实现了针对 NOR、NAND Flsh 的同游mtd_info 成员函数。
Flash 驱动中使用如下两个函数来注册 和注销MTD 设备:
int add_mtd_device(struct mtd_info *mtd);
int del_mtd_device (struct mtd_info *mtd)
|
评论暂时关闭