最近在學(xué)習(xí)驅(qū)動讀寫flash的代碼部分。經(jīng)歷了可笑的過程:開始我知道flash用通過spi口來讀寫。所以就到了driver/spi 下面看相關(guān)代碼。發(fā)現(xiàn)有個spidev.c里面有read/write/ioctl等函數(shù)。而且還有一個davinci_spi_master.以為調(diào)用spi驅(qū)動的時候會首先調(diào)用到這里,于是就想怎么在上層應(yīng)用里將spidev.c里open調(diào)用到就可以了。最后修改了一些地方就在應(yīng)用的地方打開了這個字符設(shè)備驅(qū)動。在dev下面生成了dev/spidev0.0目錄。于是打開它還調(diào)用到了spidev.c里的相關(guān)函數(shù)。甚是竊喜,但突然發(fā)現(xiàn)這個跟我要讀寫的flash又有什么關(guān)系呢?flash芯片型號是:m25p40。知道了又怎么樣?我該如何讀寫它呢。后來突然在網(wǎng)上看了這么一段話MTD(memory technology device內(nèi)存技術(shù)設(shè)備)是用于訪問memory設(shè)備(ROM、flash)的Linux的子系統(tǒng)。MTD的主要目的是為了使新的memory設(shè)備的驅(qū)動更加簡單,為此它在硬件和上層之間提供了一個抽象的接口。MTD的所有源代碼在/drivers/mtd子目錄下。我將CFI接口的MTD設(shè)備分為四層(從設(shè)備節(jié)點直到底層硬件驅(qū)動),這四層從上到下依次是:設(shè)備節(jié)點、MTD設(shè)備層、MTD原始設(shè)備層和硬件驅(qū)動層。(http://blog.csdn.net/binghuiliang/archive/2008/01/23/2060794.aspx)郁悶之余看到了這樣的信息,發(fā)現(xiàn)原來flash不是要注冊一個什么spi設(shè)備,而是要通過mtd設(shè)備來讀取后來在dev/mtd/devices文件夾里看到了m25p80.c這個代碼。打開發(fā)現(xiàn)里面是對這一類flash驅(qū)動的支持代碼。天哪怎么回事,后來就又看了里面的函數(shù)有read/write/probe函數(shù)但是沒有發(fā)現(xiàn)open函數(shù),甚是煩惱。不知道open函數(shù)哪里去了。是不是probe代替了呢??同時也沒有發(fā)現(xiàn)ops這樣的文件操作結(jié)構(gòu)體。問題出現(xiàn)了。 參看高手說在應(yīng)用里要 system("flash_eraseall /dev/mtd4");spi_fd =open("/dev/mtd4",O_RDWR, 0); 這么調(diào)用,而mtd4在哪里注冊的我就不知道了?,F(xiàn)在還在尋找中。read和write都是調(diào)用到了m25p80.c里函數(shù)。下面具體說一下如何添加m25p80.c驅(qū)動吧。 步驟如下: 1、make menuconfig里選擇MTD/下相應(yīng)的選項。內(nèi)核已經(jīng)配好了。 2、修改arch/arm/mach-davinci下面的davinci_spi_platform.c 在里面加入 static struct flash_platform_data davinci_m25P40_info = {undefined .name = "m25p80", .parts = NULL, .nr_parts = 0, .type = "m25p40", }; static struct spi_board_info dm6467_spi_board_info[] = {undefined {undefined // SPI FLash .modalias = "m25p80", .platform_data = &davinci_m25P40_info, .mode = SPI_MODE_0, .irq = 0, .max_speed_hz = 4 * 1000 * 1000, .bus_num = 0, .chip_select = 0, // device number on bus (0-based) }, }; 然后編譯重新編譯內(nèi)核之后就可以發(fā)現(xiàn)在dev目錄下多了一個dev/mtd4設(shè)備節(jié)點。這里面有很多奇怪的地方。不知道這個mtd4是怎么生成的。像那個spidev0.0設(shè)備節(jié)點是因為在文件spidev.c里有賦值給主設(shè)備和從設(shè)備的地方,而這個在m25p80.c里并沒有發(fā)現(xiàn)任何跡象,只是看到在probe里加進(jìn)了add_mtd_partitions()函數(shù),還有這樣的語句: flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; flash->mtd.write = m25p80_write; 然后繼續(xù)查找probe最后到了那里: static struct spi_driver m25p80_driver = {undefined .driver = {undefined .name = "m25p80", .bus = &spi_bus_type, .owner = THIS_MODULE, }, .probe = m25p_probe, .remove = __devexit_p(m25p_remove), }; 貌似這里又用probe來注冊了spi_driver結(jié)構(gòu)體。而最后的init和exit函數(shù)都用了spi注冊。暈倒!那怎么最后是open設(shè)備mtd4呢。這中間到底發(fā)生了什么? 這里只能說mtd設(shè)備利用了spi總線來達(dá)到注冊自己設(shè)備的目的。而這個mtd設(shè)備在本質(zhì)上是一個字符設(shè)備。 在板子登陸的內(nèi)核信息里截獲到以下信息: call video_register_device() in file videodev.c call video_register_device() in file videodev.c call adv7343_initialize() ad9889_i2c_init() OK! i2c /dev entries driver nand_davinci nand_davinci.0: Using soft ECC info->emifregs = 0xc8008000,EMIF_A1CR = 0x3ffffffc info->emifregs = 0xc8008000,EMIF_A1CR = 0x88442a8 mtd->writesize(pagesize)=2048 mtd->oobsize=64 mtd->erasesize(blocksize)=0x20000 NAND device: Manufacturer ID: 0xec, Chip ID: 0xf1 (Samsung NAND 128MiB 3,3V 8-bit) Scanning device for bad blocks chip_delay = 30 Creating 4 MTD partitions on "nand_davinci.0": 0x00000000-0x000e0000 : "bootloader" 0x000e0000-0x00100000 : "params" 0x00100000-0x004a0000 : "kernel" 0x004a0000-0x08000000 : "filesystem" nand_davinci nand_davinci.0: hardware revision: 2.2 Enter into m25p_probe m25p80 spi0.0: m25p40 (512 Kbytes) dm_spi.0: davinci SPI Controller driver at 0xc8002800 (irq = 43) use_dma=1 pcf8563 0-0051: chip found, driver version 0.4.2 上面的Enter into m25p_probe 是在m25p80.c里probe函數(shù)打印出來的。這么早就打印了而不是open時候才調(diào)用probe是在內(nèi)核加載時就調(diào)用了。 可見這塊板子是用的nandflash并把它分成四部分:bootloader、代碼、內(nèi)核、文件系統(tǒng)。而m25p80 spi0.0: m25p40 (512 Kbytes)這一句更是 經(jīng)典,是說生成了m25p80 spi0.0的一個設(shè)備m25p40吧,猜的呵呵。 現(xiàn)在發(fā)現(xiàn)mtd字符設(shè)備都在mtd/mtdchar.c里注冊,于是就懷疑是不是open調(diào)用到了這里呢,于是打印驗證后發(fā)現(xiàn)open("/dev/mtd4",O_RDWR, 0);調(diào)用到mtdchar.c程序里open函數(shù),那為什么read和write卻是調(diào)用到m25p80.c呢,這里的read有沒有調(diào)用到呢?這就更奇怪了,是不是mtd自己有一套機(jī)制讓打開字符設(shè)備的語句直接去打開mtdchar.c的open然后再具體針對某個設(shè)備來讀寫。這也不對呀,open返回的是mtd4的設(shè)備號啊。 哈哈,測試了一下原來同樣會調(diào)用到mtdchar里read和write。m25p80.c里的read和mtdchar.c里的read函數(shù),這個邏輯是怎么回事? 為了查找這個根據(jù)我看了:mtdchar.c里mtd_read函數(shù),最后終于明白了: 這個函數(shù)的基本功能是: 格式:static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos) 功能:MTD字符設(shè)備的讀操作 說明: 當(dāng)count>0時{undefined 裁減本次操作大小len至min(MAX_KMALLOC_SIZE,count), 申請一塊大小為MAX_KMALLOC_SIZE的內(nèi)核空間kbuf, 調(diào)用mtd_info->read將MTD設(shè)備中的數(shù)據(jù)讀入kbuf, 將kbuf中的數(shù)據(jù)拷貝到用戶空間buf, count自減 釋放kbuf } 參數(shù): file:系統(tǒng)給MTD字符設(shè)備驅(qū)動程序用于傳遞參數(shù)的file結(jié)構(gòu),此函數(shù)通過file得到下 層的MTD設(shè)備 buf:用戶空間的指針,用于存放讀取的數(shù)據(jù) count:被讀數(shù)據(jù)的長度 ppos:被讀數(shù)據(jù)在MTD設(shè)備中的位置 返回: 成功:返回實際讀取數(shù)據(jù)的長度 失?。悍祷劐e誤碼 調(diào)用: mtd_info->read()用于從MTD設(shè)備中讀取數(shù)據(jù) 被調(diào)用: 被注冊進(jìn)mtd_fops結(jié)構(gòu) 源代碼: static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos) {undefined struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; 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; if (count > MAX_KMALLOC_SIZE) kbuf=kmalloc(MAX_KMALLOC_SIZE, GFP_KERNEL); else kbuf=kmalloc(count, GFP_KERNEL); if (!kbuf) return -ENOMEM; printk("mtd_read called!!000000000000000\n"); while (count) {undefined if (count > MAX_KMALLOC_SIZE) len = MAX_KMALLOC_SIZE; else len = count; switch (mfi->mode) {undefined case MTD_MODE_OTP_FACTORY: ret = mtd->read_fact_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; case MTD_MODE_OTP_USER: ret = mtd->read_user_prot_reg(mtd, *ppos, len, &retlen, kbuf); break; case MTD_MODE_RAW: {undefined struct mtd_oob_ops ops; ops.mode = MTD_OOB_RAW; ops.datbuf = kbuf; ops.oobbuf = NULL; ops.len = len; ret = mtd->read_oob(mtd, *ppos, &ops); retlen = ops.retlen; break; } default: printk("mtd_read called!!111111111111\n"); ret = mtd->read(mtd, *ppos, len, &retlen, kbuf); } if (!ret || (ret == -EUCLEAN) || (ret == -EBADMSG)) {undefined *ppos += retlen; if (copy_to_user(buf, kbuf, retlen)) {undefined kfree(kbuf); return -EFAULT; } else total_retlen += retlen; count -= retlen; buf += retlen; if (retlen == 0) count = 0; } else {undefined kfree(kbuf); return ret; } } printk("mtd_read called!!2222222222222222\n"); kfree(kbuf); return total_retlen; } 注意到里面這一句:ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);這個是調(diào)用MTD原始設(shè)備層的mtd.read函數(shù)了。 而我們再回頭看看m25p80.c里probe函數(shù)里語句: flash->mtd.erase = m25p80_erase; flash->mtd.read = m25p80_read; flash->mtd.write = m25p80_write; 這個就是給mtd結(jié)構(gòu)體賦初值的地方。原來是這么聯(lián)系起來的。暈倒! 從上面的解釋可以看到這個函數(shù) 1、先申請一塊大小為MAX_KMALLOC_SIZE的內(nèi)核空間kbuf, 2、調(diào)用mtd->read將MTD設(shè)備中的數(shù)據(jù)讀入kbuf, 3、將kbuf中的數(shù)據(jù)拷貝到用戶空間buf 可以看到,原來如此mtd是通過這些層次關(guān)系來調(diào)用底層mtd設(shè)備(m25p80.c)的數(shù)據(jù)來的。這又讓我想起了視頻video驅(qū)動里v4l2層次了。原來復(fù)雜的內(nèi)核哪里都少不了這種層次的調(diào)用。這樣的話m25p80.c沒有open和ops結(jié)構(gòu)體也正常了,因為在mtdchar.c里都已經(jīng)做好了啊。同樣在mtdchar.c里mtd_write()函數(shù)也看到了 ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); 這樣的語句就是調(diào)用m25p80.c的m25p80_write函數(shù)的語句?,F(xiàn)在情況就一目了然了。 小結(jié):原來我們要對flash讀取的時候就是要給它完成底層MTD原始設(shè)備(本例中的m25p40芯片)的加入(包括配置內(nèi)核kconfig、makefile及davinci_spi_platform.c 改寫),然后這個底層設(shè)備就會通過probe函數(shù)注冊自己的mtd結(jié)構(gòu)體,(struct mtd_file_info *mfi = file->private_data;struct mtd_info *mtd = mfi->mtd;)。然后中間的mtd設(shè)備層才能夠調(diào)用這個底層設(shè)備的數(shù)據(jù),諸如:mtdchar.c里的read、write調(diào)用。最終完成擦寫具體flash的目的。上層的應(yīng)用程序要繼續(xù)研究。而設(shè)備節(jié)點代表了具體的一個mtd設(shè)備我們加載了m25p80.c以后在dev目錄下就出現(xiàn)了mtd4這個設(shè)備。(至于為什么是mtd4就需要繼續(xù)學(xué)習(xí)了)。 |
|