blob: 8e4f288f3d388178d873830169eed9474e77fbe7 [file] [log] [blame]
/****************************************************************************
* apps/fsutils/flashtool/flashtool_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 <nuttx/mtd/mtd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <debug.h>
#include <errno.h>
#include <ctype.h>
#include <sys/ioctl.h>
/****************************************************************************
* Private Types
****************************************************************************/
enum opr_list
{
HELP, DEVICE_INFO, READ_PAGE, WRITE_PAGE, ERASE_BLOCK, ERASE_ALL,
CHECK_BAD_BLOCK
};
/****************************************************************************
* Private Functions
****************************************************************************/
static void display_help(void)
{
printf("Usage : flashtool [OPTION [ARG]] device_name ...\n");
printf("-h show this help statement\n");
printf("-i display device geometry information\n");
printf("-r <page> read the specified pages\n");
printf("-w <page> write the specified pages\n");
printf("-e <block> erase the specified blocks\n");
printf("-n <num> number of pages or blocks\n");
printf("-f <file> file name for read or write\n");
printf("-b <block> check whether <block> is bad\n");
printf("-c erase all of the flash\n");
printf("-d <device> device name\n");
}
static void display_device_info(FAR const struct mtd_geometry_s *geo)
{
printf("Size of one read/write page: %lu\n", geo->blocksize);
printf("Size of one erase block: %lu\n", geo->erasesize);
printf("Number of erase blocks: %lu\n", geo->neraseblocks);
}
static int read_page(int fd, FAR const struct mtd_geometry_s *geo,
off_t start_page, size_t npages)
{
FAR unsigned char *buf;
ssize_t ret = OK;
off_t seek;
size_t i;
buf = malloc(geo->blocksize);
if (!buf)
{
printf("ERROR: read_page cannot allocate memory %d\n", geo->blocksize);
return ERROR;
}
seek = geo->blocksize * start_page;
if (lseek(fd, seek, SEEK_SET) != seek)
{
printf("ERROR: Cannot seek mtd to offset %lld: %d", seek, errno);
goto oops;
}
printf("Flash pages contents:\n");
for (i = 0; i < npages; i++)
{
ret = read(fd, buf, geo->blocksize);
if (ret < 0)
{
printf("ERROR: Read error: %d!\n", errno);
goto oops;
}
printf("Flash page %lld:\n", start_page + i);
lib_dumpbuffer(NULL, buf, ret);
}
oops:
free(buf);
return ret;
}
/* Write npages data from img to flash
* If size of img is less than npags, only write img size
*/
static int write_page(int fd, FAR const struct mtd_geometry_s *geo,
off_t start_page, size_t npages, FAR const char *img)
{
FAR unsigned char *buf;
off_t imglen;
off_t seek;
off_t i;
int fd_src;
int ret = OK;
if (img == NULL)
{
printf("ERROR: Need source file path!\n");
return ERROR;
}
printf("Write data from %s...\n", img);
seek = geo->blocksize * start_page;
if (lseek(fd, seek, SEEK_SET) != seek)
{
printf("ERROR: cannot seek mtd to offset %lld: %d", seek, errno);
return ERROR;
}
fd_src = open(img, O_RDONLY);
if (fd_src < 0)
{
printf("ERROR: Open file %s failed: %d\n", img, errno);
return ERROR;
}
imglen = lseek(fd_src, 0, SEEK_END);
if (lseek(fd_src, 0, SEEK_SET) != 0)
{
close(fd_src);
return ERROR;
}
/* Malloc a size of page for buf */
buf = malloc(geo->blocksize);
if (!buf)
{
printf("ERROR: write_page cannot allocate memory %d\n",
geo->blocksize);
close(fd_src);
return ERROR;
}
for (i = start_page; i < start_page + npages; i++)
{
ret = read(fd_src, buf,
(imglen < geo->blocksize) ? imglen : geo->blocksize);
if (ret < 0)
{
printf("ERROR: read source file error: %d!\n", errno);
goto oops;
}
ret = write(fd, buf, geo->blocksize);
if (ret < 0)
{
printf("ERROR: write error: %d!\n", errno);
goto oops;
}
imglen -= geo->blocksize;
if (imglen <= 0)
{
break;
}
}
oops:
close(fd_src);
free(buf);
return ret;
}
static int erase_block(int fd, off_t start_block,
size_t nblocks)
{
struct mtd_erase_s erase;
int ret;
printf("Erase block from %lld to %lld!\n", start_block,
start_block + nblocks -1);
erase.startblock = start_block;
erase.nblocks = nblocks;
ret = ioctl(fd, MTDIOC_ERASESECTORS, &erase);
if (ret < 0)
{
printf("ERROR: Erase failed: %d!\n", errno);
}
return ret;
}
static int erase_all(int fd)
{
if (ioctl(fd, MTDIOC_BULKERASE, 0) < 0)
{
printf("ERROR: MTD ioctl(MTDIOC_BULKERASE) failed: %d\n", errno);
return ERROR;
}
return OK;
}
static int check_bad_block(int fd, FAR const struct mtd_geometry_s *geo)
{
struct mtd_bad_block_s bad_block;
int flag = 0;
int ret;
int i;
printf("Check bad blocks in flash......\n");
for (i = 0; i < geo->neraseblocks; i++)
{
bad_block.block_num = i;
ret = ioctl(fd, MTDIOC_ISBAD, &bad_block);
if (ret != OK)
{
printf("ERROR: MTDIOC_ISBAD failed: %d!\n", errno);
return ERROR;
}
else
{
if (bad_block.bad_flag)
{
printf("Block %d is bad!\n", i);
flag = 1;
}
}
}
if (flag == 0)
{
printf("No bad blocks!\n");
}
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
int main(int argc, FAR char *argv[])
{
FAR char *mtddev = NULL;
FAR char *img = NULL;
struct mtd_geometry_s geo;
enum opr_list opr = HELP;
off_t npages = 1;
off_t start;
int option;
int mode = O_RDONLY;
int fd;
int ret;
while ((option = getopt(argc, argv, ":hir:w:e:n:f:bcd:")) != -1)
{
switch (option)
{
case 'h':
display_help();
exit(0);
case 'i':
opr = DEVICE_INFO;
break;
case 'r':
opr = READ_PAGE;
start = strtoul(optarg, NULL, 0);
break;
case 'w':
opr = WRITE_PAGE;
mode = O_RDWR;
start = strtoul(optarg, NULL, 0);
break;
case 'e':
opr = ERASE_BLOCK;
start = strtoul(optarg, NULL, 0);
break;
case 'n':
npages = strtoul(optarg, NULL, 0);
break;
case 'f':
img = optarg;
break;
case 'b':
opr = CHECK_BAD_BLOCK;
break;
case 'c':
opr = ERASE_ALL;
break;
case 'd':
mtddev = optarg;
break;
case ':':
printf("Error: Missing required argument\n");
exit(1);
case '?':
printf("Error: Unrecognized option\n");
exit(1);
}
}
if (mtddev == NULL)
{
printf("Error: Device name is required!\n");
return -1;
}
fd = open(mtddev, mode, 0777);
if (fd < 0)
{
printf("ERROR: Failed to open '%s': %d\n", mtddev, errno);
return -1;
}
ret = ioctl(fd, MTDIOC_GEOMETRY, &geo);
if (ret < 0)
{
printf("ERROR: Ioctl MTDIOC_GEOMETRY failed: %d\n", errno);
goto oops;
}
switch (opr)
{
case DEVICE_INFO:
display_device_info(&geo);
break;
case READ_PAGE:
ret = read_page(fd, &geo, start, npages);
break;
case WRITE_PAGE:
ret = write_page(fd, &geo, start, npages, img);
break;
case ERASE_BLOCK:
ret = erase_block(fd, start, npages);
break;
case ERASE_ALL:
ret = erase_all(fd);
break;
case CHECK_BAD_BLOCK:
ret = check_bad_block(fd, &geo);
break;
default:
}
oops:
close(fd);
return ret;
}