392 lines
11 KiB
C
392 lines
11 KiB
C
/*
|
|
* Copyright (c) 2d3D, Inc.
|
|
* Written by Abraham vd Merwe <abraham@2d3d.co.za>
|
|
* All rights reserved.
|
|
*
|
|
* Renamed to flashcp.c to avoid conflicts with fcp from fsh package
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the author nor the names of other contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#define PROGRAM_NAME "flashcp"
|
|
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/ioctl.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <mtd/mtd-user.h>
|
|
#include <getopt.h>
|
|
|
|
#include "common.h"
|
|
|
|
/* for debugging purposes only */
|
|
#ifdef DEBUG
|
|
#undef DEBUG
|
|
#define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
|
|
#else
|
|
#undef DEBUG
|
|
#define DEBUG(fmt,args...)
|
|
#endif
|
|
|
|
#define KB(x) ((x) / 1024)
|
|
#define PERCENTAGE(x,total) (((x) * 100) / (total))
|
|
|
|
/* size of read/write buffer */
|
|
#define BUFSIZE (10 * 1024)
|
|
|
|
/* cmd-line flags */
|
|
#define FLAG_NONE 0x00
|
|
#define FLAG_VERBOSE 0x01
|
|
#define FLAG_HELP 0x02
|
|
#define FLAG_FILENAME 0x04
|
|
#define FLAG_DEVICE 0x08
|
|
|
|
/* error levels */
|
|
#define LOG_NORMAL 1
|
|
#define LOG_ERROR 2
|
|
|
|
static void log_printf (int level,const char *fmt, ...)
|
|
{
|
|
FILE *fp = level == LOG_NORMAL ? stdout : stderr;
|
|
va_list ap;
|
|
va_start (ap,fmt);
|
|
vfprintf (fp,fmt,ap);
|
|
va_end (ap);
|
|
fflush (fp);
|
|
}
|
|
|
|
static NORETURN void showusage(bool error)
|
|
{
|
|
int level = error ? LOG_ERROR : LOG_NORMAL;
|
|
|
|
log_printf (level,
|
|
"\n"
|
|
"Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
|
|
"\n"
|
|
"usage: %1$s [ -v | --verbose ] <filename> <device>\n"
|
|
" %1$s -h | --help\n"
|
|
" %1$s -V | --version\n"
|
|
"\n"
|
|
" -h | --help Show this help message\n"
|
|
" -v | --verbose Show progress reports\n"
|
|
" -V | --version Show version information and exit\n"
|
|
" <filename> File which you want to copy to flash\n"
|
|
" <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
|
|
"\n",
|
|
PROGRAM_NAME);
|
|
|
|
exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
|
|
}
|
|
|
|
static int safe_open (const char *pathname,int flags)
|
|
{
|
|
int fd;
|
|
|
|
fd = open (pathname,flags);
|
|
if (fd < 0)
|
|
{
|
|
log_printf (LOG_ERROR,"While trying to open %s",pathname);
|
|
if (flags & O_RDWR)
|
|
log_printf (LOG_ERROR," for read/write access");
|
|
else if (flags & O_RDONLY)
|
|
log_printf (LOG_ERROR," for read access");
|
|
else if (flags & O_WRONLY)
|
|
log_printf (LOG_ERROR," for write access");
|
|
log_printf (LOG_ERROR,": %m\n");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
return (fd);
|
|
}
|
|
|
|
static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
|
|
{
|
|
ssize_t result;
|
|
|
|
result = read (fd,buf,count);
|
|
if (count != result)
|
|
{
|
|
if (verbose) log_printf (LOG_NORMAL,"\n");
|
|
if (result < 0)
|
|
{
|
|
log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void safe_rewind (int fd,const char *filename)
|
|
{
|
|
if (lseek (fd,0L,SEEK_SET) < 0)
|
|
{
|
|
log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int dev_fd = -1,fil_fd = -1;
|
|
|
|
static void cleanup (void)
|
|
{
|
|
if (dev_fd > 0) close (dev_fd);
|
|
if (fil_fd > 0) close (fil_fd);
|
|
}
|
|
|
|
int main (int argc,char *argv[])
|
|
{
|
|
const char *filename = NULL,*device = NULL;
|
|
int i,flags = FLAG_NONE;
|
|
ssize_t result;
|
|
size_t size,written;
|
|
struct mtd_info_user mtd;
|
|
struct erase_info_user erase;
|
|
struct stat filestat;
|
|
unsigned char src[BUFSIZE],dest[BUFSIZE];
|
|
|
|
/*********************
|
|
* parse cmd-line
|
|
*****************/
|
|
|
|
for (;;) {
|
|
int option_index = 0;
|
|
static const char *short_options = "hvV";
|
|
static const struct option long_options[] = {
|
|
{"help", no_argument, 0, 'h'},
|
|
{"verbose", no_argument, 0, 'v'},
|
|
{"version", no_argument, 0, 'V'},
|
|
{0, 0, 0, 0},
|
|
};
|
|
|
|
int c = getopt_long(argc, argv, short_options,
|
|
long_options, &option_index);
|
|
if (c == EOF) {
|
|
break;
|
|
}
|
|
|
|
switch (c) {
|
|
case 'h':
|
|
flags |= FLAG_HELP;
|
|
DEBUG("Got FLAG_HELP\n");
|
|
break;
|
|
case 'v':
|
|
flags |= FLAG_VERBOSE;
|
|
DEBUG("Got FLAG_VERBOSE\n");
|
|
break;
|
|
case 'V':
|
|
common_print_version();
|
|
exit(EXIT_SUCCESS);
|
|
break;
|
|
default:
|
|
DEBUG("Unknown parameter: %s\n",argv[option_index]);
|
|
showusage(true);
|
|
}
|
|
}
|
|
if (optind+2 == argc) {
|
|
flags |= FLAG_FILENAME;
|
|
filename = argv[optind];
|
|
DEBUG("Got filename: %s\n",filename);
|
|
|
|
flags |= FLAG_DEVICE;
|
|
device = argv[optind+1];
|
|
DEBUG("Got device: %s\n",device);
|
|
}
|
|
|
|
if (flags & FLAG_HELP || device == NULL)
|
|
showusage(flags != FLAG_HELP);
|
|
|
|
atexit (cleanup);
|
|
|
|
/* get some info about the flash device */
|
|
dev_fd = safe_open (device,O_SYNC | O_RDWR);
|
|
if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
|
|
{
|
|
DEBUG("ioctl(): %m\n");
|
|
log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* get some info about the file we want to copy */
|
|
fil_fd = safe_open (filename,O_RDONLY);
|
|
if (fstat (fil_fd,&filestat) < 0)
|
|
{
|
|
log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/* does it fit into the device/partition? */
|
|
if (filestat.st_size > mtd.size)
|
|
{
|
|
log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
/*****************************************************
|
|
* erase enough blocks so that we can write the file *
|
|
*****************************************************/
|
|
|
|
#warning "Check for smaller erase regions"
|
|
|
|
erase.start = 0;
|
|
erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize;
|
|
erase.length *= mtd.erasesize;
|
|
|
|
if (flags & FLAG_VERBOSE)
|
|
{
|
|
/* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
|
|
int blocks = erase.length / mtd.erasesize;
|
|
erase.length = mtd.erasesize;
|
|
log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
|
|
for (i = 1; i <= blocks; i++)
|
|
{
|
|
log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
|
|
if (ioctl (dev_fd,MEMERASE,&erase) < 0)
|
|
{
|
|
log_printf (LOG_NORMAL,"\n");
|
|
log_printf (LOG_ERROR,
|
|
"While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
|
|
(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
erase.start += mtd.erasesize;
|
|
}
|
|
log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
|
|
}
|
|
else
|
|
{
|
|
/* if not, erase the whole chunk in one shot */
|
|
if (ioctl (dev_fd,MEMERASE,&erase) < 0)
|
|
{
|
|
log_printf (LOG_ERROR,
|
|
"While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
|
|
(unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
}
|
|
DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
|
|
|
|
/**********************************
|
|
* write the entire file to flash *
|
|
**********************************/
|
|
|
|
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
|
|
size = filestat.st_size;
|
|
i = BUFSIZE;
|
|
written = 0;
|
|
while (size)
|
|
{
|
|
if (size < BUFSIZE) i = size;
|
|
if (flags & FLAG_VERBOSE)
|
|
log_printf (LOG_NORMAL,"\rWriting data: %dk/%lluk (%llu%%)",
|
|
KB (written + i),
|
|
KB ((unsigned long long)filestat.st_size),
|
|
PERCENTAGE (written + i,(unsigned long long)filestat.st_size));
|
|
|
|
/* read from filename */
|
|
safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
|
|
|
|
/* write to device */
|
|
result = write (dev_fd,src,i);
|
|
if (i != result)
|
|
{
|
|
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
|
|
if (result < 0)
|
|
{
|
|
log_printf (LOG_ERROR,
|
|
"While writing data to 0x%.8x-0x%.8x on %s: %m\n",
|
|
written,written + i,device);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
log_printf (LOG_ERROR,
|
|
"Short write count returned while writing to x%.8x-0x%.8x on %s: %d/%llu bytes written to flash\n",
|
|
written,written + i,device,written + result,(unsigned long long)filestat.st_size);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
written += i;
|
|
size -= i;
|
|
}
|
|
if (flags & FLAG_VERBOSE)
|
|
log_printf (LOG_NORMAL,
|
|
"\rWriting data: %lluk/%lluk (100%%)\n",
|
|
KB ((unsigned long long)filestat.st_size),
|
|
KB ((unsigned long long)filestat.st_size));
|
|
DEBUG("Wrote %d / %lluk bytes\n",written,(unsigned long long)filestat.st_size);
|
|
|
|
/**********************************
|
|
* verify that flash == file data *
|
|
**********************************/
|
|
|
|
safe_rewind (fil_fd,filename);
|
|
safe_rewind (dev_fd,device);
|
|
size = filestat.st_size;
|
|
i = BUFSIZE;
|
|
written = 0;
|
|
if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
|
|
while (size)
|
|
{
|
|
if (size < BUFSIZE) i = size;
|
|
if (flags & FLAG_VERBOSE)
|
|
log_printf (LOG_NORMAL,
|
|
"\rVerifying data: %dk/%lluk (%llu%%)",
|
|
KB (written + i),
|
|
KB ((unsigned long long)filestat.st_size),
|
|
PERCENTAGE (written + i,(unsigned long long)filestat.st_size));
|
|
|
|
/* read from filename */
|
|
safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
|
|
|
|
/* read from device */
|
|
safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
|
|
|
|
/* compare buffers */
|
|
if (memcmp (src,dest,i))
|
|
{
|
|
log_printf (LOG_ERROR,
|
|
"File does not seem to match flash data. First mismatch at 0x%.8x-0x%.8x\n",
|
|
written,written + i);
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
written += i;
|
|
size -= i;
|
|
}
|
|
if (flags & FLAG_VERBOSE)
|
|
log_printf (LOG_NORMAL,
|
|
"\rVerifying data: %lluk/%lluk (100%%)\n",
|
|
KB ((unsigned long long)filestat.st_size),
|
|
KB ((unsigned long long)filestat.st_size));
|
|
DEBUG("Verified %d / %lluk bytes\n",written,(unsigned long long)filestat.st_size);
|
|
|
|
exit (EXIT_SUCCESS);
|
|
}
|