summaryrefslogtreecommitdiff
path: root/miscutils/flash_eraseall.c
blob: 68596e11b002e0d389592c961e752e5de3bda993 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/* vi: set sw=4 ts=4: */
/* eraseall.c -- erase the whole of a MTD device
 *
 * Ported to busybox from mtd-utils.
 *
 * Copyright (C) 2000 Arcom Control System Ltd
 *
 * Renamed to flash_eraseall.c
 *
 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
 */

#include "libbb.h"
#include <mtd/mtd-user.h>
#include <linux/jffs2.h>

#define OPTION_J  (1 << 0)
#define OPTION_Q  (1 << 1)
#define IS_NAND   (1 << 2)
#define BBTEST    (1 << 3)

/* mtd/jffs2-user.h used to have this atrocity:
extern int target_endian;

#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })

#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})

#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))

but mtd/jffs2-user.h is gone now (at least 2.6.31.6 does not have it anymore)
*/

/* We always use native endianness */
#undef cpu_to_je16
#undef cpu_to_je32
#define cpu_to_je16(v) ((jint16_t){(v)})
#define cpu_to_je32(v) ((jint32_t){(v)})

static void show_progress(mtd_info_t *meminfo, erase_info_t *erase)
{
	printf("\rErasing %u Kibyte @ %x - %2u%% complete.",
		(unsigned)meminfo->erasesize / 1024,
		erase->start,
		(unsigned) ((unsigned long long) erase->start * 100 / meminfo->size)
	);
	fflush_all();
}

int flash_eraseall_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int flash_eraseall_main(int argc UNUSED_PARAM, char **argv)
{
	struct jffs2_unknown_node cleanmarker;
	mtd_info_t meminfo;
	int fd, clmpos, clmlen;
	erase_info_t erase;
	struct stat st;
	unsigned int flags;
	char *mtd_name;

	opt_complementary = "=1";
	flags = BBTEST | getopt32(argv, "jq");

	mtd_name = argv[optind];
	fd = xopen(mtd_name, O_RDWR);
	fstat(fd, &st);
	if (!S_ISCHR(st.st_mode))
		bb_error_msg_and_die("%s: not a char device", mtd_name);

	xioctl(fd, MEMGETINFO, &meminfo);
	erase.length = meminfo.erasesize;
	if (meminfo.type == MTD_NANDFLASH)
		flags |= IS_NAND;

	clmpos = 0;
	clmlen = 8;
	if (flags & OPTION_J) {
		uint32_t *crc32_table;

		crc32_table = crc32_filltable(NULL, 0);

		cleanmarker.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
		cleanmarker.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
		if (!(flags & IS_NAND))
			cleanmarker.totlen = cpu_to_je32(sizeof(struct jffs2_unknown_node));
		else {
			struct nand_oobinfo oobinfo;

			xioctl(fd, MEMGETOOBSEL, &oobinfo);

			/* Check for autoplacement */
			if (oobinfo.useecc == MTD_NANDECC_AUTOPLACE) {
				/* Get the position of the free bytes */
				clmpos = oobinfo.oobfree[0][0];
				clmlen = oobinfo.oobfree[0][1];
				if (clmlen > 8)
					clmlen = 8;
				if (clmlen == 0)
					bb_error_msg_and_die("autoplacement selected and no empty space in oob");
			} else {
				/* Legacy mode */
				switch (meminfo.oobsize) {
				case 8:
					clmpos = 6;
					clmlen = 2;
					break;
				case 16:
					clmpos = 8;
					/*clmlen = 8;*/
					break;
				case 64:
					clmpos = 16;
					/*clmlen = 8;*/
					break;
				}
			}
			cleanmarker.totlen = cpu_to_je32(8);
		}

		cleanmarker.hdr_crc = cpu_to_je32(
			crc32_block_endian0(0, &cleanmarker, sizeof(struct jffs2_unknown_node) - 4, crc32_table)
		);
	}

	/* Don't want to destroy progress indicator by bb_error_msg's */
	applet_name = xasprintf("\n%s: %s", applet_name, mtd_name);

	for (erase.start = 0; erase.start < meminfo.size;
	     erase.start += meminfo.erasesize) {
		if (flags & BBTEST) {
			int ret;
			loff_t offset = erase.start;

			ret = ioctl(fd, MEMGETBADBLOCK, &offset);
			if (ret > 0) {
				if (!(flags & OPTION_Q))
					bb_info_msg("\nSkipping bad block at 0x%08x", erase.start);
				continue;
			}
			if (ret < 0) {
				/* Black block table is not available on certain flash
				 * types e.g. NOR
				 */
				if (errno == EOPNOTSUPP) {
					flags &= ~BBTEST;
					if (flags & IS_NAND)
						bb_error_msg_and_die("bad block check not available");
				} else {
					bb_perror_msg_and_die("MEMGETBADBLOCK error");
				}
			}
		}

		if (!(flags & OPTION_Q))
			show_progress(&meminfo, &erase);

		xioctl(fd, MEMERASE, &erase);

		/* format for JFFS2 ? */
		if (!(flags & OPTION_J))
			continue;

		/* write cleanmarker */
		if (flags & IS_NAND) {
			struct mtd_oob_buf oob;

			oob.ptr = (unsigned char *) &cleanmarker;
			oob.start = erase.start + clmpos;
			oob.length = clmlen;
			xioctl(fd, MEMWRITEOOB, &oob);
		} else {
			xlseek(fd, erase.start, SEEK_SET);
			/* if (lseek(fd, erase.start, SEEK_SET) < 0) {
				bb_perror_msg("MTD %s failure", "seek");
				continue;
			} */
			xwrite(fd, &cleanmarker, sizeof(cleanmarker));
			/* if (write(fd, &cleanmarker, sizeof(cleanmarker)) != sizeof(cleanmarker)) {
				bb_perror_msg("MTD %s failure", "write");
				continue;
			} */
		}
		if (!(flags & OPTION_Q))
			printf(" Cleanmarker written at %x.", erase.start);
	}
	if (!(flags & OPTION_Q)) {
		show_progress(&meminfo, &erase);
		bb_putchar('\n');
	}

	if (ENABLE_FEATURE_CLEAN_UP)
		close(fd);
	return EXIT_SUCCESS;
}