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