<a>mtdchar.c</a>
MTD字元裝置的檔案
<a>notifier</a>
MTD字元裝置的notifier
static struct mtd_notifier notifier = {
add: mtd_notify_add,
remove: mtd_notify_remove,
};
<a>mtd_lseek</a>
格式:
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;
case 2:
/* SEEK_END */
file->f_pos =mtd->size + offset;
default:
return -EINVAL;
}
if (file->f_pos
file->f_pos = 0;
else if (file->f_pos >= mtd->size)
file->f_pos = mtd->size - 1;
return file->f_pos;
}
<a>mtd_open</a>
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裝置
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)
if (MTD_ABSENT == mtd->type) {
put_mtd_device(mtd);
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)) {
return 0;
} /* mtd_open */
<a>mtd_close</a>
static int mtd_close(struct inode *inode, struct file *file)
關閉一個MTD字元裝置
調用mtd_info->sync()同步MTD裝置,
調用put_mtd_device()返還MTD裝置
file:無用
傳回0
mtd_info->sync()同步MTD裝置
put_mtd_device()返還MTD裝置
被注冊進mtd_fops結構
DEBUG(MTD_DEBUG_LEVEL0, "MTD_close/n");
mtd = (struct mtd_info *)file->private_data;
if (mtd->sync)
mtd->sync(mtd);
put_mtd_device(mtd);
} /* mtd_close */
<a>MAX_KMALLOC_SIZE</a>
/* 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
<a>mtd_read</a>
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裝置中讀取資料
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 */
<a>mtd_write</a>
static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
對MTD字元裝置的寫操作
将使用者空間buf中的資料拷貝到kbuf,
調用mtd_info->write将kbuf中的資料讀入MTD裝置,
buf:使用者空間的指針,用于存放将要寫入的資料
count:被寫資料的長度
ppos:資料被寫入MTD裝置中的位置
mtd_info->write用于寫入MTD裝置
size_t retlen;
DEBUG(MTD_DEBUG_LEVEL0,"MTD_write/n");
if (*ppos == mtd->size)
return -ENOSPC;
if (!kbuf) {
printk("kmalloc is null/n");
if (copy_from_user(kbuf, buf, len)) {
return -EFAULT;
ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
total_retlen += retlen;
kfree(kbuf);
} /* mtd_write */
<a>mtd_erase_callback</a>
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);
<a>mtd_ioctl</a>
static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg)
MTD字元裝置的IO控制
根據cmd分别處理
file:傳遞參數的結構
cmd:IO控制的指令
arg:IO控制的參數
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);
switch (cmd) {
case MEMGETREGIONCOUNT:
if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
case MEMGETREGIONINFO:
{
struct region_info_user ur;
if (copy_from_user( &ur,
(struct region_info_user *)arg,
sizeof(struct region_info_user))) {
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)))
case MEMGETINFO:
if (copy_to_user((struct mtd_info *)arg, mtd,
sizeof(struct mtd_info_user)))
case MEMERASE:
struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL);
if (!erase)
ret = -ENOMEM;
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);
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);
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)))
if (buf.length > 0x4096)
if (!mtd->write_oob)
ret = -EOPNOTSUPP;
ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
if (ret)
databuf = kmalloc(buf.length, GFP_KERNEL);
if (!databuf)
if (copy_from_user(databuf, buf.ptr, buf.length)) {
kfree(databuf);
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);
case MEMREADOOB:
if (!mtd->read_oob)
ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
case MEMLOCK:
unsigned long adrs[2];
if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
if (!mtd->lock)
ret = mtd->lock(mtd, adrs[0], adrs[1]);
case MEMUNLOCK:
if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
if (!mtd->unlock)
ret = mtd->unlock(mtd, adrs[0], adrs[1]);
DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)/n", cmd, MEMGETINFO);
ret = -ENOTTY;
return ret;
} /* memory_ioctl */
<a>mtd_fops</a>
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 */
<a>init_mtdchar</a>
static int __init init_mtdchar(void)
初始化一個MTD字元裝置(裝置層)
如果定義了CONFIG_DEVFS_FS{
調用devfs_register_chrdev()注冊MTD字元裝置
調用register_mtd_user()将裝置層MTD字元裝置的notifier注冊進原始裝置層
否則調用register_chrdev()注冊MTD字元裝置
注冊失敗:傳回-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(&notifier);
#else
if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops))
#endif
<a>cleanup_mtdchar</a>
static void __exit cleanup_mtdchar(void)
清除MTD字元裝置
調用unregister_mtd_user()登出MTD字元裝置的notifier,
調用devfs_unregister_chrdev()登出MTD字元裝置
否則調用unregister_chrdev()登出MTD字元裝置
unregister_chrdev()或devfs_unregister_chrdev()登出字元裝置
__exit
module_exit
unregister_mtd_user(&notifier);
devfs_unregister(devfs_dir_handle);
devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
/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()程式驅動
<a>chipreg.c</a>
關于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程式。
<a>chip_drvs_list</a>
static LIST_HEAD(chip_drvs_list);
MTD晶片驅動器清單
<a>register_mtd_chip_driver</a>
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);
<a>unregister_mtd_chip_driver</a>
void unregister_mtd_chip_driver(struct mtd_chip_driver *drv)
删除一個MTD晶片的驅動器
從chip_drvs_list中删除一個mtd_chip_driver
drv:被删除的MTD晶片的驅動器
list_del()
各種晶片驅動的清除程式
list_del(&drv->list);
<a>get_mtd_chip_driver</a>
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;
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;
<a>do_map_probe</a>
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);
if (ret)
return ret;
return NULL;
<a>cfi_probe.c</a>
“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”型晶片驅動器的注冊程式和清除程式。
<a>cfi_chipdrv</a>
static struct mtd_chip_driver cfi_chipdrv = {
probe: cfi_probe, 晶片的探測程式
name: "cfi_probe", 晶片名稱
module: THIS_MODULE
“cfi_probe”類型MTD晶片的驅動器
<a>cfi_probe_init</a>
int __init cfi_probe_init(void)
初始化“cfi_probe”類型的MTD晶片
調用register_mtd_chip_driver()将cfi_chipdrv加入MTD驅動器清單chip_drvs_list
register_mtd_chip_driver()
register_mtd_chip_driver(&cfi_chipdrv);
<a>cfi_probe_exit</a>
static void __exit cfi_probe_exit(void)
清除“cfi_probe”MTD晶片驅動
調用unregister_mtd_chip_driver從MTD晶片驅動器清單chip_drvs_list中删除cfi_chipdrv
unregister_mtd_chip_driver()
unregister_mtd_chip_driver(&cfi_chipdrv);
<a>cfi_probe</a>
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);
<a>cfi_chip_probe</a>
static struct chip_probe cfi_chip_probe = {
name: "CFI",
probe_chip: cfi_probe_chip
cfi_probe傳遞給通用探測程式mtd_do_chip_probe的參數
<a>cfi_probe_chip</a>
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接口
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; inumchips; 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)) {
/* 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;
<a>qry_present</a>
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,
*/
檢查QRY,
讀出從0x10開始的三個位元組,如果為”QRY”,則說明是CFI接口的MTD
map:晶片資訊
base:基位址
cfi:CFI私有資訊
失敗:傳回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
<a>cfi_chip_setup</a>
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
cfi:被設定的cfi_private結構
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);
#ifdef DEBUG_CFI
printk("Number of erase regions: %d/n", num_erase_regions);
if (!num_erase_regions)
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);
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
((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);
/* Dump the information therein */
print_cfi_ident(cfi->cfiq);
for (i=0; icfiq->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);
<a>jedec_probe.c</a>
“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”型晶片驅動器的注冊程式和清除程式
<a>amd_flash_info</a>
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晶片的資訊結構
<a>jedec_table</a>
static const struct amd_flash_info jedec_table[] = {}
包含各種jedec_probe類型晶片資訊的結構
<a>jedec_chipdrv</a>
static struct mtd_chip_driver jedec_chipdrv = {
probe: jedec_probe, 晶片的探測程式
name: "jedec_probe", 晶片名稱
“jedec_probe”型晶片的驅動器
<a>jedec_probe_init</a>
int __init jedec_probe_init(void)
初始化“jedec_probe”類型的MTD晶片
調用register_mtd_chip_driver()将jedec_chipdrv加入MTD驅動器清單chip_drvs_list
register_mtd_chip_driver(&jedec_chipdrv);
<a>jedec_probe_exit</a>
static void __exit jedec_probe_exit(void)
清除“jedec_probe”MTD晶片驅動
調用unregister_mtd_chip_driver從MTD晶片驅動器清單chip_drvs_list中删除jedec_chipdrv
unregister_chip_driver
unregister_mtd_chip_driver(&jedec_chipdrv);
<a>jedec_probe</a>
struct mtd_info *jedec_probe(struct map_info *map)
“jedec_probe”型MTD晶片的探測程式
調用通用探測程式mtd_do_chip_probe(),并将jedec_chip_probe作為參數傳遞給mtd_do_chp_probe()
mtd_do_chip_probe()
注冊在jedec_chipdrv中,根據晶片類型被do_map_probe()調用
return mtd_do_chip_probe(map, &jedec_chip_probe);
<a>jedec_probe_chip</a>
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。
cfi_jedec_stup()
注冊在jedec_chip_probe中
<a>cfi_jedec_setup</a>
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中的索引
jedec_probe_chip()
<a>gen_probe.c</a>
通用晶片探測程式,由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()。
<a>mtd_do_chip_probe</a>
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結構
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)
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 */
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;
<a>genprobe_ident_chips</a>
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晶片
cp:chip_probe結構參數
cfi_private結構
genprobe_new_chip()
<a>genprobe_new_chip</a>
static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
struct cfi_private *cfi)
設定cfi
調用cp->probe_chip()
cfi-> probe_chip()
<a>check_cmd_set</a>
static struct mtd_info *check_cmd_set(struct map_info *map, int primary)
根據map_info中的廠商資訊調用不同的指令集
根據廠商類型調用兩個不同的指令集,cfi_cmdset_0001()和cfi_cmdset_0002(),如果符合的類型沒有則調用cfi_cmdset_unkown
primary:如果取1,則晶片廠商資訊為map中的primary,否則為auxillary
MTD裝置資訊mtd_info
cfi_cmdset_0001()
cfi_cmdset_0002()
cfi_cmdset_unknown()
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)
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);
#ifdef CONFIG_MTD_CFI_AMDSTD
case 0x0002:
return cfi_cmdset_0002(map, primary);
return cfi_cmdset_unknown(map, primary);
<a>cfi_cmdset_unkown</a>
static inline struct mtd_info *cfi_cmdset_unknown(struct map_info *map, int primary)
調用未知類型的指令集
如果存在傳回mtd_info,如果不存在傳回NULL
primary:FIXME
存在指令集:傳回mtd_info
否則:傳回NULL
#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);
printk(KERN_NOTICE "Support for command set %04X not present/n",
type);