找回密码
 注册
关于网站域名变更的通知
查看: 273|回复: 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存储器进行了隔离。( p; a! |" B! W% W" r

    0 K) i) t) @9 G5 ]- i) O( V 4 r3 U+ ~- j# C, }7 F. ^$ E" y+ W
    7 a  U! n3 \; N
    如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。
    1 N  b1 T' W& Z3 |
    1 Q" |; ~& G$ k, h) a# hFlash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。: ]- l- J+ e$ {( @" h5 ?  u
    ' e! z9 A1 T  m! \
    MTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。% p- |2 n) w7 {1 k5 y, o
    5 k* K& n. E  P: Q
    MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。, t1 K* w2 {! o  E
    ) M& h8 S- ?# r4 m
    设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备 # p2 B. h7 r* t6 D6 V! d6 z
    ' v' k1 \% y% }* k/ I9 k3 Z/ H
    MTD数据结构:* n2 K  p5 t  Y  d$ ?' O" E8 K3 @

    , x) A- Z( i) E7 ^9 e6 ^1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
    / ^) Q8 B) G8 h. \' @* J% Z$ {- y! T
    9 v' C) W* ]$ D) E" I. d
    struct mtd_info *mtd_table[MAX_MTD_DEVICES];
    , V8 A2 o" c4 b2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。6 h& L/ ~2 ]3 C6 D6 s0 J. p

    6 R7 E1 Q$ a: [: jstruct mtd_part {! c; M8 X' [, n; r7 L
            struct mtd_info mtd;            /* 分区信息, 大部分由master决定 */
    $ B) O$ _% a- O* N# e% {        struct mtd_info *master;        /* 分区的主分区 */2 g1 d7 v7 L9 l7 {; w, c
            uint64_t offset;                        /* 分区的偏移地址 */
      J6 |  J; {( u' d7 P        int index;                                        /* 分区号 (Linux3.0后不存在该字段) */+ V) m/ Y8 O" D
            struct list_head list;                /* 将mtd_part链成一个链表mtd_partitons */# j  j: J' {; u
            int registered;
    + N: t& a, ^5 Y) X6 [  K0 Y; C9 t};
    / h3 d% v! ^' I/ T: O$ V5 gmtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。
    ) F: s  q; E) {/ s' e" E+ |8 i
    # A5 k; O" p$ |) q0 w3 cstruct mtd_info {! e* D1 |  S% Q3 ^6 ]
            u_char type;             /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */( u% `! g# c! R3 T4 P
            uint32_t flags;             /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */
    : ^' O. h; v! H' q3 A* {& S        uint64_t size;             /* mtd设备的大小 */: s+ c+ ~, N9 U# s, M' ]6 p
            uint32_t erasesize;         /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */
    ; i& R* t) X, p" f        uint32_t writesize;         /* 写大小, 对于norFlash是字节,对nandFlash为一页 */
    ) P- q! ]: K, }: K        uint32_t oobsize;    /* OOB字节数 */
    , ?+ b9 _! I+ Y        uint32_t oobavail;   /* 可用的OOB字节数 */
    + M+ R2 I1 o  w/ g3 q        unsigned int erasesize_shift;        /* 默认为0,不重要 */
    - N' a: w  v7 x; X6 b9 m" c        unsigned int writesize_shift;        /* 默认为0,不重要 */
      S6 H/ A/ ^- v$ R) N        unsigned int erasesize_mask;        /* 默认为1,不重要 */
    / z; A  e  B0 N$ d0 S        unsigned int writesize_mask;        /* 默认为1,不重要 */
    1 X* D+ N- [& z3 B: I        const char *name;                                /* 名字,   不重要*/
    : A4 D+ ~1 m  ^7 V; ~8 y        int index;                                                /* 索引号,不重要 */" u; I$ O8 H+ y; u5 G
            int numeraseregions;                        /* 通常为1 */
    . O  H% E1 ]% L        struct mtd_erase_region_info *eraseregions;        /* 可变擦除区域 */
    1 x) V7 V$ |# `$ C# e" K7 e       
    1 C* ~3 M9 N9 P; G" w* ^6 s0 }3 S        void *priv;                /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */7 i6 t8 {2 q! G9 p
            struct module *owner;        /* 一般设置为THIS_MODULE */
    $ c% `7 r2 c1 U4 A        ; F; |0 n2 h/ p( Z) ?& z7 c
            /* 擦除函数 */
    4 d" J. P8 S9 W$ B0 h0 b- Y        int (*erase) (struct mtd_info *mtd, struct erase_info *instr);' @' d9 U8 _5 ~

    $ A! V/ d. j* z! {* i) @        /* 读写flash函数 */
    # C/ J% K4 s4 l0 V1 r+ {        int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);) K/ T0 O; e. N1 R/ o/ L
            int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);- K( S+ ~6 ?$ M& Q# G
    6 Q; z1 v9 X3 C+ E! A; @7 d$ U
            /* 带oob读写Flash函数 */5 x2 K8 u6 L/ ~5 u
            int (*read_oob) (struct mtd_info *mtd, loff_t from,
    ' e$ U9 [  ]1 j6 b7 Z* V/ o, w' }: p                         struct mtd_oob_ops *ops);
    ) @2 _6 t1 D: u5 n7 _" G        int (*write_oob) (struct mtd_info *mtd, loff_t to,
    8 p- v5 C! {* I) p0 Y3 p                         struct mtd_oob_ops *ops);
    1 x$ E$ v  \- N% c
    & |2 v+ v3 m0 V  A4 k        int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);  ]+ D! T0 C0 G7 M
            int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    + N) l7 d8 H# g2 i% d5 V        int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);' u. l6 m' `, _- h" u
            int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);9 e% R7 c) U6 h6 ^+ N" G
            int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    / ]/ D% a: |* }+ O- a        int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);( Z" M2 j% F" S- J7 ]5 M. y9 [9 X* e0 }

    . K% n0 L( B; U5 o, ]/ O        int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
    ; s. e2 n+ |" A5 J        int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
    7 w( ]- j- y5 D8 \/ e        /* Sync */
    2 [5 G3 F" c3 V1 z! l. e. X        void (*sync) (struct mtd_info *mtd);
    * D5 J# U6 L0 Q6 Y* w/ ~8 R4 o. M
    3 M5 {+ U( p8 J, w: N1 G        /* Chip-supported device locking */2 ?1 A% `/ I, E, o6 m( x
            int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    5 u  J6 I" x) P0 H0 F2 v  B5 E; X        int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    . f# ?: r$ U6 y7 U0 k; r
    7 g2 F6 o$ ~5 l5 {        /* 电源管理函数 */: o1 e  r2 P: b$ W
            int (*suspend) (struct mtd_info *mtd);3 s* {; s+ k$ O8 p, o. A5 j  |) @
            void (*resume) (struct mtd_info *mtd);2 \% H* ?: Q% G5 n& |
    7 A+ ?  \& t* |3 K/ t+ x; O
            /* 坏块管理函数 */5 P: T. s+ Q: s7 l1 L* B% E
            int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);% q) `8 `& p! U) y! Y
            int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);: A0 H+ r$ a7 ^, F

    1 ~2 K& S4 z3 ~6 L+ |        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);9 U; ?3 I% O3 a* \  x
            unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
    5 Z5 A2 y  q2 g' {' y/ Z                                            unsigned long len,% n; `: s2 r3 \+ A
                                                unsigned long offset,
    8 B5 a- ~, t' w' ]% x* c                                            unsigned long flags);
    0 d# A" E8 h% }6 f& M- i        struct backing_dev_info *backing_dev_info;+ B' `# h. ?4 x; y3 }# ^4 I# t( o
            struct notifier_block reboot_notifier;  /* default mode before reboot */
    ! L: {$ Z! C2 q8 l, d
    $ G' A. ?% R  D" c/ v3 {& K        /* ECC status information */
    & l. r' Z! E, X6 v' M        struct mtd_ecc_stats ecc_stats;
    ) ]2 N, z7 @" C4 U        int subpage_sft;; ~: Y1 a8 M+ U% R; x7 @
            struct device dev;
    0 O! R% a6 Q: r. a% W9 c        int usecount;
    2 Q4 B3 o2 R& F1 S* h+ g        int (*get_device) (struct mtd_info *mtd);6 ~' g; g( F/ G3 `$ q9 L" j
            void (*put_device) (struct mtd_info *mtd);
    6 m! k+ m+ y% U& I};
    7 J6 k* X/ |8 r! i# M3 k- smtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。
    9 F: _) v( g6 _% u- S2 F如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
    : l* W, \$ W! N! o$ ]( E& h$ R% u+ E  v" I
    % j; i6 }# a3 u3 v' i, N
    int add_mtd_device(struct mtd_info *mtd)
    % y8 K# W7 F- [. n0 y9 {1 Lint del_mtd_device (struct mtd_info *mtd)6 J/ V. B! Y: b  S$ ~6 Y
    如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。- O5 \9 j3 P$ K3 j( y$ q/ R
    int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)
    - ]) h" l9 o! q3 ~5 M5 dint del_mtd_partitions(struct mtd_info *master)
    ) G' J2 r' h  `6 J其中mtd_partition结构体表示分区的信息* R: D4 h; y; O9 M2 G' h! o. q
    struct mtd_partition {
    : p1 Z1 [9 }$ k4 K        char *name;                                /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
    ! \* B  T7 \  h7 {4 ~9 J1 }. k( s        uint64_t size;                        /* 分区大小 */" V- C( \; y5 t& n& W1 U
            uint64_t offset;                /* 分区偏移值 */
    1 d4 n  \! @& c7 e4 x5 M        uint32_t mask_flags;        /* 掩码标识,不重要 */
      z! W8 b9 o6 W" K, {        struct nand_ecclayout *ecclayout;        /* OOB布局 */  a2 D4 q( Z2 z% c
            struct mtd_info **mtdp;                /* pointer to store the MTD object */
      \- \' |" O7 b8 M: T};
    / M% A8 e2 q8 A2 m' Z( z其中nand_ecclayout结构体:& f6 l: }+ O; R! Z& F2 D5 M
    struct nand_ecclayout {
    8 \6 Q( l6 D* P6 F. H7 {3 m        __u32 eccbytes;                /* ECC字节数 */" U: k& U( [0 C7 D( l
            __u32 eccpos[64];        /* ECC校验码在OOB区域存放位置 */
    " m6 d' b. ~: x# Z. i        __u32 oobavail;               
    ' b- I3 J5 W( w2 D        /* 除了ECC校验码之外可用的OOB字节数 */; Y" Y+ ]1 K. Q& y. s' C6 t
            struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
    % {/ j+ `' `, s( K& y' h8 z, n};( X# E7 h# K. _
    关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。4 M7 M9 _  D. G9 K
    MTD设备层:
    3 P3 U: _2 k* x5 n" Y7 \mtd字符设备接口:
    + Q) z2 _( T# X! k( D
    % G8 i% {. O4 Q- X7 r# G7 M& Z/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) # g' I9 G$ k1 J( D
    ( x9 w) |  m+ S, }3 y6 i
    mtd块设备接口:1 S% F* w/ l8 v/ ~

    , ~6 ^3 M7 x, p8 |( d$ ]/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。
    0 c9 A5 [: J% \1 m2 ]MTD硬件驱动层:
    ' U2 X3 F& [" ~8 c
    1 L% f% L. F$ Z: a, x& K+ TLinux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。9 i. K: V$ o1 z5 w0 A4 f, E
    2 c) a$ h: F0 c& P( u+ J
    MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。
    / c6 r9 B( }* l0 f
    " ]  B7 d: V- P  E0 R4 H
    0 H8 ]" Y" D) S+ H: z: }struct nand_chip {
    ) i# J; l5 M) x( }        void  __iomem        *IO_ADDR_R;                /* 读8位I/O线地址 */% L5 o$ E$ s6 d' J% P
            void  __iomem        *IO_ADDR_W;                /* 写8位I/O线地址 */' ?$ q  e% Z5 J3 T# C5 ~, C

    & j/ p8 O- E( s& M  o! o        /* 从芯片中读一个字节 */) F4 i  Y3 \) p- B! [" Y- v5 m6 K/ Q
            uint8_t        (*read_byte)(struct mtd_info *mtd);                . n( j! s; w4 z2 F. L
            /* 从芯片中读一个字 */. X( ?7 r; s; s# B5 B  t
            u16                (*read_word)(struct mtd_info *mtd);                9 F9 f- s7 }6 P: Q) D( Y
            /* 将缓冲区内容写入芯片 */. i1 _' _; k( Y  T! [! y7 J* v* t
            void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);        : z& t0 G" A, }0 A0 l$ e
            /* 读芯片读取内容至缓冲区/ */
    ! [6 t' E) U" n, A& I" {/ _0 L( R        void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);) }( d) z6 w9 Z7 W+ D
            /* 验证芯片和写入缓冲区中的数据 */
    9 T& ?/ @, ^# ~# s% H) ^7 x1 z$ Q        int                (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    ' y4 c/ ~" {1 T! d- [: a        /* 选中芯片 */
    # ^2 v3 ~- o. b7 `  f: p7 W& O        void        (*select_chip)(struct mtd_info *mtd, int chip);
    1 @  F+ m: n0 l( I6 ^        /* 检测是否有坏块 */
    , {8 [/ I% g, ^. b( `2 q8 C        int                (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
    2 S: V7 X- X$ _5 x; f# j        /* 标记坏块 */1 t6 B5 z9 l9 h$ B+ E; B
            int                (*block_markbad)(struct mtd_info *mtd, loff_t ofs);# O( p. b0 r9 _0 e5 z  O* C
            /* 命令、地址、数据控制函数 */  W& j% d0 P+ e3 |  n; y. F
            void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);
    9 Q+ w7 w) ]4 J2 ~7 R  Z0 u: A/ u        /* 设备是否就绪 */
    ! t* ?$ s& g, `) i, O        int                (*dev_ready)(struct mtd_info *mtd);* j. @8 Z+ {: Y* T* V" Y
            /* 实现命令发送 */
    4 z. f" w0 \' d. C        void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
    8 i) c" e, ~+ ~. ^        int                (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    " r( P% d& G9 @9 k& e( k        /* 擦除命令的处理 */* Q- b' c* W/ Q1 }+ U) a3 ^
            void        (*erase_cmd)(struct mtd_info *mtd, int page);+ ?" H% O' G% Q( i
            /* 扫描坏块 */* y' p- O3 D$ J
            int                (*scan_bbt)(struct mtd_info *mtd);, Q3 E0 M1 }6 h. P$ W! I
            int                (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
    7 H) a6 L- k8 B) \        /* 写一页 */
    2 B/ w" m1 b) Y' O" \, {# o: r' w        int                (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,4 _% d5 b) ]$ y7 j
                                          const uint8_t *buf, int page, int cached, int raw);
    * R7 s* W2 K/ Y1 }3 t3 W3 E
    + J4 u* j* ^1 y& f$ u% L! G        int                chip_delay;                        /* 由板决定的延迟时间 */# q8 G5 K1 {% e1 O: @- Q
            /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */6 ]# K2 t) L5 l: i% H) j
            unsigned int        options;       
    7 I- F; s( m3 y( y$ t5 M, N' P5 S) V3 W& r
            /* 用位表示的NAND芯片的page大小,如某片NAND芯片
    , I" C! b1 d$ ]# h, d% u         * 的一个page有512个字节,那么page_shift就是9
    # r" R  X# o  }! [  y' A! a/ N         */7 |8 O( n1 G/ i1 R
            int                 page_shift;2 c' p0 z9 V" S& e
            /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
    - |# p: z4 L2 I! k         * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14
    : G0 J$ f; p* M( f/ Y6 W  {         */
    & i8 n; i$ u4 Q% c        int                 phys_erase_shift;
    7 p$ E  r  R" z1 U( l- X" S) Y! h        /* 用位表示的bad block table的大小,通常一个bbt占用一个block,
    ) S$ \' k# E+ {/ t7 a/ ]; Y         * 所以bbt_erase_shift通常与phys_erase_shift相等
    8 E& X  F0 V; a" g( L+ N; l+ t          */; i8 Z) B( M3 ?0 H# j+ e0 I
            int                 bbt_erase_shift;
    2 o6 C2 y/ H# V3 ]4 ~6 O( O* Z        /* 用位表示的NAND芯片的容量 */
    & S+ X7 W4 n6 ^) S- _3 h* z0 [$ r        int                 chip_shift;! S! I5 W- n$ P; C0 j  o9 ?
            /* NADN FLASH芯片的数量 */
    ' @: N5 B! I0 u4 x1 R' x1 J        int                 numchips;7 k* Z  ^2 z) p; D5 K
            /* NAND芯片的大小 */
    ) f0 |$ a: x% A        uint64_t chipsize;
    ) p" ?  c/ ~( v1 V* _        int                 pagemask;
    ) b+ {' ^1 Z9 |; o        int                 pagebuf;
    - O/ i( \2 J2 Z& [) U- L' O/ B        int                 subpagesize;3 ], G3 N$ a6 @$ g. S+ N8 [2 l
            uint8_t         cellinfo;
    0 S4 V  [( Y7 K/ c4 d/ B        int                 badblockpos;
    1 Y$ Y5 y  M8 S8 H; o9 G- x        nand_state_t        state;
    - L: ~/ O" E; n+ w! `9 s0 R/ g7 l        uint8_t                *oob_poi;
    - y/ ?' }" K# A% ~( u9 a* T        struct nand_hw_control  *controller;
    : n6 a! p6 X! O, N6 z" K        struct nand_ecclayout        *ecclayout;        /* ECC布局 */2 Q. @# e+ p* D) g% d2 p; K
            - t# }4 c& ^" q3 t
            struct nand_ecc_ctrl ecc;        /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    ! _6 {$ M3 U7 R( h4 m5 s2 v        struct nand_buffers *buffers;
    0 Y5 r" D, c) E0 I( o$ w        struct nand_hw_control hwcontrol;! `: X; w2 D& `) x# p$ U
            struct mtd_oob_ops ops;
    2 C+ g  r* ~0 w        uint8_t                *bbt;7 M; F5 j% W; n
            struct nand_bbt_descr        *bbt_td;* A# \& F. E+ _1 f* W8 _
            struct nand_bbt_descr        *bbt_md;
    8 J& Z8 b- x1 S- d2 @3 c. ^        struct nand_bbt_descr        *badblock_pattern;
    # r" b) h/ Z6 [* q+ e  I        void                *priv;7 ]# {# E& E% ~1 K
    };- B  Y& h  I2 _8 J$ r2 ^

      x1 ], j" K9 O: L8 U最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。
    - O5 |; G% o4 c/ x: u: C) H6 J; L8 f  g, |0 d! b& u3 V* h

    7 b4 h+ @- {. h7 D8 ]5 S/ T- C) c7 x, R. k  ^; _
    ( P3 i* D! E* x# B: P" b4 g, {8 ?
  • 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-10-26 00:13 , Processed in 0.156250 second(s), 26 queries , Gzip On.

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

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

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