file.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * Copyright 2015, The Chromium OS Authors
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are
  7. * met:
  8. *
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above
  12. * copyright notice, this list of conditions and the following
  13. * disclaimer in the documentation and/or other materials provided
  14. * with the distribution.
  15. * * Neither the name of Google Inc. nor the names of its
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. * scanft() is derived from mosys source,s which were released under the
  32. * BSD license
  33. */
  34. #include <dirent.h>
  35. #include <errno.h>
  36. #include <glob.h>
  37. #include <limits.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <unistd.h>
  42. #include <arpa/inet.h>
  43. #include <sys/fcntl.h>
  44. #include <sys/stat.h>
  45. #include <sys/types.h>
  46. #include "flash.h"
  47. #define FDT_ROOT "/proc/device-tree"
  48. #define FDT_ALIASES "/proc/device-tree/aliases"
  49. /* Like strstr(), but allowing NULL bytes within haystack */
  50. static int __find_string(const char *haystack, size_t hlen, const char *needle)
  51. {
  52. const char *p = haystack;
  53. const char *end = haystack + hlen;
  54. while (p < end) {
  55. if (strstr(p, needle))
  56. return 1;
  57. p = memchr(p, '\0', end - p);
  58. if (!p)
  59. /* Not found? We're at the end */
  60. return 0;
  61. else
  62. /* Skip past the NULL separator */
  63. p++;
  64. }
  65. return 0;
  66. }
  67. /* returns 1 if string if found, 0 if not, and <0 to indicate error */
  68. static int find_string(const char *path, const char *str)
  69. {
  70. char contents[4096];
  71. int fd, ret;
  72. ssize_t len;
  73. msg_pdbg("%s: checking path \"%s\" for contents \"%s\"\n",
  74. __func__, path, str);
  75. fd = open(path, O_RDONLY, 0);
  76. if (fd < 0) {
  77. msg_gerr("Cannot open file \"%s\"\n", path);
  78. ret = -1;
  79. goto find_string_exit_0;
  80. }
  81. /* mmap() (or even read() with a file length) would be nice but these
  82. * might not be implemented for files in sysfs and procfs.
  83. * Let's also leave room for a terminator. */
  84. len = read(fd, contents, sizeof(contents) - 1);
  85. if (len == -1) {
  86. msg_gerr("Cannot read file \"%s\"\n", path);
  87. ret = -1;
  88. goto find_string_exit_1;
  89. }
  90. /* Terminate the contents, in case they weren't terminated for us */
  91. contents[len++] = '\0';
  92. ret = __find_string(contents, len, str);
  93. find_string_exit_1:
  94. close(fd);
  95. find_string_exit_0:
  96. return ret;
  97. }
  98. /*
  99. * scanft - scan filetree for file with option to search for content
  100. *
  101. * @root: Where to begin search
  102. * @filename: Name of file to search for
  103. * @str: Optional NULL terminated string to search for
  104. * @symdepth: Maximum depth of symlinks to follow. A negative value means
  105. * follow indefinitely. Zero means do not follow symlinks.
  106. *
  107. * The caller should be specific enough with root and symdepth arguments
  108. * to avoid finding duplicate information (especially in sysfs).
  109. *
  110. * Also, note that we may only scan a bounded portion of the beginning of the
  111. * file for a match.
  112. *
  113. * returns allocated string with path of matching file if successful
  114. * returns NULL to indicate failure
  115. */
  116. const char *scanft(const char *root, const char *filename,
  117. const char *str, int symdepth)
  118. {
  119. DIR *dp;
  120. struct dirent *d;
  121. struct stat s;
  122. const char *ret = NULL;
  123. if (lstat(root, &s) < 0) {
  124. msg_pdbg("%s: Error stat'ing %s: %s\n",
  125. __func__, root, strerror(errno));
  126. return NULL;
  127. }
  128. if (S_ISLNK(s.st_mode)) {
  129. if (symdepth == 0) /* Leaf has been reached */
  130. return NULL;
  131. else if (symdepth > 0) /* Follow if not too deep in */
  132. symdepth--;
  133. } else if (!S_ISDIR(s.st_mode)) {
  134. return NULL;
  135. }
  136. if ((dp = opendir(root)) == NULL)
  137. return NULL;
  138. while (!ret && (d = readdir(dp))) {
  139. char newpath[PATH_MAX];
  140. /* Skip "." and ".." */
  141. if (!(strcmp(d->d_name, ".")) ||
  142. !(strcmp(d->d_name, "..")))
  143. continue;
  144. snprintf(newpath, sizeof(newpath), "%s/%s", root, d->d_name);
  145. if (!strcmp(d->d_name, filename)) {
  146. if (!str || (find_string(newpath, str) == 1))
  147. ret = strdup(newpath);
  148. }
  149. if (!ret)
  150. ret = scanft(newpath, filename, str, symdepth);
  151. }
  152. closedir(dp);
  153. return ret;
  154. }
  155. /*
  156. * do_fdt_find_spi_nor_flash - Search FDT via procfs for SPI NOR flash
  157. *
  158. * @prefix: Prefix of alias, for example "spi" will match spi*.
  159. * @compat: String to look for in "compatible" node
  160. *
  161. * This function attempt to match FDT aliases with devices that have the given
  162. * compatible string.
  163. *
  164. * Example: If prefix is "spi" and compat is "jedec,spi-nor" then this function
  165. * will read the device descriptors in every alias beginning with "spi" and
  166. * search their respective devicetree nodes for "compatible" files containing
  167. * the string "jedec,spi-nor".
  168. *
  169. * returns 0 to indicate NOR flash has been found, <0 to indicate error
  170. */
  171. static int do_fdt_find_spi_nor_flash(const char *prefix,
  172. const char *compat, unsigned int *bus, uint32_t *cs)
  173. {
  174. DIR *dp;
  175. struct dirent *d;
  176. struct stat s;
  177. int found = 0;
  178. if ((dp = opendir(FDT_ALIASES)) == NULL)
  179. return -1;
  180. /*
  181. * This loop will go thru the aliases sub-directory and kick-off a
  182. * recursive search thru matching devicetree nodes.
  183. */
  184. while (!found && (d = readdir(dp))) {
  185. char node[PATH_MAX];
  186. char pattern[PATH_MAX];
  187. char alias[64];
  188. int i, fd, len;
  189. glob_t pglob;
  190. /* allow partial match */
  191. if (strncmp(prefix, d->d_name, strlen(prefix)))
  192. continue;
  193. sprintf(node, "%s/%s", FDT_ALIASES, d->d_name);
  194. if (stat(node, &s) < 0) {
  195. msg_pdbg("%s: Error stat'ing %s: %s\n",
  196. __func__, node, strerror(errno));
  197. continue;
  198. }
  199. if (!S_ISREG(s.st_mode))
  200. continue;
  201. fd = open(node, O_RDONLY);
  202. if (fd < 0) {
  203. msg_perr("Could not open %s\n", d->d_name);
  204. continue;
  205. }
  206. /* devicetree strings and files aren't always terminated */
  207. len = read(fd, alias, sizeof(alias) - 1);
  208. if (len < 0) {
  209. msg_perr("Could not read %s\n", d->d_name);
  210. close(fd);
  211. continue;
  212. }
  213. alias[len] = '\0';
  214. close(fd);
  215. /* We expect something in the form "/<type>@<address>", for
  216. example "/spi@ff110000" */
  217. if (alias[0] != '/')
  218. continue;
  219. snprintf(node, sizeof(node), "%s%s", FDT_ROOT, alias);
  220. /*
  221. * Descend into this node's directory. According to the DT
  222. * specification, the SPI device node will be a subnode of
  223. * the bus node. Thus, we need to look for:
  224. * <path-to-spi-bus-node>/.../compatible
  225. */
  226. sprintf(pattern, "%s/*/compatible", node);
  227. msg_pspew("Scanning glob pattern \"%s\"\n", pattern);
  228. i = glob(pattern, 0, NULL, &pglob);
  229. if (i == GLOB_NOSPACE)
  230. goto err_out;
  231. else if (i != 0)
  232. continue;
  233. /*
  234. * For chip-select, look at the "reg" file located in
  235. * the same sub-directory as the "compatible" file.
  236. */
  237. for (i = 0; i < pglob.gl_pathc; i++) {
  238. char *reg_path;
  239. if (!find_string(pglob.gl_pathv[i], compat))
  240. continue;
  241. reg_path = strdup(pglob.gl_pathv[i]);
  242. if (!reg_path) {
  243. globfree(&pglob);
  244. goto err_out;
  245. }
  246. sprintf(strstr(reg_path, "compatible"), "reg");
  247. fd = open(reg_path, O_RDONLY);
  248. if (fd < 0) {
  249. msg_gerr("Cannot open \"%s\"\n", reg_path);
  250. free(reg_path);
  251. continue;
  252. }
  253. /* value is a 32-bit big-endian unsigned in FDT. */
  254. if (read(fd, cs, 4) != 4) {
  255. msg_gerr("Cannot read \"%s\"\n", reg_path);
  256. free(reg_path);
  257. close(fd);
  258. continue;
  259. }
  260. *cs = ntohl(*cs);
  261. found = 1;
  262. free(reg_path);
  263. close(fd);
  264. }
  265. if (found) {
  266. /* Extract bus from the alias filename. */
  267. if (sscanf(d->d_name, "spi%u", bus) != 1) {
  268. msg_gerr("Unexpected format: \"d->d_name\"\n");
  269. found = 0;
  270. globfree(&pglob);
  271. goto err_out;
  272. }
  273. }
  274. globfree(&pglob);
  275. }
  276. err_out:
  277. closedir(dp);
  278. return found ? 0 : -1;
  279. }
  280. /* Wrapper in case we want to use a list of compat strings or extend
  281. * this to search for other types of devices */
  282. int fdt_find_spi_nor_flash(unsigned int *bus, unsigned int *cs)
  283. {
  284. return do_fdt_find_spi_nor_flash("spi", "jedec,spi-nor", bus, cs);
  285. }