1/* 2 * Copyright (C) 2009 Nokia Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 12 * the GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 * 18 * Author: Artem Bityutskiy 19 * 20 * This file is part of the MTD library. Implements pre-2.6.30 kernels support, 21 * where MTD did not have sysfs interface. The main limitation of the old 22 * kernels was that the sub-page size was not exported to user-space, so it was 23 * not possible to get sub-page size. 24 */ 25 26#include <limits.h> 27#include <fcntl.h> 28#include <unistd.h> 29#include <stdlib.h> 30#include <errno.h> 31#include <sys/types.h> 32#include <sys/stat.h> 33#include <sys/ioctl.h> 34#include <mtd/mtd-user.h> 35 36#include "libmtd.h" 37#include "libmtd_int.h" 38#include "common.h" 39 40#define MTD_PROC_FILE "/proc/mtd" 41#define MTD_DEV_PATT "/dev/mtd%d" 42#define MTD_DEV_MAJOR 90 43 44#define PROC_MTD_FIRST "dev: size erasesize name\n" 45#define PROC_MTD_FIRST_LEN (sizeof(PROC_MTD_FIRST) - 1) 46#define PROC_MTD_MAX_LEN 4096 47#define PROC_MTD_PATT "mtd%d: %llx %x" 48 49/** 50 * struct proc_parse_info - /proc/mtd parsing information. 51 * @mtd_num: MTD device number 52 * @size: device size 53 * @eb_size: eraseblock size 54 * @name: device name 55 * @buf: contents of /proc/mtd 56 * @data_size: how much data was read into @buf 57 * @pos: next string in @buf to parse 58 */ 59struct proc_parse_info 60{ 61 int mtd_num; 62 long long size; 63 char name[MTD_NAME_MAX + 1]; 64 int eb_size; 65 char *buf; 66 int data_size; 67 char *next; 68}; 69 70static int proc_parse_start(struct proc_parse_info *pi) 71{ 72 int fd, ret; 73 74 fd = open(MTD_PROC_FILE, O_RDONLY); 75 if (fd == -1) 76 return -1; 77 78 pi->buf = xmalloc(PROC_MTD_MAX_LEN); 79 80 ret = read(fd, pi->buf, PROC_MTD_MAX_LEN); 81 if (ret == -1) { 82 sys_errmsg("cannot read \"%s\"", MTD_PROC_FILE); 83 goto out_free; 84 } 85 86 if (ret < PROC_MTD_FIRST_LEN || 87 memcmp(pi->buf, PROC_MTD_FIRST, PROC_MTD_FIRST_LEN)) { 88 errmsg("\"%s\" does not start with \"%s\"", MTD_PROC_FILE, 89 PROC_MTD_FIRST); 90 goto out_free; 91 } 92 93 pi->data_size = ret; 94 pi->next = pi->buf + PROC_MTD_FIRST_LEN; 95 96 close(fd); 97 return 0; 98 99out_free: 100 free(pi->buf); 101 close(fd); 102 return -1; 103} 104 105static int proc_parse_next(struct proc_parse_info *pi) 106{ 107 int ret, len, pos = pi->next - pi->buf; 108 char *p, *p1; 109 110 if (pos >= pi->data_size) { 111 free(pi->buf); 112 return 0; 113 } 114 115 ret = sscanf(pi->next, PROC_MTD_PATT, &pi->mtd_num, &pi->size, 116 &pi->eb_size); 117 if (ret != 3) 118 return errmsg("\"%s\" pattern not found", PROC_MTD_PATT); 119 120 p = memchr(pi->next, '\"', pi->data_size - pos); 121 if (!p) 122 return errmsg("opening \" not found"); 123 p += 1; 124 pos = p - pi->buf; 125 if (pos >= pi->data_size) 126 return errmsg("opening \" not found"); 127 128 p1 = memchr(p, '\"', pi->data_size - pos); 129 if (!p1) 130 return errmsg("closing \" not found"); 131 pos = p1 - pi->buf; 132 if (pos >= pi->data_size) 133 return errmsg("closing \" not found"); 134 135 len = p1 - p; 136 if (len > MTD_NAME_MAX) 137 return errmsg("too long mtd%d device name", pi->mtd_num); 138 139 memcpy(pi->name, p, len); 140 pi->name[len] = '\0'; 141 142 if (p1[1] != '\n') 143 return errmsg("opening \"\n\" not found"); 144 pi->next = p1 + 2; 145 return 1; 146} 147 148/** 149 * legacy_libmtd_open - legacy version of 'libmtd_open()'. 150 * 151 * This function is just checks that MTD is present in the system. Returns 152 * zero in case of success and %-1 in case of failure. In case of failure, 153 * errno contains zero if MTD is not present in the system, or contains the 154 * error code if a real error happened. This is similar to the 'libmtd_open()' 155 * return conventions. 156 */ 157int legacy_libmtd_open(void) 158{ 159 int fd; 160 161 fd = open(MTD_PROC_FILE, O_RDONLY); 162 if (fd == -1) { 163 if (errno == ENOENT) 164 errno = 0; 165 return -1; 166 } 167 168 close(fd); 169 return 0; 170} 171 172/** 173 * legacy_dev_presentl - legacy version of 'mtd_dev_present()'. 174 * @info: the MTD device information is returned here 175 * 176 * When the kernel does not provide sysfs files for the MTD subsystem, 177 * fall-back to parsing the /proc/mtd file to determine whether an mtd device 178 * number @mtd_num is present. 179 */ 180int legacy_dev_present(int mtd_num) 181{ 182 int ret; 183 struct proc_parse_info pi; 184 185 ret = proc_parse_start(&pi); 186 if (ret) 187 return -1; 188 189 while (proc_parse_next(&pi)) { 190 if (pi.mtd_num == mtd_num) 191 return 1; 192 } 193 194 return 0; 195} 196 197/** 198 * legacy_mtd_get_info - legacy version of 'mtd_get_info()'. 199 * @info: the MTD device information is returned here 200 * 201 * This function is similar to 'mtd_get_info()' and has the same conventions. 202 */ 203int legacy_mtd_get_info(struct mtd_info *info) 204{ 205 int ret; 206 struct proc_parse_info pi; 207 208 ret = proc_parse_start(&pi); 209 if (ret) 210 return -1; 211 212 info->lowest_mtd_num = INT_MAX; 213 while (proc_parse_next(&pi)) { 214 info->mtd_dev_cnt += 1; 215 if (pi.mtd_num > info->highest_mtd_num) 216 info->highest_mtd_num = pi.mtd_num; 217 if (pi.mtd_num < info->lowest_mtd_num) 218 info->lowest_mtd_num = pi.mtd_num; 219 } 220 221 return 0; 222} 223 224/** 225 * legacy_get_dev_info - legacy version of 'mtd_get_dev_info()'. 226 * @node: name of the MTD device node 227 * @mtd: the MTD device information is returned here 228 * 229 * This function is similar to 'mtd_get_dev_info()' and has the same 230 * conventions. 231 */ 232int legacy_get_dev_info(const char *node, struct mtd_dev_info *mtd) 233{ 234 struct stat st; 235 struct mtd_info_user ui; 236 int fd, ret; 237 loff_t offs = 0; 238 struct proc_parse_info pi; 239 240 if (stat(node, &st)) { 241 sys_errmsg("cannot open \"%s\"", node); 242 if (errno == ENOENT) 243 normsg("MTD subsystem is old and does not support " 244 "sysfs, so MTD character device nodes have " 245 "to exist"); 246 } 247 248 if (!S_ISCHR(st.st_mode)) { 249 errno = EINVAL; 250 return errmsg("\"%s\" is not a character device", node); 251 } 252 253 memset(mtd, '\0', sizeof(struct mtd_dev_info)); 254 mtd->major = major(st.st_rdev); 255 mtd->minor = minor(st.st_rdev); 256 257 if (mtd->major != MTD_DEV_MAJOR) { 258 errno = EINVAL; 259 return errmsg("\"%s\" has major number %d, MTD devices have " 260 "major %d", node, mtd->major, MTD_DEV_MAJOR); 261 } 262 263 mtd->mtd_num = mtd->minor / 2; 264 265 fd = open(node, O_RDONLY); 266 if (fd == -1) 267 return sys_errmsg("cannot open \"%s\"", node); 268 269 if (ioctl(fd, MEMGETINFO, &ui)) { 270 sys_errmsg("MEMGETINFO ioctl request failed"); 271 goto out_close; 272 } 273 274 ret = ioctl(fd, MEMGETBADBLOCK, &offs); 275 if (ret == -1) { 276 if (errno != EOPNOTSUPP) { 277 sys_errmsg("MEMGETBADBLOCK ioctl failed"); 278 goto out_close; 279 } 280 errno = 0; 281 mtd->bb_allowed = 0; 282 } else 283 mtd->bb_allowed = 1; 284 285 mtd->type = ui.type; 286 mtd->size = ui.size; 287 mtd->eb_size = ui.erasesize; 288 mtd->min_io_size = ui.writesize; 289 mtd->oob_size = ui.oobsize; 290 fprintf(stderr, "ui.size:%0x eb_size:%0x\n", ui.size, ui.erasesize); 291 if (mtd->min_io_size <= 0) { 292 errmsg("mtd%d (%s) has insane min. I/O unit size %d", 293 mtd->mtd_num, node, mtd->min_io_size); 294 goto out_close; 295 } 296 if (mtd->eb_size <= 0 || mtd->eb_size < mtd->min_io_size) { 297 errmsg("mtd%d (%s) has insane eraseblock size %d", 298 mtd->mtd_num, node, mtd->eb_size); 299 goto out_close; 300 } 301 if (mtd->size <= 0 || mtd->size < mtd->eb_size) { 302 errmsg("mtd%d (%s) has insane size %lld", 303 mtd->mtd_num, node, mtd->size); 304 goto out_close; 305 } 306 mtd->eb_cnt = mtd->size / mtd->eb_size; 307 308 switch(mtd->type) { 309 case MTD_ABSENT: 310 errmsg("mtd%d (%s) is removable and is not present", 311 mtd->mtd_num, node); 312 goto out_close; 313 case MTD_RAM: 314 strcpy((char *)mtd->type_str, "ram"); 315 break; 316 case MTD_ROM: 317 strcpy((char *)mtd->type_str, "rom"); 318 break; 319 case MTD_NORFLASH: 320 strcpy((char *)mtd->type_str, "nor"); 321 break; 322 case MTD_NANDFLASH: 323 strcpy((char *)mtd->type_str, "nand"); 324 break; 325 case MTD_DATAFLASH: 326 strcpy((char *)mtd->type_str, "dataflash"); 327 break; 328 case MTD_UBIVOLUME: 329 strcpy((char *)mtd->type_str, "ubi"); 330 break; 331 default: 332 goto out_close; 333 } 334 335 if (ui.flags & MTD_WRITEABLE) 336 mtd->writable = 1; 337 mtd->subpage_size = mtd->min_io_size; 338 339 close(fd); 340 341 /* 342 * Unfortunately, the device name is not available via ioctl, and 343 * we have to parse /proc/mtd to get it. 344 */ 345 ret = proc_parse_start(&pi); 346 if (ret) 347 return -1; 348 349 while (proc_parse_next(&pi)) { 350 if (pi.mtd_num == mtd->mtd_num) { 351 strcpy((char *)mtd->name, pi.name); 352 return 0; 353 } 354 } 355 356 errmsg("mtd%d not found in \"%s\"", mtd->mtd_num, MTD_PROC_FILE); 357 errno = ENOENT; 358 return -1; 359 360out_close: 361 close(fd); 362 return -1; 363} 364 365/** 366 * legacy_get_dev_info1 - legacy version of 'mtd_get_dev_info1()'. 367 * @node: name of the MTD device node 368 * @mtd: the MTD device information is returned here 369 * 370 * This function is similar to 'mtd_get_dev_info1()' and has the same 371 * conventions. 372 */ 373int legacy_get_dev_info1(int mtd_num, struct mtd_dev_info *mtd) 374{ 375 char node[sizeof(MTD_DEV_PATT) + 20]; 376 377 sprintf(node, MTD_DEV_PATT, mtd_num); 378 return legacy_get_dev_info(node, mtd); 379} 380