找回密码
 注册
关于网站域名变更的通知
查看: 276|回复: 2
打印 上一主题 下一主题

Linux MTD系统剖析

[复制链接]
  • TA的每日心情

    2019-11-20 15:22
  • 签到天数: 2 天

    [LV.1]初来乍到

    跳转到指定楼层
    1#
    发表于 2020-4-20 09:41 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

    EDA365欢迎您登录!

    您需要 登录 才可以下载或查看,没有帐号?注册

    x
    MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。MTD将文件系统与底层FLASH存储器进行了隔离。9 @8 K7 E6 n1 w, U# R  X1 l

    : |; P: h2 C: Y& v: C9 q . j% n& l5 v/ C8 {

    4 o# d, _9 o) x* l' F8 d) v如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。% E7 d# I" m3 g: a2 ^
    # @' L9 r2 }' n, c# ?. p5 h
    Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。
    & o! u  ?! u$ y* z: i5 @  w7 |
    # c9 V8 F& [1 ~. D; ?. S+ N0 s% ~MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。
    : b# m3 _- c2 R: y* A/ I$ Y' t: O
    ( o0 `0 p1 E& O( R! O3 l; iMTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。
    0 u) `5 \# F3 j6 e6 q3 ^0 Q( D: E8 E2 S" }; p
    设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备
    + ~& |* q+ m8 V1 o) B( [7 r% R; c' I% ], b1 X- o
    MTD数据结构:2 k( s5 X  W- z4 E9 z9 Q4 V# @
    ( P+ }" ?) E9 C8 p) j
    1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
    7 e8 I$ s1 b5 L" T$ z& b% {5 H  d; K2 ?& V
    2 }: @' q/ Q, i) Y6 D
    struct mtd_info *mtd_table[MAX_MTD_DEVICES];
    , c) h. f% h8 @* c2 `" ~% j2 e2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。; m5 L3 s9 V! b0 I: ^
    1 X) W4 p: ]8 l- b4 M" G4 e$ @
    struct mtd_part {
    ! K9 `0 `2 I2 B. e        struct mtd_info mtd;            /* 分区信息, 大部分由master决定 */& e- S) d7 P' e( h3 T2 a, E
            struct mtd_info *master;        /* 分区的主分区 */
    , y: h5 J( F6 c0 j        uint64_t offset;                        /* 分区的偏移地址 */# G, K$ y1 K% O& t3 x
            int index;                                        /* 分区号 (Linux3.0后不存在该字段) */
    $ I! a5 f3 [. M- W        struct list_head list;                /* 将mtd_part链成一个链表mtd_partitons */! l2 n4 S4 _& Y2 X" i. a9 f
            int registered;4 K. @' ]8 w, M3 I
    };% p4 V  ]; Q* M( v5 e' S# D. k' n
    mtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。
    ) \2 u8 f' Y- k5 P' h! k
    ( W  G( S( w2 n. P1 V3 H) Fstruct mtd_info {
    7 D% q/ t4 F- X0 k8 s, M        u_char type;             /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */0 D  ~( A$ P  r
            uint32_t flags;             /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */" D; w, M* _. c0 i2 D5 {% T
            uint64_t size;             /* mtd设备的大小 */: k  q) E9 }( o# [
            uint32_t erasesize;         /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */6 p1 j* X% k; q
            uint32_t writesize;         /* 写大小, 对于norFlash是字节,对nandFlash为一页 *// J+ `+ y$ c) N* H" p6 `
            uint32_t oobsize;    /* OOB字节数 */) H2 K$ g' T" W) [& [7 z$ q) b
            uint32_t oobavail;   /* 可用的OOB字节数 */9 q/ C8 t& o1 @; T) D3 K2 {$ w* f
            unsigned int erasesize_shift;        /* 默认为0,不重要 */0 u$ U: x* _& a) p3 b  h
            unsigned int writesize_shift;        /* 默认为0,不重要 */9 c8 O' X8 N5 ~2 x
            unsigned int erasesize_mask;        /* 默认为1,不重要 */$ H+ P# ~) A4 u" }% H9 e, J
            unsigned int writesize_mask;        /* 默认为1,不重要 */% B; _4 k6 K. A# ], @9 C4 j( ?
            const char *name;                                /* 名字,   不重要*/
    5 a' D0 c4 J  P8 H9 F) J+ c  n        int index;                                                /* 索引号,不重要 */
    + Y( W0 O+ k) m; ]5 {        int numeraseregions;                        /* 通常为1 */4 |0 ^5 z0 W9 r. ?* i+ P- _
            struct mtd_erase_region_info *eraseregions;        /* 可变擦除区域 */
    $ T/ I- ~; C9 D( f' l) j$ }       
    ' u1 `0 ~. Y: \' e0 J/ L2 U! U        void *priv;                /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */* R! A7 C  V2 R. {! w
            struct module *owner;        /* 一般设置为THIS_MODULE */# v* W" y6 |! O$ N
            , y9 K  j9 l( X4 c' t
            /* 擦除函数 */
    6 ]" h4 X/ }, E. W! U        int (*erase) (struct mtd_info *mtd, struct erase_info *instr);" C" I; U. ^, E. r0 y) e! U2 W  L
    0 E2 Y6 Z: p4 z! J0 C/ T
            /* 读写flash函数 */3 }' h7 S3 b% c( \# W1 l: q" Y6 S& ^$ t
            int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);4 h" J# N' y5 ~- K/ A
            int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);1 g. L, h$ w0 p8 a$ b# D
    , ?, s7 o5 r9 @' `1 D: k
            /* 带oob读写Flash函数 */+ {. G/ h  F; E$ G  [; ], h
            int (*read_oob) (struct mtd_info *mtd, loff_t from,
    1 f' F- W( c) L! g                         struct mtd_oob_ops *ops);
    ) m9 d' L" o4 w! r3 ?        int (*write_oob) (struct mtd_info *mtd, loff_t to,
    & R7 O' \8 q' T; o+ }8 P                         struct mtd_oob_ops *ops);2 Q$ h9 b+ L' H# L& j
    8 f9 \' X- g4 w9 h: I+ a
            int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    + D3 r- o2 w1 J# T$ s        int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    8 v- i/ V/ ~0 P3 X$ ?" J- s) ]5 u- z        int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
    5 R/ @+ s( L1 C' z" w$ v- G( R7 s        int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    ( p( b% {' q0 o! w        int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    ( B! J9 u6 \/ v9 S        int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
    ! N8 k6 ?7 V% v- I; H$ }8 R! f: c) |4 l9 c/ {
            int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
    4 n1 t  o. S2 m3 a$ _( d        int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);1 ?. N% `/ x9 W! Y% L# A5 y( _! W
            /* Sync */0 N8 v( `4 T* s( ^: N0 N0 B
            void (*sync) (struct mtd_info *mtd);! b: F. H7 ^7 K" Q) w% a1 t+ F9 s% m

    8 P# V6 l! F/ ?& j        /* Chip-supported device locking */
    ; o1 A( ~. T, F& k+ q        int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);5 S- x+ b3 L- x& [9 q: a5 n9 t
            int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    8 X4 j+ D% E0 H7 o0 |$ P6 s& Q8 A! Q6 J4 i- g% L7 }
            /* 电源管理函数 */
    * [9 q- e1 o" Z  I8 {2 E        int (*suspend) (struct mtd_info *mtd);
    4 ~1 X2 e' g. {  r9 r        void (*resume) (struct mtd_info *mtd);0 E) \/ P, \9 t9 E5 Z- j
    & ^! z- c' x& ?* D/ P
            /* 坏块管理函数 */
    7 p9 ?% z; k1 a        int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    ; U2 F9 @3 p4 g- Q7 _3 M        int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
    3 Z+ d( A* c! b" T- U* f% l
    # t: F" n6 a; O3 v( l        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);7 X9 I! U: b! j9 b9 Y
            unsigned long (*get_unmapped_area) (struct mtd_info *mtd,7 r! v) ]7 b( F# u
                                                unsigned long len,0 k0 N4 N3 A$ `& L) y2 h
                                                unsigned long offset,
    / O$ g7 }" A) E+ J9 V! J8 t                                            unsigned long flags);
    8 u1 Q* k5 \! Q4 ?        struct backing_dev_info *backing_dev_info;, Z/ U$ w, K. {1 p2 v
            struct notifier_block reboot_notifier;  /* default mode before reboot */# b" y; o4 N7 i8 B1 {* l) I& N. z
    % M5 x7 ^; n( `8 t$ ]  v
            /* ECC status information */
    1 m7 q* l* S) ]( u        struct mtd_ecc_stats ecc_stats;, n9 z$ [* a6 R4 H& N! j& {9 x" `! P
            int subpage_sft;
    7 l* e) M+ l  q/ ]0 I& b        struct device dev;$ z9 b: m5 N7 m/ J1 i
            int usecount;
    # h6 Q7 q; e* X! ~1 j        int (*get_device) (struct mtd_info *mtd);2 n1 U0 f# v! X; W& i7 M6 m+ K& C" r
            void (*put_device) (struct mtd_info *mtd);9 ?! F4 o' V2 X6 o& z7 |
    };: v2 Y2 Y0 d: X* g+ {9 n' s$ k
    mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。: H: S# D: h  |
    如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
    ( T% \4 {4 F' V6 W+ [
    ! x0 h9 e, F: p( n! o$ V
    . k3 p% y$ ], o5 F5 a6 Yint add_mtd_device(struct mtd_info *mtd)
    5 @$ f0 e8 C8 c* [. E7 `int del_mtd_device (struct mtd_info *mtd)- r6 j' X6 b% \3 _$ K
    如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。$ h; z  o; K% M! f' T1 W
    int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)# C* M$ T/ }7 c7 Z
    int del_mtd_partitions(struct mtd_info *master)
      J* B# {8 E$ j其中mtd_partition结构体表示分区的信息; a. L! e3 u% l: c- r8 G; S+ l: l% @3 P
    struct mtd_partition {
    ( h- U3 |* `' Y. U. W        char *name;                                /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */, I3 ]) ^$ \3 [
            uint64_t size;                        /* 分区大小 */+ U* a; Y! V3 r8 D/ f7 Q
            uint64_t offset;                /* 分区偏移值 */
    : D# m8 l: H$ ]6 T+ l: D# Y        uint32_t mask_flags;        /* 掩码标识,不重要 */
    ; N$ W" n' U- R6 v, b" a$ s        struct nand_ecclayout *ecclayout;        /* OOB布局 */1 Q9 {+ g# F0 `% g4 @
            struct mtd_info **mtdp;                /* pointer to store the MTD object */9 \: g- m9 b0 m5 ~+ z+ e# a/ [
    };! w2 L6 Z% F5 S6 }
    其中nand_ecclayout结构体:
    * N9 |: y/ t4 Y/ W! G. _  w! K  Wstruct nand_ecclayout {2 I/ h- C1 x; g9 b" H' I6 O1 F
            __u32 eccbytes;                /* ECC字节数 */3 W& T3 M- d! S( c) J
            __u32 eccpos[64];        /* ECC校验码在OOB区域存放位置 */- [0 |) K- q2 A3 M
            __u32 oobavail;               
    ( r. P  z& J) Y' F( H" B        /* 除了ECC校验码之外可用的OOB字节数 */( P( A: k2 V* S* E' a" b# c
            struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
    ; G6 O" Q+ C, z" ]8 r};
    ( D" }: ]9 K- a2 o关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。
    6 s' |# {4 W8 PMTD设备层:
    " a3 y; G$ V5 ^0 t! s4 r6 I* Mmtd字符设备接口:% F& b9 F* t; q4 A

    3 Y. ?$ L/ {9 h: E$ Y/drivers/mtd/mtdchar.c文件实现了MTD字符设备接口,通过它,可以直接访问Flash设备,与前面的字符驱动一样,通过file_operations结构体里面的open()、read()、write()、ioctl()可以读写Flash,通过一系列IOCTL 命令可以获取Flash 设备信息、擦除Flash、读写NAND 的OOB、获取OOB layout 及检查NAND 坏块等(MEMGETINFO、MEMERASE、MEMREADOOB、MEMWRITEOOB、MEMGETBADBLOCK IOCRL) / ?$ S. T! R& }0 h( L# g1 M

      y7 [$ _5 P& }# A) jmtd块设备接口:
    - {4 n! x. Q7 T. N' f
    / P& M% t, r4 P" N2 J/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。* h9 [7 Q# M% F& v) g
    MTD硬件驱动层:
    & u4 e, |: K9 A$ I. j: D2 J
    / B- Q5 l: a" o4 KLinux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。0 A+ u, J$ ]0 Y. ?$ D$ P. ?+ ~; u
    & E5 G5 O; N: p: B$ c" f, [- D- g$ l
    MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。
    3 ~. `# Y) _8 F: R, ~5 s7 n3 \
    6 B1 l1 }1 ?* f+ m: u" d8 ~4 O4 _
    4 l1 }& D$ v, A, ustruct nand_chip {
      T" w# m: q5 ?( b) x        void  __iomem        *IO_ADDR_R;                /* 读8位I/O线地址 */
    ; i8 h* r" r1 ]        void  __iomem        *IO_ADDR_W;                /* 写8位I/O线地址 */7 ?8 q" a8 X* j1 L8 x1 O& x
    % ~4 R& o/ Z" a# Y& R( K) T$ n
            /* 从芯片中读一个字节 */
    6 N, }9 p5 v5 a0 R0 I+ H        uint8_t        (*read_byte)(struct mtd_info *mtd);               
    0 e& @, M0 W2 A: `) k$ M        /* 从芯片中读一个字 */( w, [& J7 e8 R( i
            u16                (*read_word)(struct mtd_info *mtd);               
    : C2 Y+ I0 z" Z: W! k& w        /* 将缓冲区内容写入芯片 */6 B6 O" p0 ^3 D) m3 s3 |
            void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);       
    7 z0 _1 z( Z8 B! r5 V        /* 读芯片读取内容至缓冲区/ */5 y* C* F1 S% H  y
            void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    * m5 ~% _  B) n/ V        /* 验证芯片和写入缓冲区中的数据 */6 i1 t" N8 m  O
            int                (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);/ B" r$ r# M1 N/ b; ]
            /* 选中芯片 */
    8 g, n$ y! ~( _5 e        void        (*select_chip)(struct mtd_info *mtd, int chip);
    , u* {. b/ ]! k9 s0 S: C/ L        /* 检测是否有坏块 */
    2 H' s& C, J! Q" `) k/ |+ h        int                (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);/ T: U% j8 v/ n  X  Y
            /* 标记坏块 */
    ; F9 e$ t# T  j5 V0 _% ~        int                (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
    8 B: F- i- X& @; {) h% `5 l+ v- _        /* 命令、地址、数据控制函数 */: J0 ~- A2 d) w, f: Z  Y
            void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    ; T; d4 Y  M+ `" ]0 x        /* 设备是否就绪 */
    0 c2 u8 {8 c+ |, A0 Y7 ?( w        int                (*dev_ready)(struct mtd_info *mtd);
    7 k" |7 _5 v7 Q3 m        /* 实现命令发送 */
    1 B9 `# h5 `; Z+ b: M$ t        void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
    ) x+ y& O# D% F+ l2 m. N& C- u        int                (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    0 s: Z, N3 Z2 }        /* 擦除命令的处理 */
    " X# d- n# d  d& W& _        void        (*erase_cmd)(struct mtd_info *mtd, int page);4 G! h% V( y0 Z( ?" Z5 m
            /* 扫描坏块 */
    8 D) P3 i. |, y        int                (*scan_bbt)(struct mtd_info *mtd);
    ; E/ `2 Q! ^2 y4 M  x/ W0 S        int                (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
    % h. ?0 \, g" u  z; s9 H        /* 写一页 */
    , `! R/ Z9 w7 `* T* j- s        int                (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
    7 K5 A% i6 H+ u7 c/ q" T9 y                                      const uint8_t *buf, int page, int cached, int raw);; U- {! ]( z4 y# G( L/ _
    ) u4 s* E9 }. x& t0 ?7 I6 `
            int                chip_delay;                        /* 由板决定的延迟时间 */& H. k  r6 Z: f# P: p1 q
            /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
    - `$ v$ ~* K2 }; P- R        unsigned int        options;        ; e1 E4 ]' [4 P: S, I

    1 B, m# v, U6 \( o* h% l/ o        /* 用位表示的NAND芯片的page大小,如某片NAND芯片
    6 ~& s' j* T( ?8 x' a  p         * 的一个page有512个字节,那么page_shift就是9 0 E/ i+ B2 K/ K+ s" V  Q; W: T9 i
             */
    9 S! r" {) t6 X8 O        int                 page_shift;4 x0 ~9 S$ U% a3 {8 l
            /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
    8 H. G8 `8 D. k/ l; W# z         * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14
    ) |) l' A: \2 d% s         */1 `+ \5 a1 y; |: R
            int                 phys_erase_shift;+ h5 u5 _7 M4 H( g* J) N# Z
            /* 用位表示的bad block table的大小,通常一个bbt占用一个block," ^+ y" H/ a2 w
             * 所以bbt_erase_shift通常与phys_erase_shift相等
    6 q3 y% x8 F3 w          */
    ! X; s# v* L" l7 D, e9 f        int                 bbt_erase_shift;" s9 n# P" ~2 P2 A3 f3 G
            /* 用位表示的NAND芯片的容量 */+ Y* d* ?, y1 D% Q& n
            int                 chip_shift;5 W0 X) k6 E* m, k
            /* NADN FLASH芯片的数量 */7 C% n% W3 K( r7 k  y
            int                 numchips;
    $ k; v7 [) p- s. `        /* NAND芯片的大小 */
    6 C! Z$ }  \; x0 }0 |: s# A        uint64_t chipsize;
    : n5 N3 g$ C6 L        int                 pagemask;: \6 E/ X0 J+ K0 X* L. Q
            int                 pagebuf;# U, v0 s6 n3 ^+ Q' x1 X$ g
            int                 subpagesize;
    * U1 w" H& x9 L3 V3 ]" T0 O- f! z/ q- Z        uint8_t         cellinfo;
    * e: Z3 u  z' y! o        int                 badblockpos;
    6 B# o- R) \) K        nand_state_t        state;
    2 v& `9 |7 K* y0 {: a" k& g: ^- v        uint8_t                *oob_poi;5 Y5 A5 p7 K! m/ X! x$ }
            struct nand_hw_control  *controller;
    : g* d1 H1 T% H( o        struct nand_ecclayout        *ecclayout;        /* ECC布局 */  J, S( `* V. V  A
           
    $ a4 Y* _% v: a8 m& x& G: i        struct nand_ecc_ctrl ecc;        /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    : F' ]: b  w7 {$ v) J8 q        struct nand_buffers *buffers;
    . A; y9 z' s, |8 p6 b1 T        struct nand_hw_control hwcontrol;
    & [$ m3 ^) w% D: x9 d0 A: }. S        struct mtd_oob_ops ops;
    & a  L/ G3 q8 r/ J  Q" o        uint8_t                *bbt;
    $ N1 L8 H! U0 [        struct nand_bbt_descr        *bbt_td;( m8 t: X+ k; U& f
            struct nand_bbt_descr        *bbt_md;" [# v# P: \- [. a% D- p) n' P
            struct nand_bbt_descr        *badblock_pattern;' f' T  E) K% J4 K, b9 f4 T; G
            void                *priv;
    2 Z% p  E9 u( v};
    1 D1 f3 w/ W  ^, r5 u0 Q' g7 O+ e, O0 v& c3 w
    最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。( E) Z5 C( W' L$ y

    % M" m5 x- |6 W  r+ U 3 }  Q9 o: B! p7 s  {

    , s5 }( _# Y. b0 O2 j) c$ W
    # d. d$ R% ~4 h8 p* y  Q" w3 _
  • TA的每日心情

    2019-11-29 15:37
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-4-20 13:27 | 只看该作者
    Linux MTD系统剖析

    该用户从未签到

    3#
    发表于 2020-4-21 14:23 | 只看该作者
    Linux MTD系统剖析
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-11-25 21:37 , Processed in 0.171875 second(s), 27 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表