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