flashcp.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*
  2. * Copyright (c) 2d3D, Inc.
  3. * Written by Abraham vd Merwe <abraham@2d3d.co.za>
  4. * All rights reserved.
  5. *
  6. * Renamed to flashcp.c to avoid conflicts with fcp from fsh package
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. Neither the name of the author nor the names of other contributors
  17. * may be used to endorse or promote products derived from this software
  18. * without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  22. * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  23. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. */
  31. #define PROGRAM_NAME "flashcp"
  32. #include <stdio.h>
  33. #include <stdarg.h>
  34. #include <string.h>
  35. #include <stdlib.h>
  36. #include <sys/types.h>
  37. #include <sys/stat.h>
  38. #include <sys/ioctl.h>
  39. #include <fcntl.h>
  40. #include <unistd.h>
  41. #include <mtd/mtd-user.h>
  42. #include <getopt.h>
  43. #include "common.h"
  44. /* for debugging purposes only */
  45. #ifdef DEBUG
  46. #undef DEBUG
  47. #define DEBUG(fmt,args...) { log_printf (LOG_ERROR,"%d: ",__LINE__); log_printf (LOG_ERROR,fmt,## args); }
  48. #else
  49. #undef DEBUG
  50. #define DEBUG(fmt,args...)
  51. #endif
  52. #define KB(x) ((x) / 1024)
  53. #define PERCENTAGE(x,total) (((x) * 100) / (total))
  54. /* size of read/write buffer */
  55. #define BUFSIZE (10 * 1024)
  56. /* cmd-line flags */
  57. #define FLAG_NONE 0x00
  58. #define FLAG_VERBOSE 0x01
  59. #define FLAG_HELP 0x02
  60. #define FLAG_FILENAME 0x04
  61. #define FLAG_DEVICE 0x08
  62. #define FLAG_ERASE_ALL 0x10
  63. /* error levels */
  64. #define LOG_NORMAL 1
  65. #define LOG_ERROR 2
  66. static void log_printf (int level,const char *fmt, ...)
  67. {
  68. FILE *fp = level == LOG_NORMAL ? stdout : stderr;
  69. va_list ap;
  70. va_start (ap,fmt);
  71. vfprintf (fp,fmt,ap);
  72. va_end (ap);
  73. fflush (fp);
  74. }
  75. static NORETURN void showusage(bool error)
  76. {
  77. int level = error ? LOG_ERROR : LOG_NORMAL;
  78. log_printf (level,
  79. "\n"
  80. "Flash Copy - Written by Abraham van der Merwe <abraham@2d3d.co.za>\n"
  81. "\n"
  82. "usage: %1$s [ -v | --verbose | -A | --erase-all ] <filename> <device>\n"
  83. " %1$s -h | --help\n"
  84. " %1$s -V | --version\n"
  85. "\n"
  86. " -h | --help Show this help message\n"
  87. " -v | --verbose Show progress reports\n"
  88. " -A | --erase-all Erases the whole device regardless of the image size\n"
  89. " -V | --version Show version information and exit\n"
  90. " <filename> File which you want to copy to flash\n"
  91. " <device> Flash device to write to (e.g. /dev/mtd0, /dev/mtd1, etc.)\n"
  92. "\n",
  93. PROGRAM_NAME);
  94. exit (error ? EXIT_FAILURE : EXIT_SUCCESS);
  95. }
  96. static int safe_open (const char *pathname,int flags)
  97. {
  98. int fd;
  99. fd = open (pathname,flags);
  100. if (fd < 0)
  101. {
  102. log_printf (LOG_ERROR,"While trying to open %s",pathname);
  103. if (flags & O_RDWR)
  104. log_printf (LOG_ERROR," for read/write access");
  105. else if (flags & O_RDONLY)
  106. log_printf (LOG_ERROR," for read access");
  107. else if (flags & O_WRONLY)
  108. log_printf (LOG_ERROR," for write access");
  109. log_printf (LOG_ERROR,": %m\n");
  110. exit (EXIT_FAILURE);
  111. }
  112. return (fd);
  113. }
  114. static void safe_read (int fd,const char *filename,void *buf,size_t count,bool verbose)
  115. {
  116. ssize_t result;
  117. result = read (fd,buf,count);
  118. if (count != result)
  119. {
  120. if (verbose) log_printf (LOG_NORMAL,"\n");
  121. if (result < 0)
  122. {
  123. log_printf (LOG_ERROR,"While reading data from %s: %m\n",filename);
  124. exit (EXIT_FAILURE);
  125. }
  126. log_printf (LOG_ERROR,"Short read count returned while reading from %s\n",filename);
  127. exit (EXIT_FAILURE);
  128. }
  129. }
  130. static void safe_rewind (int fd,const char *filename)
  131. {
  132. if (lseek (fd,0L,SEEK_SET) < 0)
  133. {
  134. log_printf (LOG_ERROR,"While seeking to start of %s: %m\n",filename);
  135. exit (EXIT_FAILURE);
  136. }
  137. }
  138. /******************************************************************************/
  139. static int dev_fd = -1,fil_fd = -1;
  140. static void cleanup (void)
  141. {
  142. if (dev_fd > 0) close (dev_fd);
  143. if (fil_fd > 0) close (fil_fd);
  144. }
  145. int main (int argc,char *argv[])
  146. {
  147. const char *filename = NULL,*device = NULL;
  148. int i,flags = FLAG_NONE;
  149. ssize_t result;
  150. size_t size,written;
  151. struct mtd_info_user mtd;
  152. struct erase_info_user erase;
  153. struct stat filestat;
  154. unsigned char src[BUFSIZE],dest[BUFSIZE];
  155. /*********************
  156. * parse cmd-line
  157. *****************/
  158. for (;;) {
  159. int option_index = 0;
  160. static const char *short_options = "hvAV";
  161. static const struct option long_options[] = {
  162. {"help", no_argument, 0, 'h'},
  163. {"verbose", no_argument, 0, 'v'},
  164. {"erase-all", no_argument, 0, 'A'},
  165. {"version", no_argument, 0, 'V'},
  166. {0, 0, 0, 0},
  167. };
  168. int c = getopt_long(argc, argv, short_options,
  169. long_options, &option_index);
  170. if (c == EOF) {
  171. break;
  172. }
  173. switch (c) {
  174. case 'h':
  175. flags |= FLAG_HELP;
  176. DEBUG("Got FLAG_HELP\n");
  177. break;
  178. case 'v':
  179. flags |= FLAG_VERBOSE;
  180. DEBUG("Got FLAG_VERBOSE\n");
  181. break;
  182. case 'A':
  183. flags |= FLAG_ERASE_ALL;
  184. DEBUG("Got FLAG_ERASE_ALL\n");
  185. break;
  186. case 'V':
  187. common_print_version();
  188. exit(EXIT_SUCCESS);
  189. break;
  190. default:
  191. DEBUG("Unknown parameter: %s\n",argv[option_index]);
  192. showusage(true);
  193. }
  194. }
  195. if (optind+2 == argc) {
  196. flags |= FLAG_FILENAME;
  197. filename = argv[optind];
  198. DEBUG("Got filename: %s\n",filename);
  199. flags |= FLAG_DEVICE;
  200. device = argv[optind+1];
  201. DEBUG("Got device: %s\n",device);
  202. }
  203. if (flags & FLAG_HELP || device == NULL)
  204. showusage(flags != FLAG_HELP);
  205. atexit (cleanup);
  206. /* get some info about the flash device */
  207. dev_fd = safe_open (device,O_SYNC | O_RDWR);
  208. if (ioctl (dev_fd,MEMGETINFO,&mtd) < 0)
  209. {
  210. DEBUG("ioctl(): %m\n");
  211. log_printf (LOG_ERROR,"This doesn't seem to be a valid MTD flash device!\n");
  212. exit (EXIT_FAILURE);
  213. }
  214. /* get some info about the file we want to copy */
  215. fil_fd = safe_open (filename,O_RDONLY);
  216. if (fstat (fil_fd,&filestat) < 0)
  217. {
  218. log_printf (LOG_ERROR,"While trying to get the file status of %s: %m\n",filename);
  219. exit (EXIT_FAILURE);
  220. }
  221. /* does it fit into the device/partition? */
  222. if (filestat.st_size > mtd.size)
  223. {
  224. log_printf (LOG_ERROR,"%s won't fit into %s!\n",filename,device);
  225. exit (EXIT_FAILURE);
  226. }
  227. /*****************************************************
  228. * erase enough blocks so that we can write the file *
  229. *****************************************************/
  230. #warning "Check for smaller erase regions"
  231. erase.start = 0;
  232. if (flags & FLAG_ERASE_ALL)
  233. {
  234. erase.length = mtd.size;
  235. }
  236. else
  237. {
  238. erase.length = (filestat.st_size + mtd.erasesize - 1) / mtd.erasesize;
  239. erase.length *= mtd.erasesize;
  240. }
  241. if (flags & FLAG_VERBOSE)
  242. {
  243. /* if the user wants verbose output, erase 1 block at a time and show him/her what's going on */
  244. int blocks = erase.length / mtd.erasesize;
  245. erase.length = mtd.erasesize;
  246. log_printf (LOG_NORMAL,"Erasing blocks: 0/%d (0%%)",blocks);
  247. for (i = 1; i <= blocks; i++)
  248. {
  249. log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (%d%%)",i,blocks,PERCENTAGE (i,blocks));
  250. if (ioctl (dev_fd,MEMERASE,&erase) < 0)
  251. {
  252. log_printf (LOG_NORMAL,"\n");
  253. log_printf (LOG_ERROR,
  254. "While erasing blocks 0x%.8x-0x%.8x on %s: %m\n",
  255. (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
  256. exit (EXIT_FAILURE);
  257. }
  258. erase.start += mtd.erasesize;
  259. }
  260. log_printf (LOG_NORMAL,"\rErasing blocks: %d/%d (100%%)\n",blocks,blocks);
  261. }
  262. else
  263. {
  264. /* if not, erase the whole chunk in one shot */
  265. if (ioctl (dev_fd,MEMERASE,&erase) < 0)
  266. {
  267. log_printf (LOG_ERROR,
  268. "While erasing blocks from 0x%.8x-0x%.8x on %s: %m\n",
  269. (unsigned int) erase.start,(unsigned int) (erase.start + erase.length),device);
  270. exit (EXIT_FAILURE);
  271. }
  272. }
  273. DEBUG("Erased %u / %luk bytes\n",erase.length,filestat.st_size);
  274. /**********************************
  275. * write the entire file to flash *
  276. **********************************/
  277. if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Writing data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
  278. size = filestat.st_size;
  279. i = BUFSIZE;
  280. written = 0;
  281. while (size)
  282. {
  283. if (size < BUFSIZE) i = size;
  284. if (flags & FLAG_VERBOSE)
  285. log_printf (LOG_NORMAL,"\rWriting data: %dk/%lluk (%llu%%)",
  286. KB (written + i),
  287. KB ((unsigned long long)filestat.st_size),
  288. PERCENTAGE (written + i,(unsigned long long)filestat.st_size));
  289. /* read from filename */
  290. safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
  291. /* write to device */
  292. result = write (dev_fd,src,i);
  293. if (i != result)
  294. {
  295. if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"\n");
  296. if (result < 0)
  297. {
  298. log_printf (LOG_ERROR,
  299. "While writing data to 0x%.8lx-0x%.8lx on %s: %m\n",
  300. written,written + i,device);
  301. exit (EXIT_FAILURE);
  302. }
  303. log_printf (LOG_ERROR,
  304. "Short write count returned while writing to x%.8zx-0x%.8zx on %s: %zu/%llu bytes written to flash\n",
  305. written,written + i,device,written + result,(unsigned long long)filestat.st_size);
  306. exit (EXIT_FAILURE);
  307. }
  308. written += i;
  309. size -= i;
  310. }
  311. if (flags & FLAG_VERBOSE)
  312. log_printf (LOG_NORMAL,
  313. "\rWriting data: %lluk/%lluk (100%%)\n",
  314. KB ((unsigned long long)filestat.st_size),
  315. KB ((unsigned long long)filestat.st_size));
  316. DEBUG("Wrote %d / %lluk bytes\n",written,(unsigned long long)filestat.st_size);
  317. /**********************************
  318. * verify that flash == file data *
  319. **********************************/
  320. safe_rewind (fil_fd,filename);
  321. safe_rewind (dev_fd,device);
  322. size = filestat.st_size;
  323. i = BUFSIZE;
  324. written = 0;
  325. if (flags & FLAG_VERBOSE) log_printf (LOG_NORMAL,"Verifying data: 0k/%lluk (0%%)",KB ((unsigned long long)filestat.st_size));
  326. while (size)
  327. {
  328. if (size < BUFSIZE) i = size;
  329. if (flags & FLAG_VERBOSE)
  330. log_printf (LOG_NORMAL,
  331. "\rVerifying data: %luk/%lluk (%llu%%)",
  332. KB (written + i),
  333. KB ((unsigned long long)filestat.st_size),
  334. PERCENTAGE (written + i,(unsigned long long)filestat.st_size));
  335. /* read from filename */
  336. safe_read (fil_fd,filename,src,i,flags & FLAG_VERBOSE);
  337. /* read from device */
  338. safe_read (dev_fd,device,dest,i,flags & FLAG_VERBOSE);
  339. /* compare buffers */
  340. if (memcmp (src,dest,i))
  341. {
  342. log_printf (LOG_ERROR,
  343. "File does not seem to match flash data. First mismatch at 0x%.8zx-0x%.8zx\n",
  344. written,written + i);
  345. exit (EXIT_FAILURE);
  346. }
  347. written += i;
  348. size -= i;
  349. }
  350. if (flags & FLAG_VERBOSE)
  351. log_printf (LOG_NORMAL,
  352. "\rVerifying data: %lluk/%lluk (100%%)\n",
  353. KB ((unsigned long long)filestat.st_size),
  354. KB ((unsigned long long)filestat.st_size));
  355. DEBUG("Verified %d / %lluk bytes\n",written,(unsigned long long)filestat.st_size);
  356. exit (EXIT_SUCCESS);
  357. }