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 */ }