基于块设备子系统的MTD子系统(2.6.26)

作者:guolele

blog:http://blog.csdn.net/guolele2010

这图说明了MTD子系统的架构,那么它跟块设备子系统有什么关系?其实MTD就是利用了块设备子系统,只是它中间多了一层MTD设备驱动层。

在drivers/mtd/mtdblock.c里,

static int

__init init_mtdblock(void)

{

return

register_mtd_blktrans(&mtdblock_tr);

}

只有一行,看看

int

register_mtd_blktrans(struct mtd_blktrans_ops

*tr)

{

int

ret, i;

if

(!blktrans_notifier.list.next)

register_mtd_user(&blktrans_notifier);

……

ret

= register_blkdev(tr->major,

tr->name);

……

tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request,

&tr->blkcore_priv->queue_lock);

……

tr->blkcore_priv->rq->queuedata

= tr;

blk_queue_hardsect_size(tr->blkcore_priv->rq,

tr->blksize);

tr->blkshift

= ffs(tr->blksize) - 1;

tr->blkcore_priv->thread

= kthread_run(mtd_blktrans_thread,

tr,

"%sd",

tr->name);

……

INIT_LIST_HEAD(&tr->devs);

list_add(&tr->list,

&blktrans_majors);

for

(i=0; i

if

(mtd_table[i] && mtd_table[i]->type !=

MTD_ABSENT)

tr->add_mtd(tr,

mtd_table[i]);

}

mutex_unlock(&mtd_table_mutex);

return

0;

}

只要不是瞎的都知道这就是块设备驱动,那它没指定make_requset函数,那么就默认为__make_request,这就不分析了,然后就交给mtd_blktrans_request,这个函数只有两行:

static void

mtd_blktrans_request(struct request_queue

*rq)

{

struct

mtd_blktrans_ops *tr = rq->queuedata;

wake_up_process(tr->blkcore_priv->thread);

}

正常人也能猜到这是唤醒一个线程,但是唤醒哪个?在register_mtd_blktrans有一句. tr->blkcore_priv->thread =

kthread_run(mtd_blktrans_thread,

tr,

"%sd",

tr->name);

看看mtd_blktrans_thread

static int

mtd_blktrans_thread(void *arg)

{

……

req

= elv_next_request(rq);

……

res

= do_blktrans_request(tr, dev,

req);

…..

end_request(req,

res);

}

spin_unlock_irq(rq->queue_lock);

return

0;

}

req =

elv_next_request(rq);

多熟悉的身影,就是拿一个请求出来,然后处理res = do_blktrans_request(tr, dev,

req);

Static int

do_blktrans_request(struct mtd_blktrans_ops

*tr,

struct

mtd_blktrans_dev *dev,

struct

request *req)

{

……

switch(rq_data_dir(req))

{

case

READ:

for

(; nsect > 0; nsect--, block++, buf +=

tr->blksize)

if

(tr->readsect(dev, block,

buf))//它在mtdblock_tr里指定为mtdblock_readsect

return

0;

return

1;

case

WRITE:

if

(!tr->writesect)

return

0;

for

(; nsect > 0; nsect--, block++, buf +=

tr->blksize)

if

(tr->writesect(dev, block, buf))

return

0;

return

1;

default:

printk(KERN_NOTICE

"Unknown request %u/n", rq_data_dir(req));

return

0;

}

}

其实就是调用mtdblock_readsect,在mtdblock_tr上。

static int

mtdblock_readsect(struct mtd_blktrans_dev

*dev,

unsigned

long block, char *buf)

{

struct

mtdblk_dev *mtdblk =

mtdblks[dev->devnum];//最终从注册的mtddev中取出对应的tdblk_dev结构

return do_cached_read(mtdblk,

block<<9, 512, buf);

}

static int

do_cached_read (struct mtdblk_dev *mtdblk, unsigned long

pos,

int

len, char *buf)

{

struct

mtd_info *mtd = mtdblk->mtd;//在注册的mtdblk中拿出mtd_info其中最里面包含操作的函数

unsigned

int sect_size = mtdblk->cache_size;

size_t

retlen;

int

ret;

DEBUG(MTD_DEBUG_LEVEL2,

"mtdblock: read on /"%s/" at 0x%lx, size

0x%x/n",

mtd->name,

pos, len);

if

(!sect_size)

return

mtd->read(mtd, pos, len, &retlen,

buf);//s3c2410.c指定为nand_read

while

(len > 0)

……

}

mtd->read(mtd, pos, len, &retlen,

buf);以s3c2410.c为例,在probe函数里的nand_scan_tail,在指定为nand_read.

Nand_read就属于drivers/mtd/nand/nand_base.c

这一层就是多加的一层,它下面就是直接的驱动程序

static int

nand_read(struct mtd_info *mtd, loff_t from, size_t

len,

size_t

*retlen, uint8_t *buf)

{

……

ret

= nand_do_read_ops(mtd, from,

&chip->ops);

……

}

nand_do_read_ops执行读操作

static int

nand_do_read_ops(struct mtd_info *mtd, loff_t

from,

struct

mtd_oob_ops *ops)

{

……

if

(unlikely(ops->mode == MTD_OOB_RAW))

ret

= chip->ecc.read_page_raw(mtd, chip,

bufpoi);//在s3c2410.c里的nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw

……

}

chip->ecc.read_page_raw这个函数指针呢,还是在nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw,这要在默认不没有的时候内核才填的,当然也可以自己实现。

static int

nand_read_page_raw(struct mtd_info *mtd, struct nand_chip

*chip,

uint8_t

*buf)

{

chip->read_buf(mtd,

buf, mtd->writesize);//最终调用的是nand_ chip里的read_buf函数指针,s3c2410为s3c2440_nand_read_buf

chip->read_buf(mtd,

chip->oob_poi, mtd->oobsize);

return

0;

}

最终,调用的是我们要写的驱动程序里的读flash函数。

还有一边是为了提供给用户空间的一个接口,所以采用了一个以字符设备驱动为主的一层。

在drivers/mtd/mtdchar.c

注册啊那些我就不说了,只接讲函数操作。

static

const struct file_operations mtd_fops = {

.owner =

THIS_MODULE,

.llseek =

mtd_lseek,

.read =

mtd_read,

.write =

mtd_write,

.ioctl =

mtd_ioctl,

.open =

mtd_open,

.release =

mtd_close,

};

只看打开跟读。

static int

mtd_open(struct inode *inode, struct file

*file)

{

……

mtd

= get_mtd_device(NULL, devnum);

……

mfi->mtd

= mtd;

file->private_data

= mfi;

return

0;

}

打开就为了获得struct

mtd_info这个结构体,有个读操作进来。

static

ssize_t mtd_read(struct file *file, char __user *buf, size_t

count,loff_t *ppos)

{

……

ret

= mtd->read_oob(mtd, *ppos,

&ops);//在nand_scan_tail里指定为nand_read_oob

retlen

= ops.retlen;

break;

}

default:

ret

= mtd->read(mtd, *ppos, len, &retlen,

kbuf);//在nand_scan_tail里指定为nand_read

}

……

}

就是采用我们要写的驱动程序里的读函数,前面几个什么read_oob这些要指定才有,我们没指定,所以就直接到mtd->read也就是nand_read,也就重复上面说的内容了。

再看看上面的图,清楚的明白了这个机制了吧?然后呢,就是要谈我们怎么写的时候了。内核已经给我们做完了大部分工作,我们要实现的就是s3c2410_nand.c这个驱动程序,这个驱动程序的主要工作是什么?

说这之前要先说几个结构体:

struct

mtd_info {

u_char

type;

u_int32_t

flags;

u_int32_t

size; //

Total size of the MTD

u_int32_t

erasesize;

u_int32_t

writesize;

u_int32_t

oobsize; //

Amount of OOB data per block (e.g. 16)

u_int32_t

oobavail; // Available OOB bytes

per block

//

Kernel-only stuff starts here.

char

*name;

int

index;

struct

nand_ecclayout *ecclayout;

int

numeraseregions;

struct

mtd_erase_region_info *eraseregions;

int

(*erase) (struct mtd_info *mtd, struct erase_info

*instr);

int

(*point) (struct mtd_info *mtd, loff_t from, size_t

len,

size_t

*retlen, void **virt, resource_size_t

*phys);

void

(*unpoint) (struct mtd_info *mtd, loff_t from, size_t

len);

int

(*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t

*retlen, u_char *buf);

int

(*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t

*retlen, const u_char *buf);

int

(*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t

*retlen, const u_char *buf);

int

(*read_oob) (struct mtd_info *mtd, loff_t

from,

struct

mtd_oob_ops *ops);

int

(*write_oob) (struct mtd_info *mtd, loff_t

to,

struct

mtd_oob_ops *ops);

int

(*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,

size_t len);

int

(*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,

size_t len);

int

(*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len, size_t *retlen, u_char *buf);

int

(*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t

len);

int

(*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned

long count, loff_t to, size_t *retlen);

void

(*sync) (struct mtd_info *mtd);

int

(*lock) (struct mtd_info *mtd, loff_t ofs, size_t

len);

int

(*unlock) (struct mtd_info *mtd, loff_t ofs, size_t

len);

int

(*suspend) (struct mtd_info *mtd);

void

(*resume) (struct mtd_info *mtd);

int

(*block_isbad) (struct mtd_info *mtd, loff_t

ofs);

int

(*block_markbad) (struct mtd_info *mtd, loff_t

ofs);

struct

notifier_block

reboot_notifier;

struct

mtd_ecc_stats ecc_stats;

int

subpage_sft;

void

*priv;

struct

module *owner;

int

usecount;

int

(*get_device) (struct mtd_info *mtd);

void

(*put_device) (struct mtd_info *mtd);

};

Mtd_info这个结构体是描述每一个mtd操作

还有一个就是struct

nand_chip,觉得太长,就只复制个内核描述就可以了。

Struct

nand_chip是底层操作的,接到命令,就把数据写到mtd_info里,然后处理后再转为struct

nand_chip,才真正执行。

1、实现底层硬件操作,struct

nand_chip

2、填充struct

mtd_info

整个驱动程序就这么简单!

linux mtd 块设备,基于块设备子系统的MTD子系统(2.6.26)相关推荐

  1. linux mtd 块设备,Linux系统中/dev/mtd与/dev/mtdblock的区别,即MTD字符设备和块设备的区别...

    转:http://www.crifan.com/linux_system_in__dev__mtd_and__dev__mtdblock_distinction_character_devices_a ...

  2. bio linux 创建_Linux设备驱动--块设备之概念和框架以及相关结构体

    基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性读到 ...

  3. Linux 块设备 读写,块设备读写流程

    块设备与字符设备的区别 1.  从字面上理解,块设备和字符设备最大的区别在于读写数据的基本单元不同.块设备读写数据的基本单元为块,例如磁盘通常为一个sector,而字符设备的基本单元为字节.所以Lin ...

  4. linux看门狗设备,基于Linux构建无人值守系统(看门狗)

    基于Linux构建无人值守系统(看门狗) 在各种嵌入式设备soc中基本都提供了看门狗,在很长一段时间里我对看门狗的理解就是"关掉它,不然它会找麻烦".但是当某种需求存在的时候,它又 ...

  5. 嵌入式linux作为hid设备,基于嵌入式系统的USB(HID)设备

    基于嵌入式系统的USB(HID)设备 目前嵌入式系统在数字化电子产品领域应用越来越广泛.随着其成本的降低,大有取代单片机的趋势. USB设备以其小巧.便携.即插即用.成本低廉等优势在当前的桌面应用中有 ...

  6. Linux块设备驱动-MTD子系统

    Linux块设备驱动 块设备驱动 块设备驱动的引入 1. 简单字符驱动程序思想 2. 块设备驱动程序思想 块设备驱动框架 1. 层次框架 2. 分析ll_rw_block 块设备驱动程序编写 1.分配 ...

  7. Linux块设备驱动总结

    <Linux设备驱动程序>第十六章 块设备驱动程序读书笔记 简介 一个块设备驱动程序主要通过传输固定大小的随机数据来访问设备 Linux内核视块设备为与字符设备相异的基本设备类型 Linu ...

  8. Linux块设备IO子系统

    块设备是Linux三大设备之一,其驱动模型主要针对磁盘,Flash等存储类设备,块设备(blockdevice)是一种具有一定结构的随机存取设备,对这种设备的读写是按块(所以叫块设备)进行的,他使用缓 ...

  9. Linux驱动开发--写一个块设备驱动

    原文地址:[原创] 写一个块设备驱动 http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=2017377&fromuid=288 ...

最新文章

  1. CI框架css引入出现问题
  2. Java 线程池框架核心代码分析--转
  3. 传英特尔将在6月下旬发布X299芯片组和Skylake-X处理器
  4. lnmp php fpm 默认,LNMP(PHP-FPM)
  5. NYOJ176 整数划分(二)
  6. python处理xml文件_Python解析并修改XML文件
  7. oracle 数据分页功能,Oracle数据库实现分页功能
  8. Dojo1.6 中的事件处理
  9. Python基础-range()函数(定义一段整数范围)
  10. Hadoop的调度器总结(转)
  11. JavaScript知识点之:delete操作符
  12. jsMind 使用直角画线
  13. python安装imageai库方法_小白对 imageAI环境搭建 实现object detection 的初使用(自己所用)...
  14. 64位系统可以装python32位吗_Python - pyinstaller在64位系统下打包32位程序
  15. 给大家普及呼叫中心和电话营销系统相关知识--中继线路
  16. 开源监控Prometheus介绍,安装,配置,使用详解
  17. 人工智能(AI)和机器学习——未来的发展趋势
  18. org.springframework.boot.actuate.endpoint.EndpointId cannot be cast to java.lang.String 异常处理
  19. css 图片变大缩小,css3实现图片的变大变小
  20. Creator3D:shader13_水面涟漪

热门文章

  1. recon-ng的使用
  2. 北京信息科技大学计算机学院官网,北京信息科技大学教务处官网入口地址
  3. 【新星计划】如何写好你的博客,涨粉技巧总结
  4. 《从菜鸟到大师》Python工程师之 Python语言基础 00
  5. NBA运动员球员数据分析
  6. NBA球员生涯数据统计系统(中南大学C语言课设)
  7. 3个字节转换为另外3个字节的简单加密算法
  8. Android手机卡顿原因
  9. QT 设置label内字体,字体颜色,背景色 ; 字体的中英对照
  10. [转贴]民国记者有多牛:揭黑损人骂街是常事