一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

通過mtd讀寫flash...

 無我無他 2021-12-09

最近在學(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í)了)。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    精品日韩欧美一区久久| 欧美不卡一区二区在线视频| 福利视频一区二区在线| 欧美胖熟妇一区二区三区| 夫妻性生活一级黄色录像| 四季精品人妻av一区二区三区| 少妇毛片一区二区三区| 亚洲国产av在线观看一区| 日本深夜福利在线播放| 国产精品白丝一区二区| 在线观看那种视频你懂的| 欧美成人欧美一级乱黄| 日韩精品一区二区一牛| 丰满人妻熟妇乱又乱精品古代| 日本成人中文字幕一区| 亚洲中文字幕视频一区二区| 亚洲中文字幕三区四区| 91日韩欧美中文字幕| 在线视频免费看你懂的| av在线免费观看在线免费观看| 99久免费精品视频在线观| 亚洲精品伦理熟女国产一区二区 | 欧美大胆美女a级视频| 久久本道综合色狠狠五月| 精品综合欧美一区二区三区| 中文字幕一区二区免费| 中文字幕日产乱码一区二区| 91麻豆视频国产一区二区| 黑丝袜美女老师的小逼逼| 成年人免费看国产视频| 99久久无色码中文字幕免费| 好吊色欧美一区二区三区顽频| 国产老女人性生活视频| 亚洲av一区二区三区精品| 日本人妻精品中文字幕不卡乱码 | 欧美丰满人妻少妇精品| 五月婷婷综合激情啪啪| 好吊一区二区三区在线看| 男人大臿蕉香蕉大视频| 黄色国产自拍在线观看| 日韩亚洲激情在线观看|