| /**************************************************************************** |
| * apps/examples/mtdpart/mtdpart_main.c |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <debug.h> |
| |
| #include <nuttx/mtd/mtd.h> |
| #include <nuttx/drivers/drivers.h> |
| #include <nuttx/fs/ioctl.h> |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* Configuration ************************************************************/ |
| |
| /* Make sure that support for MTD partitions is enabled */ |
| |
| #ifndef CONFIG_MTD_PARTITION |
| # error "CONFIG_MTD_PARTITION is required" |
| #endif |
| |
| /* The default is to use the RAM MTD device at drivers/mtd/rammtd.c. But |
| * an architecture-specific MTD driver can be used instead by defining |
| * CONFIG_EXAMPLES_MTDPART_ARCHINIT. In this case, the initialization logic |
| * will call mtdpart_archinitialize() to obtain the MTD driver instance. |
| */ |
| |
| #ifndef CONFIG_EXAMPLES_MTDPART_ARCHINIT |
| |
| /* Make sure that the RAM MTD driver is enabled */ |
| |
| # ifndef CONFIG_RAMMTD |
| # error "CONFIG_RAMMTD is required without CONFIG_EXAMPLES_MTDPART_ARCHINIT" |
| # endif |
| |
| /* This must exactly match the default configuration in |
| * drivers/mtd/rammtd.c |
| */ |
| |
| # ifndef CONFIG_RAMMTD_ERASESIZE |
| # define CONFIG_RAMMTD_ERASESIZE 4096 |
| # endif |
| |
| /* Given the ERASESIZE, CONFIG_EXAMPLES_MTDPART_NEBLOCKS will determine the |
| * size of the RAM allocation needed. |
| */ |
| |
| # ifndef CONFIG_EXAMPLES_MTDPART_NEBLOCKS |
| # define CONFIG_EXAMPLES_MTDPART_NEBLOCKS (32) |
| # endif |
| |
| # undef MTDPART_BUFSIZE |
| # define MTDPART_BUFSIZE \ |
| (CONFIG_RAMMTD_ERASESIZE * CONFIG_EXAMPLES_MTDPART_NEBLOCKS) |
| |
| #endif |
| |
| #ifndef CONFIG_EXAMPLES_MTDPART_NPARTITIONS |
| # define CONFIG_EXAMPLES_MTDPART_NPARTITIONS 3 |
| #endif |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| struct mtdpart_filedesc_s |
| { |
| FAR char *name; |
| bool deleted; |
| size_t len; |
| uint32_t crc; |
| }; |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Pre-allocated simulated flash */ |
| |
| #ifndef CONFIG_EXAMPLES_MTDPART_ARCHINIT |
| static uint8_t g_simflash[MTDPART_BUFSIZE]; |
| #endif |
| |
| /**************************************************************************** |
| * External Functions |
| ****************************************************************************/ |
| |
| #ifdef CONFIG_EXAMPLES_MTDPART_ARCHINIT |
| extern FAR struct mtd_dev_s *mtdpart_archinitialize(void); |
| #endif |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: mtdpart_main |
| ****************************************************************************/ |
| |
| int main(int argc, FAR char *argv[]) |
| { |
| FAR struct mtd_dev_s *master; |
| FAR struct mtd_dev_s *part[CONFIG_EXAMPLES_MTDPART_NPARTITIONS + 1]; |
| FAR struct mtd_geometry_s geo; |
| FAR uint32_t *buffer; |
| char blockname[32]; |
| char charname[32]; |
| size_t partsize; |
| ssize_t nbytes; |
| off_t nblocks; |
| off_t offset; |
| off_t check; |
| off_t sectoff; |
| off_t seekpos; |
| unsigned int blkpererase; |
| int fd; |
| int i; |
| int j; |
| int k; |
| int ret; |
| |
| /* Create and initialize a RAM MTD FLASH driver instance */ |
| |
| #ifdef CONFIG_EXAMPLES_MTDPART_ARCHINIT |
| master = mtdpart_archinitialize(); |
| #else |
| master = rammtd_initialize(g_simflash, MTDPART_BUFSIZE); |
| #endif |
| if (!master) |
| { |
| printf("ERROR: Failed to create RAM MTD instance\n"); |
| fflush(stdout); |
| exit(1); |
| } |
| |
| /* Perform the IOCTL to erase the entire FLASH part */ |
| |
| ret = master->ioctl(master, MTDIOC_BULKERASE, 0); |
| if (ret < 0) |
| { |
| printf("ERROR: MTDIOC_BULKERASE ioctl failed: %d\n", ret); |
| } |
| |
| /* Initialize to provide an FTL block driver on the MTD FLASH interface. |
| * |
| * NOTE: We could just skip all of this FTL and BCH stuff. We could |
| * instead just use the MTD drivers bwrite and bread to perform this |
| * test. Creating the character drivers, however, makes this test more |
| * interesting. |
| */ |
| |
| ret = ftl_initialize(0, master); |
| if (ret < 0) |
| { |
| printf("ERROR: ftl_initialize /dev/mtdblock0 failed: %d\n", ret); |
| fflush(stdout); |
| exit(2); |
| } |
| |
| /* Now create a character device on the block device */ |
| |
| ret = bchdev_register("/dev/mtdblock0", "/dev/mtd0", false); |
| if (ret < 0) |
| { |
| printf("ERROR: bchdev_register /dev/mtd0 failed: %d\n", ret); |
| fflush(stdout); |
| exit(3); |
| } |
| |
| /* Get the geometry of the FLASH device */ |
| |
| ret = master->ioctl(master, MTDIOC_GEOMETRY, |
| (unsigned long)((uintptr_t)&geo)); |
| if (ret < 0) |
| { |
| ferr("ERROR: mtd->ioctl failed: %d\n", ret); |
| exit(3); |
| } |
| |
| printf("Flash Geometry:\n"); |
| printf(" blocksize: %lu\n", (unsigned long)geo.blocksize); |
| printf(" erasesize: %lu\n", (unsigned long)geo.erasesize); |
| printf(" neraseblocks: %lu\n", (unsigned long)geo.neraseblocks); |
| |
| /* Determine the size of each partition. Make each partition an even |
| * multiple of the erase block size (perhaps not using some space at the |
| * end of the FLASH). |
| */ |
| |
| blkpererase = geo.erasesize / geo.blocksize; |
| nblocks = (geo.neraseblocks / CONFIG_EXAMPLES_MTDPART_NPARTITIONS) * |
| blkpererase; |
| partsize = nblocks * geo.blocksize; |
| |
| printf(" No. partitions: %u\n", CONFIG_EXAMPLES_MTDPART_NPARTITIONS); |
| printf(" Partition size: %ju Blocks (%lu bytes)\n", (uintmax_t)nblocks, |
| partsize); |
| |
| /* Now create MTD FLASH partitions */ |
| |
| printf("Creating partitions\n"); |
| |
| for (offset = 0, i = 1; |
| i <= CONFIG_EXAMPLES_MTDPART_NPARTITIONS; |
| offset += nblocks, i++) |
| { |
| printf(" Partition %d. Block offset=%lu, size=%lu\n", |
| i, (unsigned long)offset, (unsigned long)nblocks); |
| |
| /* Create the partition */ |
| |
| part[i] = mtd_partition(master, offset, nblocks); |
| if (!part[i]) |
| { |
| printf("ERROR: mtd_partition failed. offset=%lu nblocks=%lu\n", |
| (unsigned long)offset, (unsigned long)nblocks); |
| fflush(stdout); |
| exit(4); |
| } |
| |
| /* Initialize to provide an FTL block driver on the MTD FLASH |
| * interface |
| */ |
| |
| snprintf(blockname, sizeof(blockname), "/dev/mtdblock%d", i); |
| snprintf(charname, sizeof(charname), "/dev/mtd%d", i); |
| |
| ret = ftl_initialize(i, part[i]); |
| if (ret < 0) |
| { |
| printf("ERROR: ftl_initialize %s failed: %d\n", blockname, ret); |
| fflush(stdout); |
| exit(5); |
| } |
| |
| /* Now create a character device on the block device */ |
| |
| ret = bchdev_register(blockname, charname, false); |
| if (ret < 0) |
| { |
| printf("ERROR: bchdev_register %s failed: %d\n", charname, ret); |
| fflush(stdout); |
| exit(6); |
| } |
| } |
| |
| /* Allocate a buffer */ |
| |
| buffer = (FAR uint32_t *)malloc(geo.blocksize); |
| if (!buffer) |
| { |
| printf("ERROR: failed to allocate a sector buffer\n"); |
| fflush(stdout); |
| exit(7); |
| } |
| |
| /* Open the master MTD FLASH character driver for writing */ |
| |
| fd = open("/dev/mtd0", O_WRONLY); |
| if (fd < 0) |
| { |
| printf("ERROR: open /dev/mtd0 failed: %d\n", errno); |
| fflush(stdout); |
| exit(8); |
| } |
| |
| /* Now write the offset into every block */ |
| |
| printf("Initializing media:\n"); |
| |
| offset = 0; |
| for (i = 0; i < geo.neraseblocks; i++) |
| { |
| for (j = 0; j < blkpererase; j++) |
| { |
| /* Fill the block with the offset */ |
| |
| for (k = 0; k < geo.blocksize / sizeof(uint32_t); k++) |
| { |
| buffer[k] = offset; |
| offset += 4; |
| } |
| |
| /* And write it using the character driver */ |
| |
| nbytes = write(fd, buffer, geo.blocksize); |
| if (nbytes < 0) |
| { |
| printf("ERROR: write to /dev/mtd0 failed: %d\n", errno); |
| fflush(stdout); |
| exit(9); |
| } |
| } |
| } |
| |
| close(fd); |
| |
| /* Now read each partition */ |
| |
| printf("Checking partitions:\n"); |
| |
| for (offset = 0, i = 1; |
| i <= CONFIG_EXAMPLES_MTDPART_NPARTITIONS; |
| offset += partsize, i++) |
| { |
| printf(" Partition %d. Byte offset=%lu, size=%lu\n", |
| i, (unsigned long)offset, (unsigned long)partsize); |
| |
| /* Open the master MTD partition character driver for writing */ |
| |
| snprintf(charname, sizeof(charname), "/dev/mtd%d", i); |
| fd = open(charname, O_RDWR); |
| if (fd < 0) |
| { |
| printf("ERROR: open %s failed: %d\n", charname, errno); |
| fflush(stdout); |
| exit(10); |
| } |
| |
| /* Now verify the offset in every block */ |
| |
| check = offset; |
| sectoff = 0; |
| |
| for (j = 0; j < nblocks; j++) |
| { |
| #if 0 /* Too much */ |
| printf(" block=%u offset=%lu\n", j, (unsigned long) check); |
| #endif |
| /* Seek to the next read position */ |
| |
| seekpos = lseek(fd, sectoff, SEEK_SET); |
| if (seekpos != sectoff) |
| { |
| printf("ERROR: lseek to offset %ld failed: %d\n", |
| (unsigned long)sectoff, errno); |
| fflush(stdout); |
| exit(11); |
| } |
| |
| /* Read the next block into memory */ |
| |
| nbytes = read(fd, buffer, geo.blocksize); |
| if (nbytes < 0) |
| { |
| printf("ERROR: read from %s failed: %d\n", charname, errno); |
| fflush(stdout); |
| exit(12); |
| } |
| else if (nbytes == 0) |
| { |
| printf("ERROR: Unexpected end-of file in %s\n", charname); |
| fflush(stdout); |
| exit(13); |
| } |
| else if (nbytes != geo.blocksize) |
| { |
| printf("ERROR: Unexpected read size from %s: %ld\n", |
| charname, (unsigned long)nbytes); |
| fflush(stdout); |
| exit(14); |
| } |
| |
| /* Since we forced the size of the partition to be an even number |
| * of erase blocks, we do not expect to encounter the end of file |
| * indication. |
| */ |
| |
| else if (nbytes == 0) |
| { |
| printf("ERROR: Unexpected end of file on %s\n", charname); |
| fflush(stdout); |
| exit(15); |
| } |
| |
| /* This is not expected at all */ |
| |
| else if (nbytes != geo.blocksize) |
| { |
| printf("ERROR: Short read from %s failed: %lu\n", |
| charname, (unsigned long)nbytes); |
| fflush(stdout); |
| exit(16); |
| } |
| |
| /* Verify the offsets in the block */ |
| |
| for (k = 0; k < geo.blocksize / sizeof(uint32_t); k++) |
| { |
| if (buffer[k] != check) |
| { |
| printf("ERROR: Bad offset %lu, expected %lu\n", |
| (long)buffer[k], (long)check); |
| fflush(stdout); |
| exit(17); |
| } |
| |
| /* Invert the value to indicate that we have verified |
| * this value. |
| */ |
| |
| buffer[k] = ~check; |
| check += sizeof(uint32_t); |
| } |
| |
| /* Seek to the next write position */ |
| |
| seekpos = lseek(fd, sectoff, SEEK_SET); |
| if (seekpos != sectoff) |
| { |
| printf("ERROR: lseek to offset %ld failed: %d\n", |
| (unsigned long)sectoff, errno); |
| fflush(stdout); |
| exit(18); |
| } |
| |
| /* Now write the block back to FLASH with the modified value */ |
| |
| nbytes = write(fd, buffer, geo.blocksize); |
| if (nbytes < 0) |
| { |
| printf("ERROR: write to %s failed: %d\n", charname, errno); |
| fflush(stdout); |
| exit(19); |
| } |
| else if (nbytes != geo.blocksize) |
| { |
| printf("ERROR: Unexpected write size to %s: %ld\n", |
| charname, (unsigned long)nbytes); |
| fflush(stdout); |
| exit(20); |
| } |
| |
| /* Get the offset to the next block */ |
| |
| sectoff += geo.blocksize; |
| } |
| |
| /* Try reading one more time. We should get the end of file */ |
| |
| nbytes = read(fd, buffer, geo.blocksize); |
| if (nbytes != 0) |
| { |
| printf("ERROR: Expected end-of-file from %s failed: %zd %d\n", |
| charname, nbytes, errno); |
| fflush(stdout); |
| exit(22); |
| } |
| |
| close(fd); |
| } |
| |
| /* Now verify that all of the verifed blocks appear where we thing they |
| * should on the device. |
| */ |
| |
| printf("Verifying media:\n"); |
| |
| fd = open("/dev/mtd0", O_RDONLY); |
| if (fd < 0) |
| { |
| printf("ERROR: open /dev/mtd0 failed: %d\n", errno); |
| fflush(stdout); |
| exit(23); |
| } |
| |
| offset = 0; |
| check = 0; |
| |
| for (i = 0; i < nblocks * CONFIG_EXAMPLES_MTDPART_NPARTITIONS; i++) |
| { |
| /* Read the next block into memory */ |
| |
| nbytes = read(fd, buffer, geo.blocksize); |
| if (nbytes < 0) |
| { |
| printf("ERROR: read from %s failed: %d\n", charname, errno); |
| fflush(stdout); |
| exit(24); |
| } |
| else if (nbytes == 0) |
| { |
| printf("ERROR: Unexpected end-of file in %s\n", charname); |
| fflush(stdout); |
| exit(25); |
| } |
| else if (nbytes != geo.blocksize) |
| { |
| printf("ERROR: Unexpected read size from %s: %ld\n", |
| charname, (unsigned long)nbytes); |
| fflush(stdout); |
| exit(26); |
| } |
| |
| /* Verify the values in the block */ |
| |
| for (k = 0; k < geo.blocksize / sizeof(uint32_t); k++) |
| { |
| if (buffer[k] != ~check) |
| { |
| printf("ERROR: Bad value %lu, expected %lu\n", |
| (long)buffer[k], (long)(~check)); |
| fflush(stdout); |
| exit(27); |
| } |
| |
| check += sizeof(uint32_t); |
| } |
| } |
| |
| close(fd); |
| |
| /* And exit without bothering to clean up */ |
| |
| printf("PASS: Everything looks good\n"); |
| fflush(stdout); |
| return 0; |
| } |