zoukankan      html  css  js  c++  java
  • Linux MTD (Memory Technology Device) subsystem analysis -For Atheros char device

    Linux MTD (Memory Technology Device) subsystem analysis

    For Atheros char device

    读了Linux MTD 源代码分析 对这部分有了整体的认识,结合现有代码,分析一下Atheros的MTD是如何使用的。

    Linux kernel: 2.6.31.

    Atheros platform: QCA9890???

    参考Linux MTD 源代码分析,这里把MTD分为4层,从上到下依次是:字符设备节点、字符设备、MTD 核心、FLASH 硬件驱动。

    字符设备节点:/dev/mtd0 /dev/mtd1 等节点,可以通过fopen()等直接使用。这些节点是MTD子系统创建的,用户通过访问这些节点操作MTD。

    字符设备:MTD子系统把MTD封装为字符设备。

    MTD核心:管理所有的MTD设备,提供get/add/del MTD功能;同时,一个MTD设备可能逻辑上会被划分为多个区,每个区被当成一个MTD设备,所以还提供了通过分区信息批量加入、删除MTD设备的功能。为了从kernel启动参数获取分区信息,MTD核心提供了分析启动参数中分区信息的功能。

    FLASH硬件驱动:提供FLASH的read/write/erase等功能。因为Atheros的分区信息是在kernel的启动参数里面取得,所以这里还有分析启动参数分区信息的功能;根据分区信息把MTD设备加到MTD核心中。

    系统启动时,MTD核心层可以直接编译到kernel中,FLASH驱动层在系统启动时解析分区信息,把MTD加入到MTD核心层,MTD核心层完成设备节点的创建,MTD字符设备注册MTD字符设备信息,这样访问设备节点的时候就可以找到具体的执行函数。

     

    图1 Atheros MTD Subsystem Architecture

    图1显示了各层的主要信息。

    MTD设备的使用是通过访问设备节点/dev/mtd0 /dev/mtd1,一般用法是

    File *f = fopen(“/dev/mtd0”, “r”);

     

    fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

    fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    /dev/mtd0 等设备是由MTD核心创建的,由add_mtd_device()->device_register()创建。fread/fwrite是由MTD字符设备层实现具体的读写,至于是如何找到MTD字符设备层的函数的,以及设备节点如何和这些函数关联的,需要看下fread以及Linux的驱动模型(device_register/register_chrdev),以后再总结吧。

             MTD字符设备层是通过register_chrdev()来实现的,需要提供struct file_operations中的函数,这些函数完成对指定MTD的read/write/erase等。register_chrdev()之后,当用fread等访问MTD的时候,就会找到这里注册的函数。

    static const struct file_operations mtd_fops = {

                .owner                     = THIS_MODULE,

                .llseek                       = mtd_lseek,

                .read             = mtd_read,

                .write                       = mtd_write,

                .ioctl              = mtd_ioctl,

    #ifdef CONFIG_COMPAT

                .compat_ioctl       = mtd_compat_ioctl,

    #endif

                .open                       = mtd_open,

                .release        = mtd_close,

                .mmap                    = mtd_mmap,

    #ifndef CONFIG_MMU

                .get_unmapped_area = mtd_get_unmapped_area,

    #endif

    };

     

    static int __init init_mtdchar(void)

    {

                int status;

     

                status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);

                if (status < 0) {

                           printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices. ",

                                  MTD_CHAR_MAJOR);

                }

     

                return status;

    }

             具体看下open/read/close是如何使用MTD的,get/put_mtd_device有MTD核心层提供,下面会具体看到。mtd_open 通过inode取得设备号,进一步通过get_mtd_device取得MTD的struct mtd_info信息,也就获得了具体操作MTD的函数;以后的mtd_read/write等函数就可以直接使用mtd->read/mtd->write完成具体的操作。

    static int mtd_open(struct inode *inode, struct file *file)

    {

                int minor = iminor(inode);

                int devnum = minor >> 1;

                …

                mtd = get_mtd_device(NULL, devnum);

        ….

                mfi = kzalloc(sizeof(*mfi), GFP_KERNEL);

                mfi->mtd = mtd;

                file->private_data = mfi;

    out:

                unlock_kernel();

                return ret;

    } /* mtd_open */

     

    static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)

    {

                struct mtd_file_info *mfi = file->private_data;

                struct mtd_info *mtd = mfi->mtd;

                …

                while (count) {

                           switch (mfi->mode) {

                           default:

                                       ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);

                           }

                }

        …

                return total_retlen;

    } /* mtd_read */

     

    static int mtd_close(struct inode *inode, struct file *file)

    {

                struct mtd_file_info *mfi = file->private_data;

                struct mtd_info *mtd = mfi->mtd;

                put_mtd_device(mtd);

                file->private_data = NULL;

                kfree(mfi);

                return 0;

    } /* mtd_close */

             MTD核心层提供的功能可以分为两部分:面向上层用户的API和面向底层用户的API。上层用户主要是指MTD字符设备层,字符设备层可以通过get_mtd_device获得MTD的struct mtd_info 信息,然后通过mtd->read、mtd->write等操作MTD。使用方法可以参考字符设备层中函数的实现。下层用户是FLASH驱动层,FLASH驱动层通过parse_mtd_partitions获得kernel启动参数中的分区信息,然后通过add_mtd_partitions把各个分区当做单独的MTD设备加入到MTD核心层;为了分析kernel启动参数,需要注册parser函数,通过register_mtd_parser实现,这个函数的注册可以由FLASH驱动层完成,也可以编译到kernel中(make menuconfig->Device drivers->MTD support->Command line partition table parsing)。

    至于注册的parser是如何得到kernel启动参数的,大概的过程是,bootloader通过bootargs的mtdparts传入分区信息,这个分区信息会被当成kernel的启动参数;kernel通过__setup("mtdparts=", mtdpart_setup) 注册函数mtdpart_setup,mtdpart_setup保存分区信息,parser解析保存的信息,参考(cmdlinepart.c)。

             MTD核心层的实现原理是:

    每个MTD用一个struct mtd_info描述,所有的mtd_info保存在struct mtd_info *mtd_table[]中,get/add/del_mtd_device都是操作mtd_table。

    一个MTD FLASH可以逻辑上分为多个区,每个区当成一个MTD,分区信息用struct mtd_partition描述,通过分区信息加入MTD时,需要知道master MTD的信息,每个分区的信息多数来源于master MTD,master MTD不会加入MTD核心层,只有里面的各个分区会加入核心层,当成单独的MTD。

    MTD核心层还有notifier的功能,保存在struct mtd_notifier,所有的notifier保存在一个链表中。

    具体可以参考Linux MTD 源代码分析。

             FLASH驱动层提供mtd_info中read/write等的具体实现,如果有逻辑分区的话,这个mtd_info就是master MTD。然后调用parser_mtd_partitions 和add_mtd_partitions把逻辑分区加入到MTD核心层。

  • 相关阅读:
    java实现httpclient 访问
    推荐博文
    Running With xpi
    1 Spring MVC 原理
    windows服务相关
    求职面试三部曲
    使用mvn插件执行工程单元测试OOM的解决办法
    maven-surefire插件问题
    小问题
    NFA到DFA实例
  • 原文地址:https://www.cnblogs.com/hfyinsdu/p/5601381.html
Copyright © 2011-2022 走看看