找回密码
 注册
关于网站域名变更的通知
查看: 259|回复: 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存储器进行了隔离。1 ~" {: Z8 G- S2 S; m0 S! E0 p7 m

    + u# I; P; }3 T8 _% v* ^+ z % y1 n8 n- B1 u+ _6 x! u
    8 s2 F* x$ H) ]$ z- _
    如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。9 H: L6 C2 n/ Q* b8 i

    . Z- v8 |7 L" q; J- k$ X+ H: c* CFlash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。& M2 b+ b1 o! \' V! S. w5 ^5 h% y

    3 P7 A5 J- g' h4 Q3 N$ W5 TMTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。7 k$ S7 z* X2 \4 }! I4 D' J

    $ _# i# _( Z* [8 g) ^3 r/ CMTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。
    0 J, d8 a4 A% t
    ; I2 D6 x9 v+ L0 f" n; F' [设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备
    ' n; @' A) m$ W0 z# U1 v0 F6 R0 b; F  j$ c) A% ~/ G
    MTD数据结构:6 V( ^+ ^+ A' Z! j# ]: m, z2 J

    # F# o6 n# k% u8 N2 j1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
    + I2 u* Z8 i1 n% f4 T8 H  ^7 P
    + G' t  p0 h, k' P2 T( y4 o$ N) j" K- s/ I, E/ E+ C1 I6 G
    struct mtd_info *mtd_table[MAX_MTD_DEVICES];* j8 |( q5 l: J$ C! \  W
    2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。) }% Y4 ?$ V1 C! R

    + A& V' G) y. _  v9 a% i; t1 T3 gstruct mtd_part {
    ) D5 ~3 i% R; s- u4 z        struct mtd_info mtd;            /* 分区信息, 大部分由master决定 */" l% ^; J4 [, J' B+ ^: G% O* i  U$ Q) V1 e
            struct mtd_info *master;        /* 分区的主分区 */6 ~% X6 s. m2 A
            uint64_t offset;                        /* 分区的偏移地址 */! O+ i/ Q: a* x  p2 L
            int index;                                        /* 分区号 (Linux3.0后不存在该字段) */
    ; {8 D3 Y9 S# L9 G        struct list_head list;                /* 将mtd_part链成一个链表mtd_partitons */
    ) ?$ g( m) x" p5 f1 _, |  C* R        int registered;, Z$ A; {  ?  S/ a! @( ?+ Z7 d
    };
    " B; U2 H% b8 Z# r( Z8 l0 Umtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。
    / j8 V( |# _" S3 B8 B* @, G( y4 y) V: F) F; P* b3 A/ m
    struct mtd_info {
    0 b$ P  A3 B$ m3 |        u_char type;             /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
    1 O5 V2 \0 Q# M) R/ U/ q% ]        uint32_t flags;             /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */7 f) l+ E8 Q  |' {) Z" f. V, \! D: c
            uint64_t size;             /* mtd设备的大小 */
    ) `+ G5 ?) @& U# |- p/ I7 P6 O1 N        uint32_t erasesize;         /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
    ( k" w+ e# V+ ~- E6 ]        uint32_t writesize;         /* 写大小, 对于norFlash是字节,对nandFlash为一页 */8 u  u. H" _6 A' p
            uint32_t oobsize;    /* OOB字节数 */
    + c' z2 {# @/ J, O  ]: P        uint32_t oobavail;   /* 可用的OOB字节数 */3 [+ O% X6 A- k7 ^$ F
            unsigned int erasesize_shift;        /* 默认为0,不重要 */
    6 T( C" w7 k; s/ H5 K5 [# n9 \* q3 _        unsigned int writesize_shift;        /* 默认为0,不重要 */* |( o, B9 o( u* w  _# j% j* H
            unsigned int erasesize_mask;        /* 默认为1,不重要 */, e) ~2 a. @  u
            unsigned int writesize_mask;        /* 默认为1,不重要 */; F/ {* V. }- a) G6 ^# l& m
            const char *name;                                /* 名字,   不重要*/
    & a9 }/ Q) V  d* \. l: h        int index;                                                /* 索引号,不重要 */
    & B# R9 Y, L; s4 U        int numeraseregions;                        /* 通常为1 */
    ; k% s- l7 u3 k- D1 Z6 x/ S        struct mtd_erase_region_info *eraseregions;        /* 可变擦除区域 */
    ' {$ u; [2 A- h9 ]9 x       
    & q& J' W+ L1 t0 X0 J/ J        void *priv;                /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 *// k. a! Q9 C, D8 U6 G8 \
            struct module *owner;        /* 一般设置为THIS_MODULE */4 ?$ Q) u! {7 }2 H. m9 o
            / ~& {8 W; |2 }9 Z+ r1 J: \9 ?  E  n
            /* 擦除函数 */7 p4 `' j( g. r+ F% b7 l1 {
            int (*erase) (struct mtd_info *mtd, struct erase_info *instr);' j0 x4 `6 C, ?0 f

    + s7 M  a; G: Z8 I! ^        /* 读写flash函数 */0 I- a0 V' O, t1 B3 n
            int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);$ @' c7 K6 k/ M' l; T
            int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    ) t0 I5 W. e- ]- J
    3 H2 m1 e3 e# f4 t. E0 V& u        /* 带oob读写Flash函数 */
    ' a" l$ h, @8 V        int (*read_oob) (struct mtd_info *mtd, loff_t from,
    0 K) L, u0 u3 T% r% h                         struct mtd_oob_ops *ops);  u( ?8 W$ F4 ]  M9 V
            int (*write_oob) (struct mtd_info *mtd, loff_t to,+ m* o7 t* G2 @' p9 T
                             struct mtd_oob_ops *ops);
    6 g# g9 ?6 R# X- `# C
    $ V7 V2 \/ n% d/ ]3 L2 \# A        int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);: C4 ]# I4 ~7 h0 c: G, k! Z. x
            int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);4 g' K! o5 p" o! r, j
            int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);/ v1 C3 Z2 d- [. Y  B0 d1 M
            int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);& r# B3 }! j  Y( |
            int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    : C; Q. ~+ I3 d7 s4 A        int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
      C, p0 Q7 u4 s' ]
    ( c/ q9 N4 p. [5 c        int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);! Z: a4 o: v' J4 n; W& {" q
            int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);0 W9 I$ C: a7 D
            /* Sync */# R# D, D! ^6 x
            void (*sync) (struct mtd_info *mtd);
    2 i& \5 U; B# ~. p( Q( t" t2 b/ D
    ( O' D: ^0 B+ E2 h        /* Chip-supported device locking *// l5 m2 e; D! r# P% H; n& G5 B
            int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);6 A& n! Q" b# _6 ?0 r6 V, d
            int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);8 {  h5 J* V8 R* x  k

    # w$ F0 y( w) y! U1 p        /* 电源管理函数 */& B( }5 g) P& i) X# L' L8 K  b
            int (*suspend) (struct mtd_info *mtd);
    4 H6 e7 ?# L, O4 ~, d6 w& v        void (*resume) (struct mtd_info *mtd);% y  `# l3 ~7 |/ x; ^4 E/ d+ w

    . B4 l9 c, x1 O9 s' s; K( w0 ?        /* 坏块管理函数 */# F$ ~+ K6 n; N# T0 |) q5 p
            int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    1 r* ?& H/ W+ t# [# ^% _) v        int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
    ) X* ]: n2 u3 l+ H6 G7 a
    # e  U2 D2 n% e8 q+ }8 m        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);* i8 z3 D+ A; g: d
            unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
    # p6 J0 @6 a* n& D1 \: V% V                                            unsigned long len,
    9 g. g# H# J& C$ Z* _* H; H                                            unsigned long offset,
    ( ^5 R0 ~: \! e                                            unsigned long flags);0 z! z( f1 C* o
            struct backing_dev_info *backing_dev_info;& q: T; r3 O" h
            struct notifier_block reboot_notifier;  /* default mode before reboot */
    0 b! ]$ V. {: T; Z, Z9 U/ ]* n- s3 c" C# C7 v) N7 l
            /* ECC status information */
    & F1 q2 t+ {& {9 I/ B2 P        struct mtd_ecc_stats ecc_stats;2 T6 f/ `: e! o% p# Z' H: H$ [
            int subpage_sft;
    $ e! u8 J7 ?2 |- K) e. c        struct device dev;
    ) P( x" y1 F8 O% Q8 Y5 y) O9 Y' Z5 K        int usecount;1 [0 {' p) q0 |3 s7 U$ F
            int (*get_device) (struct mtd_info *mtd);; K( X5 B: W( A; @, ^
            void (*put_device) (struct mtd_info *mtd);) d- G' O8 E: i( g2 x
    };9 _+ s: J$ y( Y+ |9 M) \
    mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。& M4 {- b+ R: d6 F9 D& g3 q, y8 @
    如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
    , G- z! K$ M' N9 c
    2 @- U' K1 ~% }  l+ u
    * v* H' f% {6 R' sint add_mtd_device(struct mtd_info *mtd)3 q, p# r) k: _7 B- u; @. D
    int del_mtd_device (struct mtd_info *mtd)
    # [7 F* I* X4 s! I: n" s如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。8 v2 A9 o. O0 g/ q" Z' @+ P
    int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
    / w1 Z5 Z1 X% B# K1 a: Lint del_mtd_partitions(struct mtd_info *master)
    5 }1 m: {& V+ X其中mtd_partition结构体表示分区的信息
      n' Q, P; b7 T) e' ~$ y; u" estruct mtd_partition {0 n  R5 G- a0 N- A9 t) [9 v2 |
            char *name;                                /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
    - |$ t3 `! R. j9 m- T$ m( ~7 ~. s% ?* Q        uint64_t size;                        /* 分区大小 */- H8 |: C6 t  |
            uint64_t offset;                /* 分区偏移值 */# |" l, c0 A/ ^
            uint32_t mask_flags;        /* 掩码标识,不重要 */
    5 Y8 a$ f( q6 F$ J        struct nand_ecclayout *ecclayout;        /* OOB布局 */
    % z1 @' J9 o6 D2 \* n% \        struct mtd_info **mtdp;                /* pointer to store the MTD object */
    & I8 S. d  }$ j! c' o* O! M; {};4 _: v* V5 \# x6 J( v+ D6 m* C
    其中nand_ecclayout结构体:6 X1 T( ]; |0 ?' Z, a2 w) ~' w2 e
    struct nand_ecclayout {
    ; r; e4 |( O8 Y% r& r        __u32 eccbytes;                /* ECC字节数 */' I# X3 u. p& C  C( `
            __u32 eccpos[64];        /* ECC校验码在OOB区域存放位置 */
    8 z& l8 d0 a4 N" X9 ?& S        __u32 oobavail;                # W& `4 X5 J  i7 H& B
            /* 除了ECC校验码之外可用的OOB字节数 */3 i# I) v1 u! O
            struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];- n3 S% ~7 ]- m0 }8 v" R
    };
    5 O! q$ ]! X7 W; ^关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。
    ; [+ L+ O" s1 {4 S! _) @% B. XMTD设备层:
    ' U( |% O4 `$ i) K: }+ ?mtd字符设备接口:7 j; g5 t: e# Z) q" t4 X4 l

    + P+ _) Y3 X" J. {7 L/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)
    6 n5 e2 L% c4 L( D5 @: W7 }- F) P" H2 ^1 ~8 r( j. F3 }7 k/ J, {
    mtd块设备接口:( r+ P2 }" P' n$ f2 O

    ) ^" I7 @0 Y( r/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。* N9 Q0 E$ q1 k* K* J
    MTD硬件驱动层:1 I9 n* W. C3 ~, }( Q- Q

    & X- ~- w. {+ c- NLinux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。
    % x8 r' o3 N/ |- B: P* l- b1 K# X7 h
    ! k( z8 {4 K: C% sMTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。
    % ~1 J" g- m; G. u4 Q% D+ p! N; t* ^1 @2 _3 L
    " _, i, {$ a# l  q3 Z
    struct nand_chip {
      _" {% ]) l3 i" |, p0 K. i# r        void  __iomem        *IO_ADDR_R;                /* 读8位I/O线地址 */
    6 y4 L- \0 l4 E" \        void  __iomem        *IO_ADDR_W;                /* 写8位I/O线地址 */5 s5 q, h7 |2 L5 |; k3 @, X

    + B" P+ d$ e) i. \) F$ z0 g$ X        /* 从芯片中读一个字节 */
    6 ?. Z8 f  j  k# ?        uint8_t        (*read_byte)(struct mtd_info *mtd);                ! ^+ t- L2 b: t
            /* 从芯片中读一个字 */
    3 T; |2 O3 E  j* q        u16                (*read_word)(struct mtd_info *mtd);               
    ! F- A2 Z/ a& X2 c5 S! |        /* 将缓冲区内容写入芯片 */
    7 J5 t4 \/ z" m! H( _        void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);       
    % {4 V' w; H: w! ^5 M% v- u) p. A        /* 读芯片读取内容至缓冲区/ */
    8 r: s& R/ |. F, G        void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    . R& }, m) k0 e" k6 w1 t) P: `        /* 验证芯片和写入缓冲区中的数据 */. v! y4 k" a. z5 k* F; j$ O
            int                (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);* d# F, q' x# x7 q/ \
            /* 选中芯片 */
    & o" R: j6 s9 o  `( S, y        void        (*select_chip)(struct mtd_info *mtd, int chip);
    % ]2 }9 k6 y4 B6 L        /* 检测是否有坏块 */1 z: n7 m7 n7 X) B
            int                (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);$ P* Z$ N& Z, e' X+ x7 H) K
            /* 标记坏块 */
    : R6 d9 t+ J3 A3 @. e; v. s+ r        int                (*block_markbad)(struct mtd_info *mtd, loff_t ofs);& f% x* V& Y) N& p$ G
            /* 命令、地址、数据控制函数 */' X! i, t0 j' s1 O- F. `7 \) ^
            void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    * M0 _$ n- B- ^& A3 n, \        /* 设备是否就绪 */* r) p; l. ^9 {4 D% I
            int                (*dev_ready)(struct mtd_info *mtd);
    ( `) j* E, S% m/ H( t& O        /* 实现命令发送 */( [: m% `3 D0 j' j! Y
            void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);' k. b6 `8 _; j
            int                (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);0 Q  z5 @* }# D
            /* 擦除命令的处理 */
    : ^/ O! M+ p* K& ?, @% @        void        (*erase_cmd)(struct mtd_info *mtd, int page);
    . M' |6 D/ Y* F5 B6 [% H! x        /* 扫描坏块 */# Q, i+ c3 a0 `0 y, w- w9 N
            int                (*scan_bbt)(struct mtd_info *mtd);- I4 i$ Q! p4 _% \8 L1 y
            int                (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);5 O4 B# M1 {7 G- T! s- ^1 ]
            /* 写一页 */8 X6 y8 N- B9 X( \6 c
            int                (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,5 m; C# f! M: U* Z. u" e0 S
                                          const uint8_t *buf, int page, int cached, int raw);  a/ q/ j1 O2 U% O2 x5 U* K7 {
    5 T. N1 t& Z5 }& ^
            int                chip_delay;                        /* 由板决定的延迟时间 */5 X. n9 q$ w* D
            /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
    : `0 S7 k! u2 _, ^4 {0 ?4 D! E        unsigned int        options;       
    & a5 q) |: j. r# l; [+ Q4 c3 x+ c; h8 H" o
            /* 用位表示的NAND芯片的page大小,如某片NAND芯片
    3 }7 i/ e" k0 U% ^% I. f         * 的一个page有512个字节,那么page_shift就是9 8 T) ^  k. t" ~& S4 p( x) Z. R
             */# ^$ ?  R2 u6 J* C4 s5 ^* M
            int                 page_shift;
    3 V$ ^2 o" l5 ?) V        /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
    / @: b! P7 x6 O: _( K" N         * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14. l: z, I0 Y0 w1 P
             */# i" |& e3 \- d' |) P1 H9 f
            int                 phys_erase_shift;% k( Y0 J% ]1 G
            /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
    0 `1 {, R- Q) H, e! S( u5 p         * 所以bbt_erase_shift通常与phys_erase_shift相等
    / n3 c! K" e" T8 |) K+ y* {          */
    $ ^7 Z2 k3 x7 t3 _        int                 bbt_erase_shift;
    0 H; G' i5 c( |; m8 U; T# p! @        /* 用位表示的NAND芯片的容量 */7 U5 E' l, ]' h' _2 B, c. m
            int                 chip_shift;; ]" ^% o! j& }7 E; ~9 ~6 h  \3 ^
            /* NADN FLASH芯片的数量 */# p5 N7 k) K2 ^: y9 R/ a. Y
            int                 numchips;
    6 z* o* m7 `3 A3 y        /* NAND芯片的大小 */
    ; ?: |" J2 {. N& X        uint64_t chipsize;
    , c, Q7 @5 {1 \% n        int                 pagemask;" G' w" L/ x8 Q9 M2 j
            int                 pagebuf;
    / z, j1 O( Q" V8 m! B( X2 ]: {4 B        int                 subpagesize;
    ! {+ |  c, |( U0 E& q. b        uint8_t         cellinfo;
    7 l5 T. a  o4 t        int                 badblockpos;
    * @' `/ |6 Z" d% s1 P        nand_state_t        state;
    0 A; P. |' @+ q* c0 D8 o! V        uint8_t                *oob_poi;9 k& n( ]6 u. c
            struct nand_hw_control  *controller;6 ]1 w6 }! x/ Q. N1 S; P8 D
            struct nand_ecclayout        *ecclayout;        /* ECC布局 */; q9 w( f) p0 _& a. |" x/ G- i, N
           
    $ G5 q5 z, B. c/ O; ^) c9 v        struct nand_ecc_ctrl ecc;        /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    2 [( L. F, }# t. C1 T; Y6 U        struct nand_buffers *buffers;
    8 l3 g$ Z8 m" E- p        struct nand_hw_control hwcontrol;7 j! E8 J  X$ @
            struct mtd_oob_ops ops;  b0 p' D$ l, j, ]. k& b1 \! ^7 M5 n7 g
            uint8_t                *bbt;
    0 U4 V4 N( Z3 h6 J1 s- w) i        struct nand_bbt_descr        *bbt_td;6 }5 j8 c8 S) ]5 f
            struct nand_bbt_descr        *bbt_md;0 H6 X% n; l) ]% o
            struct nand_bbt_descr        *badblock_pattern;7 |. B0 u1 k  [
            void                *priv;
    6 m; q7 ?  I) q. a% s};
    ; r2 S0 P; L4 F7 Y, P1 O: e. a4 \
    ( t# N2 b" O8 Q2 D最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。
    ) Q5 m7 e2 K1 d$ {' S" P% u3 s: y- ^, ]* I% D& q

    1 g9 @& X1 R% f. Y  K. O0 S0 Z9 h
    6 t( }# ]" V" X6 f* c0 N6 I
    5 N$ {  O8 |3 O
  • 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-7-2 20:24 , Processed in 0.093750 second(s), 26 queries , Gzip On.

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

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

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