mtdchar.c
MTD 字符设备的文件
MTD 字符设备的 notifier
static struct mtd_notifier notifier = {
add: mtd_notify_add,
remove: mtd_notify_remove,
};
格式:
static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
注释:
无
功能:
置 MTD 字符设备的指针
说明:
无
参数:
file : MTD 字符设备文件
offset :偏移
orig :设置方式
返回:
成功:返回当前指针位置
设置方式无效:返回 -EINVAL
调用:
无
被调用:
注册进 mtd_fops 结构
源代码:
{
struct mtd_info *mtd=(struct mtd_info *)file->private_data;
switch (orig) {
case 0:
/* SEEK_SET */
file->f_pos = offset;
break;
case 1:
/* SEEK_CUR */
file->f_pos += offset;
break;
case 2:
/* SEEK_END */
file->f_pos =mtd->size + offset;
break;
default:
return -EINVAL;
}
if (file->f_pos < 0)
file->f_pos = 0;
else if (file->f_pos >= mtd->size)
file->f_pos = mtd->size - 1;
return file->f_pos;
}
格式:
static int mtd_open(struct inode *inode, struct file *file)
注释:
无
功能:
打开一个 MTD 字符设备
说明:
devnum=minor>>2 (参看 Documentations/devices.txt ),
进行安全性检查,
调用 get_mtd_device 获取 MTD 设备,并将 file->private_data 指向它
参数:
inode : FIXME
file :是系统提供给 MTD 字符设备用于传递参数的 file 结构,此函数中它的 private_data
成员被指向原始设备层的 MTD 设备
返回:
成功:返回 0
失败:返回错误码
调用:
get_mtd_device() 获得原始设备层的 MTD 设备
被调用:
注册进 mtd_fops 结构
源代码:
{
int minor = MINOR(inode->i_rdev);
int devnum = minor >> 1;
struct mtd_info *mtd;
DEBUG(MTD_DEBUG_LEVEL0, "MTD_open/n");
if (devnum >= MAX_MTD_DEVICES)
return -ENODEV;
/* You can't open the RO devices RW */
if ((file->f_mode & 2) && (minor & 1))
return -EACCES;
mtd = get_mtd_device(NULL, devnum);
if (!mtd)
return -ENODEV;
if (MTD_ABSENT == mtd->type) {
put_mtd_device(mtd);
return -ENODEV;
}
file->private_data = mtd;
/* You can't open it RW if it's not a writeable device */
if ((file->f_mode & 2) && !(mtd->flags & MTD_WRITEABLE)) {
put_mtd_device(mtd);
return -EACCES;
}
return 0;
} /* mtd_open */
格式:
static int mtd_close(struct inode *inode, struct file *file)
注释:
无
功能:
关闭一个 MTD 字符设备
说明:
调用 mtd_info->sync() 同步 MTD 设备,
调用 put_mtd_device() 返还 MTD 设备
参数:
inode : FIXME
file :无用
返回:
返回 0
调用:
mtd_info->sync() 同步 MTD 设备
put_mtd_device() 返还 MTD 设备
被调用:
被注册进 mtd_fops 结构
源代码:
{
struct mtd_info *mtd;
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close/n");
mtd = (struct mtd_info *)file->private_data;
if (mtd->sync)
mtd->sync(mtd);
put_mtd_device(mtd);
return 0;
} /* mtd_close */
/* FIXME: This _really_ needs to die. In 2.5, we should lock the
userspace buffer down and use it directly with readv/writev.
*/
#define MAX_KMALLOC_SIZE 0x20000
格式:
static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
注释:
无
功能:
MTD 字符设备的写操作
说明:
当 count>0 时 {
裁减本次操作大小 len 至 min(MAX_KMALLOC_SIZE,count) ,
申请一块大小为 MAX_KMALLOC_SIZE 的内核空间 kbuf ,
调用 mtd_info->read 将 MTD 设备中的数据读入 kbuf ,
将 kbuf 中的数据拷贝到用户空间 buf ,
count 自减
释放 kbuf
}
参数:
file :系统给 MTD 字符设备驱动程序用于传递参数的 file 结构,此函数通过 file 得到下
层的 MTD 设备
buf :用户空间的指针,用于存放读取的数据
count :被读数据的长度
ppos :被读数据在 MTD 设备中的位置
返回:
成功:返回实际读取数据的长度
失败:返回错误码
调用:
mtd_info->read() 用于从 MTD 设备中读取数据
被调用:
被注册进 mtd_fops 结构
源代码:
{
struct mtd_info *mtd = (struct mtd_info *)file->private_data;
size_t retlen=0;
size_t total_retlen=0;
int ret=0;
int len;
char *kbuf;
DEBUG(MTD_DEBUG_LEVEL0,"MTD_read/n");
if (*ppos + count > mtd->size)
count = mtd->size - *ppos;
if (!count)
return 0;
/* FIXME: Use kiovec in 2.5 to lock down the user's buffers
and pass them directly to the MTD functions */
while (count) {
if (count > MAX_KMALLOC_SIZE)
len = MAX_KMALLOC_SIZE;
else
len = count;
kbuf=kmalloc(len,GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
if (!ret) {
*ppos += retlen;
if (copy_to_user(buf, kbuf, retlen)) {
kfree(kbuf);
return -EFAULT;
}
else
total_retlen += retlen;
count -= retlen;
buf += retlen;
}
else {
kfree(kbuf);
return ret;
}
kfree(kbuf);
}
return total_retlen;
} /* mtd_read */
格式:
static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
注释:
无
功能:
对 MTD 字符设备的写操作
说明:
当 count>0 时 {
裁减本次操作大小 len 至 min(MAX_KMALLOC_SIZE,count) ,
申请一块大小为 MAX_KMALLOC_SIZE 的内核空间 kbuf ,
将用户空间 buf 中的数据拷贝到 kbuf ,
调用 mtd_info->write 将 kbuf 中的数据读入 MTD 设备,
count 自减
释放 kbuf
}
参数:
file :系统给 MTD 字符设备驱动程序用于传递参数的 file 结构,此函数通过 file 得到下
层的 MTD 设备
buf :用户空间的指针,用于存放将要写入的数据
count :被写数据的长度
ppos :数据被写入 MTD 设备中的位置
返回:
成功:返回实际读取数据的长度
失败:返回错误码
调用:
mtd_info->write 用于写入 MTD 设备
被调用:
被注册进 mtd_fops 结构
源代码:
{
struct mtd_info *mtd = (struct mtd_info *)file->private_data;
char *kbuf;
size_t retlen;
size_t total_retlen=0;
int ret=0;
int len;
DEBUG(MTD_DEBUG_LEVEL0,"MTD_write/n");
if (*ppos == mtd->size)
return -ENOSPC;
if (*ppos + count > mtd->size)
count = mtd->size - *ppos;
if (!count)
return 0;
while (count) {
if (count > MAX_KMALLOC_SIZE)
len = MAX_KMALLOC_SIZE;
else
len = count;
kbuf=kmalloc(len,GFP_KERNEL);
if (!kbuf) {
printk("kmalloc is null/n");
return -ENOMEM;
}
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 */
格式:
static void mtd_erase_callback (struct erase_info *instr)
注释:
无
功能:
唤醒进程
说明:
在擦除进行完后被调用
参数:
instr :进行的擦除的 erase_info 结构
返回:
无
调用:
wake_up()
被调用:
在 mtd_ioctl 中被赋给 erase_info->callback
源代码:
{
wake_up((wait_queue_head_t *)instr->priv);
}
格式:
static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
注释:
无
功能:
MTD 字符设备的 IO 控制
说明:
根据 cmd 分别处理
参数:
inode : FIXME
file :传递参数的结构
cmd : IO 控制的命令
arg : IO 控制的参数
返回:
成功:返回 0
失败:返回错误码
调用:
无
被调用:
注册进 mtd_fops 结构
源代码:
{
struct mtd_info *mtd = (struct mtd_info *)file->private_data;
int ret = 0;
u_long size;
DEBUG(MTD_DEBUG_LEVEL0, "MTD_ioctl/n");
size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
if (cmd & IOC_IN) {
ret = verify_area(VERIFY_READ, (char *)arg, size);
if (ret) return ret;
}
if (cmd & IOC_OUT) {
ret = verify_area(VERIFY_WRITE, (char *)arg, size);
if (ret) return ret;
}
switch (cmd) {
case MEMGETREGIONCOUNT:
if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
return -EFAULT;
break;
case MEMGETREGIONINFO:
{
struct region_info_user ur;
if (copy_from_user( &ur,
(struct region_info_user *)arg,
sizeof(struct region_info_user))) {
return -EFAULT;
}
if (ur.regionindex >= mtd->numeraseregions)
return -EINVAL;
if (copy_to_user((struct mtd_erase_region_info *) arg,
&(mtd->eraseregions[ur.regionindex]),
sizeof(struct mtd_erase_region_info)))
return -EFAULT;
break;
}
case MEMGETINFO:
if (copy_to_user((struct mtd_info *)arg, mtd,
sizeof(struct mtd_info_user)))
return -EFAULT;
break;
case MEMERASE:
{
struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
if (!erase)
ret = -ENOMEM;
else {
wait_queue_head_t waitq;
DECLARE_WAITQUEUE(wait, current);
init_waitqueue_head(&waitq);
memset (erase,0,sizeof(struct erase_info));
if (copy_from_user(&erase->addr, (u_long *)arg,
2 * sizeof(u_long))) {
kfree(erase);
return -EFAULT;
}
erase->mtd = mtd;
erase->callback = mtd_erase_callback;
erase->priv = (unsigned long)&waitq;
/*
FIXME: Allow INTERRUPTIBLE. Which means
not having the wait_queue head on the stack.
If the wq_head is on the stack, and we
leave because we got interrupted, then the
wq_head is no longer there when the
callback routine tries to wake us up.
*/
ret = mtd->erase(mtd, erase);
if (!ret) {
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&waitq, &wait);
if (erase->state != MTD_ERASE_DONE &&
erase->state != MTD_ERASE_FAILED)
schedule();
remove_wait_queue(&waitq, &wait);
set_current_state(TASK_RUNNING);
ret = (erase->state == MTD_ERASE_FAILED)?-EIO:0;
}
kfree(erase);
}
break;
}
case MEMWRITEOOB:
{
struct mtd_oob_buf buf;
void *databuf;
ssize_t retlen;
if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
else
ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
if (ret)
return ret;
databuf = kmalloc(buf.length, GFP_KERNEL);
if (!databuf)
return -ENOMEM;
if (copy_from_user(databuf, buf.ptr, buf.length)) {
kfree(databuf);
return -EFAULT;
}
ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
ret = -EFAULT;
kfree(databuf);
break;
}
case MEMREADOOB:
{
struct mtd_oob_buf buf;
void *databuf;
ssize_t retlen;
if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
return -EFAULT;
if (buf.length > 0x4096)
return -EINVAL;
if (!mtd->read_oob)
ret = -EOPNOTSUPP;
else
ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
if (ret)
return ret;
databuf = kmalloc(buf.length, GFP_KERNEL);
if (!databuf)
return -ENOMEM;
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
ret = -EFAULT;
else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
ret = -EFAULT;
kfree(databuf);
break;
}
case MEMLOCK:
{
unsigned long adrs[2];
if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
return -EFAULT;
if (!mtd->lock)
ret = -EOPNOTSUPP;
else
ret = mtd->lock(mtd, adrs[0], adrs[1]);
break;
}
case MEMUNLOCK:
{
unsigned long adrs[2];
if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
return -EFAULT;
if (!mtd->unlock)
ret = -EOPNOTSUPP;
else
ret = mtd->unlock(mtd, adrs[0], adrs[1]);
break;
}
default:
DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)/n", cmd, MEMGETINFO);
ret = -ENOTTY;
}
return ret;
} /* memory_ioctl */
MTD 字符设备的操作函数结构
static struct file_operations mtd_fops = {
owner: THIS_MODULE,
llseek: mtd_lseek, /* lseek */
read: mtd_read, /* read */
write: mtd_write, /* write */
ioctl: mtd_ioctl, /* ioctl */
open: mtd_open, /* open */
release: mtd_close, /* release */
};
格式:
static int __init init_mtdchar(void)
注释:
无
功能:
初始化一个 MTD 字符设备(设备层)
说明:
如果定义了 CONFIG_DEVFS_FS{
调用 devfs_register_chrdev() 注册 MTD 字符设备
调用 register_mtd_user() 将设备层 MTD 字符设备的 notifier 注册进原始设备层
}
否则调用 register_chrdev() 注册 MTD 字符设备
参数:
无
返回:
成功:返回 0
注册失败:返回 -EAGAIN
调用:
devfs_register_chrdev() 或 register_chrdev() 注册字符设备
被调用:
__init
module_init
源代码:
{
#ifdef CONFIG_DEVFS_FS
if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
{
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices./n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
register_mtd_user(¬ifier);
#else
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
{
printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices./n",
MTD_CHAR_MAJOR);
return -EAGAIN;
}
#endif
return 0;
}
格式:
static void __exit cleanup_mtdchar(void)
注释:
无
功能:
清除 MTD 字符设备
说明:
如果定义了 CONFIG_DEVFS_FS{
调用 unregister_mtd_user() 注销 MTD 字符设备的 notifier,
调用 devfs_unregister_chrdev() 注销 MTD 字符设备
}
否则调用 unregister_chrdev() 注销 MTD 字符设备
参数:
无
返回:
无
调用:
unregister_chrdev() 或 devfs_unregister_chrdev() 注销字符设备
被调用:
__exit
module_exit
源代码:
{
#ifdef CONFIG_DEVFS_FS
unregister_mtd_user(¬ifier);
devfs_unregister(devfs_dir_handle);
devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
#else
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
#endif
}
/drivers/mtd/chips 子目录
/drivers/mtd/chips 下文件的主要功能是探测 MTD ,该目录下文件是 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 ,下面介绍每个文件的功能
为了确定一个 Flash 是否是一个 CFI 使能的 flash memory 器件,首先要往 Flash 的地址 0x55H 写入数据 0x98H ,然后从 Flash 的地址 0x10H 处开始连续读取 3 个存储单元中的内容,如果数据总线返回的 3 个存储单元的字符分别为 'Q','R' 和 'Y' ,那么该器件是一个 CFI 使能的 Flash 。在识别 Flash 为 CFI 使能器件后,通过查询命令来读取 CFI 查询结构,这些数据的地址和含义在 cfi_ident.h 文件中。探测 CFI 接口 Flash 设备的程序在文件 cfi_probe.c 中,这些设备的类型为“ cfi_probe ”。
也可以用 JEDEC (电子电器设备联合会)标准设备模仿 CFI 接口,探测 JEDEC 设备的程序在 jedec_probe.c 中, JEDEC 设备的类型为“ jedec_probe ”。
CFI 设备和 JEDEC 设备都要用到 gen_probe.c 文件。
不同的制造商使用不同的命令集,目前 Linux 的 MTD 实现的命令集有 AMD/Fujitsu 的标准命令集和 Intel/Sharp 的扩展命令集(兼容 Intel/Sharp 标准命令集)两个,这两个命令集分别在 cfi_cmdset_0002.c 和 cfi_cmdset_0001.c 中实现。
此外还有一些非 CFI 标准的 Flash ,其中“ jedec ”类型的 Flash 的探测程序在 jedec.c 中 , “ sharp ”类型的 Flash 的探测程序在 sharp.c 中“ amd_flash ”类型的 Flash 的探测程序在 amd_flash.c 中。
最后,还有一些非 Flash 的 MTD ,比如 ROM 或 absent (无)设备。这些设备的探测程序在 map_rom.c 、 map_ram.c 和 map_absent.c 中。
所有类型的芯片都通过 chipreg.c 中的 do_map_probe() 程序驱动
chipreg.c
关于 MTD 芯片注册的文件,此文件中定义的 chip_drvs_list 是所有芯片类型的驱动器链表,在 /drivers/mtd/chips 子目录下的其他文件通过调用 register_mtd_chip_driver() 和 unregister_mtd_chip_driver() 向此链表中添加或去除 MTD 芯片驱动器。
在 /drivers/mtd/map/ 下的文件根据芯片类型调用 do_map_probe() , do_map_probe() 通过 get_mtd_chip_driver() 获得符合指定 name 的 MTD 芯片驱动器,再调用获得的芯片驱动器的 probe 程序。
static LIST_HEAD(chip_drvs_list);
MTD 芯片驱动器列表
格式:
void register_mtd_chip_driver(struct mtd_chip_driver *drv)
注释:
无
功能:
注册 MTD 的芯片驱动器
说明:
向 chip_drvs_list 里添加 mtd_chip_driver
参数:
drv :被注册的 MTD 的芯片驱动器
返回:
无
调用:
list_add()
被调用:
各种芯片驱动的初始化程序
源代码:
{
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)
注释:
无
功能:
删除一个 MTD 芯片的驱动器
说明:
从 chip_drvs_list 中删除一个 mtd_chip_driver
参数:
drv :被删除的 MTD 芯片的驱动器
返回:
无
调用:
list_del()
被调用:
各种芯片驱动的清除程序
源代码:
{
spin_lock(&chip_drvs_lock);
list_del(&drv->list);
spin_unlock(&chip_drvs_lock);
}
格式:
static struct mtd_chip_driver *get_mtd_chip_driver (char *name)
注释:
无
功能:
获得指定名称的 MTD 的芯片驱动器
说明:
根据 name 从 chip_drvs_list 中获得一个 mtd_chip_driver
参数:
name :该芯片驱动器的名称
返回:
找到:返回该 mtd_chip_driver
没找到:返回 NULL
调用:
无
被调用:
do_map_probe
源代码:
{
struct list_head *pos;
struct mtd_chip_driver *ret = NULL, *this;
spin_lock(&chip_drvs_lock);
list_for_each(pos, &chip_drvs_list) {
this = list_entry(pos, typeof(*this), list);
if (!strcmp(this->name, name)) {
ret = this;
break;
}
}
if (ret && !try_inc_mod_count(ret->module)) {
/* Eep. Failed. */
ret = NULL;
}
spin_unlock(&chip_drvs_lock);
return ret;
}
格式:
struct mtd_info *do_map_probe(char *name, struct map_info *map)
注释:
无
功能:
根据 name 和 map 的信息探测 MTD 并返回 mtd_info 结构
说明:
1. 根据 name 获得 mtd_chip_driver
2. 调用 mtd_chip_driver 中的 probe 函数
参数:
name : MTD 芯片类型
map : MTD 芯片信息
返回:
成功:返回 MTD 设备的结构 mtd_info
失败:返回 NULL
调用:
get_mtd_chip_driver()
drv->probe()
被调用:
/drivers/mtd/maps/ 下的板子相关文件中的 init_xxxx
源代码:
{
struct mtd_chip_driver *drv;
struct mtd_info *ret;
drv = get_mtd_chip_driver(name);
if (!drv && !request_module(name))
drv = get_mtd_chip_driver(name);
if (!drv)
return NULL;
ret = drv->probe(map);
#ifdef CONFIG_MODULES
/* We decrease the use count here. It may have been a
probe-only module, which is no longer required from this
point, having given us a handle on (and increased the use
count of) the actual driver code.
*/
if(drv->module)
__MOD_DEC_USE_COUNT(drv->module);
#endif
if (ret)
return ret;
return NULL;
}
cfi_probe.c
“cfi_probe” 型芯片的探测程序,主要由 cfi_chip_probe() 、 cfi_probe() 、 cfi_chip_setup() 、 qry_present() 、 cfi_probe_init() 和 cfi_probe_exit() 这几个函数组成。
cfi_probe() 是 “cfi_probe” 类型芯片的探测程序,它调用通用探测程序 mtd_do_chip_probe() ,并将 cfi_chip_probe 作为参数传递给 mtd_do_chip_probe() , mtd_do_chip_probe() 将间接调用 cfi_chip_probe 的成员函数 cfi_probe_chip() 。 cfi_probe() 注册在 “cfi_probe” 芯片的驱动器 cfi_chipdrv 中。
cfi_probe_chip() 将调用 qry_present() 和 cfi_chip_setup() 初始化 cfi_private 结构, qry_presetn() 负责验证该 MTD 设备支持 CFI 接口, cfi_chip_setup() 则读出 CFI 查询结构中的数据(见 cfi.h )
cfi_probe_init() 和 cfi_probe_exit() 是 “cfi_prbe” 型芯片驱动器的注册程序和清除程序。
static struct mtd_chip_driver cfi_chipdrv = {
probe: cfi_probe, 芯片的探测程序
name: "cfi_probe", 芯片名称
module: THIS_MODULE
};
“cfi_probe” 类型 MTD 芯片的驱动器
格式:
int __init cfi_probe_init(void)
注释:
无
功能:
初始化 “cfi_probe” 类型的 MTD 芯片
说明:
调用 register_mtd_chip_driver() 将 cfi_chipdrv 加入 MTD 驱动器列表 chip_drvs_list
参数:
无
返回:
0
调用:
register_mtd_chip_driver()
被调用:
__init
module_init
源代码:
{
register_mtd_chip_driver(&cfi_chipdrv);
return 0;
}
格式:
static void __exit cfi_probe_exit(void)
注释:
无
功能:
清除 “cfi_probe”MTD 芯片驱动
说明:
调用 unregister_mtd_chip_driver 从 MTD 芯片驱动器列表 chip_drvs_list 中删除 cfi_chipdrv
参数:
无
返回:
无
调用:
unregister_mtd_chip_driver()
被调用:
__exit
module_exit
源代码:
{
unregister_mtd_chip_driver(&cfi_chipdrv);
}
格式:
struct mtd_info *cfi_probe(struct map_info *map)
注释:
无
功能:
“cfi_probe” 类型 MTD 芯片的探测程序
说明:
调用通用的探测程序 mtd_do_chip_probe() ,并将 cfi_chip_probe 作为参数传进去
参数:
map :芯片的相关信息
返回:
MTD 设备信息结构 mtd_info
调用:
mtd_do_chip_probe
被调用:
注册在 cfi_chipdrv 中,根据芯片类型被 do_map_probe() 调用
源代码:
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &cfi_chip_probe);
}
static struct chip_probe cfi_chip_probe = {
name: "CFI",
probe_chip: cfi_probe_chip
};
cfi_probe 传递给通用探测程序 mtd_do_chip_probe 的参数
格式:
static int cfi_probe_chip(struct map_info *map, __u32 base,
struct flchip *chips, struct cfi_private *cfi)
注释:
无
功能:
“cfi_probe” 类型 MTD 芯片驱动程序
说明:
1. 调用 qry_present() 检查是否 CFI 接口的 MTD
2. 如果 cfi->numchips=0 ,调用 cfi_chip_setup() 设置;(搜索新芯片)
3. 否则探测此芯片是否为原芯片的别名,如果不是,此芯片作为同类芯片加入
参数:
FIXME
返回:
成功:返回 1
失败:返回 0 或 -1
调用:
qry_present()
cfi_chip_setup()
被调用:
注册在 cfi_chip_probe 中
源代码:
{
int i;
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); //reset ,进入读模式
cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); // 发送查询命令
if (!qry_present(map,base,cfi)) // 如果不存在 QRY ,则不是 CFI 接口
return 0;
if (!cfi->numchips) { // 如果是 MTD 原始设备中的第一块芯片
/* This is the first time we're called. Set up the CFI
stuff accordingly and return */
return cfi_chip_setup(map, cfi);
}
/* Check each previous chip to see if it's an alias */
for (i=0; i<cfi->numchips; i++) {
/* This chip should be in read mode if it's one
we've already touched. */
if (qry_present(map,chips[i].start,cfi)) {
/* Eep. This chip also had the QRY marker.
* Is it an alias for the new one? */
cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL);
/* If the QRY marker goes away, it's an alias */
if (!qry_present(map, chips[i].start, cfi)) {
printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx/n",
map->name, base, chips[i].start);
return 0;
}
/* Yes, it's actually got QRY for data. Most
* unfortunate. Stick the new chip in read mode
* too and if it's the same, assume it's an alias. */
/* FIXME: Use other modes to do a proper check */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
if (qry_present(map, base, cfi)) {
printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx/n",
map->name, base, chips[i].start);
return 0;
}
}
}
/* OK, if we got to here, then none of the previous chips appear to
be aliases for the current one. */
if (cfi->numchips == MAX_CFI_CHIPS) {
printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d./n", map->name, MAX_CFI_CHIPS);
/* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
return -1;
}
chips[cfi->numchips].start = base;
chips[cfi->numchips].state = FL_READY;
cfi->numchips++;
/* Put it back into Read Mode */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode/n",
map->name, cfi->interleave, cfi->device_type*8, base,
map->buswidth*8);
return 1;
}
格式:
static inline int qry_present(struct map_info *map, __u32 base,
struct cfi_private *cfi)
注释:
/* check for QRY, or search for jedec id.
in: interleave,type,mode
ret: table index, <0 for error
*/
功能:
检查 QRY ,
说明:
读出从 0x10 开始的三个字节,如果为 ”QRY” ,则说明是 CFI 接口的 MTD
参数:
map :芯片信息
base :基地址
cfi : CFI 私有信息
返回:
成功:返回 1
失败:返回 0
调用:
无
被调用:
cfi_probe_chip()
源代码:
{
int osf = cfi->interleave * cfi->device_type; // scale factor
if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) &&
cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) &&
cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi))
return 1; // ok !
return 0; // nothing found
}
格式:
static int cfi_chip_setup(struct map_info *map,
struct cfi_private *cfi)
注释:
无
功能:
设置 cfi_private 结构变量 cfi 的成员 cfiq ( cfi_ident 结构)
说明:
1. 读取器件可擦除块区域个数
2. 分配并清零内存块
3. 调用 cfi_read_query() 读取 CFI 数据结构 cfi_ident
参数:
map :芯片信息
cfi :被设置的 cfi_private 结构
返回:
成功:返回 1
失败:返回 0
调用:
无
被调用:
cfi_chip_probe()
源代码:
{
int ofs_factor = cfi->interleave*cfi->device_type;
__u32 base = 0;
int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor);
int i;
#ifdef DEBUG_CFI
printk("Number of erase regions: %d/n", num_erase_regions);
#endif
if (!num_erase_regions)
return 0;
cfi->cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);
if (!cfi->cfiq) {
printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure/n", map->name);
return 0;
}
memset(cfi->cfiq,0,sizeof(struct cfi_ident));
cfi->cfi_mode = 1; //JEDEC 仿真模式
cfi->fast_prog=1; /* CFI supports fast programming */ //CFI 支持 Fast Program 模式
/* Read the CFI info structure */
for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) {
((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);
}
/* Do any necessary byteswapping */
cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID);
cfi->cfiq->P_ADR = le16_to_cpu(cfi->cfiq->P_ADR);
cfi->cfiq->A_ID = le16_to_cpu(cfi->cfiq->A_ID);
cfi->cfiq->A_ADR = le16_to_cpu(cfi->cfiq->A_ADR);
cfi->cfiq->InterfaceDesc = le16_to_cpu(cfi->cfiq->InterfaceDesc);
cfi->cfiq->MaxBufWriteSize = le16_to_cpu(cfi->cfiq->MaxBufWriteSize);
#ifdef DEBUG_CFI
/* Dump the information therein */
print_cfi_ident(cfi->cfiq);
#endif
for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
cfi->cfiq->EraseRegionInfo[i] = le32_to_cpu(cfi->cfiq->EraseRegionInfo[i]);
#ifdef DEBUG_CFI
printk(" Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks/n",
i, (cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff,
(cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
#endif
}
/* Put it back into Read Mode */
cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
return 1;
}
jedec_probe.c
“jedec_probe” 型芯片的探测程序,主要由 jedec_probe() 、 jedec_probe_chip() 、 cfi_jedec_setup() 、 jedec_probe_init() 和 jedec_probe_exit() 这几个函数组成。
jedec_probe() 是 “jedec_probe” 类型芯片的探测程序,它调用通用探测程序 mtd_do_chip_probe() ,并将 jedec_chip_probe 作为参数传递给 mtd_do_chip_probe() , mtd_do_chip_probe() 将间接调用 jedec_chip_probe 的成员函数 jedec_probe_chip() 。 jedec_probe() 注册在 “jedec_probe” 芯片的驱动器 jedec_chipdrv 中。
jedec_probe_chip() 调用 cfi_jedec_setup() 初始化 cfi_private 结构, cfi_jedec_setup() 根据
jedec_probe_init() 和 jedec_probe_exit() 是 “cfi_prbe” 型芯片驱动器的注册程序和清除程序
struct amd_flash_info {
const __u16 mfr_id;
const __u16 dev_id;
const char *name;
const int DevSize;
const int InterfaceDesc;
const int NumEraseRegions;
const int CmdSet;
const ulong regions[4];
};
AMD Flash 芯片的信息结构
static const struct amd_flash_info jedec_table[] = {}
包含各种 jedec_probe 类型芯片信息的结构
static struct mtd_chip_driver jedec_chipdrv = {
probe: jedec_probe, 芯片的探测程序
name: "jedec_probe", 芯片名称
module: THIS_MODULE
};
“jedec_probe” 型芯片的驱动器
格式:
int __init jedec_probe_init(void)
注释:
无
功能:
初始化 “jedec_probe” 类型的 MTD 芯片
说明:
调用 register_mtd_chip_driver() 将 jedec_chipdrv 加入 MTD 驱动器列表 chip_drvs_list
参数:
无
返回:
0
调用:
register_mtd_chip_driver()
被调用:
__init
module_init
源代码:
{
register_mtd_chip_driver(&jedec_chipdrv);
return 0;
}
格式:
static void __exit jedec_probe_exit(void)
注释:
无
功能:
清除 “jedec_probe”MTD 芯片驱动
说明:
调用 unregister_mtd_chip_driver 从 MTD 芯片驱动器列表 chip_drvs_list 中删除 jedec_chipdrv
参数:
无
返回:
无
调用:
unregister_chip_driver
被调用:
__exit
module_exit
源代码:
{
unregister_mtd_chip_driver(&jedec_chipdrv);
}
格式:
struct mtd_info *jedec_probe(struct map_info *map)
注释:
无
功能:
“jedec_probe” 型 MTD 芯片的探测程序
说明:
调用通用探测程序 mtd_do_chip_probe() ,并将 jedec_chip_probe 作为参数传递给 mtd_do_chp_probe()
参数:
map :芯片信息
返回:
MTD 设备信息结构 mtd_info
调用:
mtd_do_chip_probe()
被调用:
注册在 jedec_chipdrv 中,根据芯片类型被 do_map_probe() 调用
源代码:
{
/*
* Just use the generic probe stuff to call our CFI-specific
* chip_probe routine in all the possible permutations, etc.
*/
return mtd_do_chip_probe(map, &jedec_chip_probe);
}
static struct chip_probe jedec_chip_probe = {
name: "JEDEC",
probe_chip: jedec_probe_chip
};
jedec_probe 传递给通用探测程序 mtd_do_chip_probe 的参数
格式:
static int jedec_probe_chip(struct map_info *map, __u32 base,
struct flchip *chips, struct cfi_private *cfi)
注释:
无
功能:
“jedec_probe” 类型 MTD 芯片驱动程序
说明:
主要工作是设置传进的 cfi_private 型参数 cfi 。
参数:
FIXME
返回:
成功:返回 1
失败:返回 0
调用:
cfi_jedec_stup()
被调用:
注册在 jedec_chip_probe 中
源代码:
无
格式:
static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
注释:
无
功能:
根据 index 从 jedec_table 中选择对应的信息赋给 p_cfi
说明:
无
参数:
p_cfi : cfi_private 结构的 CFI 私有信息
index :芯片在 jedec_table 中的索引
返回:
成功:返回 1
失败:返回 0
调用:
无
被调用:
jedec_probe_chip()
源代码:
无
gen_probe.c
通用芯片探测程序,由 mtd_do_chip_probe() 、 genprobe_ident_chips() 、 genprobe_new_chip() 、 check_cmd_set() 和 cfi_cmdset_unknown() 组成
cfi_probe() 或 jedec_probe() 调用 mtd_do_chip_probe() , mtd_do_chip_probe() 调用 genprobe_ident_chips() , genprobe_ident_chips() 调用 genprobe_new_chip() , genprobe_new_chip() 则调用 mtd_do_chip_probe() 的参数 chip_probe->probe_chip() 。
格式:
struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp)
注释:
无
功能:
根据 MTD 芯片的信息 map 和参数 cp 返回 MTD 设备 mtd_info 结构
说明:
获得 cfi_private 结构
调用 check_cmd_set() 返回 mtd_info 结构
参数:
map : MTD 芯片信息
cp :由 cfi_probe() 或 jedec_probe() 传进来的信息
返回:
MTD 设备信息
调用:
genprobe_ident_chips()
check_cmd_set()
被调用:
cfi_probe()
jedec_probe()
源代码:
{
struct mtd_info *mtd = NULL;
struct cfi_private *cfi;
/* First probe the map to see if we have CFI stuff there. */
cfi = genprobe_ident_chips(map, cp);
if (!cfi)
return NULL;
map->fldrv_priv = cfi;
/* OK we liked it. Now find a driver for the command set it talks */
mtd = check_cmd_set(map, 1); /* First the primary cmdset */
if (!mtd)
mtd = check_cmd_set(map, 0); /* Then the secondary */
if (mtd)
return mtd;
printk(KERN_WARNING"cfi_probe: No supported Vendor Command Set found/n");
kfree(cfi->cfiq);
kfree(cfi);
map->fldrv_priv = NULL;
return NULL;
}
格式:
struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
注释:
无
功能:
生成 cfi_private 结构并返回
说明:
1. 调用 genprobe_new_chip 搜索第一块 flash 芯片
2. 设置 cfi.chipshift ,并将 cfi.numchips 设置为 1
3. 循环调用 cp->probe_chip() 搜索所有的 flash 芯片
参数:
map :芯片信息
cp : chip_probe 结构参数
返回:
cfi_private 结构
调用:
genprobe_new_chip()
被调用:
mtd_do_chip_probe()
源代码:
无
格式:
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
注释:
无
功能:
设置 cfi
说明:
调用 cp->probe_chip()
参数:
map :芯片信息
cp : chip_probe 结构参数
cfi :被设置的 cfi_private 结构
返回:
成功:返回 1
失败:返回 0
调用:
cfi-> probe_chip()
被调用:
genprobe_ident_chips()
源代码:
无
格式:
static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
注释:
无
功能:
根据 map_info 中的厂商信息调用不同的命令集
说明:
根据厂商类型调用两个不同的命令集, cfi_cmdset_0001() 和 cfi_cmdset_0002() ,如果符合的类型没有则调用 cfi_cmdset_unkown
参数:
map : MTD 芯片信息
primary :如果取 1 ,则芯片厂商信息为 map 中的 primary ,否则为 auxillary
返回:
MTD 设备信息 mtd_info
调用:
cfi_cmdset_0001()
cfi_cmdset_0002()
cfi_cmdset_unknown()
被调用:
mtd_do_chip_probe()
源代码:
{
struct cfi_private *cfi = map->fldrv_priv;
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
if (type == P_ID_NONE || type == P_ID_RESERVED)
return NULL;
switch(type){
/* Urgh. Ifdefs. The version with weak symbols was
* _much_ nicer. Shame it didn't seem to work on
* anything but x86, really.
* But we can't rely in inter_module_get() because
* that'd mean we depend on link order.
*/
#ifdef CONFIG_MTD_CFI_INTELEXT
case 0x0001:
case 0x0003:
return cfi_cmdset_0001(map, primary);
#endif
#ifdef CONFIG_MTD_CFI_AMDSTD
case 0x0002:
return cfi_cmdset_0002(map, primary);
#endif
}
return cfi_cmdset_unknown(map, primary);
}
格式:
static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, int primary)
注释:
无
功能:
调用未知类型的命令集
说明:
如果存在返回 mtd_info ,如果不存在返回 NULL
参数:
map : MTD 芯片信息
primary : FIXME
返回:
存在命令集:返回 mtd_info
否则:返回 NULL
调用:
无
被调用:
check_cmd_set()
源代码:
{
struct cfi_private *cfi = map->fldrv_priv;
__u16 type = primary?cfi->cfiq->P_ID:cfi->cfiq->A_ID;
#if defined(CONFIG_MODULES) && defined(HAVE_INTER_MODULE)
char probename[32];
cfi_cmdset_fn_t *probe_function;
sprintf(probename, "cfi_cmdset_%4.4X", type);
probe_function = inter_module_get_request(probename, probename);
if (probe_function) {
struct mtd_info *mtd;
mtd = (*probe_function)(map, primary);
/* If it was happy, it'll have increased its own use count */
inter_module_put(probename);
return mtd;
}
#endif
printk(KERN_NOTICE "Support for command set %04X not present/n",
type);
return NULL;
}