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