• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/fstools/libfstools/mtd.c

  1 /*
  2  * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
  3  *
  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
  7  *
  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.
 12  */
 13 
 14 #include <sys/mount.h>
 15 #include <sys/types.h>
 16 #include <sys/stat.h>
 17 #include <fcntl.h>
 18 #include <asm/byteorder.h>
 19 #include <unistd.h>
 20 #include <stdio.h>
 21 #include <stdlib.h>
 22 #include <mtd/mtd-user.h>
 23 
 24 #include "libfstools.h"
 25 
 26 #include "volume.h"
 27 
 28 #define PATH_MAX                256
 29 
 30 struct mtd_volume {
 31         struct volume v;
 32         int     fd;
 33         int     idx;
 34         char    *chr;
 35 };
 36 
 37 static struct driver mtd_driver;
 38 
 39 static int mtd_open(const char *mtd, int block)
 40 {
 41         FILE *fp;
 42         char dev[PATH_MAX];
 43         int i, ret, flags = O_RDWR | O_SYNC;
 44 
 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);
 50                                 if (ret < 0) {
 51                                         snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
 52                                         ret = open(dev, flags);
 53                                 }
 54                                 fclose(fp);
 55                                 return ret;
 56                         }
 57                 }
 58                 fclose(fp);
 59         }
 60 
 61         return open(mtd, flags);
 62 }
 63 
 64 static void mtd_volume_close(struct mtd_volume *p)
 65 {
 66         if (!p->fd)
 67                 return;
 68 
 69         close(p->fd);
 70         p->fd = -1;
 71 }
 72 
 73 static int mtd_volume_load(struct mtd_volume *p)
 74 {
 75         struct volume *v = &p->v;
 76         struct mtd_info_user mtdInfo;
 77         struct erase_info_user mtdLockInfo;
 78 
 79         if (p->fd >= 0)
 80                 return (lseek(p->fd, 0, SEEK_SET) == -1);
 81 
 82         if (!p->chr)
 83                 return -1;
 84 
 85         p->fd = mtd_open(p->chr, 0);
 86         if (p->fd < 0) {
 87                 ULOG_ERR("Could not open mtd device: %s\n", p->chr);
 88                 return -1;
 89         }
 90 
 91         if (ioctl(p->fd, MEMGETINFO, &mtdInfo)) {
 92                 mtd_volume_close(p);
 93                 ULOG_ERR("Could not get MTD device info from %s\n", p->chr);
 94                 return -1;
 95         }
 96 
 97         v->size = mtdInfo.size;
 98         v->block_size = mtdInfo.erasesize;
 99         switch (mtdInfo.type) {
100         case MTD_NORFLASH:
101                 v->type = NORFLASH;
102                 break;
103         case MTD_NANDFLASH:
104                 v->type = NANDFLASH;
105                 break;
106         case MTD_UBIVOLUME:
107                 v->type = UBIVOLUME;
108                 break;
109         default:
110                 v->type = UNKNOWN_TYPE;
111                 break;
112         }
113 
114         mtdLockInfo.start = 0;
115         mtdLockInfo.length = v->size;
116         ioctl(p->fd, MEMUNLOCK, &mtdLockInfo);
117 
118         return 0;
119 }
120 
121 static char* mtd_find_index(char *name)
122 {
123         FILE *fp = fopen("/proc/mtd", "r");
124         static char line[256];
125         char *index = NULL;
126 
127         if(!fp)
128                 return index;
129 
130         while (!index && fgets(line, sizeof(line), fp)) {
131                 char *ret;
132 
133                 if ((ret = strstr(line, name)) && (ret[strlen(name)] == '"')) {
134                         char *eol = strstr(line, ":");
135 
136                         if (!eol)
137                                 continue;
138 
139                         *eol = '\0';
140                         index = &line[3];
141                 }
142         }
143 
144         fclose(fp);
145 
146         return index;
147 }
148 
149 static struct volume *mtd_volume_find(char *name)
150 {
151         char *idx = mtd_find_index(name);
152         struct mtd_volume *p;
153         struct volume *v;
154         char buffer[32];
155 
156         if (!idx)
157                 return NULL;
158 
159         p = calloc(1, sizeof(struct mtd_volume));
160         if (!p)
161                 return NULL;
162 
163         v = &p->v;
164         v->name = strdup(name);
165         v->drv = &mtd_driver;
166         p->idx = atoi(idx);
167         p->fd = -1;
168 
169         snprintf(buffer, sizeof(buffer), "/dev/mtdblock%s", idx);
170         v->blk = strdup(buffer);
171 
172         snprintf(buffer, sizeof(buffer), "/dev/mtd%s", idx);
173         p->chr = strdup(buffer);
174 
175         if (mtd_volume_load(p)) {
176                 ULOG_ERR("reading %s failed\n", v->name);
177                 free(p);
178                 return NULL;
179         }
180 
181         return v;
182 }
183 
184 static int mtd_volume_identify(struct volume *v)
185 {
186         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
187         __u32 deadc0de;
188         size_t sz;
189 
190         if (mtd_volume_load(p)) {
191                 ULOG_ERR("reading %s failed\n", v->name);
192                 return -1;
193         }
194 
195         sz = read(p->fd, &deadc0de, sizeof(deadc0de));
196 
197         if (sz != sizeof(deadc0de)) {
198                 ULOG_ERR("reading %s failed: %m\n", v->name);
199                 return -1;
200         }
201 
202         if (deadc0de == ~0) {
203                 struct mtd_oob_buf oob = {
204                         .start = 0,
205                         .length = sizeof(deadc0de),
206                         .ptr = (void *)&deadc0de,
207                 };
208 
209                 ioctl(p->fd, MEMREADOOB, &oob);
210         }
211 
212         if (deadc0de == __be32_to_cpu(0x4f575254))
213                 return FS_SNAPSHOT;
214 
215         deadc0de = __be32_to_cpu(deadc0de);
216         if (deadc0de == 0xdeadc0de) {
217                 return FS_DEADCODE;
218         }
219 
220         if (__be16_to_cpu(deadc0de) == 0x1985 ||
221             __be16_to_cpu(deadc0de >> 16) == 0x1985)
222                 return FS_JFFS2;
223 
224         if (v->type == UBIVOLUME && deadc0de == 0xffffffff) {
225                 return FS_JFFS2;
226         }
227 
228         return FS_NONE;
229 }
230 
231 static int mtd_volume_erase(struct volume *v, int offset, int len)
232 {
233         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
234         struct erase_info_user eiu;
235         int first_block, num_blocks;
236 
237         if (mtd_volume_load(p))
238                 return -1;
239 
240         if (offset % v->block_size || len % v->block_size) {
241                 ULOG_ERR("mtd erase needs to be block aligned\n");
242                 return -1;
243         }
244 
245         first_block = offset / v->block_size;
246         num_blocks = len / v->block_size;
247         eiu.length = v->block_size;
248 
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);
256         }
257 
258         mtd_volume_close(p);
259 
260         return 0;
261 }
262 
263 static int mtd_volume_erase_all(struct volume *v)
264 {
265         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
266 
267         mtd_volume_erase(v, 0, v->size);
268         mtd_volume_close(p);
269 
270         return 0;
271 }
272 
273 static int mtd_volume_init(struct volume *v)
274 {
275         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
276         struct mtd_info_user mtdinfo;
277         int ret;
278 
279         if (mtd_volume_load(p))
280                 return -1;
281 
282         ret = ioctl(p->fd, MEMGETINFO, &mtdinfo);
283         if (ret) {
284                 ULOG_ERR("ioctl(%d, MEMGETINFO) failed: %m\n", p->fd);
285         } else {
286                 struct erase_info_user mtdlock;
287 
288                 mtdlock.start = 0;
289                 mtdlock.length = mtdinfo.size;
290                 ioctl(p->fd, MEMUNLOCK, &mtdlock);
291         }
292 
293         return ret;
294 }
295 
296 static int mtd_volume_read(struct volume *v, void *buf, int offset, int length)
297 {
298         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
299 
300         if (mtd_volume_load(p))
301                 return -1;
302 
303         if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
304                 ULOG_ERR("lseek/read failed\n");
305                 return -1;
306         }
307 
308         if (read(p->fd, buf, length) == -1) {
309                 ULOG_ERR("read failed\n");
310                 return -1;
311         }
312 
313         return 0;
314 }
315 
316 static int mtd_volume_write(struct volume *v, void *buf, int offset, int length)
317 {
318         struct mtd_volume *p = container_of(v, struct mtd_volume, v);;
319 
320         if (mtd_volume_load(p))
321                 return -1;
322 
323         if (lseek(p->fd, offset, SEEK_SET) == (off_t) -1) {
324                 ULOG_ERR("lseek/write failed at offset %d\n", offset);
325                 perror("lseek");
326                 return -1;
327         }
328 
329         if (write(p->fd, buf, length) == -1) {
330                 ULOG_ERR("write failed\n");
331                 return -1;
332         }
333 
334         return 0;
335 }
336 
337 static struct driver mtd_driver = {
338         .name = "mtd",
339         .priority = 10,
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,
347 };
348 DRIVER(mtd_driver);
349 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt