天天看點

Linux MTD源代碼分析(二)

<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-&gt;private_data;

       switch (orig) {

       case 0:

              /* SEEK_SET */

              file-&gt;f_pos = offset;

              break;

       case 1:

              /* SEEK_CUR */

              file-&gt;f_pos += offset;

       case 2:

              /* SEEK_END */

              file-&gt;f_pos =mtd-&gt;size + offset;

       default:

              return -EINVAL;

       }

       if (file-&gt;f_pos

              file-&gt;f_pos = 0;

       else if (file-&gt;f_pos &gt;= mtd-&gt;size)

              file-&gt;f_pos = mtd-&gt;size - 1;

       return file-&gt;f_pos;

}

<a>mtd_open</a>

       static int mtd_open(struct inode *inode, struct file *file)

       打開一個MTD字元裝置

       devnum=minor&gt;&gt;2(參看Documentations/devices.txt),

       進行安全性檢查,

       調用get_mtd_device擷取MTD裝置,并将file-&gt;private_data指向它

       inode:FIXME

       file:是系統提供給MTD字元裝置用于傳遞參數的file結構,此函數中它的private_data

成員被指向原始裝置層的MTD裝置

       成功:傳回0

       失敗:傳回錯誤碼

       get_mtd_device()獲得原始裝置層的MTD裝置

       int minor = MINOR(inode-&gt;i_rdev);

       int devnum = minor &gt;&gt; 1;

       struct mtd_info *mtd;

       DEBUG(MTD_DEBUG_LEVEL0, "MTD_open/n");

       if (devnum &gt;= MAX_MTD_DEVICES)

              return -ENODEV;

       /* You can't open the RO devices RW */

       if ((file-&gt;f_mode &amp; 2) &amp;&amp; (minor &amp; 1))

              return -EACCES;

       mtd = get_mtd_device(NULL, devnum);

       if (!mtd)

       if (MTD_ABSENT == mtd-&gt;type) {

              put_mtd_device(mtd);

       file-&gt;private_data = mtd;

       /* You can't open it RW if it's not a writeable device */

       if ((file-&gt;f_mode &amp; 2) &amp;&amp; !(mtd-&gt;flags &amp; MTD_WRITEABLE)) {

       return 0;

} /* mtd_open */

<a>mtd_close</a>

       static int mtd_close(struct inode *inode, struct file *file)

       關閉一個MTD字元裝置

       調用mtd_info-&gt;sync()同步MTD裝置,

       調用put_mtd_device()返還MTD裝置

       file:無用

       傳回0

       mtd_info-&gt;sync()同步MTD裝置

put_mtd_device()返還MTD裝置

       被注冊進mtd_fops結構

       DEBUG(MTD_DEBUG_LEVEL0, "MTD_close/n");

       mtd = (struct mtd_info *)file-&gt;private_data;

       if (mtd-&gt;sync)

              mtd-&gt;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&gt;0時{

              裁減本次操作大小len至min(MAX_KMALLOC_SIZE,count),

              申請一塊大小為MAX_KMALLOC_SIZE的核心空間kbuf,

              調用mtd_info-&gt;read将MTD裝置中的資料讀入kbuf,

              将kbuf中的資料拷貝到使用者空間buf,

              count自減

              釋放kbuf

file:系統給MTD字元裝置驅動程式用于傳遞參數的file結構,此函數通過file得到下

層的MTD裝置

       buf:使用者空間的指針,用于存放讀取的資料

       count:被讀資料的長度

       ppos:被讀資料在MTD裝置中的位置

       成功:傳回實際讀取資料的長度

       mtd_info-&gt;read()用于從MTD裝置中讀取資料

       struct mtd_info *mtd = (struct mtd_info *)file-&gt;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 &gt; mtd-&gt;size)

              count = mtd-&gt;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 &gt; 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, &amp;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-&gt;write将kbuf中的資料讀入MTD裝置,

       buf:使用者空間的指針,用于存放将要寫入的資料

       count:被寫資料的長度

       ppos:資料被寫入MTD裝置中的位置

       mtd_info-&gt;write用于寫入MTD裝置

       size_t retlen;

       DEBUG(MTD_DEBUG_LEVEL0,"MTD_write/n");

       if (*ppos == mtd-&gt;size)

              return -ENOSPC;

              if (!kbuf) {

                     printk("kmalloc is null/n");

              if (copy_from_user(kbuf, buf, len)) {

                     return -EFAULT;

               ret = (*(mtd-&gt;write))(mtd, *ppos, len, &amp;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-&gt;callback

       wake_up((wait_queue_head_t *)instr-&gt;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 &amp; IOCSIZE_MASK) &gt;&gt; IOCSIZE_SHIFT;

       if (cmd &amp; IOC_IN) {

              ret = verify_area(VERIFY_READ, (char *)arg, size);

              if (ret) return ret;

       if (cmd &amp; IOC_OUT) {

              ret = verify_area(VERIFY_WRITE, (char *)arg, size);

       switch (cmd) {

       case MEMGETREGIONCOUNT:

              if (copy_to_user((int *) arg, &amp;(mtd-&gt;numeraseregions), sizeof(int)))

       case MEMGETREGIONINFO:

       {

              struct region_info_user ur;

              if (copy_from_user(      &amp;ur,

                                   (struct region_info_user *)arg,

                                   sizeof(struct region_info_user))) {

              if (ur.regionindex &gt;= mtd-&gt;numeraseregions)

                     return -EINVAL;

              if (copy_to_user((struct mtd_erase_region_info *) arg,

                            &amp;(mtd-&gt;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(&amp;waitq);

                     memset (erase,0,sizeof(struct erase_info));

                     if (copy_from_user(&amp;erase-&gt;addr, (u_long *)arg,

                                      2 * sizeof(u_long))) {

                            kfree(erase);

                     erase-&gt;mtd = mtd;

                     erase-&gt;callback = mtd_erase_callback;

                     erase-&gt;priv = (unsigned long)&amp;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-&gt;erase(mtd, erase);

                     if (!ret) {

                            set_current_state(TASK_UNINTERRUPTIBLE);

                            add_wait_queue(&amp;waitq, &amp;wait);

                            if (erase-&gt;state != MTD_ERASE_DONE &amp;&amp;

                                erase-&gt;state != MTD_ERASE_FAILED)

                                   schedule();

                            remove_wait_queue(&amp;waitq, &amp;wait);

                            set_current_state(TASK_RUNNING);

                            ret = (erase-&gt;state == MTD_ERASE_FAILED)?-EIO:0;

                     kfree(erase);

       case MEMWRITEOOB:

              struct mtd_oob_buf buf;

              void *databuf;

              ssize_t retlen;

              if (copy_from_user(&amp;buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))

              if (buf.length &gt; 0x4096)

              if (!mtd-&gt;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-&gt;write_oob)(mtd, buf.start, buf.length, &amp;retlen, databuf);

              if (copy_to_user((void *)arg + sizeof(u_int32_t), &amp;retlen, sizeof(u_int32_t)))

                     ret = -EFAULT;

              kfree(databuf);

       case MEMREADOOB:

              if (!mtd-&gt;read_oob)

                     ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);

              ret = (mtd-&gt;read_oob)(mtd, buf.start, buf.length, &amp;retlen, databuf);

              else if (retlen &amp;&amp; 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-&gt;lock)

                     ret = mtd-&gt;lock(mtd, adrs[0], adrs[1]);

       case MEMUNLOCK:

              if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))

              if (!mtd-&gt;unlock)

                     ret = mtd-&gt;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", &amp;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(&amp;notifier);

#else

       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &amp;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(&amp;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(&amp;chip_drvs_lock);

       list_add(&amp;drv-&gt;list, &amp;chip_drvs_list);

       spin_unlock(&amp;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(&amp;drv-&gt;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, &amp;chip_drvs_list) {

              this = list_entry(pos, typeof(*this), list);

              if (!strcmp(this-&gt;name, name)) {

                     ret = this;

                     break;

       if (ret &amp;&amp; !try_inc_mod_count(ret-&gt;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-&gt;probe()

       /drivers/mtd/maps/下的闆子相關檔案中的init_xxxx

       struct mtd_chip_driver *drv;

       struct mtd_info *ret;

       drv = get_mtd_chip_driver(name);

       if (!drv &amp;&amp; !request_module(name))

              drv = get_mtd_chip_driver(name);

       if (!drv)

              return NULL;

       ret = drv-&gt;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-&gt;module)

              __MOD_DEC_USE_COUNT(drv-&gt;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(&amp;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(&amp;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, &amp;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-&gt;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-&gt;device_type, NULL);   //reset,進入讀模式

       cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi-&gt;device_type, NULL);       //發送查詢指令

       if (!qry_present(map,base,cfi))            //如果不存在QRY,則不是CFI接口

       if (!cfi-&gt;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-&gt;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-&gt;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-&gt;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-&gt;numchips == MAX_CFI_CHIPS) {

              printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d./n", map-&gt;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-&gt;numchips].start = base;

       chips[cfi-&gt;numchips].state = FL_READY;

       cfi-&gt;numchips++;

       /* Put it back into Read Mode */

       cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi-&gt;device_type, NULL);

       printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode/n",

              map-&gt;name, cfi-&gt;interleave, cfi-&gt;device_type*8, base,

              map-&gt;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-&gt;interleave * cfi-&gt;device_type; // scale factor

       if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) &amp;&amp;

           cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) &amp;&amp;

           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-&gt;interleave*cfi-&gt;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-&gt;cfiq = kmalloc(sizeof(struct cfi_ident) + num_erase_regions * 4, GFP_KERNEL);

       if (!cfi-&gt;cfiq) {

              printk(KERN_WARNING "%s: kmalloc failed for CFI ident structure/n", map-&gt;name);

       memset(cfi-&gt;cfiq,0,sizeof(struct cfi_ident));      

       cfi-&gt;cfi_mode = 1;        //JEDEC仿真模式

       cfi-&gt;fast_prog=1;          /* CFI supports fast programming *///CFI支援Fast Program模式

       /* Read the CFI info structure */

       for (i=0; i

              ((unsigned char *)cfi-&gt;cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor);

       /* Do any necessary byteswapping */

       cfi-&gt;cfiq-&gt;P_ID = le16_to_cpu(cfi-&gt;cfiq-&gt;P_ID);

       cfi-&gt;cfiq-&gt;P_ADR = le16_to_cpu(cfi-&gt;cfiq-&gt;P_ADR);

       cfi-&gt;cfiq-&gt;A_ID = le16_to_cpu(cfi-&gt;cfiq-&gt;A_ID);

       cfi-&gt;cfiq-&gt;A_ADR = le16_to_cpu(cfi-&gt;cfiq-&gt;A_ADR);

       cfi-&gt;cfiq-&gt;InterfaceDesc = le16_to_cpu(cfi-&gt;cfiq-&gt;InterfaceDesc);

       cfi-&gt;cfiq-&gt;MaxBufWriteSize = le16_to_cpu(cfi-&gt;cfiq-&gt;MaxBufWriteSize);

       /* Dump the information therein */

       print_cfi_ident(cfi-&gt;cfiq);

       for (i=0; icfiq-&gt;NumEraseRegions; i++) {

              cfi-&gt;cfiq-&gt;EraseRegionInfo[i] = le32_to_cpu(cfi-&gt;cfiq-&gt;EraseRegionInfo[i]);

#ifdef DEBUG_CFI             

              printk("  Erase Region #%d: BlockSize 0x%4.4X bytes, %d blocks/n",

                     i, (cfi-&gt;cfiq-&gt;EraseRegionInfo[i] &gt;&gt; 8) &amp; ~0xff,

                     (cfi-&gt;cfiq-&gt;EraseRegionInfo[i] &amp; 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(&amp;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(&amp;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, &amp;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-&gt;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-&gt;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-&gt;cfiq);

       kfree(cfi);

       map-&gt;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-&gt;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-&gt;probe_chip()

       cfi-&gt; 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-&gt;fldrv_priv;

       __u16 type = primary?cfi-&gt;cfiq-&gt;P_ID:cfi-&gt;cfiq-&gt;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) &amp;&amp; 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);

繼續閱讀