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"
37 static struct driver mtd_driver
;
39 static int mtd_open(const char *mtd
, int block
)
43 int i
, ret
, flags
= O_RDWR
| O_SYNC
;
45 if ((fp
= fopen("/proc/mtd", "r"))) {
46 while (fgets(dev
, sizeof(dev
), fp
)) {
47 if (sscanf(dev
, "mtd%d:", &i
) && strstr(dev
, mtd
)) {
48 snprintf(dev
, sizeof(dev
), "/dev/mtd%s/%d", (block
? "block" : ""), i
);
49 ret
= open(dev
, flags
);
51 snprintf(dev
, sizeof(dev
), "/dev/mtd%s%d", (block
? "block" : ""), i
);
52 ret
= open(dev
, flags
);
61 return open(mtd
, flags
);
64 static void mtd_volume_close(struct mtd_volume
*p
)
73 static int mtd_volume_load(struct mtd_volume
*p
)
75 struct volume
*v
= &p
->v
;
76 struct mtd_info_user mtdInfo
;
77 struct erase_info_user mtdLockInfo
;
80 return (lseek(p
->fd
, 0, SEEK_SET
) == -1);
85 p
->fd
= mtd_open(p
->chr
, 0);
87 ULOG_ERR("Could not open mtd device: %s\n", p
->chr
);
91 if (ioctl(p
->fd
, MEMGETINFO
, &mtdInfo
)) {
93 ULOG_ERR("Could not get MTD device info from %s\n", p
->chr
);
97 v
->size
= mtdInfo
.size
;
98 v
->block_size
= mtdInfo
.erasesize
;
99 switch (mtdInfo
.type
) {
110 v
->type
= UNKNOWN_TYPE
;
114 mtdLockInfo
.start
= 0;
115 mtdLockInfo
.length
= v
->size
;
116 ioctl(p
->fd
, MEMUNLOCK
, &mtdLockInfo
);
121 static char* mtd_find_index(char *name
)
123 FILE *fp
= fopen("/proc/mtd", "r");
124 static char line
[256];
130 while (!index
&& fgets(line
, sizeof(line
), fp
)) {
133 if ((ret
= strstr(line
, name
)) && (ret
[strlen(name
)] == '"')) {
134 char *eol
= strstr(line
, ":");
149 static struct volume
*mtd_volume_find(char *name
)
151 char *idx
= mtd_find_index(name
);
152 struct mtd_volume
*p
;
159 p
= calloc(1, sizeof(struct mtd_volume
));
164 v
->name
= strdup(name
);
165 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(p
)) {
176 ULOG_ERR("reading %s failed\n", v
->name
);
184 static int mtd_volume_identify(struct volume
*v
)
186 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
190 if (mtd_volume_load(p
)) {
191 ULOG_ERR("reading %s failed\n", v
->name
);
195 sz
= read(p
->fd
, &deadc0de
, sizeof(deadc0de
));
197 if (sz
!= sizeof(deadc0de
)) {
198 ULOG_ERR("reading %s failed: %m\n", v
->name
);
202 if (deadc0de
== ~0) {
203 struct mtd_oob_buf oob
= {
205 .length
= sizeof(deadc0de
),
206 .ptr
= (void *)&deadc0de
,
209 ioctl(p
->fd
, MEMREADOOB
, &oob
);
212 if (deadc0de
== __be32_to_cpu(0x4f575254))
215 deadc0de
= __be32_to_cpu(deadc0de
);
216 if (deadc0de
== 0xdeadc0de) {
220 if (__be16_to_cpu(deadc0de
) == 0x1985 ||
221 __be16_to_cpu(deadc0de
>> 16) == 0x1985)
224 if (v
->type
== UBIVOLUME
&& deadc0de
== 0xffffffff) {
231 static int mtd_volume_erase(struct volume
*v
, int offset
, int len
)
233 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
234 struct erase_info_user eiu
;
235 int first_block
, num_blocks
;
237 if (mtd_volume_load(p
))
240 if (offset
% v
->block_size
|| len
% v
->block_size
) {
241 ULOG_ERR("mtd erase needs to be block aligned\n");
245 first_block
= offset
/ v
->block_size
;
246 num_blocks
= len
/ v
->block_size
;
247 eiu
.length
= v
->block_size
;
249 for (eiu
.start
= first_block
* v
->block_size
;
250 eiu
.start
< v
->size
&& eiu
.start
< (first_block
+ num_blocks
) * v
->block_size
;
251 eiu
.start
+= v
->block_size
) {
252 ULOG_INFO("erasing %x %x\n", eiu
.start
, v
->block_size
);
253 ioctl(p
->fd
, MEMUNLOCK
, &eiu
);
254 if (ioctl(p
->fd
, MEMERASE
, &eiu
))
255 ULOG_ERR("Failed to erase block at 0x%x\n", eiu
.start
);
263 static int mtd_volume_erase_all(struct volume
*v
)
265 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
267 mtd_volume_erase(v
, 0, v
->size
);
273 static int mtd_volume_init(struct volume
*v
)
275 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
276 struct mtd_info_user mtdinfo
;
279 if (mtd_volume_load(p
))
282 ret
= ioctl(p
->fd
, MEMGETINFO
, &mtdinfo
);
284 ULOG_ERR("ioctl(%d, MEMGETINFO) failed: %m\n", p
->fd
);
286 struct erase_info_user mtdlock
;
289 mtdlock
.length
= mtdinfo
.size
;
290 ioctl(p
->fd
, MEMUNLOCK
, &mtdlock
);
296 static int mtd_volume_read(struct volume
*v
, void *buf
, int offset
, int length
)
298 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
300 if (mtd_volume_load(p
))
303 if (lseek(p
->fd
, offset
, SEEK_SET
) == (off_t
) -1) {
304 ULOG_ERR("lseek/read failed\n");
308 if (read(p
->fd
, buf
, length
) == -1) {
309 ULOG_ERR("read failed\n");
316 static int mtd_volume_write(struct volume
*v
, void *buf
, int offset
, int length
)
318 struct mtd_volume
*p
= container_of(v
, struct mtd_volume
, v
);;
320 if (mtd_volume_load(p
))
323 if (lseek(p
->fd
, offset
, SEEK_SET
) == (off_t
) -1) {
324 ULOG_ERR("lseek/write failed at offset %d\n", offset
);
329 if (write(p
->fd
, buf
, length
) == -1) {
330 ULOG_ERR("write failed\n");
337 static struct driver mtd_driver
= {
340 .find
= mtd_volume_find
,
341 .init
= mtd_volume_init
,
342 .erase
= mtd_volume_erase
,
343 .erase_all
= mtd_volume_erase_all
,
344 .read
= mtd_volume_read
,
345 .write
= mtd_volume_write
,
346 .identify
= mtd_volume_identify
,