more cosmetic makefile fixes
[openwrt.git] / package / mtd / 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@vd-s.ath.cx>
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  * code is based on linux-mtd example code
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 (10 * 1024)
47 #define MAX_ARGS 8
48
49 struct trx_header {
50         uint32_t magic;         /* "HDR0" */
51         uint32_t len;           /* Length of file including header */
52         uint32_t crc32;         /* 32-bit CRC from flag_version to end of file */
53         uint32_t flag_version;  /* 0:15 flags, 16:31 version */
54         uint32_t offsets[3];    /* Offsets of partitions from start of header */
55 };
56
57 int
58 trx_check(const char *trxfile, const char *mtd, int force)
59 {
60         struct mtd_info_user mtdInfo;
61         int trxfd, fd;
62         size_t count;
63         struct trx_header trx;
64         struct stat trxstat;
65
66         trxfd = open(trxfile,O_RDONLY); 
67         if(trxfd < 0) {
68                 fprintf(stderr, "Could not open image: %s\n", trxfile);
69                 exit(1);
70         }
71
72         if (fstat(trxfd,&trxstat) < 0) {
73                 fprintf(stderr, "Could not get image file status: %s\n", trxfile);
74                 close(trxfd);
75                 exit(1);
76         }
77
78         if (force == 0) {
79                 count = read(trxfd, &trx, sizeof(struct trx_header));
80                 if (count < sizeof(struct trx_header)) {
81                         fprintf(stderr, "Could not get trx header, file too small (%ld bytes)\n", count);
82                         close(trxfd);
83                         exit(1);
84                 }
85
86                 if (trx.magic != TRX_MAGIC || trx.len < sizeof(struct trx_header)) {
87                         fprintf(stderr, "Bad trx header\n");
88                         fprintf(stderr, "If this is a firmware in bin format, like some of the\n"
89                                         "original firmware files are, use following command to convert to trx:\n"
90                                         "dd if=firmware.bin of=firmware.trx bs=32 skip=1\n");
91                         close(trxfd);
92                         exit(1);
93                 }
94         
95                 lseek(trxfd, 0, SEEK_SET);
96         }
97
98         /* check if image fits to mtd device */
99
100         fd = mtd_open(mtd, O_RDWR);
101         if(fd < 0) {
102                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
103                 exit(1);
104         }
105
106         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
107                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
108                 close(fd);
109                 exit(1);
110         }
111                 
112         if(mtdInfo.size < trxstat.st_size) {
113                 fprintf(stderr, "Image too big for partition: %s\n", mtd);
114                 close(trxfd);
115                 close(fd);
116                 exit(1);
117         }       
118         
119         printf("Writing %s to %s ...\n", trxfile, mtd);
120
121         close(fd);
122
123         return(trxfd);
124 }
125
126 int
127 mtd_unlock(const char *mtd)
128 {
129         int fd;
130         struct mtd_info_user mtdInfo;
131         struct erase_info_user mtdLockInfo;
132
133         fd = mtd_open(mtd, O_RDWR);
134         if(fd < 0) {
135                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
136                 exit(1);
137         }
138
139         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
140                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
141                 close(fd);
142                 exit(1);
143         }
144
145         printf("Unlocking %s ...\n", mtd);
146         mtdLockInfo.start = 0;
147         mtdLockInfo.length = mtdInfo.size;
148         if(ioctl(fd, MEMUNLOCK, &mtdLockInfo)) {
149                 close(fd);
150                 return 0;
151         }
152                 
153         close(fd);
154         return 0;
155 }
156
157 int
158 mtd_open(const char *mtd, int flags)
159 {
160         FILE *fp;
161         char dev[PATH_MAX];
162         int i;
163
164         if ((fp = fopen("/proc/mtd", "r"))) {
165                 while (fgets(dev, sizeof(dev), fp)) {
166                         if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
167                                 snprintf(dev, sizeof(dev), "/dev/mtd/%d", i);
168                                 fclose(fp);
169                                 return open(dev, flags);
170                         }
171                 }
172                 fclose(fp);
173         }
174
175         return open(mtd, flags);
176 }
177
178 int
179 mtd_erase(const char *mtd)
180 {
181         int fd;
182         struct mtd_info_user mtdInfo;
183         struct erase_info_user mtdEraseInfo;
184
185         fd = mtd_open(mtd, O_RDWR);
186         if(fd < 0) {
187                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
188                 exit(1);
189         }
190
191         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
192                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
193                 close(fd);
194                 exit(1);
195         }
196
197         printf("Erasing %s ...\n", mtd);
198         mtdEraseInfo.length = mtdInfo.erasesize;
199
200         for (mtdEraseInfo.start = 0;
201                  mtdEraseInfo.start < mtdInfo.size;
202                  mtdEraseInfo.start += mtdInfo.erasesize) {
203                 
204                 ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
205                 if(ioctl(fd, MEMERASE, &mtdEraseInfo)) {
206                         fprintf(stderr, "Could not erase MTD device: %s\n", mtd);
207                         close(fd);
208                         exit(1);
209                 }
210         }               
211
212         close(fd);
213         return 0;
214
215 }
216
217 int
218 mtd_write(int trxfd, const char *mtd)
219 {
220         int fd,i;
221         size_t result,size,written;
222         struct mtd_info_user mtdInfo;
223         struct erase_info_user mtdEraseInfo;
224         unsigned char src[BUFSIZE],dest[BUFSIZE];
225         struct stat trxstat;
226
227         if (fstat(trxfd,&trxstat) < 0) {
228                 fprintf(stderr, "Could not get trx image file status\n");
229                 close(trxfd);
230                 exit(1);
231         }
232
233         fd = mtd_open(mtd, O_RDWR);
234         if(fd < 0) {
235                 fprintf(stderr, "Could not open mtd device: %s\n", mtd);
236                 exit(1);
237         }
238
239         if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
240                 fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
241                 close(fd);
242                 exit(1);
243         }
244                 
245         mtdEraseInfo.start = 0;
246         mtdEraseInfo.length = trxstat.st_size & ~(mtdInfo.erasesize -1);
247         if(trxstat.st_size % mtdInfo.erasesize) mtdEraseInfo.length += mtdInfo.erasesize;
248
249         /* erase the chunk */
250         if (ioctl (fd,MEMERASE,&mtdEraseInfo) < 0) {
251                 fprintf(stderr, "Erasing mtd failed: %s\n", mtd);
252                 exit(1);
253         }
254         
255         size = trxstat.st_size;
256         i = BUFSIZE;
257         written = 0;
258
259         while (size) {
260                 if (size < BUFSIZE) i = size;
261                 read(trxfd,src,i);
262                 result = write(fd,src,i);
263                 if (i != result) {
264                         if (result < 0) {
265                                 fprintf(stderr,"Error while writing image");
266                                 exit(1);
267                         }
268                         fprintf(stderr,"Error writing image");
269                         exit(1);
270                 }
271                 written += i;
272                 size -= i;
273         }
274         
275         return 0;
276 }
277
278 void usage(void)
279 {
280         printf("Usage: mtd [<options> ...] <command> [<arguments> ...] <device>\n\n"
281         "The device is in the format of mtdX (eg: mtd4) or its label.\n"
282         "mtd recognizes these commands:\n"
283         "       unlock                  unlock the device\n"
284         "       erase                   erase all data on device\n"
285         "       write <imagefile>       write imagefile to device\n"
286         "Following options are available:\n"
287         "       -r                      reboot after successful command\n"
288         "       -f                      force write without trx checks\n"
289         "       -e <device>             erase <device> before executing the command\n\n"
290         "Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
291         "         mtd -r write linux.trx linux\n\n");
292         exit(1);
293 }
294
295 int main (int argc, char **argv)
296 {
297         int ch, i, boot, unlock, trxfd, force;
298         char *erase[MAX_ARGS], *device;
299         enum {
300                 CMD_ERASE,
301                 CMD_WRITE,
302                 CMD_UNLOCK
303         } cmd;
304         
305         erase[0] = NULL;
306         boot = 0;
307         force = 0;
308
309         while ((ch = getopt(argc, argv, "fre:")) != -1)
310                 switch (ch) {
311                         case 'f':
312                                 force = 1;
313                                 break;
314                         case 'r':
315                                 boot = 1;
316                                 break;
317                         case 'e':
318                                 i = 0;
319                                 while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
320                                         i++;
321                                         
322                                 erase[i++] = optarg;
323                                 erase[i] = NULL;
324                                 break;
325                         
326                         case '?':
327                         default:
328                                 usage();
329                 }
330         argc -= optind;
331         argv += optind;
332         
333         if (argc < 2)
334                 usage();
335
336         if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
337                 cmd = CMD_UNLOCK;
338                 device = argv[1];
339         } else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
340                 cmd = CMD_ERASE;
341                 device = argv[1];
342         } else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
343                 cmd = CMD_WRITE;
344                 device = argv[2];
345                 /* check trx file before erasing or writing anything */
346                 trxfd = trx_check(argv[1], device, force);
347         } else {
348                 usage();
349         }
350
351         sync();
352
353         i = 0;
354         while (erase[i] != NULL) {
355                 mtd_unlock(erase[i]);
356                 mtd_erase(erase[i]);
357                 i++;
358         }
359         
360         mtd_unlock(device);
361
362         switch (cmd) {
363                 case CMD_UNLOCK:
364                         break;
365                 case CMD_ERASE:
366                         mtd_erase(device);
367                         break;
368                 case CMD_WRITE:
369                         mtd_write(trxfd, device);
370                         break;
371         }
372
373         if (boot)
374                 kill(1, 15); // send SIGTERM to init for reboot
375
376         return 0;
377 }