找回密码
 注册
关于网站域名变更的通知
查看: 279|回复: 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存储器进行了隔离。% r2 p. l+ l1 r- A6 I0 \& O

    " i% F3 L* V6 B. W+ V$ P% q' H 6 q* G- G; I) `
    6 B9 Q0 p* A0 L- l2 X6 \
    如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。  l2 V! \$ ?2 l# v- g6 x' p
      G( x3 W8 @1 \2 X
    Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。1 c9 c# b1 f& j6 _$ \4 N
    6 _0 e5 u2 j, d8 t0 _
    MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。
      ?, f% z3 T, `2 I* }
    9 J( R, G- T1 oMTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。1 S: u) u( W1 k5 l! M
    $ i5 O- ^# d2 l9 t
    设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备 * |3 |9 }( [1 A
    4 Z4 z9 F1 i2 T, e$ c  P5 x0 s
    MTD数据结构:' N3 W# V6 o) V
    - P. z( X" R: {  _. m
    1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:2 q4 q& D/ F" J7 J# z* ?% ], I
    9 q; Q* G, Q* n" r6 A' F

    " S4 [2 N) @5 y7 Cstruct mtd_info *mtd_table[MAX_MTD_DEVICES];
    9 a7 O9 I3 C6 Y: J( g# X$ m9 o2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。
      s7 W7 I$ M# B; k* m4 X* g# j1 r# L2 _7 d# ?+ I4 b6 m1 l7 S
    struct mtd_part {% I; K) e  y  d5 B5 b
            struct mtd_info mtd;            /* 分区信息, 大部分由master决定 */
    % V, g' R9 n" N' t, r        struct mtd_info *master;        /* 分区的主分区 */: r, O. }7 }, \
            uint64_t offset;                        /* 分区的偏移地址 */& E" U' X) g3 j. D: _; I  z: y
            int index;                                        /* 分区号 (Linux3.0后不存在该字段) */
    ) G% m. t6 R* T) H, y0 z        struct list_head list;                /* 将mtd_part链成一个链表mtd_partitons */
    7 s& a+ s) _% s9 ?* _9 D        int registered;
    / q5 e* ?5 T' T};
    & W4 d" t. h3 m% b1 @9 omtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。0 \  m% t' O3 P: x* h2 Z" W1 n) Q8 a

    * X0 @7 Y; v, h7 A7 H# I$ zstruct mtd_info {
    : B7 o: y: s/ [2 o5 ]        u_char type;             /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */
    3 I/ j( G; P) u7 K+ E8 l% a& @6 ]" g2 ^        uint32_t flags;             /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */. s! m8 Q8 q- U, e
            uint64_t size;             /* mtd设备的大小 */
    - V* K. F+ ^# l        uint32_t erasesize;         /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
    , m5 r# Y2 L& {$ j& \" p! ]+ _        uint32_t writesize;         /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
    3 b7 q+ L  M: L5 ~% K, \        uint32_t oobsize;    /* OOB字节数 */& d% o2 T$ D: L2 E
            uint32_t oobavail;   /* 可用的OOB字节数 */
    ' r4 ~+ y# [6 U% m( m        unsigned int erasesize_shift;        /* 默认为0,不重要 */- V2 M4 Q7 p: R  T
            unsigned int writesize_shift;        /* 默认为0,不重要 */
      R& X" A. D- u% |# ?4 _! O        unsigned int erasesize_mask;        /* 默认为1,不重要 */
    / j, ?% \( _& c6 f        unsigned int writesize_mask;        /* 默认为1,不重要 */) ]+ \: @+ Z$ B' ~
            const char *name;                                /* 名字,   不重要*/; P: V$ J  C! w/ z. v3 H6 O# }
            int index;                                                /* 索引号,不重要 */3 c+ W, r6 ^* [5 _
            int numeraseregions;                        /* 通常为1 */
    7 h, B/ V! R* Y, u        struct mtd_erase_region_info *eraseregions;        /* 可变擦除区域 */
    $ ?0 \3 l7 A+ l! y) Q- [       
    4 [8 P: Q0 b" V" f, J        void *priv;                /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
    6 k1 w9 a8 I) N/ P" G        struct module *owner;        /* 一般设置为THIS_MODULE */
    5 s% U3 |3 Q! |5 i* V3 R       
    2 P9 C. n4 l" r8 z1 o        /* 擦除函数 */% B6 I( A, s! E6 E, [% N2 V# I0 v) J
            int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
      A- H/ K$ }$ K4 H7 ]! h- ~6 s+ B+ v3 e* N# }- g. ^6 \, r! V
            /* 读写flash函数 */$ ~* v1 I2 g; p, y2 |5 m" e' I
            int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);. |; T1 @# M  z
            int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    2 S7 w, Y1 ?9 R" e' X) a/ L' |/ l9 I. D6 p
            /* 带oob读写Flash函数 */' T$ I5 G2 j. @3 j# o
            int (*read_oob) (struct mtd_info *mtd, loff_t from,
    - R" ~8 Z; |: p7 e4 y                         struct mtd_oob_ops *ops);
    3 X- A( E7 F) I% V* v! l        int (*write_oob) (struct mtd_info *mtd, loff_t to,! t  T7 R5 u1 p3 ]1 F; ]" e
                             struct mtd_oob_ops *ops);
    7 S) D' X/ l& {$ V0 `) X0 e- K  a, d* t8 ^
            int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);8 q& M. K3 C" f! L3 h1 Q
            int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    * F6 s) e; p( _  v        int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);; z$ g, j. |9 d# j! X
            int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);8 }1 u" g6 D) x8 D
            int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);8 D6 g+ z/ D7 v  W& U0 B) \
            int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);' s9 O+ ^$ j5 \8 `. z/ q

    . \" h. o+ e2 q        int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
    + H: Q: D& Z' I& _        int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    + Y* L' w( }: s$ p4 K  \( W; A2 p% z        /* Sync */  Q5 ?: T0 Y7 ]3 x
            void (*sync) (struct mtd_info *mtd);# [. F  z: Q( T, v$ x; H+ [

    " b$ w6 V6 z. @& i- D0 M6 m! m        /* Chip-supported device locking */
    % G# u# o! L" t" Z        int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    . b: ?. p, L! W+ }1 N% ~9 ^        int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    ( h! b; g- O. Y' Y' N  K0 A. S7 r0 ~7 |+ B) s; x
            /* 电源管理函数 */
    1 X: W8 c$ r/ M4 v! s5 M' f. u" n! N        int (*suspend) (struct mtd_info *mtd);7 o/ Z; g4 Z& Q9 G
            void (*resume) (struct mtd_info *mtd);
    & [' L+ x7 L3 o, q) v7 @; w6 y# E+ w: m# ]9 M+ s9 l
            /* 坏块管理函数 */
    ! h# K! R6 j, S0 H! L* l        int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    ' q- F+ O0 m9 }* w3 ~& c        int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);( }0 N1 [/ a$ z' T8 T0 s

    7 M9 A1 m# m- K7 ]        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);, @* e- H+ [( z* X0 L
            unsigned long (*get_unmapped_area) (struct mtd_info *mtd,0 O8 O7 D$ U8 X3 ^' e5 C& y
                                                unsigned long len,, p# S1 H; G* T
                                                unsigned long offset,
    2 M6 O2 D- P- o# }  [% E7 J7 `  Y                                            unsigned long flags);9 x( _1 C9 E. Z) m* A' ]8 F
            struct backing_dev_info *backing_dev_info;7 \$ E' G1 k$ b2 h4 G- Q
            struct notifier_block reboot_notifier;  /* default mode before reboot */
    3 ?7 q% |  o6 U3 U! v' M' J6 A, ~3 @. |6 _% r: H* i
            /* ECC status information */
    ! u* d: f) D* M, @& f) Q        struct mtd_ecc_stats ecc_stats;
    * ^2 g8 W# j# h" v        int subpage_sft;
    ; o  [( Q' d. F$ p' u0 m2 M        struct device dev;
    - C! P7 ]5 H5 z  v, k        int usecount;
    % p4 F- M0 b8 s1 n        int (*get_device) (struct mtd_info *mtd);8 o2 [& w* r2 @' f& |, }
            void (*put_device) (struct mtd_info *mtd);
    " A5 h/ d6 t: I- k7 O' X0 g};1 L- D: I, Q" z& Y; C! F, G! b
    mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。
    ! d7 m( j4 n2 _/ l* `3 O: Z" c# y5 Y! V如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
    7 o. l# G2 p! e1 B# Z, T5 p* ]9 i- j0 B+ ?$ T' v
    5 E/ P( |8 V- T( Y+ |
    int add_mtd_device(struct mtd_info *mtd)6 X/ C" b% ]/ B& ]( t4 `' w
    int del_mtd_device (struct mtd_info *mtd)
    4 f" [4 q+ }  v8 {7 L' |: z如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。2 J" Y* k9 y% g2 q! c
    int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)4 T& n) B" a- S/ H) R
    int del_mtd_partitions(struct mtd_info *master)8 i3 k" `# h2 R8 c9 Y& ^- I
    其中mtd_partition结构体表示分区的信息1 w/ ]: P1 r5 {/ s2 F6 r
    struct mtd_partition {" k# T! c, i4 E- e
            char *name;                                /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
    / Z, @6 `1 ^, U' l% \        uint64_t size;                        /* 分区大小 */
    % E+ O! d; l  M( _        uint64_t offset;                /* 分区偏移值 */
    8 ^  c" Q+ J  `        uint32_t mask_flags;        /* 掩码标识,不重要 */+ a1 n8 [% z, k6 Z$ `1 ~
            struct nand_ecclayout *ecclayout;        /* OOB布局 */% Q* r) p# R) J' y2 Z( m4 \7 g' Z
            struct mtd_info **mtdp;                /* pointer to store the MTD object */
    3 W; B7 Q9 b: j& B: C2 ?: y};) b* e( x" \( N3 K/ @6 o2 m
    其中nand_ecclayout结构体:5 K' j- L) K9 x, h
    struct nand_ecclayout {
    6 |$ F$ E' u3 p% N) ^        __u32 eccbytes;                /* ECC字节数 */
    ( b" C4 Z# Y9 B+ A$ T  R5 |% y2 e        __u32 eccpos[64];        /* ECC校验码在OOB区域存放位置 */
    " J% T& @, X" ]  v1 a* r' k        __u32 oobavail;                $ I$ s( s  G- v. B8 n
            /* 除了ECC校验码之外可用的OOB字节数 */
    0 n# G, r5 i9 q, L        struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
    1 E2 x, a: [; D4 _' u: n};
    0 P" ^" O* k: M4 g/ r关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。! P$ y3 i6 z5 ~9 S3 J0 I2 o
    MTD设备层:0 f' F/ K  u! d( m" @% R0 `, G
    mtd字符设备接口:
    # e% o* J. g! \% r$ ?8 C8 ?) a2 p  d# ^
    /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)
    7 S7 s9 v& ^. U
    . ~$ {( f: S4 Omtd块设备接口:2 _/ M' W1 e( C/ U; ?' U& {9 z" R4 L

      s0 W8 O8 X! X2 C/ |' n- o  u/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。
    : |# K- `# t# U5 z& `: NMTD硬件驱动层:6 M6 Q$ k% g4 e# ~1 ?
    * n/ j& r* z) E& ]0 ^6 ]- ]& c% `
    Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。6 y8 n- P% Z* \$ n, H7 Q$ L

    % X# `  y* M$ t7 x* C2 n" g# o8 xMTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。( B3 Y- U& J9 e! \

    + ]6 H7 J! q9 z9 m' {+ g7 r/ E! p7 u( |- W: `
    struct nand_chip {4 j% o4 X8 M* f" n  g$ r
            void  __iomem        *IO_ADDR_R;                /* 读8位I/O线地址 */
    5 x1 i" F( w7 R# q2 Y$ H: a" F) k7 h        void  __iomem        *IO_ADDR_W;                /* 写8位I/O线地址 */7 J2 J" Z6 Z' k6 g/ i* C
    ) O1 B! V3 p2 X  i
            /* 从芯片中读一个字节 */
    + @  Z# b% M) B3 l: P, j: Z        uint8_t        (*read_byte)(struct mtd_info *mtd);                % m) v/ A2 V' V6 D8 B$ \! l
            /* 从芯片中读一个字 */" Z) O1 o# f' V
            u16                (*read_word)(struct mtd_info *mtd);                , D. ?+ E! W+ B# @8 ]1 f( U
            /* 将缓冲区内容写入芯片 */! V' b5 G3 ]) O. r* {( X+ m
            void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);          {# K/ Q- P' B* T
            /* 读芯片读取内容至缓冲区/ */
    & m. `$ l4 a. z1 s; Q        void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    # d* K' o0 K1 W; \        /* 验证芯片和写入缓冲区中的数据 */7 r4 _: ^7 G6 e7 h  h
            int                (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
      I0 u7 N/ g( Y+ O4 Q; ]        /* 选中芯片 */6 t  d% R% S9 H/ h: K5 q( |
            void        (*select_chip)(struct mtd_info *mtd, int chip);
    2 Y1 W! Q6 Y) X$ v' x6 o        /* 检测是否有坏块 */
    - j# k1 s! y, w) j: X4 V        int                (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);% f& Q9 g+ n4 t* o" L8 j0 W
            /* 标记坏块 */. [0 U2 n) |/ `$ v+ R
            int                (*block_markbad)(struct mtd_info *mtd, loff_t ofs);0 D3 ^' y) R: C8 m3 J6 ]
            /* 命令、地址、数据控制函数 */7 h$ D$ L  y, \  x% u
            void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    7 k7 s* T+ A! P        /* 设备是否就绪 */2 `! |8 d- {  A4 Z) a
            int                (*dev_ready)(struct mtd_info *mtd);
    9 T* ]) d* [. I        /* 实现命令发送 */
    - b7 ~8 c+ T* g4 V' y: K- X        void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
    4 T# a: Y' P: b+ Z% g4 x3 H        int                (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    * E% F4 A* c6 O, q* J* j        /* 擦除命令的处理 */
    ; c+ h0 |* F( l- d7 h7 T" l        void        (*erase_cmd)(struct mtd_info *mtd, int page);
    4 u. G& v' Y8 L& E# |$ S        /* 扫描坏块 */; J. z" i9 {/ I2 A, i7 }
            int                (*scan_bbt)(struct mtd_info *mtd);
    4 n: m& D) D7 U* S: ]        int                (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
    ! ^* Z* Q. e1 l& {5 Y9 U1 O        /* 写一页 */0 G: \# b3 y" {% y7 r) M
            int                (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
    - h! Q' E, T" D. I8 q. S                                      const uint8_t *buf, int page, int cached, int raw);& u2 c2 |2 h- C; E, n
    ) p6 L* p" s+ C5 \* W4 X
            int                chip_delay;                        /* 由板决定的延迟时间 */
    8 B  u' N# ]* V$ ]  A/ t* n- L        /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */
    1 W( q4 S7 v; ?$ v8 p: A" D( ?, O        unsigned int        options;       
    9 b# R% K  e: O/ ^
    . j, c1 i' B+ ?' n, Z% T0 }        /* 用位表示的NAND芯片的page大小,如某片NAND芯片
    " L5 r+ z4 ~5 C( o; S, u         * 的一个page有512个字节,那么page_shift就是9 8 [8 y* U# h* N
             */
    ! c5 B2 V4 O! A        int                 page_shift;
    # w  u- J( m- \: m3 d; d6 p6 `7 z        /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可  q, Z! ?. R/ G2 U
             * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14( ]9 g! v3 Y6 d0 I" z
             */- d# q* @# f) ?$ Z
            int                 phys_erase_shift;
    " I) [1 p% a8 s0 ?) h. }8 X1 y9 {        /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
    6 q* d7 c8 B0 j9 t3 e7 E3 ^7 Z         * 所以bbt_erase_shift通常与phys_erase_shift相等
    % H5 `5 D$ d1 c9 J1 _# K$ C          */- X9 L3 N/ Y# L% X* W
            int                 bbt_erase_shift;- Z. M- d2 {2 U
            /* 用位表示的NAND芯片的容量 */8 K& P+ c: i' k
            int                 chip_shift;! v9 e9 g2 K3 c& \) \
            /* NADN FLASH芯片的数量 */
    ; ^! q- s  u! v9 T        int                 numchips;
    * u+ o2 Z! ^% f, t        /* NAND芯片的大小 */3 c& t" ^, Z. `
            uint64_t chipsize;" S6 q6 H+ Z+ A7 Z( f; O& B) e
            int                 pagemask;
      |4 X- `9 _5 X  e) x  M        int                 pagebuf;. J9 t% P+ z9 i% B# O( F
            int                 subpagesize;
    . s3 J  ?( L" p% q+ H; @* P- i8 B        uint8_t         cellinfo;
    " D& Z* m- O& q4 s1 s        int                 badblockpos;+ ]: t# G! \9 n0 y& a
            nand_state_t        state;
    " `4 b" r- {6 V; m        uint8_t                *oob_poi;8 H- a1 `8 E( i. G; ~/ O3 f1 }
            struct nand_hw_control  *controller;
    5 e0 I7 V+ K; C- U        struct nand_ecclayout        *ecclayout;        /* ECC布局 */
    7 H* j. N1 [8 E% v" r$ h        ( f5 {, s5 n8 \( h1 f
            struct nand_ecc_ctrl ecc;        /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    5 E. Z, b4 x* ^# L9 G        struct nand_buffers *buffers;
    % }8 _4 g: v# j/ `5 O  ^0 c        struct nand_hw_control hwcontrol;
    - v$ O4 W- D2 q/ s6 n; I( A! g5 B2 P        struct mtd_oob_ops ops;! Q# `' o. n0 c. b
            uint8_t                *bbt;9 T) ^' o- E- {! w% t
            struct nand_bbt_descr        *bbt_td;7 }' k9 U/ i+ u6 U0 g3 Q) @* o. p& D
            struct nand_bbt_descr        *bbt_md;1 h7 g; A4 r: L' n4 P
            struct nand_bbt_descr        *badblock_pattern;
    0 m6 I' D  e4 [        void                *priv;
    / }3 D1 {. G0 B& T7 \};( v! k+ q' E; Q1 ?

    % g6 m- b8 V0 V- C9 c最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。
    : ?# I4 m, P7 _& U4 P
    4 ?' p$ g% b0 c% S
      w  R0 o: y* p* o+ \0 X% g6 @% K% r) T

    7 c6 z" C; u3 m+ ~
  • 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-26 02:06 , Processed in 0.156250 second(s), 27 queries , Gzip On.

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

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

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