libmtd_legacy.c 9.7 KB

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