0a968409a8bb4a62710f243ab75789ac412209a9
[openwrt/openwrt.git] / package / mtd / src / mtd.c
1 /*
2 * mtd - simple memory technology device manipulation tool
3 *
4 * Copyright (C) 2005 Waldemar Brodkorb <wbx@dass-it.de>,
5 * Felix Fietkau <nbd@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * $Id$
22 *
23 * The code is based on the linux-mtd examples.
24 */
25
26 #include <limits.h>
27 #include <unistd.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <stdint.h>
31 #include <sys/ioctl.h>
32 #include <sys/syscall.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <error.h>
36 #include <time.h>
37 #include <string.h>
38 #include <sys/ioctl.h>
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/mount.h>
42 #include <sys/stat.h>
43 #include <sys/reboot.h>
44 #include <linux/reboot.h>
45
46 #include "mtd.h"
47
48 #define TRX_MAGIC 0x30524448 /* "HDR0" */
49 #define BUFSIZE (16 * 1024)
50 #define MAX_ARGS 8
51
52 #define DEBUG
53
54 #define SYSTYPE_UNKNOWN 0
55 #define SYSTYPE_BROADCOM 1
56 /* to be continued */
57
58 struct trx_header {
59 uint32_t magic; /* "HDR0" */
60 uint32_t len; /* Length of file including header */
61 uint32_t crc32; /* 32-bit CRC from flag_version to end of file */
62 uint32_t flag_version; /* 0:15 flags, 16:31 version */
63 uint32_t offsets[3]; /* Offsets of partitions from start of header */
64 };
65
66 char buf[BUFSIZE];
67 int buflen;
68 int quiet;
69
70 #ifdef target_brcm
71 int
72 image_check_brcm(int imagefd, const char *mtd)
73 {
74 struct trx_header *trx = (struct trx_header *) buf;
75 struct mtd_info_user mtdInfo;
76 int fd;
77
78 if (strcmp(mtd, "linux") != 0)
79 return 1;
80
81 buflen = read(imagefd, buf, 32);
82 if (buflen < 32) {
83 fprintf(stdout, "Could not get image header, file too small (%ld bytes)\n", buflen);
84 return 0;
85 }
86
87 switch(trx->magic) {
88 case 0x47343557: /* W54G */
89 case 0x53343557: /* W54S */
90 case 0x73343557: /* W54s */
91 case 0x46343557: /* W54F */
92 case 0x55343557: /* W54U */
93 /* ignore the first 32 bytes */
94 buflen = read(imagefd, buf, sizeof(struct trx_header));
95 break;
96 }
97
98 if (trx->magic != TRX_MAGIC || trx->len < sizeof(struct trx_header)) {
99 if (quiet < 2) {
100 fprintf(stderr, "Bad trx header\n");
101 fprintf(stderr, "If this is a firmware in bin format, like some of the\n"
102 "original firmware files are, you need to convert it to trx.\n");
103 }
104 return 0;
105 }
106
107 /* check if image fits to mtd device */
108 fd = mtd_open(mtd, O_RDWR | O_SYNC);
109 if(fd < 0) {
110 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
111 exit(1);
112 }
113
114 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
115 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
116 exit(1);
117 }
118
119 if(mtdInfo.size < trx->len) {
120 fprintf(stderr, "Image too big for partition: %s\n", mtd);
121 close(fd);
122 return 0;
123 }
124
125 close(fd);
126 return 1;
127 }
128 #endif /* target_brcm */
129
130 int
131 image_check(int imagefd, const char *mtd)
132 {
133 int fd, systype;
134 size_t count;
135 char *c;
136 FILE *f;
137
138 #ifdef target_brcm
139 return image_check_brcm(imagefd, mtd);
140 #endif
141 }
142
143 int mtd_check(char *mtd)
144 {
145 struct mtd_info_user mtdInfo;
146 int fd;
147
148 fd = mtd_open(mtd, O_RDWR | O_SYNC);
149 if(fd < 0) {
150 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
151 return 0;
152 }
153
154 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
155 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
156 close(fd);
157 return 0;
158 }
159
160 close(fd);
161 return 1;
162 }
163
164 int
165 mtd_unlock(const char *mtd)
166 {
167 int fd;
168 struct mtd_info_user mtdInfo;
169 struct erase_info_user mtdLockInfo;
170
171 fd = mtd_open(mtd, O_RDWR | O_SYNC);
172 if(fd < 0) {
173 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
174 exit(1);
175 }
176
177 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
178 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
179 close(fd);
180 exit(1);
181 }
182
183 mtdLockInfo.start = 0;
184 mtdLockInfo.length = mtdInfo.size;
185 if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
186 close(fd);
187 return 0;
188 }
189
190 close(fd);
191 return 0;
192 }
193
194 int
195 mtd_open(const char *mtd, int flags)
196 {
197 FILE *fp;
198 char dev[PATH_MAX];
199 int i;
200 int ret;
201
202 if ((fp = fopen("/proc/mtd", "r"))) {
203 while (fgets(dev, sizeof(dev), fp)) {
204 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
205 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
206 if ((ret=open(dev, flags))<0) {
207 snprintf(dev, sizeof(dev), "/dev/mtd%d", i);
208 ret=open(dev, flags);
209 }
210 fclose(fp);
211 return ret;
212 }
213 }
214 fclose(fp);
215 }
216
217 return open(mtd, flags);
218 }
219
220 int
221 mtd_erase(const char *mtd)
222 {
223 int fd;
224 struct mtd_info_user mtdInfo;
225 struct erase_info_user mtdEraseInfo;
226
227 fd = mtd_open(mtd, O_RDWR | O_SYNC);
228 if(fd < 0) {
229 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
230 exit(1);
231 }
232
233 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
234 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
235 close(fd);
236 exit(1);
237 }
238
239 mtdEraseInfo.length = mtdInfo.erasesize;
240
241 for (mtdEraseInfo.start = 0;
242 mtdEraseInfo.start < mtdInfo.size;
243 mtdEraseInfo.start += mtdInfo.erasesize) {
244
245 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
246 if(ioctl(fd, MEMERASE, &mtdEraseInfo))
247 fprintf(stderr, "Failed to erase block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
248 }
249
250 close(fd);
251 return 0;
252
253 }
254
255 int
256 mtd_write(int imagefd, const char *mtd)
257 {
258 int fd, i, result;
259 size_t r, w, e;
260 struct mtd_info_user mtdInfo;
261 struct erase_info_user mtdEraseInfo;
262 int ret = 0;
263
264 fd = mtd_open(mtd, O_RDWR | O_SYNC);
265 if(fd < 0) {
266 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
267 exit(1);
268 }
269
270 if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
271 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
272 close(fd);
273 exit(1);
274 }
275
276 r = w = e = 0;
277 if (!quiet)
278 fprintf(stderr, " [ ]");
279
280 for (;;) {
281 /* buffer may contain data already (from trx check) */
282 r = buflen;
283 r += read(imagefd, buf + buflen, BUFSIZE - buflen);
284 w += r;
285
286 /* EOF */
287 if (r <= 0) break;
288
289 /* need to erase the next block before writing data to it */
290 while (w > e) {
291 mtdEraseInfo.start = e;
292 mtdEraseInfo.length = mtdInfo.erasesize;
293
294 if (!quiet)
295 fprintf(stderr, "\b\b\b[e]");
296 /* erase the chunk */
297 if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
298 fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
299 exit(1);
300 }
301 e += mtdInfo.erasesize;
302 }
303
304 if (!quiet)
305 fprintf(stderr, "\b\b\b[w]");
306
307 if ((result = write(fd, buf, r)) < r) {
308 if (result < 0) {
309 fprintf(stderr, "Error writing image.\n");
310 exit(1);
311 } else {
312 fprintf(stderr, "Insufficient space.\n");
313 exit(1);
314 }
315 }
316
317 buflen = 0;
318 }
319 if (!quiet)
320 fprintf(stderr, "\b\b\b\b");
321
322 close(fd);
323 return 0;
324 }
325
326 void usage(void)
327 {
328 fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
329 "The device is in the format of mtdX (eg: mtd4) or its label.\n"
330 "mtd recognizes these commands:\n"
331 " unlock unlock the device\n"
332 " erase erase all data on device\n"
333 " write <imagefile>|- write <imagefile> (use - for stdin) to device\n"
334 "Following options are available:\n"
335 " -q quiet mode (once: no [w] on writing,\n"
336 " twice: no status messages)\n"
337 " -r reboot after successful command\n"
338 " -f force write without trx checks\n"
339 " -e <device> erase <device> before executing the command\n\n"
340 "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
341 " mtd -r write linux.trx linux\n\n");
342 exit(1);
343 }
344
345 int main (int argc, char **argv)
346 {
347 int ch, i, boot, unlock, imagefd, force, unlocked;
348 char *erase[MAX_ARGS], *device, *imagefile;
349 enum {
350 CMD_ERASE,
351 CMD_WRITE,
352 CMD_UNLOCK
353 } cmd;
354
355 erase[0] = NULL;
356 boot = 0;
357 force = 0;
358 buflen = 0;
359 quiet = 0;
360
361 while ((ch = getopt(argc, argv, "frqe:")) != -1)
362 switch (ch) {
363 case 'f':
364 force = 1;
365 break;
366 case 'r':
367 boot = 1;
368 break;
369 case 'q':
370 quiet++;
371 break;
372 case 'e':
373 i = 0;
374 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
375 i++;
376
377 erase[i++] = optarg;
378 erase[i] = NULL;
379 break;
380
381 case '?':
382 default:
383 usage();
384 }
385 argc -= optind;
386 argv += optind;
387
388 if (argc < 2)
389 usage();
390
391 if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
392 cmd = CMD_UNLOCK;
393 device = argv[1];
394 } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
395 cmd = CMD_ERASE;
396 device = argv[1];
397 } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
398 cmd = CMD_WRITE;
399 device = argv[2];
400
401 if (strcmp(argv[1], "-") == 0) {
402 imagefile = "<stdin>";
403 imagefd = 0;
404 } else {
405 imagefile = argv[1];
406 if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
407 fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
408 exit(1);
409 }
410 }
411
412 /* check trx file before erasing or writing anything */
413 if (!image_check(imagefd, device)) {
414 if (!force) {
415 fprintf(stderr, "Image check failed.\n");
416 exit(1);
417 }
418 } else {
419 if (!mtd_check(device)) {
420 fprintf(stderr, "Can't open device for writing!\n");
421 exit(1);
422 }
423 }
424 } else {
425 usage();
426 }
427
428 sync();
429
430 i = 0;
431 unlocked = 0;
432 while (erase[i] != NULL) {
433 if (quiet < 2)
434 fprintf(stderr, "Unlocking %s ...\n", erase[i]);
435 mtd_unlock(erase[i]);
436 if (quiet < 2)
437 fprintf(stderr, "Erasing %s ...\n", erase[i]);
438 mtd_erase(erase[i]);
439 if (strcmp(erase[i], device) == 0)
440 unlocked = 1;
441 i++;
442 }
443
444 if (!unlocked) {
445 if (quiet < 2)
446 fprintf(stderr, "Unlocking %s ...\n", device);
447 mtd_unlock(device);
448 }
449
450 switch (cmd) {
451 case CMD_UNLOCK:
452 break;
453 case CMD_ERASE:
454 if (quiet < 2)
455 fprintf(stderr, "Erasing %s ...\n", device);
456 mtd_erase(device);
457 break;
458 case CMD_WRITE:
459 if (quiet < 2)
460 fprintf(stderr, "Writing from %s to %s ... ", imagefile, device);
461 mtd_write(imagefd, device);
462 if (quiet < 2)
463 fprintf(stderr, "\n");
464 break;
465 }
466
467 sync();
468
469 if (boot) {
470 fflush(stdout);
471 syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
472 }
473 return 0;
474 }