基于块设备子系统的MTD子系统(2.6.26)

基于块设备子系统的MTD子系统(2.6.26)

作者:guolele

blog:http://blog.csdn.net/guolele2010

基于块设备子系统的MTD子系统(2.6.26)_第1张图片

这图说明了MTD子系统的架构,那么它跟块设备子系统有什么关系?其实MTD就是利用了块设备子系统,只是它中间多了一层MTD设备驱动层。

drivers/mtd/mtdblock.c里,

static int __init init_mtdblock(void)

{

         return register_mtd_blktrans(&mtdblock_tr);

}

只有一行,看看

int register_mtd_blktrans(struct mtd_blktrans_ops *tr)

{

         int ret, i;

 

         /* Register the notifier if/when the first device type is

            registered, to prevent the link/init ordering from fucking

            us over. */

         if (!blktrans_notifier.list.next)

                   register_mtd_user(&blktrans_notifier);

 

         ……

         ret = register_blkdev(tr->major, tr->name);

         ……

         tr->blkcore_priv->rq=blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);

         ……

 

         tr->blkcore_priv->rq->queuedata = tr;

         blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize);

         tr->blkshift = ffs(tr->blksize) - 1;

 

         tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,

                            "%sd", tr->name);

         ……

 

         INIT_LIST_HEAD(&tr->devs);

         list_add(&tr->list, &blktrans_majors);

 

         for (i=0; i<MAX_MTD_DEVICES; i++) {

                   if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)

                            tr->add_mtd(tr, mtd_table[i]);

         }

 

         mutex_unlock(&mtd_table_mutex);

 

         return 0;

}

只要不是瞎的都知道这就是块设备驱动,那它没指定make_requset函数,那么就默认为__make_request,这就不分析了,然后就交给mtd_blktrans_request,这个函数只有两行:

static void mtd_blktrans_request(struct request_queue *rq)

{

         struct mtd_blktrans_ops *tr = rq->queuedata;

         wake_up_process(tr->blkcore_priv->thread);

}

正常人也能猜到这是唤醒一个线程,但是唤醒哪个?在register_mtd_blktrans有一句. tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,

                            "%sd", tr->name);

看看mtd_blktrans_thread

 

static int mtd_blktrans_thread(void *arg)

{

         ……

 

                   req = elv_next_request(rq);

 

                   ……

                   res = do_blktrans_request(tr, dev, req);

                   …..

 

                   end_request(req, res);

         }

         spin_unlock_irq(rq->queue_lock);

 

         return 0;

}

 

req = elv_next_request(rq);

多熟悉的身影,就是拿一个请求出来,然后处理res = do_blktrans_request(tr, dev, req);

Static int do_blktrans_request(struct mtd_blktrans_ops *tr,

                                   struct mtd_blktrans_dev *dev,

                                   struct request *req)

{

         ……

 

         switch(rq_data_dir(req)) {

         case READ:

                   for (; nsect > 0; nsect--, block++, buf += tr->blksize)

                            if (tr->readsect(dev, block, buf))//它在mtdblock_tr里指定为mtdblock_readsect

                                     return 0;

                   return 1;

 

         case WRITE:

                   if (!tr->writesect)

                            return 0;

 

                   for (; nsect > 0; nsect--, block++, buf += tr->blksize)

                            if (tr->writesect(dev, block, buf))

                                     return 0;

                   return 1;

 

         default:

                   printk(KERN_NOTICE "Unknown request %u/n", rq_data_dir(req));

                   return 0;

         }

}

其实就是调用mtdblock_readsect,在mtdblock_tr上。

static int mtdblock_readsect(struct mtd_blktrans_dev *dev,

                                  unsigned long block, char *buf)

{

         struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];//最终从注册的mtddev中取出对应的tdblk_dev结构

         return do_cached_read(mtdblk, block<<9, 512, buf);

}

static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos,

                               int len, char *buf)

{

         struct mtd_info *mtd = mtdblk->mtd;//在注册的mtdblk中拿出mtd_info其中最里面包含操作的函数

         unsigned int sect_size = mtdblk->cache_size;

         size_t retlen;

         int ret;

 

         DEBUG(MTD_DEBUG_LEVEL2, "mtdblock: read on /"%s/" at 0x%lx, size 0x%x/n",

                            mtd->name, pos, len);

 

         if (!sect_size)

                   return mtd->read(mtd, pos, len, &retlen, buf);//s3c2410.c指定为nand_read

 

         while (len > 0)

……

}

mtd->read(mtd, pos, len, &retlen, buf);s3c2410.c 为例,在probe函数里的nand_scan_tail,在指定为nand_read.

Nand_read就属于drivers/mtd/nand/nand_base.c

这一层就是多加的一层,它下面就是直接的驱动程序

static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,

                        size_t *retlen, uint8_t *buf)

{

         ……

 

         ret = nand_do_read_ops(mtd, from, &chip->ops);

……

}

nand_do_read_ops执行读操作

 

static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,

                                struct mtd_oob_ops *ops)

{

         ……

 

                            /* Now read the page into the buffer */

                            if (unlikely(ops->mode == MTD_OOB_RAW))

                                     ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);//s3c2410.c里的nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw

                            ……

}

 

chip->ecc.read_page_raw这个函数指针呢,还是在nand_scan_tail(&nmtd->mtd);指定了nand_read_page_raw,这要在默认不没有的时候内核才填的,当然也可以自己实现。

 

static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,

                                  uint8_t *buf)

{

         chip->read_buf(mtd, buf, mtd->writesize);//最终调用的是nand_ chip里的read_buf函数指针,s3c2410s3c2440_nand_read_buf

         chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);

         return 0;

}

最终,调用的是我们要写的驱动程序里的读flash函数。

 

还有一边是为了提供给用户空间的一个接口,所以采用了一个以字符设备驱动为主的一层。

drivers/mtd/mtdchar.c

注册啊那些我就不说了,只接讲函数操作。

static const struct file_operations mtd_fops = {

         .owner               = THIS_MODULE,

         .llseek                = mtd_lseek,

         .read                  = mtd_read,

         .write                 = mtd_write,

         .ioctl                   = mtd_ioctl,

         .open                 = mtd_open,

         .release   = mtd_close,

};

只看打开跟读。

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

{

         ……

 

         mtd = get_mtd_device(NULL, devnum);

 

         ……

         mfi->mtd = mtd;

         file->private_data = mfi;

 

         return 0;

} /* mtd_open */

打开就为了获得struct mtd_info这个结构体,有个读操作进来。

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

{

 

                            ……

 

                            ret = mtd->read_oob(mtd, *ppos, &ops);//nand_scan_tail里指定为nand_read_oob

                            retlen = ops.retlen;

                            break;

                   }

                   default:

                            ret = mtd->read(mtd, *ppos, len, &retlen, kbuf);//nand_scan_tail里指定为nand_read

                   }

                   ……

} /* mtd_read */

就是采用我们要写的驱动程序里的读函数,前面几个什么read_oob这些要指定才有,我们没指定,所以就直接到mtd->read也就是nand_read,也就重复上面说的内容了。

 

再看看上面的图,清楚的明白了这个机制了吧?然后呢,就是要谈我们怎么写的时候了。内核已经给我们做完了大部分工作,我们要实现的就是s3c2410_nand.c这个驱动程序,这个驱动程序的主要工作是什么?

说这之前要先说几个结构体:

struct mtd_info {

         u_char type;

         u_int32_t flags;

         u_int32_t size;         // Total size of the MTD

 

         /* "Major" erase size for the device. Nave users may take this

          * to be the only erase size available, or may use the more detailed

          * information below if they desire

          */

         u_int32_t erasesize;

         /* Minimal writable flash unit size. In case of NOR flash it is 1 (even

          * though individual bits can be cleared), in case of NAND flash it is

          * one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR

          * it is of ECC block size, etc. It is illegal to have writesize = 0.

          * Any driver registering a struct mtd_info must ensure a writesize of

          * 1 or larger.

          */

         u_int32_t writesize;

 

         u_int32_t oobsize;   // Amount of OOB data per block (e.g. 16)

         u_int32_t oobavail;  // Available OOB bytes per block

 

         // Kernel-only stuff starts here.

         char *name;

         int index;

 

         /* ecc layout structure pointer - read only ! */

         struct nand_ecclayout *ecclayout;

 

         /* Data for variable erase regions. If numeraseregions is zero,

          * it means that the whole device has erasesize as given above.

          */

         int numeraseregions;

         struct mtd_erase_region_info *eraseregions;

 

         /*

          * Erase is an asynchronous operation.  Device drivers are supposed

          * to call instr->callback() whenever the operation completes, even

          * if it completes with a failure.

          * Callers are supposed to pass a callback function and wait for it

          * to be called before writing to the block.

          */

         int (*erase) (struct mtd_info *mtd, struct erase_info *instr);

 

         /* This stuff for eXecute-In-Place */

         /* phys is optional and may be set to NULL */

         int (*point) (struct mtd_info *mtd, loff_t from, size_t len,

                            size_t *retlen, void **virt, resource_size_t *phys);

 

         /* We probably shouldn't allow XIP if the unpoint isn't a NULL */

         void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);

 

 

         int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

         int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

         /* In blackbox flight recorder like scenarios we want to make successful

            writes in interrupt context. panic_write() is only intended to be

            called when its known the kernel is about to panic and we need the

            write to succeed. Since the kernel is not going to be running for much

            longer, this function can break locks and delay to ensure the write

            succeeds (but not sleep). */

 

         int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

 

         int (*read_oob) (struct mtd_info *mtd, loff_t from,

                             struct mtd_oob_ops *ops);

         int (*write_oob) (struct mtd_info *mtd, loff_t to,

                             struct mtd_oob_ops *ops);

 

         /*

          * Methods to access the protection register area, present in some

          * flash devices. The user data is one time programmable but the

          * factory data is read only.

          */

         int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);

         int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

         int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);

         int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

         int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

         int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);

 

         /* kvec-based read/write methods.

            NB: The 'count' parameter is the number of _vectors_, each of

            which contains an (ofs, len) tuple.

         */

         int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);

 

         /* Sync */

         void (*sync) (struct mtd_info *mtd);

 

         /* Chip-supported device locking */

         int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);

         int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

 

         /* Power Management functions */

         int (*suspend) (struct mtd_info *mtd);

         void (*resume) (struct mtd_info *mtd);

 

         /* Bad block management functions */

         int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);

         int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

 

         struct notifier_block reboot_notifier;  /* default mode before reboot */

 

         /* ECC status information */

         struct mtd_ecc_stats ecc_stats;

         /* Subpage shift (NAND) */

         int subpage_sft;

 

         void *priv;

 

         struct module *owner;

         int usecount;

 

         /* If the driver is something smart, like UBI, it may need to maintain

          * its own reference counting. The below functions are only for driver.

          * The driver may register its callbacks. These callbacks are not

          * supposed to be called by MTD users */

         int (*get_device) (struct mtd_info *mtd);

         void (*put_device) (struct mtd_info *mtd);

};

Mtd_info这个结构体是描述每一个mtd操作

还有一个就是struct nand_chip,觉得太长,就只复制个内核描述就可以了。

/**

 * struct nand_chip - NAND Private Flash Chip Data

 * @IO_ADDR_R:            [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device

 * @IO_ADDR_W:           [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device

 * @read_byte:                [REPLACEABLE] read one byte from the chip

 * @read_word:               [REPLACEABLE] read one word from the chip

 * @write_buf:                 [REPLACEABLE] write data from the buffer to the chip

 * @read_buf:                  [REPLACEABLE] read data from the chip into the buffer

 * @verify_buf:                 [REPLACEABLE] verify buffer contents against the chip data

 * @select_chip:     [REPLACEABLE] select chip nr

 * @block_bad:                [REPLACEABLE] check, if the block is bad

 * @block_markbad:       [REPLACEABLE] mark the block bad

 * @cmd_ctrl:                   [BOARDSPECIFIC] hardwarespecific funtion for controlling

 *                       ALE/CLE/nCE. Also used to write command and address

 * @dev_ready:                [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line

 *                       If set to NULL no access to ready/busy is available and the ready/busy information

 *                       is read from the chip status register

 * @cmdfunc:          [REPLACEABLE] hardwarespecific function for writing commands to the chip

 * @waitfunc:                   [REPLACEABLE] hardwarespecific function for wait on ready

 * @ecc:          [BOARDSPECIFIC] ecc control ctructure

 * @buffers:             buffer structure for read/write

 * @hwcontrol:                platform-specific hardware control structure

 * @ops:          oob operation operands

 * @erase_cmd:              [INTERN] erase command write function, selectable due to AND support

 * @scan_bbt:                  [REPLACEABLE] function to scan bad block table

 * @chip_delay:               [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)

 * @wq:                    [INTERN] wait queue to sleep on if a NAND operation is in progress

 * @state:                [INTERN] the current state of the NAND device

 * @oob_poi:           poison value buffer

 * @page_shift:               [INTERN] number of address bits in a page (column address bits)

 * @phys_erase_shift:   [INTERN] number of address bits in a physical eraseblock

 * @bbt_erase_shift:     [INTERN] number of address bits in a bbt entry

 * @chip_shift:                 [INTERN] number of address bits in one chip

 * @datbuf:              [INTERN] internal buffer for one page + oob

 * @oobbuf:             [INTERN] oob buffer for one eraseblock

 * @oobdirty:          [INTERN] indicates that oob_buf must be reinitialized

 * @data_poi:                   [INTERN] pointer to a data buffer

 * @options:            [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about

 *                       special functionality. See the defines for further explanation

 * @badblockpos:   [INTERN] position of the bad block marker in the oob area

 * @cellinfo:             [INTERN] MLC/multichip data from chip ident

 * @numchips:                 [INTERN] number of physical chips

 * @chipsize:           [INTERN] the size of one chip for multichip arrays

 * @pagemask:                [INTERN] page number mask = number of (pages / chip) - 1

 * @pagebuf:           [INTERN] holds the pagenumber which is currently in data_buf

 * @subpagesize:   [INTERN] holds the subpagesize

 * @ecclayout:                 [REPLACEABLE] the default ecc placement scheme

 * @bbt:          [INTERN] bad block table pointer

 * @bbt_td:             [REPLACEABLE] bad block table descriptor for flash lookup

 * @bbt_md:           [REPLACEABLE] bad block table mirror descriptor

 * @badblock_pattern:  [REPLACEABLE] bad block scan pattern used for initial bad block scan

 * @controller:                 [REPLACEABLE] a pointer to a hardware controller structure

 *                       which is shared among multiple independend devices

 * @priv:                   [OPTIONAL] pointer to private chip date

 * @errstat:             [OPTIONAL] hardware specific function to perform additional error status checks

 *                       (determine if errors are correctable)

 * @write_page:              [REPLACEABLE] High-level page write function

 */

Struct nand_chip是底层操作的,接到命令,就把数据写到mtd_info里,然后处理后再转为struct nand_chip,才真正执行。

1、  实现底层硬件操作,struct nand_chip

2、  填充struct mtd_info

 

整个驱动程序就这么简单!

你可能感兴趣的:(struct,function,Flash,buffer,Descriptor,structure)