2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <sys/mount.h>
15 #include <sys/types.h>
18 #include <asm/byteorder.h>
22 #include <mtd/mtd-user.h>
24 #include "libfstools.h"
36 static struct driver mtd_driver;
38 static int mtd_open(const char *mtd, int block)
42 int i, ret, flags = O_RDWR | O_SYNC;
44 if ((fp = fopen("/proc/mtd", "r"))) {
45 while (fgets(dev, sizeof(dev), fp)) {
46 if (sscanf(dev, "mtd%d:", &i) && strstr(dev, mtd)) {
47 snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
48 ret = open(dev, flags);
50 snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
51 ret = open(dev, flags);
60 return open(mtd, flags);
63 static void mtd_volume_close(struct volume *v)
65 struct mtd_priv *p = (struct mtd_priv*) v->priv;
74 static int mtd_volume_load(struct volume *v)
76 struct mtd_priv *p = (struct mtd_priv*) v->priv;
77 struct mtd_info_user mtdInfo;
78 struct erase_info_user mtdLockInfo;
86 p->fd = mtd_open(p->chr, 0);
89 fprintf(stderr, "Could not open mtd device: %s\n", p->chr);
93 if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
95 fprintf(stderr, "Could not get MTD device info from %s\n", p->chr);
99 v->size = mtdInfo.size;
100 v->block_size = mtdInfo.erasesize;
101 switch (mtdInfo.type) {
112 v->type = UNKNOWN_TYPE;
116 mtdLockInfo.start = 0;
117 mtdLockInfo.length = v->size;
118 ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
123 static char* mtd_find_index(char *name)
125 FILE *fp = fopen("/proc/mtd", "r");
126 static char line[256];
132 while (!index && fgets(line, sizeof(line), fp)) {
135 if ((ret = strstr(line, name)) && (ret[strlen(name)] == '"')) {
136 char *eol = strstr(line, ":");
151 static int mtd_volume_find(struct volume *v, char *name)
153 char *idx = mtd_find_index(name);
160 p = calloc(1, sizeof(struct mtd_priv));
165 v->name = strdup(name);
166 v->drv = &mtd_driver;
169 snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
170 v->blk = strdup(buffer);
172 snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
173 p->chr = strdup(buffer);
175 if (mtd_volume_load(v)) {
176 fprintf(stderr, "reading %s failed\n", v->name);
183 static int mtd_volume_identify(struct volume *v)
185 struct mtd_priv *p = (struct mtd_priv*) v->priv;
190 if (mtd_volume_load(v)) {
191 fprintf(stderr, "reading %s failed\n", v->name);
195 sz = read(p->fd, &deadc0de, sizeof(deadc0de));
197 if (sz != sizeof(deadc0de)) {
198 fprintf(stderr, "reading %s failed: %s\n", v->name, strerror(errno));
202 if (deadc0de == __be32_to_cpu(0x4f575254))
205 deadc0de = __be32_to_cpu(deadc0de);
206 if (deadc0de == 0xdeadc0de) {
207 fprintf(stderr, "jffs2 is not ready - marker found\n");
211 jffs2 = __be16_to_cpu(deadc0de >> 16);
212 if (jffs2 == 0x1985) {
213 fprintf(stderr, "jffs2 is ready\n");
217 if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
218 fprintf(stderr, "jffs2 is ready\n");
222 fprintf(stderr, "No jffs2 marker was found\n");
227 static int mtd_volume_erase(struct volume *v, int offset, int len)
229 struct mtd_priv *p = (struct mtd_priv*) v->priv;
230 struct erase_info_user eiu;
231 int first_block, num_blocks;
233 if (mtd_volume_load(v))
236 if (offset % v->block_size || len % v->block_size) {
237 fprintf(stderr, "mtd erase needs to be block aligned\n");
241 first_block = offset / v->block_size;
242 num_blocks = len / v->block_size;
243 eiu.length = v->block_size;
245 for (eiu.start = first_block * v->block_size;
246 eiu.start < v->size && eiu.start < (first_block + num_blocks) * v->block_size;
247 eiu.start += v->block_size) {
248 fprintf(stderr, "erasing %x %x\n", eiu.start, v->block_size);
249 ioctl(p->fd, MEMUNLOCK, &eiu);
250 if (ioctl(p->fd, MEMERASE, &eiu))
251 fprintf(stderr, "Failed to erase block at 0x%x\n", eiu.start);
259 static int mtd_volume_erase_all(struct volume *v)
261 mtd_volume_erase(v, 0, v->size);
267 static int mtd_volume_init(struct volume *v)
269 struct mtd_priv *p = (struct mtd_priv*) v->priv;
270 struct mtd_info_user mtdinfo;
273 if (mtd_volume_load(v))
276 ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
278 fprintf(stderr, "ioctl(%d, MEMGETINFO) failed: %s\n", p->fd, strerror(errno));
280 struct erase_info_user mtdlock;
283 mtdlock.length = mtdinfo.size;
284 ioctl(p->fd, MEMUNLOCK, &mtdlock);
290 static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
292 struct mtd_priv *p = (struct mtd_priv*) v->priv;
294 if (mtd_volume_load(v))
297 if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
298 fprintf(stderr, "lseek/read failed\n");
302 if (read(p->fd, buf, length) == -1) {
303 fprintf(stderr, "read failed\n");
310 static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
312 struct mtd_priv *p = (struct mtd_priv*) v->priv;
314 if (mtd_volume_load(v))
317 if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
318 fprintf(stderr, "lseek/write failed at offset %d\n", offset);
323 if (write(p->fd, buf, length) == -1) {
324 fprintf(stderr, "write failed\n");
331 static struct driver mtd_driver = {
333 .find = mtd_volume_find,
334 .init = mtd_volume_init,
335 .erase = mtd_volume_erase,
336 .erase_all = mtd_volume_erase_all,
337 .read = mtd_volume_read,
338 .write = mtd_volume_write,
339 .identify = mtd_volume_identify,