blockdev.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. /*
  2. * Copyright (C) 2008-2009 by egnite GmbH
  3. * Copyright (C) 2006 by egnite Software GmbH
  4. *
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. * 3. Neither the name of the copyright holders nor the names of
  17. * contributors may be used to endorse or promote products derived
  18. * from this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  23. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  24. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  25. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  26. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  27. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  28. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  30. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31. * SUCH DAMAGE.
  32. *
  33. * For additional information see http://www.ethernut.de/
  34. */
  35. /*!
  36. * \file dev/blockdev.c
  37. * \brief Generic block I/O device routines.
  38. *
  39. * \verbatim
  40. * $Id: blockdev.c 5472 2013-12-06 00:16:28Z olereinhardt $
  41. * \endverbatim
  42. */
  43. #include <cfg/os.h>
  44. #include <cfg/memory.h>
  45. #include <dev/blockdev.h>
  46. #include <sys/nutdebug.h>
  47. #include <fs/fs.h>
  48. #include <stdlib.h>
  49. #include <errno.h>
  50. #include <memdebug.h>
  51. /*!
  52. * \brief Block volume information structure type.
  53. */
  54. typedef struct _BLOCKVOLUME BLOCKVOLUME;
  55. /*!
  56. * \brief Block volume information structure.
  57. */
  58. struct _BLOCKVOLUME {
  59. /*! \brief Attached file system device.
  60. */
  61. NUTDEVICE *vol_fsdev;
  62. /*! \brief Number of blocks available to the file system.
  63. */
  64. uint32_t vol_blk_cnt;
  65. /*! \brief Number of bytes in a block.
  66. */
  67. int vol_blk_len;
  68. /*! \brief First page used by the file system.
  69. */
  70. uint32_t vol_blk_off;
  71. /*! \brief Next block number to read.
  72. *
  73. * The file system driver will send a NUTBLKDEV_SEEK control command
  74. * to set this value before calling the read or the write routine.
  75. *
  76. * The number is zero based.
  77. */
  78. uint32_t vol_blk_num;
  79. /*! \brief Internal block buffer.
  80. *
  81. * A file system driver may use this one or optionally provide it's
  82. * own buffers.
  83. *
  84. * Minimal systems may share their external bus interface with
  85. * device I/O lines, in which case the buffer must be located
  86. * in internal memory.
  87. */
  88. uint8_t *vol_blk_buf;
  89. };
  90. /*!
  91. * \brief Initialize the block I/O device.
  92. *
  93. * This dummy routine may be used by drivers, which do not need any
  94. * specific initialization.
  95. *
  96. * \param dev Specifies the device.
  97. *
  98. * \return Always 0.
  99. */
  100. int NutBlockDeviceInit(NUTDEVICE * dev)
  101. {
  102. NUTASSERT(dev != NULL);
  103. return 0;
  104. }
  105. /*!
  106. * \brief Mount a volume.
  107. *
  108. * Nut/OS doesn't provide specific routines for mounting. Instead routines
  109. * for opening files are used.
  110. *
  111. * Applications should not directly call this function, but use the high
  112. * level stdio routines for opening a file.
  113. *
  114. * \param dev Specifies the block I/O device.
  115. * \param name Partition number followed by a slash followed by a name
  116. * of the file system device. Both items are optional. If no
  117. * file system driver name is given, the first file system
  118. * driver found in the list of registered devices will be
  119. * used. If no partition number is specified or if partition
  120. * zero is given, the first active primary partition will be
  121. * used.
  122. * \param mode Opening mode. Currently ignored, but
  123. * \code _O_RDWR | _O_BINARY \endcode should be used for
  124. * compatibility with future enhancements.
  125. * \param acc File attributes, ignored.
  126. *
  127. * \return Pointer to a newly created file pointer to the mounted
  128. * partition or NUTFILE_EOF in case of any error.
  129. */
  130. NUTFILE *NutBlockDeviceOpen(NUTDEVICE * dev, const char *name, int mode, int acc)
  131. {
  132. NUTDEVICE *fsdev;
  133. /* Parse name for a file system driver, skip partition number. */
  134. NUTASSERT(name != NULL);
  135. if (*name) {
  136. do {
  137. name++;
  138. } while (*name && *name != '/');
  139. if (*name == '/') {
  140. name++;
  141. }
  142. }
  143. /*
  144. * Check the list of registered devices for the given name of the
  145. * files system driver. If none has been specified, get the first
  146. * file system driver in the list. Hopefully the application
  147. * registered one only.
  148. */
  149. if (*name) {
  150. fsdev = NutDeviceLookup(name);
  151. } else {
  152. fsdev = NutDeviceLookupType(NULL, IFTYP_FS);
  153. }
  154. if (fsdev == NULL) {
  155. errno = ENODEV;
  156. } else {
  157. BLOCKVOLUME *fcb = malloc(sizeof(BLOCKVOLUME));
  158. if (fcb) {
  159. NUTBLOCKIO *blkio;
  160. NUTASSERT(dev != NULL);
  161. blkio = (NUTBLOCKIO *) (dev->dev_dcb);
  162. NUTASSERT(blkio != NULL);
  163. fcb->vol_fsdev = fsdev;
  164. fcb->vol_blk_off = blkio->blkio_vol_bot;
  165. fcb->vol_blk_cnt = blkio->blkio_blk_cnt - blkio->blkio_vol_bot - blkio->blkio_vol_top;
  166. fcb->vol_blk_num = 0;
  167. fcb->vol_blk_len = blkio->blkio_blk_siz;
  168. fcb->vol_blk_buf = malloc(fcb->vol_blk_len);
  169. if (fcb->vol_blk_buf) {
  170. NUTFILE *nfp = malloc(sizeof(NUTFILE));
  171. if (nfp) {
  172. FSCP_VOL_MOUNT mparm;
  173. nfp->nf_dev = dev;
  174. nfp->nf_fcb = fcb;
  175. /* Mount the file system volume. */
  176. mparm.fscp_bmnt = nfp;
  177. mparm.fscp_part_type = 0;
  178. if (fsdev->dev_ioctl(fsdev, FS_VOL_MOUNT, &mparm) == 0) {
  179. /* Successful return. */
  180. return nfp;
  181. }
  182. free(nfp);
  183. }
  184. }
  185. free(fcb);
  186. }
  187. }
  188. return NUTFILE_EOF;
  189. }
  190. /*!
  191. * \brief Unmount a currently mounted volume.
  192. *
  193. * \param nfp File pointer to a previously mounted volume.
  194. *
  195. * \return 0 on success, -1 otherwise.
  196. */
  197. int NutBlockDeviceClose(NUTFILE * nfp)
  198. {
  199. int rc = -1;
  200. BLOCKVOLUME *fcb;
  201. NUTASSERT(nfp != NULL);
  202. fcb = (BLOCKVOLUME *) nfp->nf_fcb;
  203. if (fcb) {
  204. NUTASSERT(fcb->vol_fsdev != NULL);
  205. NUTASSERT(fcb->vol_fsdev->dev_ioctl != NULL);
  206. rc = fcb->vol_fsdev->dev_ioctl(fcb->vol_fsdev, FS_VOL_UNMOUNT, NULL);
  207. free(fcb);
  208. }
  209. free(nfp);
  210. return rc;
  211. }
  212. /*!
  213. * \brief Perform block I/O device control functions.
  214. *
  215. * This function is called by the ioctl() function of the C runtime
  216. * library. Applications should not directly call this function.
  217. *
  218. * Unkown requests are passed to the block I/O device.
  219. *
  220. * \param dev Identifies the device that receives the device-control
  221. * function.
  222. * \param req Requested control function. May be set to one of the
  223. * following constants:
  224. * - \ref NUTBLKDEV_INFO, conf points to a \ref BLKPAR_INFO structure.
  225. * - \ref NUTBLKDEV_SEEK, conf points to a \ref BLKPAR_SEEK structure.
  226. *
  227. * \param conf Points to a buffer that contains any data required for
  228. * the given control function or receives data from that
  229. * function.
  230. *
  231. * \return 0 on success, -1 otherwise.
  232. */
  233. int NutBlockDeviceIOCtl(NUTDEVICE * dev, int req, void *conf)
  234. {
  235. int rc = 0;
  236. switch (req) {
  237. case NUTBLKDEV_SEEK:
  238. {
  239. BLKPAR_SEEK *par = (BLKPAR_SEEK *) conf;
  240. BLOCKVOLUME *fcb;
  241. /* Sanity check. */
  242. NUTASSERT(conf != NULL);
  243. NUTASSERT(par->par_nfp != NULL);
  244. NUTASSERT(par->par_nfp->nf_fcb != NULL);
  245. fcb = (BLOCKVOLUME *) par->par_nfp->nf_fcb;
  246. fcb->vol_blk_num = par->par_blknum;
  247. }
  248. break;
  249. case NUTBLKDEV_INFO:
  250. {
  251. BLKPAR_INFO *par;
  252. BLOCKVOLUME *fcb;
  253. NUTASSERT(conf != NULL);
  254. par = (BLKPAR_INFO *) conf;
  255. NUTASSERT(par->par_nfp != NULL);
  256. NUTASSERT(par->par_nfp->nf_fcb != NULL);
  257. fcb = (BLOCKVOLUME *) par->par_nfp->nf_fcb;
  258. par->par_nblks = fcb->vol_blk_cnt;
  259. par->par_blksz = fcb->vol_blk_len;
  260. par->par_blkbp = fcb->vol_blk_buf;
  261. }
  262. break;
  263. default:
  264. {
  265. NUTBLOCKIO *blkio;
  266. NUTASSERT(dev != NULL);
  267. NUTASSERT(dev->dev_dcb != NULL);
  268. blkio = (NUTBLOCKIO *) (dev->dev_dcb);
  269. NUTASSERT(blkio->blkio_ioctl != NULL);
  270. rc = blkio->blkio_ioctl(dev, req, conf);
  271. }
  272. break;
  273. }
  274. return rc;
  275. }
  276. /*!
  277. * \brief Read data blocks from a mounted volume.
  278. *
  279. * The current position may have been set by a previous I/O control
  280. * \ref NUTBLKDEV_SEEK.
  281. *
  282. * Applications should not call this function directly, but use the
  283. * stdio interface.
  284. *
  285. * \param nfp File pointer to a previously mounted volume.
  286. * \param buffer Pointer to the data buffer to fill.
  287. * \param num Number of blocks to read.
  288. *
  289. * \return The number of blocks actually read. The current position is
  290. * set to the next unread block. A return value of -1 indicates
  291. * an error.
  292. */
  293. int NutBlockDeviceRead(NUTFILE * nfp, void *buffer, int num)
  294. {
  295. int rc;
  296. int cnt;
  297. uint8_t *bp = buffer;
  298. NUTBLOCKIO *blkio;
  299. BLOCKVOLUME *fcb;
  300. /* Sanity checks. */
  301. if (num == 0) {
  302. return 0;
  303. }
  304. NUTASSERT(buffer != NULL);
  305. NUTASSERT(nfp != NULL);
  306. NUTASSERT(nfp->nf_fcb != NULL);
  307. NUTASSERT(nfp->nf_dev != NULL);
  308. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  309. blkio = nfp->nf_dev->dev_dcb;
  310. fcb = (BLOCKVOLUME *) nfp->nf_fcb;
  311. NUTASSERT(blkio->blkio_read != NULL);
  312. for (rc = 0; rc < num; rc++) {
  313. if (fcb->vol_blk_num >= fcb->vol_blk_cnt) {
  314. break;
  315. }
  316. cnt = (*blkio->blkio_read) (nfp->nf_dev, fcb->vol_blk_num + fcb->vol_blk_off, bp, fcb->vol_blk_len);
  317. if (cnt != fcb->vol_blk_len) {
  318. break;
  319. }
  320. fcb->vol_blk_num++;
  321. bp += fcb->vol_blk_len;
  322. }
  323. return rc ? rc : -1;
  324. }
  325. /*!
  326. * \brief Write data blocks to a mounted volume.
  327. *
  328. * The current position may have been set by a previous I/O control
  329. * \ref NUTBLKDEV_SEEK.
  330. *
  331. * Applications should not call this function directly, but use the
  332. * stdio interface.
  333. *
  334. * \param nfp File pointer to a previously mounted volume.
  335. * \param buffer Pointer to the data to be written.
  336. * \param num Number of blocks to write.
  337. *
  338. * \return The number of blocks actually written. The current position is
  339. * set to the next block. A return value of -1 indicates an error.
  340. */
  341. int NutBlockDeviceWrite(NUTFILE * nfp, const void *buffer, int num)
  342. {
  343. int rc;
  344. int cnt;
  345. const uint8_t *bp = buffer;
  346. NUTBLOCKIO *blkio;
  347. BLOCKVOLUME *fcb;
  348. /* Sanity checks. */
  349. if (num == 0) {
  350. return 0;
  351. }
  352. NUTASSERT(nfp != NULL);
  353. NUTASSERT(nfp->nf_fcb != NULL);
  354. NUTASSERT(nfp->nf_dev != NULL);
  355. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  356. fcb = (BLOCKVOLUME *) nfp->nf_fcb;
  357. blkio = nfp->nf_dev->dev_dcb;
  358. NUTASSERT(blkio->blkio_write != NULL);
  359. for (rc = 0; rc < num; rc++) {
  360. if (fcb->vol_blk_num >= fcb->vol_blk_cnt) {
  361. break;
  362. }
  363. cnt = (*blkio->blkio_write) (nfp->nf_dev, fcb->vol_blk_num + fcb->vol_blk_off, bp, fcb->vol_blk_len);
  364. if (cnt != fcb->vol_blk_len) {
  365. break;
  366. }
  367. fcb->vol_blk_num++;
  368. bp += fcb->vol_blk_len;
  369. }
  370. return rc ? rc : -1;
  371. }
  372. #ifdef __HARVARD_ARCH__
  373. /*!
  374. * \brief Write data blocks from program space to a mounted volume.
  375. *
  376. * Similar to NutBlockDeviceWrite() except that the data is located in
  377. * program memory.
  378. *
  379. * Applications should not call this function directly, but use the
  380. * stdio interface.
  381. *
  382. * \param nfp File pointer to a previously mounted volume.
  383. * \param buffer Pointer to the data bytes in program space to be written.
  384. * \param num Number of blocks to write.
  385. *
  386. * \return The number of blocks actually written. The current position is
  387. * set to the next block. A return value of -1 indicates an error.
  388. */
  389. int NutBlockDeviceWrite_P(NUTFILE * nfp, PGM_P buffer, int num)
  390. {
  391. int rc = 0;
  392. int cnt;
  393. PGM_P bp = buffer;
  394. NUTBLOCKIO *blkio;
  395. BLOCKVOLUME *fcb;
  396. /* Sanity checks. */
  397. if (num == 0) {
  398. return 0;
  399. }
  400. NUTASSERT(nfp != NULL);
  401. NUTASSERT(nfp->nf_fcb != NULL);
  402. NUTASSERT(nfp->nf_dev != NULL);
  403. NUTASSERT(nfp->nf_dev->dev_dcb != NULL);
  404. fcb = (BLOCKVOLUME *) nfp->nf_fcb;
  405. blkio = nfp->nf_dev->dev_dcb;
  406. NUTASSERT(blkio->blkio_write != NULL);
  407. for (rc = 0; rc < num; rc++) {
  408. if (fcb->vol_blk_num >= fcb->vol_blk_cnt) {
  409. break;
  410. }
  411. cnt = (*blkio->blkio_write_P) (nfp->nf_dev, fcb->vol_blk_num + fcb->vol_blk_off, bp, fcb->vol_blk_len);
  412. if (cnt != fcb->vol_blk_len) {
  413. break;
  414. }
  415. fcb->vol_blk_num++;
  416. bp += fcb->vol_blk_len;
  417. }
  418. return rc ? rc : -1;
  419. }
  420. #endif
  421. /*!
  422. * \brief Return the size of a mounted volume.
  423. *
  424. * \param nfp File pointer to a previously mounted volume.
  425. *
  426. * \return Total number of bytes.
  427. */
  428. long NutBlockDeviceSize(NUTFILE * nfp)
  429. {
  430. BLOCKVOLUME *fcb;
  431. NUTASSERT(nfp != NULL);
  432. fcb = (BLOCKVOLUME *) nfp->nf_fcb;
  433. NUTASSERT(fcb != NULL);
  434. return (long) fcb->vol_blk_cnt * (long) fcb->vol_blk_len;
  435. }