找回密码
 注册
关于网站域名变更的通知
查看: 277|回复: 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存储器进行了隔离。% L3 \! t$ }: G1 ?; O
    8 @- J( l3 _' O1 S8 \9 ?8 ^
    ; l' J# D4 J0 ?* {: u+ y1 n7 f
    + K, l6 N# e# Z) B8 s( l0 l
    如上图所示,MTD设备通常可分为四层,从上到下依次是:设备节点、MTD设备层、MTD原始设备层、硬件驱动层。' H) r8 f7 K1 O. N
    5 N! D+ A$ k6 \
    Flash硬件驱动层:Flash硬件驱动层负责对Flash硬件的读、写和擦除操作。MTD设备的Nand Flash芯片的驱动则drivers/mtd/nand/子目录下,Nor Flash芯片驱动位于drivers/mtd/chips/子目录下。
    . H! G6 H7 K  |/ P# [
    * ]3 G2 u  o- R8 UMTD原始设备层:用于描述MTD原始设备的数据结构是mtd_info,它定义了大量的关于MTD的数据和操作函数。其中mtdcore.c:  MTD原始设备接口相关实现,mtdpart.c :  MTD分区接口相关实现。
    8 @8 A5 y8 F/ E- D
    $ Y; e9 s" K; w; H9 S+ @5 A4 aMTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。其中mtdchar.c :  MTD字符设备接口相关实现,mtdblock.c : MTD块设备接口相关实现。; J7 J5 m# l" J% m& q. }9 t+ d
    9 ]9 Y6 h8 \( b0 D. H6 P
    设备节点:通过mknod在/dev子目录下建立MTD块设备节点(主设备号为31)和MTD字符设备节点(主设备号为90)。通过访问此设备节点即可访问MTD字符设备和块设备   X8 k3 a' K" T& U8 p0 Z

    8 G" D; t- Z: b: Y- \) MMTD数据结构:
    3 m2 J- N" c& \  D6 l  ?3 u/ P; F7 k" B8 v
    1.Linux内核使用mtd_info结构体表示MTD原始设备,这其中定义了大量关于MTD的数据和操作函数(后面将会看到),所有的mtd_info结构体存放在mtd_table结构体数据里。在/drivers/mtd/mtdcore.c里:
      _& V1 I. S. X3 j/ c% c1 h/ h6 y  |) `( |$ B
      H1 p2 \1 l8 y' O' K
    struct mtd_info *mtd_table[MAX_MTD_DEVICES];' g& F& Y* Y- L# T* Q0 b
    2.Linux内核使用mtd_part结构体表示分区,其中mtd_info结构体成员用于描述该分区,大部分成员由其主分区mtd_part->master决定,各种函数也指向主分区的相应函数。
    , G- p$ p$ C7 ~2 H8 T/ ^
    , v+ W& H2 C# Z: A* ^struct mtd_part {$ ^9 T8 r* f9 f5 C! w) ~
            struct mtd_info mtd;            /* 分区信息, 大部分由master决定 */, `9 h" R3 e, u2 v0 N, t: `
            struct mtd_info *master;        /* 分区的主分区 */
    5 H' M& h+ T0 A9 ^        uint64_t offset;                        /* 分区的偏移地址 */+ U* D1 L( s: h0 L7 E
            int index;                                        /* 分区号 (Linux3.0后不存在该字段) */
    * P) {9 n2 F; L, L        struct list_head list;                /* 将mtd_part链成一个链表mtd_partitons */
    / Z2 k) F% W; f7 U1 T        int registered;
    3 C& @9 R7 _$ d+ V' `; t0 j7 e};' A$ D4 ?, x1 R8 G  z- @* r
    mtd_info结构体主要成员,为了便于观察,将重要的数据放在前面,不大重要的编写在后面。
    * [& @: M! D  o$ W- y" v  u
    2 ^: p/ |! O" R5 Qstruct mtd_info {4 t$ k% l# i% t, {& C- ^
            u_char type;             /* MTD类型,包括MTD_NORFLASH,MTD_NANDFLASH等(可参考mtd-abi.h) */" `" a/ E2 f6 {; s$ c) Y5 Z5 i# {9 }
            uint32_t flags;             /* MTD属性标志,MTD_WRITEABLE,MTD_NO_ERASE等(可参考mtd-abi.h) */6 z* }6 r% x4 ^' Q+ L( o0 l/ T
            uint64_t size;             /* mtd设备的大小 */; w6 T0 ]5 V8 @/ {: B$ Q. B
            uint32_t erasesize;         /* MTD设备的擦除单元大小,对于NandFlash来说就是Block的大小 */2 |: m, @8 J2 @+ n3 B
            uint32_t writesize;         /* 写大小, 对于norFlash是字节,对nandFlash为一页 */. t' V9 t+ Z. h5 b: G' R& O8 C. g
            uint32_t oobsize;    /* OOB字节数 */
    % n6 u6 e7 g* F        uint32_t oobavail;   /* 可用的OOB字节数 */' h2 ~, m) W# q/ R/ Z* K
            unsigned int erasesize_shift;        /* 默认为0,不重要 */% f( u- s: H$ T& [# }( N' T0 m
            unsigned int writesize_shift;        /* 默认为0,不重要 */
    + ~! D5 e0 C7 g& H% y% t        unsigned int erasesize_mask;        /* 默认为1,不重要 */
    , Y3 D4 A1 O9 j5 C% t        unsigned int writesize_mask;        /* 默认为1,不重要 */
    & x: g! d5 O9 @4 {        const char *name;                                /* 名字,   不重要*/
    + Y* ^& [' }8 Z5 x/ k. Q& f4 l        int index;                                                /* 索引号,不重要 */" @" Z0 B! C* a* s) c% s
            int numeraseregions;                        /* 通常为1 */
    4 B9 h5 m9 f9 t  }/ r' ], c; `        struct mtd_erase_region_info *eraseregions;        /* 可变擦除区域 */
    # w. G# s; v& o4 W       
    " Z* ?4 |7 C9 J        void *priv;                /* 设备私有数据指针,对于NandFlash来说指nand_chip结构体 */
    6 Q+ Y) q+ A# K% d! W        struct module *owner;        /* 一般设置为THIS_MODULE */
    ) B4 I, H8 D. k1 [        - [' U% o# ~/ J1 i4 A
            /* 擦除函数 */
    % U) z. O: F& P, V3 v) s& G( ~7 K* \        int (*erase) (struct mtd_info *mtd, struct erase_info *instr);5 L7 w6 b0 q+ J# [, e. J9 o

    * f+ A8 N7 L5 O; M: u        /* 读写flash函数 */  g, q  Q; H" ~( i! h
            int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    ! X: c8 |4 t' p5 O2 z7 H1 Y        int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);! _9 y- z$ o8 u" c

    % F/ M! l7 d; _        /* 带oob读写Flash函数 */
    " C" T- R. ~2 Y8 L" U        int (*read_oob) (struct mtd_info *mtd, loff_t from,( u6 {0 X: H+ S0 v
                             struct mtd_oob_ops *ops);2 f& ^, o. o1 [7 K+ f, Z# V
            int (*write_oob) (struct mtd_info *mtd, loff_t to,
    ( m: S- x8 g& z                         struct mtd_oob_ops *ops);
    - i) y  l& R% F( n4 A" G+ B9 f" s& {0 T8 |* q
            int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);. g+ l$ I$ @0 S: w( L# N
            int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
    % r; y- @6 H( R/ p        int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);' r, E& }2 x9 m  I) o2 Q9 W( Q, s
            int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);$ W& B: ^$ r7 k' ^/ ]+ F! p8 K* Z
            int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);/ @; B1 w, J2 a% P+ J  r7 [
            int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);, {, K: ?7 i" C

    & \* c  r2 r- z7 K1 \& u2 a! X        int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
    $ U, R1 S, M: S; L6 V6 Q! c6 s        int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);! x4 o9 Z3 R  p9 T: B# E) A1 L
            /* Sync */
    ) Q( r0 Q# c- C/ K9 Y0 g4 e        void (*sync) (struct mtd_info *mtd);  a) C- K- ^5 j0 U( ]  k0 ~

    - @; P5 w. b  u6 c' z        /* Chip-supported device locking */
    ; T" W2 X0 q$ ]: b3 O! t2 a        int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);/ ?$ G1 y0 O; G$ b, `
            int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
    8 g) k- w* s4 N  j6 _" d  E( Y  M' v
            /* 电源管理函数 */
    & \+ ^& U6 H7 k( E        int (*suspend) (struct mtd_info *mtd);
    + v7 V6 {' b, B# G2 g! ?" B        void (*resume) (struct mtd_info *mtd);
    / ?  H% F' q0 o9 @- g" F) ]% S% m* n, k2 K8 C6 ~
            /* 坏块管理函数 */
    1 F' ~7 N, w% F* ^, j5 G        int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
    " n7 U4 [& M( u        int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
    : r2 F7 R$ M5 j/ J7 N" [4 @
    - p9 x7 O+ @5 z9 w( Y7 @0 A3 V& F        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);+ {9 |. `( b* \, M0 I
            unsigned long (*get_unmapped_area) (struct mtd_info *mtd," L. A! {1 H9 ]0 m
                                                unsigned long len,+ V. h& |$ X3 i& i7 {4 C
                                                unsigned long offset,
    * ]  ?; d# t4 x* ]) G                                            unsigned long flags);
    " y4 Z* n7 A2 e2 @+ T# Q) D/ t        struct backing_dev_info *backing_dev_info;0 f% r: ^, i+ @! K% l
            struct notifier_block reboot_notifier;  /* default mode before reboot */2 x# F# |& n2 c; `. Q# H
    : Z+ ^1 X5 F) a3 ]) i
            /* ECC status information */
    ) L/ j- u; x, ~; \1 y        struct mtd_ecc_stats ecc_stats;
    ) R, U. K" G3 A        int subpage_sft;
    : L6 c/ n7 V3 e) _6 h; D# y        struct device dev;$ c9 b  L: k# @( S$ f
            int usecount;
    6 Y, p1 K0 P7 d3 p  p, y; v' ?        int (*get_device) (struct mtd_info *mtd);% K0 t  h$ a% c' Y3 V; k
            void (*put_device) (struct mtd_info *mtd);/ B# T5 ]! S  t) u; _' a) X
    };9 m* d5 O  D$ O+ L5 b" ]2 T
    mtd_info结构体中的read()、write()、read_oob()、write_oob()、erase()是MTD设备驱动要实现的主要函数,幸运的是Linux大牛已经帮我们实现了一套适合大部分FLASH设备的mtd_info成员函数。
    ) @, ]; \& C- {/ @0 A% P9 c' N2 ]如果MTD设备只有一个分区,那么使用下面两个函数注册和注销MTD设备。
    0 L; D( G4 ^# d) ~& K% c$ a7 V5 ]  \7 Q$ w
    $ T! f) B: T) J- P6 F) f0 a" ~# G/ m
    int add_mtd_device(struct mtd_info *mtd)" |+ r- L  s! b* M$ z# o$ r
    int del_mtd_device (struct mtd_info *mtd)
    * r+ a6 X' @6 n1 E0 e) H如果MTD设备存在其他分区,那么使用下面两个函数注册和注销MTD设备。  C" i' P& i3 H* `6 F( _$ R2 f# l
    int add_mtd_partitions(struct mtd_info *master,const struct mtd_partition *parts,int nbparts)" K' Y" O3 g7 g* X. U
    int del_mtd_partitions(struct mtd_info *master); g* p4 j& {8 T! t( ^$ B
    其中mtd_partition结构体表示分区的信息
    , w1 ?4 t: i6 p, g3 X; r4 vstruct mtd_partition {: |$ b% T( \8 `. L; B. G: P
            char *name;                                /* 分区名,如TQ2440_Board_uboot、TQ2440_Board_kernel、TQ2440_Board_yaffs2 */
    # q) c. i! T: }3 }/ U! h        uint64_t size;                        /* 分区大小 */
    , R9 I7 N. G! G- T& P3 f! d        uint64_t offset;                /* 分区偏移值 */$ b% r1 ]: J- F2 ]7 X
            uint32_t mask_flags;        /* 掩码标识,不重要 */3 M, t' z( K5 y9 l
            struct nand_ecclayout *ecclayout;        /* OOB布局 */
    ) d% b8 h5 @  J& U        struct mtd_info **mtdp;                /* pointer to store the MTD object */7 Z( z; i4 N! e& M! H1 g
    };3 C: p1 P$ r/ q! k: ?
    其中nand_ecclayout结构体:1 t9 k. ?$ J8 \  d2 ]) |- f
    struct nand_ecclayout {
    3 Y1 e" Q6 n( Z3 Z        __u32 eccbytes;                /* ECC字节数 */
    9 W, p( `! w8 p: _4 ]        __u32 eccpos[64];        /* ECC校验码在OOB区域存放位置 */
    . M; v2 P) x, H2 i        __u32 oobavail;                ) K. W9 |' i7 Q  V% P
            /* 除了ECC校验码之外可用的OOB字节数 */- s( y9 A) I( K* q
            struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];( d8 y2 ?: k9 S8 g. `5 m' i7 o% k2 i
    };
    % A4 j; P4 r9 m, B% d/ R关于nand_ecclayout结构体实例,更多可参考drivers/mtd/nand/nand_base.c下的nand_oob_8、nand_oob_16、nand_oob_64实例。5 p" L4 _! I- U8 D) G
    MTD设备层:1 \) |/ P# R* Y& o8 C/ @' z
    mtd字符设备接口:& E2 ]5 Y( J( c9 r' @# M; _7 I4 j: {
    ! g' V4 q/ d/ f; x6 m
    /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) . O; F3 \' I4 j& p5 t+ H1 q

    3 _8 f, n$ I  d1 Nmtd块设备接口:/ y: ?# t  s- z% S" b5 S3 a  }

    6 V5 G: O/ ^+ H, S- g8 `4 l/drivers/mtd/mtdblock.c文件实现了MTD块设备接口,主要原理是将Flash的erase block 中的数据在内存中建立映射,然后对其进行修改,最后擦除Flash 上的block,将内存中的映射块写入Flash 块。整个过程被称为read/modify/erase/rewrite 周期。 但是,这样做是不安全的,当下列操作序列发生时,read/modify/erase/poweroff,就会丢失这个block 块的数据。
    4 Z0 `) ]" Y( VMTD硬件驱动层:
    $ `! l; C2 x; n9 x; \& a3 r# ^2 V5 J  J: S' b1 j
    Linux内核再MTD层下实现了通用的NAND驱动(/driver/mtd/nand/nand_base.c),因此芯片级的NAND驱动不再需要实现mtd_info结构体中的read()、write()、read_oob()、write_oob()等成员函数。
    9 F" u5 q: ~  s' V  p% k- q5 z, l) ]! C5 ]
    MTD使用nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。
    9 ^8 ~2 C6 f8 l% h: k& @7 \! I/ \. T6 ~9 ~6 {( R, Y+ i3 L. H$ X
    & i# S& h) o" M8 ]6 L
    struct nand_chip {
    % t' ^7 J# ?  \3 ~4 B. t0 n- u        void  __iomem        *IO_ADDR_R;                /* 读8位I/O线地址 */
    / t; F% `. ?2 ?% H        void  __iomem        *IO_ADDR_W;                /* 写8位I/O线地址 */
    $ l+ K3 F7 ]6 d" k! z, k) O- J+ U* Q
            /* 从芯片中读一个字节 *// g! `; ]& j8 l7 I
            uint8_t        (*read_byte)(struct mtd_info *mtd);                ( a! G" j" }+ p
            /* 从芯片中读一个字 */) w5 C5 O7 M- b. m. q
            u16                (*read_word)(struct mtd_info *mtd);               
    0 G1 `: q& ~6 i$ a# o        /* 将缓冲区内容写入芯片 */
    & [" Q$ M# q9 u1 R        void        (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);       
    5 y' I8 y; g) ~" l7 {        /* 读芯片读取内容至缓冲区/ */
    2 h! i" A5 R' w0 f7 i4 s7 E& ^        void        (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
    / x0 P  O* c" F9 R3 c        /* 验证芯片和写入缓冲区中的数据 */& D' f# F+ p- i6 k
            int                (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
    9 Q: o7 O/ U0 w7 D+ A' g6 M; O' C        /* 选中芯片 *// C0 Y9 N# P, C; L5 M9 |
            void        (*select_chip)(struct mtd_info *mtd, int chip);
    , d1 ~+ o: R4 T! g- g7 b5 C0 B        /* 检测是否有坏块 */. R8 c" @5 V' r/ L4 Y. O6 A- X
            int                (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);5 ]" o0 ~& H6 x# {8 ^
            /* 标记坏块 */
    7 a; f. @% x9 @% _$ Y& p/ c* B        int                (*block_markbad)(struct mtd_info *mtd, loff_t ofs);. B6 K2 D) C" ?
            /* 命令、地址、数据控制函数 */5 [3 V5 P# K5 m. z8 m
            void        (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);; S- s% N* ]! y1 y  H
            /* 设备是否就绪 */) G& _. |) n- B# X$ b/ {
            int                (*dev_ready)(struct mtd_info *mtd);0 j' Y% p# V+ U" ^3 p8 B4 w
            /* 实现命令发送 */8 I2 v$ ~4 |2 X
            void        (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);5 c! _+ c. ?/ c. E! Q0 t; `
            int                (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
    - X, f8 n) I, t% R0 @  K        /* 擦除命令的处理 */" b- f' q, X! }. B
            void        (*erase_cmd)(struct mtd_info *mtd, int page);# `  i' ]8 l. d1 T+ F
            /* 扫描坏块 */. d+ Q3 x4 ?3 ]* {  U+ l
            int                (*scan_bbt)(struct mtd_info *mtd);0 i: {+ i! }' _' {0 q
            int                (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);# L6 v5 k- \0 s
            /* 写一页 */) P& W! ]' H! t: S
            int                (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,/ L4 R9 N7 e. p9 E5 P) \8 y3 f' l
                                          const uint8_t *buf, int page, int cached, int raw);
    ' A2 w! P3 O, x+ m
    1 }% V( g. G8 c/ ~- N/ z        int                chip_delay;                        /* 由板决定的延迟时间 */- {" }) l1 c3 f; h1 A* t; h6 h
            /* 与具体的NAND芯片相关的一些选项,如NAND_NO_AUTOINCR,NAND_BUSWIDTH_16等 */& X! `( l8 m; Z- B1 ]. ^# }0 V2 O
            unsigned int        options;        6 D9 o9 t) q  V/ E# v! g

    ' e* ^2 `) v' y- @- c/ J+ ~        /* 用位表示的NAND芯片的page大小,如某片NAND芯片2 w# M& d2 n2 ?2 y; e# C) Y
             * 的一个page有512个字节,那么page_shift就是9
    $ W1 P2 x/ y+ q! j, {* d% `         */
    2 @+ h0 |8 A# m" T3 g; n        int                 page_shift;4 f7 M& A# W5 h6 O* S
            /* 用位表示的NAND芯片的每次可擦除的大小,如某片NAND芯片每次可
    5 m3 p+ K! r5 c! J         * 擦除16K字节(通常就是一个block的大小),那么phys_erase_shift就是14* F6 N5 p. {. m4 ?( U* i
             */% e% Q+ v- i* ?6 O) ]
            int                 phys_erase_shift;
    6 r% F1 d2 f9 X6 `0 D$ A        /* 用位表示的bad block table的大小,通常一个bbt占用一个block,' G+ N7 J- |& l) [! k
             * 所以bbt_erase_shift通常与phys_erase_shift相等
    6 E' O) d9 f( ^4 \          */2 u# l9 n' x: a8 C2 K
            int                 bbt_erase_shift;
    % P7 s/ D# r2 z% O, a+ Q3 o3 M; g        /* 用位表示的NAND芯片的容量 */
    2 z- W- Z9 V# r% v8 J: h' B        int                 chip_shift;2 N( c4 w9 c& U: _7 v0 F1 c
            /* NADN FLASH芯片的数量 */$ g+ O$ V8 j1 {$ q
            int                 numchips;+ i! S: F/ \0 }
            /* NAND芯片的大小 */
    8 ]7 @- l/ h# F5 ?        uint64_t chipsize;
    - W& S- g# r1 G4 N        int                 pagemask;% E% @+ G* e3 G
            int                 pagebuf;
    % X6 x) Y$ y& w0 {8 W6 I& s        int                 subpagesize;
    3 {: e5 Q: ?& H: u        uint8_t         cellinfo;
    5 B: I. \1 `  n6 Z1 `        int                 badblockpos;
    . N" q3 `9 h/ Z' E& ~: ]# F        nand_state_t        state;" [4 Q* b; s+ U1 j' `# g
            uint8_t                *oob_poi;
    & t0 y6 @5 ^2 A# ?5 C- M        struct nand_hw_control  *controller;
    & ^% F+ B6 p  x        struct nand_ecclayout        *ecclayout;        /* ECC布局 */
    ( D: U( i+ R: s+ R       
    $ n" k# u. C6 ~# Y' ^, z        struct nand_ecc_ctrl ecc;        /* ECC校验结构体,里面有大量的函数进行ECC校验 */
    % m- m9 N5 k5 ?/ J6 G7 y        struct nand_buffers *buffers;2 S3 Q# C! R( t6 A
            struct nand_hw_control hwcontrol;
    8 e  ]) [( N0 Q: i" O        struct mtd_oob_ops ops;
    ( `$ e0 H* c- r5 {: D        uint8_t                *bbt;
    , [  j) \! |3 h1 `8 M8 S        struct nand_bbt_descr        *bbt_td;. h. l2 \! N- K! _" u( y) D
            struct nand_bbt_descr        *bbt_md;
    3 x& U5 q$ `0 ~; T        struct nand_bbt_descr        *badblock_pattern;9 A3 H/ _. v- S$ f0 M
            void                *priv;9 S. @1 Y5 J5 n' |8 L* J' ?+ t# C
    };+ j" f7 d1 z5 D; S4 q
    - r' L0 B# h6 H9 ]# m  [1 I
    最后,我们来用图表的形式来总结一下,MTD设备层、MTD原始设备层、FLASH硬件驱动层之间的联系。
    - x: H( K' z) @$ W7 @( B+ }2 j9 _* H; H

    ) z" I, l0 e. {9 g  W5 }/ I" B' m. g) x" x1 [6 z5 E

    0 o9 q1 \5 z* x* K2 s# o+ l
  • 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 23:07 , Processed in 0.234375 second(s), 26 queries , Gzip On.

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

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

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