diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2006-04-11 19:09:16 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2006-04-11 19:09:16 -0400 |
commit | 72212c137c574b564723327af751c4054c7cfca6 (patch) | |
tree | 13be2da9d4be695002d6ea9c396660c986e5fa5c /flashcp.c |
Initial commit
Diffstat (limited to 'flashcp.c')
-rw-r--r-- | flashcp.c | 392 |
1 files changed, 392 insertions, 0 deletions
diff --git a/flashcp.c b/flashcp.c new file mode 100644 index 0000000..8033f53 --- /dev/null +++ b/flashcp.c @@ -0,0 +1,392 @@ + +/* + * Copyright (c) 2d3D, Inc. + * Written by Abraham vd Merwe <abraham@2d3d.co.za> + * All rights reserved. + * + * $Id: flashcp.c,v 1.6 2005/11/07 11:15:11 gleixner Exp $ + * + * 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. + */ + +#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> + +typedef int bool; +#define true 1 +#define false 0 + +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS 0 + +/* 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 void showusage (const char *progname,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: %s [ -v | --verbose ] <filename> <device>\n" + " %s -h | --help\n" + "\n" + " -h | --help Show this help message\n" + " -v | --verbose Show progress reports\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", + progname,progname); + + 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 *progname,*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]; + + (progname = strrchr (argv[0],'/')) ? progname++ : (progname = argv[0]); + + /********************* + * parse cmd-line + *****************/ + + for (;;) { + int option_index = 0; + static const char *short_options = "hv"; + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", 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; + default: + DEBUG("Unknown parameter: %s\n",argv[option_index]); + showusage (progname,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 || progname == NULL || device == NULL) + showusage (progname,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); + if (filestat.st_size % 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/%luk (0%%)",KB (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/%luk (%lu%%)", + KB (written + i), + KB (filestat.st_size), + PERCENTAGE (written + i,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/%lu bytes written to flash\n", + written,written + i,device,written + result,filestat.st_size); + exit (EXIT_FAILURE); + } + + written += i; + size -= i; + } + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL, + "\rWriting data: %luk/%luk (100%%)\n", + KB (filestat.st_size), + KB (filestat.st_size)); + DEBUG("Wrote %d / %luk bytes\n",written,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/%luk (0%%)",KB (filestat.st_size)); + while (size) + { + if (size < BUFSIZE) i = size; + if (flags & FLAG_VERBOSE) + log_printf (LOG_NORMAL, + "\rVerifying data: %dk/%luk (%lu%%)", + KB (written + i), + KB (filestat.st_size), + PERCENTAGE (written + i,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: %luk/%luk (100%%)\n", + KB (filestat.st_size), + KB (filestat.st_size)); + DEBUG("Verified %d / %luk bytes\n",written,filestat.st_size); + + exit (EXIT_SUCCESS); +} + |