Linux Block Device Driver (II)_MTD Driver and User Space Programming

Posted by VirtuosiMedia on Wed, 17 Jul 2019 00:20:07 +0200

MTD(Memory Technology Device) is commonly referred to as Flash and other storage devices using memory chips. The MTD subsystem corresponds to the device driver layer in the block device driver framework. It can be said that MTD is a standardized hardware driver framework designed for Flash devices. This paper discusses the MTD driver framework based on the 3.14 kernel.

MTD Subsystem Framework

  • Device Node Layer: MTD framework can create character device node (main device number 90) and block device node (main device number 31) under / dev. Users can access MTD character device or block device by accessing this device node.
  • MTD device layer: Based on MTD original device, Linux defines MTD character device and block device at this level. Character device is implemented in mtdchar.c. Block device is described by structure mtdblk_dev, "/drivers/mtd/mtdchar.c" file realizes MTD character device interface; "/drivers/mtd/mtdblock.c" file realizes MTD block device interface.
  • MTD Primitive Device Layer: It consists of the general code of MTD Primitive Device + specific Flash data. Objects such as mtd_info, mtd_part, mtd_partition and mtd_partitions and their operation methods belong to this layer. The corresponding file is drivers/mtd/mtdcore.c. Similar to the core layer in the i2c driver framework.
  • Hardware Driver Layer: The kernel implements flash operations in this level. Driver development only needs to add the corresponding device information. For example, the chip driver of NOR flash is located in drivers/mtd/chips/, and Nand flash is located in drivers/mtd/nand/(eg s3c2410.c).

A Brief Introduction to Core Structure and Method

To implement the above framework, the following classes and API s are used in the kernel, which are almost necessary for developing an MTD driver

Core structure

  • mtd_info describes the structure of a partition in the original device layer and a partition in a device or a multi-partition device.
  • mtd_table manages the array of mtd_info in the original device layer
  • Mtd_part represents a partition in which struct mtd_info mtd describes the partition's information. A physical Flash device can have more than one mtd_part, and each mtd_part corresponds to one mtd_info.
  • Mtd_partition describes a partition table. It describes all partitions by managing mtd_part and mtd_info in each mtd_part. A physical Flash device has only one mtd_partition.
  • mtd_partitions is a list_head object for managing mtd_partition s

Core approach

  • add_mtd_device()/del_mtd_device() Register/cancel an MTD device
  • add_mtd_partitions()/del_mtd_partitions() registers and cancels one or more partition tables.

Detailed description of core structure and methodology

mtd_info

In itself, there is no list_head for kernel management, and the management of mtd_info objects is achieved through mtd_part. The mtd_info object belongs to the original device layer, and many function interface cores have been implemented. The operation of read()/write() in mtd_info is the main function of MTD device driver. The member functions of mtd_info can hardly be seen in the driver code of NORFlash or NANDFlash. That is to say, these functions are transparent to Flash chips, because Linux implements general mtd_info functions for NORFlash and NANDFlash in the lower layer of MTD. Number.

114 struct mtd_info {
115         u_char type;
116         uint32_t flags;
117         uint64_t size;   // Total size of the MTD
118 
123         uint32_t erasesize;
131         uint32_t writesize;
132 
142         uint32_t writebufsize;
143 
144         uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
145         uint32_t oobavail;  // Available OOB bytes per block
146 
151         unsigned int erasesize_shift;
152         unsigned int writesize_shift;
153         /* Masks based on erasesize_shift and writesize_shift */
154         unsigned int erasesize_mask;
155         unsigned int writesize_mask;
156 
164         unsigned int bitflip_threshold;
165 
166         // Kernel-only stuff starts here.
167         const char *name;
168         int index;
169 
170         /* ECC layout structure pointer - read only! */ 
171         struct nand_ecclayout *ecclayout;
172 
173         /* the ecc step size. */
174         unsigned int ecc_step_size;
175 
176         /* max number of correctible bit errors per ecc step */
177         unsigned int ecc_strength;
178 
179         /* Data for variable erase regions. If numeraseregions is zero,
180          * it means that the whole device has erasesize as given above.
181          */
182         int numeraseregions;
183         struct mtd_erase_region_info *eraseregions;
184 
185         /*
186          * Do not call via these pointers, use corresponding mtd_*()
187          * wrappers instead.
188          */
189         int (*_erase) (struct mtd_info *mtd, struct erase_info *instr);
190         int (*_point) (struct mtd_info *mtd, loff_t from, size_t len,
191                        size_t *retlen, void **virt, resource_size_t *phys);
192         int (*_unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
193         unsigned long (*_get_unmapped_area) (struct mtd_info *mtd,
194                                              unsigned long len,
195                                              unsigned long offset,
196                                              unsigned long flags);
197         int (*_read) (struct mtd_info *mtd, loff_t from, size_t len,
198                       size_t *retlen, u_char *buf);
199         int (*_write) (struct mtd_info *mtd, loff_t to, size_t len,
200                        size_t *retlen, const u_char *buf);
201         int (*_panic_write) (struct mtd_info *mtd, loff_t to, size_t len,
202                              size_t *retlen, const u_char *buf);
203         int (*_read_oob) (struct mtd_info *mtd, loff_t from,
204                           struct mtd_oob_ops *ops);
205         int (*_write_oob) (struct mtd_info *mtd, loff_t to,
206                            struct mtd_oob_ops *ops);
207         int (*_get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
208                                     size_t len);
209         int (*_read_fact_prot_reg) (struct mtd_info *mtd, loff_t from,
210                                     size_t len, size_t *retlen, u_char *buf);
211         int (*_get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf,
212                                     size_t len);
213         int (*_read_user_prot_reg) (struct mtd_info *mtd, loff_t from,
214                                     size_t len, size_t *retlen, u_char *buf);
215         int (*_write_user_prot_reg) (struct mtd_info *mtd, loff_t to,
216                                      size_t len, size_t *retlen, u_char *buf);
217         int (*_lock_user_prot_reg) (struct mtd_info *mtd, loff_t from,
218                                     size_t len);
219         int (*_writev) (struct mtd_info *mtd, const struct kvec *vecs,
220                         unsigned long count, loff_t to, size_t *retlen);
221         void (*_sync) (struct mtd_info *mtd);
222         int (*_lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
223         int (*_unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
224         int (*_is_locked) (struct mtd_info *mtd, loff_t ofs, uint64_t len);
225         int (*_block_isbad) (struct mtd_info *mtd, loff_t ofs);
226         int (*_block_markbad) (struct mtd_info *mtd, loff_t ofs);
227         int (*_suspend) (struct mtd_info *mtd);
228         void (*_resume) (struct mtd_info *mtd);
229         /*
230          * If the driver is something smart, like UBI, it may need to maintain
231          * its own reference counting. The below functions are only for driver.
232          */
233         int (*_get_device) (struct mtd_info *mtd);
234         void (*_put_device) (struct mtd_info *mtd);
235 
236         /* Backing device capabilities for this device
237          * - provides mmap capabilities
238          */
239         struct backing_dev_info *backing_dev_info;
240 
241         struct notifier_block reboot_notifier;  /* default mode before reboot */
242 
243         /* ECC status information */
244         struct mtd_ecc_stats ecc_stats;
245         /* Subpage shift (NAND) */
246         int subpage_sft;
247 
248         void *priv;
249 
250         struct module *owner;
251         struct device dev;
252         int usecount;
253 };

struct mtd_info
--115-->MTD Type of equipment, there are MTD_RAM,MTD_ROM,MTD_NORFLASH,MTD_NAND_FLASH
--116-->Read-write and permission tokens, with MTD_WRITEABLE,MTD_BIT_WRITEABLE,MTD_NO_ERASE,MTD_UP_LOCK
--117-->MTD Size of equipment
--123-->The main erase block size, NandFlash Namely"block"Size
--131-->Minimum number of writable sections. NandFlash General correspondence"page"Size
--144-->One block Medium OOB Number of bytes
--145-->One block Available oob Number of bytes
--171-->ECC Layout Structure Pointer
--190-->In the light of eXecute-In-Place,Namely XIP
--192-->If this pointer is empty, it is not allowed. XIP
--197-->Read function pointer
--199-->Write function pointer
--248-->Private data

mtd_part

The linked list node of the kernel management partition is used to manage the mtd_info object.

 41 struct mtd_part {
 42         struct mtd_info mtd;
 43         struct mtd_info *master;                                            
 44         uint64_t offset;
 45         struct list_head list;
 46 };

struct mtd_part
--42-->Corresponding mtd_info object
--43-->Parent object pointer
--44-->Offset
--45-->struct Node

mtd_partition

Describe a partition

 39 struct mtd_partition {
 40         const char *name;               /* identifier string */
 41         uint64_t size;                  /* partition size */
 42         uint64_t offset;                /* offset within the master MTD space */                                                
 43         uint32_t mask_flags;            /* master MTD flags to mask out for this partition */
 44         struct nand_ecclayout *ecclayout;       /* out of band layout for this partition (NAND only) */
 45 };

mtd_partition
--40-->Partition name
--41-->Partition size, using MTDPART_SIZ_FULL Represents the use of all space
--42-->The partition is in the master The offset in the device. MTDPART_OFS_APPEND Represents starting at the end of the previous partition. MTDPART_OFS_NXTBLK Represents starting with the next erase block; MTDPART_OFS_RETAIN It means to lean backwards as far as possible. size The size of the space can be left behind.
--43-->Permission mask, MTD_WRITEABLE Represents turning the read-only option of the parent device into writable(Writable partition requirements size and offset want erasesize Alignment, eg MTDPART_OFS_NEXTBLK)
--44-->NANDFlash Of OOB Layout, OOB yes NANDFlash It's very useful in space, such as yaffs2 You need to store bad block information OOB region

mtd_partitions

The list header links all mtd_partition s together.

 36 /* Our partition linked list */
 37 static LIST_HEAD(mtd_partitions);  

The following figure shows the invocation relationships of the key API s.

mtd_add_partition()
   └── add_mtd_device()
add_mtd_partitions()
   └── add_mtd_device()

add_mtd_device()

Assign and initialize an mtd object.

 367 334 int add_mtd_device(struct mtd_info *mtd)
 335 {
 336         struct mtd_notifier *not;
 337         int i, error;
 338 
 339         if (!mtd->backing_dev_info) {
 340                 switch (mtd->type) {
 341                 case MTD_RAM:
 342                         mtd->backing_dev_info = &mtd_bdi_rw_mappable;
 343                         break;
 344                 case MTD_ROM:
 345                         mtd->backing_dev_info = &mtd_bdi_ro_mappable;
 346                         break;
 347                 default:
 348                         mtd->backing_dev_info = &mtd_bdi_unmappable;
 349                         break;
 350                 }
 351         }
 355 
 356         i = idr_alloc(&mtd_idr, mtd, 0, 0, GFP_KERNEL);
 357         if (i < 0)
 358                 goto fail_locked;
 359 
 360         mtd->index = i;
 361         mtd->usecount = 0;
 362 
 363         /* default value if not set by driver */
 364         if (mtd->bitflip_threshold == 0)
 365                 mtd->bitflip_threshold = mtd->ecc_strength;
 366 
 367         if (is_power_of_2(mtd->erasesize))
 368                 mtd->erasesize_shift = ffs(mtd->erasesize) - 1;
 369         else
 370                 mtd->erasesize_shift = 0;
 371 
 372         if (is_power_of_2(mtd->writesize))                                                         
 373                 mtd->writesize_shift = ffs(mtd->writesize) - 1;
 374         else
 375                 mtd->writesize_shift = 0;
 376 
 377         mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1;
 378         mtd->writesize_mask = (1 << mtd->writesize_shift) - 1;
 379 
 380         /* Some chips always power up locked. Unlock them now */
 381         if ((mtd->flags & MTD_WRITEABLE) && (mtd->flags & MTD_POWERUP_LOCK)) {
 382                 error = mtd_unlock(mtd, 0, mtd->size);
 387         }
 388 
 392         mtd->dev.type = &mtd_devtype;
 393         mtd->dev.class = &mtd_class;
 394         mtd->dev.devt = MTD_DEVT(i);
 395         dev_set_name(&mtd->dev, "mtd%d", i);
 396         dev_set_drvdata(&mtd->dev, mtd);
 397         if (device_register(&mtd->dev) != 0)
 399 
 400         if (MTD_DEVT(i))
 401                 device_create(&mtd_class, mtd->dev.parent,
 402                               MTD_DEVT(i) + 1,
 403                               NULL, "mtd%dro", i);
 408         list_for_each_entry(not, &mtd_notifiers, list)
 409                 not->add(mtd);
 417         return 0;
 424 }

add_mtd_device()
--395-->Set up MTD Name of equipment
--396-->Setting up private data, will mtd Address hidden to device->device_private->void* driver_data
--408-->Traveling through all mtd_notifier,Add it to the notification chain

mtd_add_partition()

By registering a mtd_part object to the kernel, the mtd_info object is registered to the kernel, that is, adding a partition to a device.

537 int mtd_add_partition(struct mtd_info *master, const char *name,
538                       long long offset, long long length)
539 {
540         struct mtd_partition part;
541         struct mtd_part *p, *new;
542         uint64_t start, end;
543         int ret = 0;
545         /* the direct offset is expected */
546         if (offset == MTDPART_OFS_APPEND ||                                                         
547             offset == MTDPART_OFS_NXTBLK)
548                 return -EINVAL;
549 
550         if (length == MTDPART_SIZ_FULL)
551                 length = master->size - offset;
552 
553         if (length <= 0)
554                 return -EINVAL;
555 
556         part.name = name;
557         part.size = length;
558         part.offset = offset;
559         part.mask_flags = 0;
560         part.ecclayout = NULL;
561 
562         new = allocate_partition(master, &part, -1, offset);
563         if (IS_ERR(new))
564                 return PTR_ERR(new);
565 
566         start = offset;
567         end = offset + length;
568 
569         mutex_lock(&mtd_partitions_mutex);
570         list_for_each_entry(p, &mtd_partitions, list)
571                 if (p->master == master) {
572                         if ((start >= p->offset) &&
573                             (start < (p->offset + p->mtd.size)))
574                                 goto err_inv;
575 
576                         if ((end >= p->offset) &&
577                             (end < (p->offset + p->mtd.size)))
578                                 goto err_inv;
579                 }
580 
581         list_add(&new->list, &mtd_partitions);
582         mutex_unlock(&mtd_partitions_mutex);
583 
584         add_mtd_device(&new->mtd);
585 
586         return ret;
591 }

add_mtd_partitions()

Add a partition table to the kernel, an MTD device, and a partition table

626 int add_mtd_partitions(struct mtd_info *master,
627                        const struct mtd_partition *parts,
628                        int nbparts)
629 {
630         struct mtd_part *slave;
631         uint64_t cur_offset = 0;
632         int i;
636         for (i = 0; i < nbparts; i++) {
637                 slave = allocate_partition(master, parts + i, i, cur_offset);
642                 list_add(&slave->list, &mtd_partitions);
645                 add_mtd_device(&slave->mtd);
647                 cur_offset = slave->offset + slave->mtd.size;
648         }
649 
650         return 0;                                                                                   
651 }

User Space Programming

MTD device provides two kinds of interfaces: character device and block device. For character device interface, it is implemented in driver/mtd/mtdchar.c. For example, user program can be directly implemented by ioctl() callback corresponding driver. The following are commonly used structures in these operations, which are open to user space, similar to the input_event structure in the input subsystem.

mtd_info_user

//include/uapi/mtd/mtd-abi.h
125 struct mtd_info_user {                                                                              
126         __u8 type;
127         __u32 flags;
128         __u32 size;     /* Total size of the MTD */
129         __u32 erasesize;    
130         __u32 writesize;        
131         __u32 oobsize;  /* Amount of OOB data per block (e.g. 16) */
132         __u64 padding;  /* Old obsolete field; do not use */
133 };

mtd_oob_buf

Describes the OOB (Out Of Band) information of NandFlash.

 35 struct mtd_oob_buf {                                                                                
 36         __u32 start;
 37         __u32 length;
 38         unsigned char __user *ptr;
 39 };

erase_info_user

 25 struct erase_info_user {
 26         __u32 start;
 27         __u32 length;
 28 };

Example

mtd_oob_buf oob;
erase_info_user erase;
mtd_info_user meminfo;

/* Access to equipment information */
if(0 != ioctl(fd, MEMGETINFO, &meminfo))
    perror("MEMGETINFO");
    
/* erase block */
if(0 != ioctl(fd, MEMERASE, &erase))
    perror("MEMERASE");

/* Read OOB */
if(0 != ioctl(fd, MEMREADOOB, &oob))
    perror("MEMREADOOB");

/* Write OOB???? */    
if(0 != ioctl(fd, MEMWRITEOOB, &oob))
    perror("MEMWRITEOOB");
    
/* Check for bad lumps */
if(blockstart != (ofs & (~meminfo.erase + 1))){
    blockstart = ofs & (~meminfo.erasesize + 1);
    if((badblock = ioctl(fd, MEMGETBADBLOCK, &blockstart)) < 0)
        perror("MEMGETBADBLOCK");
    else if(badblock)
        /* Bad Block Code */
    else
        /* Good block code */
}

Topics: Linux Programming