if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else
len = count;
kbuf=kmalloc(len,GFP_KERNEL);//分配buffer if (!kbuf) {
printk(“kmalloc is null\\n”); return -ENOMEM; }
//从用户空间buf拷贝数据到内核空间kbuf if (copy_from_user(kbuf, buf, len)) { kfree(kbuf); return -EFAULT; }
//调用设备的写函数
ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); if (!ret) {
*ppos += retlen;
total_retlen += retlen; count -= retlen; buf += retlen; }
else {
kfree(kbuf); return ret; }
kfree(kbuf); }
return total_retlen; } /* mtd_write */
具体flash芯片的探测及映射
(1)flash芯片映射信息结构
flash芯片映射信息结构map_info描述了每一排闪存的映射信息。如:每一排闪存的驱动程序、物理地址、读写操作函数和映射地址等。如果设备需要它,系统就必须把它传递到芯片探测例程do_map_probe中。JEDEC和CFI接口的芯片都用这个探测函数。如果芯片被识别,就会激活合适的芯片驱动程序并返回一个mtd_info结构。同时,系统使用这个驱动程序的模块地址填充mtd->module,并把它注册到MTD核心代码中。或者如果有分区,就注册分区。map_info结构保存在mtd->priv域,芯片驱动程序需要的更多的信息通过链接mtd->priv->fldrv_priv可得到。
flash芯片映射信息结构map_info列出如下(在include/linux/mtd/mtd.h中): struct map_info { char *name;
unsigned long size; //flash大小 unsigned long phys; //起始物理地址
#define NO_XIP (-1UL)
void __iomem *virt; //I/O映射的虚拟地址
void *cached; //8位的字节,它不是实际总线的必要宽度。 //在再次与第一个芯片通信之前,它是字节上重复的间隔 int bankwidth;
#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
map_word (*read)(struct map_info *, unsigned long); void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t); void (*write)(struct map_info *, const map_word, unsigned long); void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
/* We can perhaps put in ‘point’ and ‘unpoint’ methods, if we really
want to enable XIP for non-linear mappings. Not yet though. */ #endif
/*在映射驱动程序的copy_from应用中,映射驱动程序使用缓存是可能的。然而,当芯片驱动程序知道一些flash区域已改变内容时,系统在必要时将通过这个例程发信号给芯片驱动程序,让映射驱动程序使缓存无效。如果没有缓存时,把这个域设为NULL。*/
void (*inval_cache)(struct map_info *, unsigned long, ssize_t); /* set_vpp() must handle being reentered—enable, enable, disable must leave it enabled. */
void (*set_vpp)(struct map_info *, int); unsigned long map_priv_1; unsigned long map_priv_2;
void *fldrv_priv;
struct mtd_chip_driver *fldrv; //flash芯片驱动程序 };
结构mtd_chip_driver是flash芯片驱动程序的描述,列出如下: struct mtd_chip_driver {
struct mtd_info *(*probe)(struct map_info *map);//探测函数 void (*destroy)(struct mtd_info *);
struct module *module; //驱动程序的模块结构 char *name; //驱动程序名 struct list_head list; };
(2)flash芯片探测方法及接口标准
每种flash控制芯片可控制多种闪存,这个控制芯片的驱动程序有自己的读写和探测操作函数或者使用通用的操作函数,它注册MTD驱动程序结构
mtd_chip_driver到一个全局链表chip_drvs_list中。当用户使用一种flash闪存时,用户在称为\映射驱动程序\的文件中分配好地址、进行闪存空间分区后,使用探测程序查找相应的控制芯片驱动程序。映射驱动程序用来填充一些闪存空间分配的一些信息,代码放在drivers/mtd/map目录下。
在/drivers/mtd/chips目录下有各种flash控制芯片的驱动程序及芯片探测程序,这些文件有chipreg.c、gen_probe.c、cfi_probe.c、jedec_probe.c、cfi_cmdset_0001.c、cfi_cmdset_0002.c、map_rom.c、map_ram.c、map_absent.c、amd_flash.c、jedec.c和sharp.c。CFI设备和JEDEC设备都要用到gen_probe.c文件。
确定flash闪存芯片是否支持CFI接口的方法是:向flash闪存的地址0x55H写入数据0x98H,再从flash闪存的地址0x10H处开始,读取3个存储单元,如果字符分别为’Q’,’R’和’Y’,那么flash闪存芯片是支持CFI接口的。这个方法在文件cfi_probe.c函数qry_present中实现。支持CFI接口flash闪存芯片的类型名称为 \。
也可以用JEDEC(电子电器设备联合会)标准设备模仿CFI接口,探测JEDEC设备的程序在jedec_probe.c中,JEDEC设备的类型为\。
对于flash芯片,不同的制造商使用不同的命令集,目前Linux的MTD实现的命令集有AMD/Fujitsu的标准命令集和Intel/Sharp的扩展命令集(兼容
Intel/Sharp标准命令集)两个,这两个命令集分别在cfi_cmdset_0002.c和cfi_cmdset_0001.c中实现。
此外还有一些非CFI标准的Flash,其中\类型的Flash的探测程序在jedec.c中,\类型的Flash的探测程序在sharp.c中,\类型的Flash的探测程序在amd_flash.c中。
最后,还有一些非Flash的MTD,比如ROM或absent(无)设备。这些设备的探测程序在map_rom.c、map_ram.c和map_absent.c中。
chip_drvs_list是所有芯片类型的驱动器链表,flash控制芯片的驱动程序通过调用register_mtd_chip_driver()和unregister_mtd_chip_driver()向此链表中添加或去除MTD芯片驱动结构。这两个函数列出如下(在drivers/mtd/chips/chipreg.c中):
void register_mtd_chip_driver(struct mtd_chip_driver *drv) {
spin_lock(&chip_drvs_lock);
list_add(&drv->list, &chip_drvs_list); spin_unlock(&chip_drvs_lock); }
void unregister_mtd_chip_driver(struct mtd_chip_driver *drv) {
spin_lock(&chip_drvs_lock); list_del(&drv->list);
spin_unlock(&chip_drvs_lock); }
映射驱动程序调用函数do_map_probe来查找对应的控制芯片驱动程序。函数中参数name是控制芯片类型名称,参数是映射驱动程序中设置的flash闪存空间信息。若调用成功时返回MTD设备的结构mtd_info,失败时返回NULL。 函数do_map_probe分析如下(在drivers/mtd/chips/chipreg.c中):
struct mtd_info *do_map_probe(const char *name, struct map_info *map) {
struct mtd_chip_driver *drv; struct mtd_info *ret;
//查找得到name类型的控制芯片驱动程序结构
drv = get_mtd_chip_driver(name);
if (!drv && !request_module(“%s”, name)) drv = get_mtd_chip_driver(name); if (!drv)
return NULL;
ret = drv->probe(map); //具体控制芯片驱动程序的探测函数
//使用计数减1,它可能已是一个探测过的模块,在这不需要再探测, //而在实际的驱动程序中已做处理。 module_put(drv->module); if (ret)
return ret; return NULL; }
驱动程序实例分析
(1)CFI控制芯片驱动程序
CFI控制芯片驱动程序cfi_probe在drivers/mtd/chip/cfi_probe.c中,这里只做了简单的说明。
static struct mtd_chip_driver cfi_chipdrv = { .probe = cfi_probe, .name = “cfi_probe”, .module = THIS_MODULE };
函数cfi_probe_init注册驱动程序cfi_chipdrv到全局链表中,函数列出如下: int __init cfi_probe_init(void) {
register_mtd_chip_driver(&cfi_chipdrv); return 0;