rwbuffer.c 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147
  1. /****************************************************************************
  2. * drivers/rwbuffer.c
  3. *
  4. * Copyright (C) 2009, 2011, 2013-2014, 2017 Gregory Nutt. All rights
  5. * reserved.
  6. * Author: Gregory Nutt <gnutt@nuttx.org>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. * 3. Neither the name NuttX nor the names of its contributors may be
  19. * used to endorse or promote products derived from this software
  20. * without specific prior written permission.
  21. *
  22. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  23. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  24. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  25. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  26. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  27. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  28. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  29. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  30. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  31. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  32. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  33. * POSSIBILITY OF SUCH DAMAGE.
  34. *
  35. ****************************************************************************/
  36. /****************************************************************************
  37. * Included Files
  38. ****************************************************************************/
  39. #include <nuttx/config.h>
  40. #include <sys/types.h>
  41. #include <stdint.h>
  42. #include <stdbool.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include <time.h>
  46. #include <assert.h>
  47. #include <semaphore.h>
  48. #include <errno.h>
  49. #include <debug.h>
  50. #include <nuttx/kmalloc.h>
  51. #include <nuttx/wqueue.h>
  52. #include <nuttx/drivers/rwbuffer.h>
  53. #if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
  54. /****************************************************************************
  55. * Pre-processor Definitions
  56. ****************************************************************************/
  57. /* Configuration ************************************************************/
  58. #ifndef CONFIG_DRVR_WRDELAY
  59. # define CONFIG_DRVR_WRDELAY 350
  60. #endif
  61. #if !defined(CONFIG_SCHED_WORKQUEUE) && CONFIG_DRVR_WRDELAY != 0
  62. # error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
  63. #endif
  64. /****************************************************************************
  65. * Private Types
  66. ****************************************************************************/
  67. /****************************************************************************
  68. * Private Data
  69. ****************************************************************************/
  70. /****************************************************************************
  71. * Public Data
  72. ****************************************************************************/
  73. /****************************************************************************
  74. * Private Functions
  75. ****************************************************************************/
  76. /****************************************************************************
  77. * Name: rwb_semtake
  78. ****************************************************************************/
  79. static void rwb_semtake(sem_t *sem)
  80. {
  81. int ret;
  82. do
  83. {
  84. /* Take the semaphore (perhaps waiting) */
  85. ret = nxsem_wait(sem);
  86. /* The only case that an error should occur here is if the wait was
  87. * awakened by a signal.
  88. */
  89. DEBUGASSERT(ret == OK || ret == -EINTR);
  90. }
  91. while (ret == -EINTR);
  92. }
  93. /****************************************************************************
  94. * Name: rwb_semgive
  95. ****************************************************************************/
  96. #define rwb_semgive(s) nxsem_post(s)
  97. /****************************************************************************
  98. * Name: rwb_overlap
  99. ****************************************************************************/
  100. static inline bool rwb_overlap(off_t blockstart1, size_t nblocks1,
  101. off_t blockstart2, size_t nblocks2)
  102. {
  103. off_t blockend1 = blockstart1 + nblocks1 - 1;
  104. off_t blockend2 = blockstart2 + nblocks2 - 1;
  105. /* If the buffer 1 is wholly outside of buffer 2, return false */
  106. if ((blockend1 < blockstart2) || /* Wholly "below" */
  107. (blockstart1 > blockend2)) /* Wholly "above" */
  108. {
  109. return false;
  110. }
  111. else
  112. {
  113. return true;
  114. }
  115. }
  116. /****************************************************************************
  117. * Name: rwb_resetwrbuffer
  118. ****************************************************************************/
  119. #ifdef CONFIG_DRVR_WRITEBUFFER
  120. static inline void rwb_resetwrbuffer(struct rwbuffer_s *rwb)
  121. {
  122. /* We assume that the caller holds the wrsem */
  123. rwb->wrnblocks = 0;
  124. rwb->wrblockstart = (off_t)-1;
  125. rwb->wrexpectedblock = (off_t)-1;
  126. }
  127. #endif
  128. /****************************************************************************
  129. * Name: rwb_wrflush
  130. *
  131. * Assumptions:
  132. * The caller holds the wrsem semaphore.
  133. *
  134. ****************************************************************************/
  135. #ifdef CONFIG_DRVR_WRITEBUFFER
  136. static void rwb_wrflush(struct rwbuffer_s *rwb)
  137. {
  138. int ret;
  139. if (rwb->wrnblocks > 0)
  140. {
  141. finfo("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n",
  142. (long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer);
  143. /* Flush cache. On success, the flush method will return the number
  144. * of blocks written. Anything other than the number requested is
  145. * an error.
  146. */
  147. ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
  148. if (ret != rwb->wrnblocks)
  149. {
  150. ferr("ERROR: Error flushing write buffer: %d\n", ret);
  151. }
  152. rwb_resetwrbuffer(rwb);
  153. }
  154. }
  155. #endif
  156. /****************************************************************************
  157. * Name: rwb_wrtimeout
  158. ****************************************************************************/
  159. #if defined(CONFIG_DRVR_WRITEBUFFER) && CONFIG_DRVR_WRDELAY != 0
  160. static void rwb_wrtimeout(FAR void *arg)
  161. {
  162. /* The following assumes that the size of a pointer is 4-bytes or less */
  163. FAR struct rwbuffer_s *rwb = (struct rwbuffer_s *)arg;
  164. DEBUGASSERT(rwb != NULL);
  165. finfo("Timeout!\n");
  166. /* If a timeout elapses with with write buffer activity, this watchdog
  167. * handler function will be evoked on the thread of execution of the
  168. * worker thread.
  169. */
  170. rwb_semtake(&rwb->wrsem);
  171. rwb_wrflush(rwb);
  172. rwb_semgive(&rwb->wrsem);
  173. }
  174. #endif
  175. /****************************************************************************
  176. * Name: rwb_wrstarttimeout
  177. ****************************************************************************/
  178. #ifdef CONFIG_DRVR_WRITEBUFFER
  179. static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
  180. {
  181. #if CONFIG_DRVR_WRDELAY != 0
  182. /* CONFIG_DRVR_WRDELAY provides the delay period in milliseconds. CLK_TCK
  183. * provides the clock tick of the system (frequency in Hz).
  184. */
  185. int ticks = MSEC2TICK(CONFIG_DRVR_WRDELAY);
  186. (void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
  187. #endif
  188. }
  189. #endif
  190. /****************************************************************************
  191. * Name: rwb_wrcanceltimeout
  192. ****************************************************************************/
  193. #ifdef CONFIG_DRVR_WRITEBUFFER
  194. static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb)
  195. {
  196. #if CONFIG_DRVR_WRDELAY != 0
  197. (void)work_cancel(LPWORK, &rwb->work);
  198. #endif
  199. }
  200. #endif
  201. /****************************************************************************
  202. * Name: rwb_writebuffer
  203. ****************************************************************************/
  204. #ifdef CONFIG_DRVR_WRITEBUFFER
  205. static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
  206. off_t startblock, uint32_t nblocks,
  207. FAR const uint8_t *wrbuffer)
  208. {
  209. int ret;
  210. /* Write writebuffer Logic */
  211. rwb_wrcanceltimeout(rwb);
  212. /* First: Should we flush out our cache? We would do that if (1) we already
  213. * buffering blocks and the next block writing is not in the same sequence,
  214. * or (2) the number of blocks would exceed our allocated buffer capacity
  215. */
  216. if (((startblock != rwb->wrexpectedblock) && (rwb->wrnblocks)) ||
  217. ((rwb->wrnblocks + nblocks) > rwb->wrmaxblocks))
  218. {
  219. finfo("writebuffer miss, expected: %08x, given: %08x\n",
  220. rwb->wrexpectedblock, startblock);
  221. /* Flush the write buffer */
  222. ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
  223. if (ret < 0)
  224. {
  225. ferr("ERROR: Error writing multiple from cache: %d\n", -ret);
  226. return ret;
  227. }
  228. rwb_resetwrbuffer(rwb);
  229. }
  230. /* writebuffer is empty? Then initialize it */
  231. if (rwb->wrnblocks == 0)
  232. {
  233. finfo("Fresh cache starting at block: 0x%08x\n", startblock);
  234. rwb->wrblockstart = startblock;
  235. }
  236. /* Add data to cache */
  237. finfo("writebuffer: copying %d bytes from %p to %p\n",
  238. nblocks * rwb->blocksize, wrbuffer,
  239. &rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize]);
  240. memcpy(&rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize],
  241. wrbuffer, nblocks * rwb->blocksize);
  242. rwb->wrnblocks += nblocks;
  243. rwb->wrexpectedblock = rwb->wrblockstart + rwb->wrnblocks;
  244. rwb_wrstarttimeout(rwb);
  245. return nblocks;
  246. }
  247. #endif
  248. /****************************************************************************
  249. * Name: rwb_resetrhbuffer
  250. ****************************************************************************/
  251. #ifdef CONFIG_DRVR_READAHEAD
  252. static inline void rwb_resetrhbuffer(struct rwbuffer_s *rwb)
  253. {
  254. /* We assume that the caller holds the readAheadBufferSemphore */
  255. rwb->rhnblocks = 0;
  256. rwb->rhblockstart = (off_t)-1;
  257. }
  258. #endif
  259. /****************************************************************************
  260. * Name: rwb_bufferread
  261. ****************************************************************************/
  262. #ifdef CONFIG_DRVR_READAHEAD
  263. static inline void
  264. rwb_bufferread(struct rwbuffer_s *rwb, off_t startblock,
  265. size_t nblocks, uint8_t **rdbuffer)
  266. {
  267. /* We assume that (1) the caller holds the readAheadBufferSemphore, and (2)
  268. * that the caller already knows that all of the blocks are in the
  269. * read-ahead buffer.
  270. */
  271. /* Convert the units from blocks to bytes */
  272. off_t blockoffset = startblock - rwb->rhblockstart;
  273. off_t byteoffset = rwb->blocksize * blockoffset;
  274. size_t nbytes = rwb->blocksize * nblocks;
  275. /* Get the byte address in the read-ahead buffer */
  276. uint8_t *rhbuffer = rwb->rhbuffer + byteoffset;
  277. /* Copy the data from the read-ahead buffer into the IO buffer */
  278. memcpy(*rdbuffer, rhbuffer, nbytes);
  279. /* Update the caller's copy for the next address */
  280. *rdbuffer += nbytes;
  281. }
  282. #endif
  283. /****************************************************************************
  284. * Name: rwb_rhreload
  285. ****************************************************************************/
  286. #ifdef CONFIG_DRVR_READAHEAD
  287. static int rwb_rhreload(struct rwbuffer_s *rwb, off_t startblock)
  288. {
  289. off_t endblock;
  290. size_t nblocks;
  291. int ret;
  292. /* Check for attempts to read beyond the end of the media */
  293. if (startblock >= rwb->nblocks)
  294. {
  295. return -ESPIPE;
  296. }
  297. /* Get the block number +1 of the last block that will fit in the
  298. * read-ahead buffer
  299. */
  300. endblock = startblock + rwb->rhmaxblocks;
  301. /* Make sure that we don't read past the end of the device */
  302. if (endblock > rwb->nblocks)
  303. {
  304. endblock = rwb->nblocks;
  305. }
  306. nblocks = endblock - startblock;
  307. /* Reset the read buffer */
  308. rwb_resetrhbuffer(rwb);
  309. /* Now perform the read */
  310. ret = rwb->rhreload(rwb->dev, rwb->rhbuffer, startblock, nblocks);
  311. if (ret == nblocks)
  312. {
  313. /* Update information about what is in the read-ahead buffer */
  314. rwb->rhnblocks = nblocks;
  315. rwb->rhblockstart = startblock;
  316. /* The return value is not the number of blocks we asked to be loaded. */
  317. return nblocks;
  318. }
  319. return -EIO;
  320. }
  321. #endif
  322. /****************************************************************************
  323. * Name: rwb_invalidate_writebuffer
  324. *
  325. * Description:
  326. * Invalidate a region of the write buffer
  327. *
  328. ****************************************************************************/
  329. #if defined(CONFIG_DRVR_WRITEBUFFER) && defined(CONFIG_DRVR_INVALIDATE)
  330. int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
  331. off_t startblock, size_t blockcount)
  332. {
  333. int ret = OK;
  334. /* Is there a write buffer? Is data saved in the write buffer? */
  335. if (rwb->wrmaxblocks > 0 && rwb->wrnblocks > 0)
  336. {
  337. off_t wrbend;
  338. off_t invend;
  339. finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
  340. rwb_semtake(&rwb->wrsem);
  341. /* Now there are five cases:
  342. *
  343. * 1. We invalidate nothing
  344. */
  345. wrbend = rwb->wrblockstart + rwb->wrnblocks;
  346. invend = startblock + blockcount;
  347. if (rwb->wrblockstart > invend || wrbend < startblock)
  348. {
  349. ret = OK;
  350. }
  351. /* 2. We invalidate the entire write buffer. */
  352. else if (rwb->wrblockstart >= startblock && wrbend <= invend)
  353. {
  354. rwb->wrnblocks = 0;
  355. ret = OK;
  356. }
  357. /* We are going to invalidate a subset of the write buffer. Three
  358. * more cases to consider:
  359. *
  360. * 2. We invalidate a portion in the middle of the write buffer
  361. */
  362. else if (rwb->wrblockstart < startblock && wrbend > invend)
  363. {
  364. uint8_t *src;
  365. off_t block;
  366. off_t offset;
  367. size_t nblocks;
  368. /* Write the blocks at the end of the media to hardware */
  369. nblocks = wrbend - invend;
  370. block = invend;
  371. offset = block - rwb->wrblockstart;
  372. src = rwb->wrbuffer + offset * rwb->blocksize;
  373. ret = rwb->wrflush(rwb->dev, src, block, nblocks);
  374. if (ret < 0)
  375. {
  376. ferr("ERROR: wrflush failed: %d\n", ret);
  377. }
  378. /* Keep the blocks at the beginning of the buffer up the
  379. * start of the invalidated region.
  380. */
  381. else
  382. {
  383. rwb->wrnblocks = startblock - rwb->wrblockstart;
  384. ret = OK;
  385. }
  386. }
  387. /* 3. We invalidate a portion at the end of the write buffer */
  388. else if (wrbend > startblock && wrbend <= invend)
  389. {
  390. rwb->wrnblocks = wrbend - startblock;
  391. ret = OK;
  392. }
  393. /* 4. We invalidate a portion at the beginning of the write buffer */
  394. else /* if (rwb->wrblockstart >= startblock && wrbend > invend) */
  395. {
  396. uint8_t *src;
  397. size_t ninval;
  398. size_t nkeep;
  399. DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend > invend);
  400. /* Copy the data from the uninvalidated region to the beginning
  401. * of the write buffer.
  402. *
  403. * First calculate the source and destination of the transfer.
  404. */
  405. ninval = invend - rwb->wrblockstart;
  406. src = rwb->wrbuffer + ninval * rwb->blocksize;
  407. /* Calculate the number of blocks we are keeping. We keep
  408. * the ones that we don't invalidate.
  409. */
  410. nkeep = rwb->wrnblocks - ninval;
  411. /* Then move the data that we are keeping to the beginning
  412. * the write buffer.
  413. */
  414. memcpy(rwb->wrbuffer, src, nkeep * rwb->blocksize);
  415. /* Update the block info. The first block is now the one just
  416. * after the invalidation region and the number buffered blocks
  417. * is the number that we kept.
  418. */
  419. rwb->wrblockstart = invend;
  420. rwb->wrnblocks = nkeep;
  421. ret = OK;
  422. }
  423. rwb_semgive(&rwb->wrsem);
  424. }
  425. return ret;
  426. }
  427. #endif
  428. /****************************************************************************
  429. * Name: rwb_invalidate_readahead
  430. *
  431. * Description:
  432. * Invalidate a region of the read-ahead buffer
  433. *
  434. ****************************************************************************/
  435. #if defined(CONFIG_DRVR_READAHEAD) && defined(CONFIG_DRVR_INVALIDATE)
  436. int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
  437. off_t startblock, size_t blockcount)
  438. {
  439. int ret;
  440. if (rwb->rhmaxblocks > 0 && rwb->rhnblocks > 0)
  441. {
  442. off_t rhbend;
  443. off_t invend;
  444. finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
  445. rwb_semtake(&rwb->rhsem);
  446. /* Now there are five cases:
  447. *
  448. * 1. We invalidate nothing
  449. */
  450. rhbend = rwb->rhblockstart + rwb->rhnblocks;
  451. invend = startblock + blockcount;
  452. if (rhbend <= startblock || rwb->rhblockstart >= invend)
  453. {
  454. ret = OK;
  455. }
  456. /* 2. We invalidate the entire read-ahead buffer. */
  457. else if (rwb->rhblockstart >= startblock && rhbend <= invend)
  458. {
  459. rwb->rhnblocks = 0;
  460. ret = OK;
  461. }
  462. /* We are going to invalidate a subset of the read-ahead buffer.
  463. * Three more cases to consider:
  464. *
  465. * 2. We invalidate a portion in the middle of the read-ahead buffer
  466. */
  467. else if (rwb->rhblockstart < startblock && rhbend > invend)
  468. {
  469. /* Keep the blocks at the beginning of the buffer up the
  470. * start of the invalidated region.
  471. */
  472. rwb->rhnblocks = startblock - rwb->rhblockstart;
  473. ret = OK;
  474. }
  475. /* 3. We invalidate a portion at the end of the read-ahead buffer */
  476. else if (rhbend > startblock && rhbend <= invend)
  477. {
  478. rwb->rhnblocks = rhbend - startblock;
  479. ret = OK;
  480. }
  481. /* 4. We invalidate a portion at the beginning of the write buffer */
  482. else /* if (rwb->rhblockstart >= startblock && rhbend > invend) */
  483. {
  484. uint8_t *src;
  485. size_t ninval;
  486. size_t nkeep;
  487. DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend > invend);
  488. /* Copy the data from the uninvalidated region to the beginning
  489. * of the read buffer.
  490. *
  491. * First calculate the source and destination of the transfer.
  492. */
  493. ninval = invend - rwb->rhblockstart;
  494. src = rwb->rhbuffer + ninval * rwb->blocksize;
  495. /* Calculate the number of blocks we are keeping. We keep
  496. * the ones that we don't invalidate.
  497. */
  498. nkeep = rwb->rhnblocks - ninval;
  499. /* Then move the data that we are keeping to the beginning
  500. * the read buffer.
  501. */
  502. memmove(rwb->rhbuffer, src, nkeep * rwb->blocksize);
  503. /* Update the block info. The first block is now the one just
  504. * after the invalidation region and the number buffered blocks
  505. * is the number that we kept.
  506. */
  507. rwb->rhblockstart = invend;
  508. rwb->rhnblocks = nkeep;
  509. }
  510. rwb_semgive(&rwb->rhsem);
  511. }
  512. return ret;
  513. }
  514. #endif
  515. /****************************************************************************
  516. * Public Functions
  517. ****************************************************************************/
  518. /****************************************************************************
  519. * Name: rwb_initialize
  520. ****************************************************************************/
  521. int rwb_initialize(FAR struct rwbuffer_s *rwb)
  522. {
  523. uint32_t allocsize;
  524. /* Sanity checking */
  525. DEBUGASSERT(rwb != NULL);
  526. DEBUGASSERT(rwb->blocksize > 0);
  527. DEBUGASSERT(rwb->nblocks > 0);
  528. DEBUGASSERT(rwb->dev != NULL);
  529. /* Setup so that rwb_uninitialize can handle a failure */
  530. #ifdef CONFIG_DRVR_WRITEBUFFER
  531. DEBUGASSERT(rwb->wrflush != NULL);
  532. rwb->wrbuffer = NULL;
  533. #endif
  534. #ifdef CONFIG_DRVR_READAHEAD
  535. DEBUGASSERT(rwb->rhreload != NULL);
  536. rwb->rhbuffer = NULL;
  537. #endif
  538. #ifdef CONFIG_DRVR_WRITEBUFFER
  539. if (rwb->wrmaxblocks > 0)
  540. {
  541. finfo("Initialize the write buffer\n");
  542. /* Initialize the write buffer access semaphore */
  543. nxsem_init(&rwb->wrsem, 0, 1);
  544. /* Initialize write buffer parameters */
  545. rwb_resetwrbuffer(rwb);
  546. /* Allocate the write buffer */
  547. rwb->wrbuffer = NULL;
  548. if (rwb->wrmaxblocks > 0)
  549. {
  550. allocsize = rwb->wrmaxblocks * rwb->blocksize;
  551. rwb->wrbuffer = kmm_malloc(allocsize);
  552. if (!rwb->wrbuffer)
  553. {
  554. ferr("Write buffer kmm_malloc(%d) failed\n", allocsize);
  555. return -ENOMEM;
  556. }
  557. }
  558. finfo("Write buffer size: %d bytes\n", allocsize);
  559. }
  560. #endif /* CONFIG_DRVR_WRITEBUFFER */
  561. #ifdef CONFIG_DRVR_READAHEAD
  562. if (rwb->rhmaxblocks > 0)
  563. {
  564. finfo("Initialize the read-ahead buffer\n");
  565. /* Initialize the read-ahead buffer access semaphore */
  566. nxsem_init(&rwb->rhsem, 0, 1);
  567. /* Initialize read-ahead buffer parameters */
  568. rwb_resetrhbuffer(rwb);
  569. /* Allocate the read-ahead buffer */
  570. rwb->rhbuffer = NULL;
  571. if (rwb->rhmaxblocks > 0)
  572. {
  573. allocsize = rwb->rhmaxblocks * rwb->blocksize;
  574. rwb->rhbuffer = kmm_malloc(allocsize);
  575. if (!rwb->rhbuffer)
  576. {
  577. ferr("Read-ahead buffer kmm_malloc(%d) failed\n", allocsize);
  578. return -ENOMEM;
  579. }
  580. }
  581. finfo("Read-ahead buffer size: %d bytes\n", allocsize);
  582. }
  583. #endif /* CONFIG_DRVR_READAHEAD */
  584. return OK;
  585. }
  586. /****************************************************************************
  587. * Name: rwb_uninitialize
  588. ****************************************************************************/
  589. void rwb_uninitialize(FAR struct rwbuffer_s *rwb)
  590. {
  591. #ifdef CONFIG_DRVR_WRITEBUFFER
  592. if (rwb->wrmaxblocks > 0)
  593. {
  594. rwb_wrcanceltimeout(rwb);
  595. nxsem_destroy(&rwb->wrsem);
  596. if (rwb->wrbuffer)
  597. {
  598. kmm_free(rwb->wrbuffer);
  599. }
  600. }
  601. #endif
  602. #ifdef CONFIG_DRVR_READAHEAD
  603. if (rwb->rhmaxblocks > 0)
  604. {
  605. nxsem_destroy(&rwb->rhsem);
  606. if (rwb->rhbuffer)
  607. {
  608. kmm_free(rwb->rhbuffer);
  609. }
  610. }
  611. #endif
  612. }
  613. /****************************************************************************
  614. * Name: rwb_read_
  615. ****************************************************************************/
  616. static ssize_t rwb_read_(FAR struct rwbuffer_s *rwb, off_t startblock,
  617. size_t nblocks, FAR uint8_t *rdbuffer)
  618. {
  619. int ret = OK;
  620. #ifdef CONFIG_DRVR_READAHEAD
  621. if (rwb->rhmaxblocks > 0)
  622. {
  623. size_t remaining;
  624. /* Loop until we have read all of the requested blocks */
  625. rwb_semtake(&rwb->rhsem);
  626. for (remaining = nblocks; remaining > 0; )
  627. {
  628. /* Is there anything in the read-ahead buffer? */
  629. if (rwb->rhnblocks > 0)
  630. {
  631. off_t bufferend;
  632. /* How many blocks are available in this buffer? */
  633. bufferend = rwb->rhblockstart + rwb->rhnblocks;
  634. if (startblock >= rwb->rhblockstart && startblock < bufferend)
  635. {
  636. size_t rdblocks = bufferend - startblock;
  637. if (rdblocks > remaining)
  638. {
  639. rdblocks = remaining;
  640. }
  641. /* Then read the data from the read-ahead buffer */
  642. rwb_bufferread(rwb, startblock, rdblocks, &rdbuffer);
  643. startblock += rdblocks;
  644. remaining -= rdblocks;
  645. }
  646. }
  647. /* If we did not get all of the data from the buffer, then we have
  648. * to refill the buffer and try again.
  649. */
  650. if (remaining > 0)
  651. {
  652. ret = rwb_rhreload(rwb, startblock);
  653. if (ret < 0)
  654. {
  655. ferr("ERROR: Failed to fill the read-ahead buffer: %d\n", ret);
  656. rwb_semgive(&rwb->rhsem);
  657. return (ssize_t)ret;
  658. }
  659. }
  660. }
  661. /* On success, return the number of blocks that we were requested to
  662. * read. This is for compatibility with the normal return of a block
  663. * driver read method
  664. */
  665. rwb_semgive(&rwb->rhsem);
  666. ret = nblocks;
  667. }
  668. else
  669. #endif
  670. {
  671. /* No read-ahead buffering, (re)load the data directly into
  672. * the user buffer.
  673. */
  674. ret = rwb->rhreload(rwb->dev, rdbuffer, startblock, nblocks);
  675. }
  676. return (ssize_t)ret;
  677. }
  678. /****************************************************************************
  679. * Name: rwb_read
  680. ****************************************************************************/
  681. ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
  682. size_t nblocks, FAR uint8_t *rdbuffer)
  683. {
  684. int ret = OK;
  685. size_t readblocks = 0;
  686. finfo("startblock=%ld nblocks=%ld rdbuffer=%p\n",
  687. (long)startblock, (long)nblocks, rdbuffer);
  688. #ifdef CONFIG_DRVR_WRITEBUFFER
  689. /* If the new read data overlaps any part of the write buffer, we
  690. * directly copy write buffer to read buffer. This boost performance.
  691. */
  692. if (rwb->wrmaxblocks > 0)
  693. {
  694. /* If the write buffer overlaps the block(s) requested */
  695. rwb_semtake(&rwb->wrsem);
  696. if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
  697. {
  698. size_t rdblocks = 0;
  699. size_t wrnpass = 0;
  700. if (rwb->wrblockstart > startblock)
  701. {
  702. rdblocks = rwb->wrblockstart - startblock;
  703. ret = rwb_read_(rwb, startblock, rdblocks, rdbuffer);
  704. if (ret < 0)
  705. {
  706. rwb_semgive(&rwb->wrsem);
  707. return (ssize_t)ret;
  708. }
  709. startblock += ret;
  710. nblocks -= ret;
  711. rdbuffer += ret * rwb->blocksize;
  712. readblocks += ret;
  713. }
  714. if (rwb->wrblockstart < startblock)
  715. {
  716. wrnpass = startblock - rwb->wrblockstart;
  717. }
  718. rdblocks = nblocks > (rwb->wrnblocks - wrnpass) ?
  719. (rwb->wrnblocks - wrnpass) : nblocks;
  720. memcpy(rdbuffer, &rwb->wrbuffer[wrnpass * rwb->blocksize],
  721. rdblocks * rwb->blocksize);
  722. startblock += rdblocks;
  723. nblocks -= rdblocks;
  724. rdbuffer += rdblocks * rwb->blocksize;
  725. readblocks += rdblocks;
  726. }
  727. rwb_semgive(&rwb->wrsem);
  728. }
  729. #endif
  730. ret = rwb_read_(rwb, startblock, nblocks, rdbuffer);
  731. if (ret < 0)
  732. {
  733. return ret;
  734. }
  735. return readblocks + ret;
  736. }
  737. /****************************************************************************
  738. * Name: rwb_write
  739. ****************************************************************************/
  740. ssize_t rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
  741. size_t nblocks, FAR const uint8_t *wrbuffer)
  742. {
  743. int ret = OK;
  744. #ifdef CONFIG_DRVR_READAHEAD
  745. if (rwb->rhmaxblocks > 0)
  746. {
  747. /* If the new write data overlaps any part of the read buffer, then
  748. * flush the data from the read buffer. We could attempt some more
  749. * exotic handling -- but this simple logic is well-suited for simple
  750. * streaming applications.
  751. */
  752. rwb_semtake(&rwb->rhsem);
  753. if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
  754. {
  755. #ifdef CONFIG_DRVR_INVALIDATE
  756. /* Just invalidate the read buffer startblock + nblocks data */
  757. ret = rwb_invalidate_readahead(rwb, startblock, nblocks);
  758. if (ret < 0)
  759. {
  760. ferr("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
  761. rwb_semgive(&rwb->rhsem);
  762. return (ssize_t)ret;
  763. }
  764. #else
  765. rwb_resetrhbuffer(rwb);
  766. #endif
  767. }
  768. rwb_semgive(&rwb->rhsem);
  769. }
  770. #endif
  771. #ifdef CONFIG_DRVR_WRITEBUFFER
  772. if (rwb->wrmaxblocks > 0)
  773. {
  774. finfo("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
  775. /* Use the block cache unless the buffer size is bigger than block cache */
  776. if (nblocks > rwb->wrmaxblocks)
  777. {
  778. /* First flush the cache */
  779. rwb_semtake(&rwb->wrsem);
  780. rwb_wrflush(rwb);
  781. rwb_semgive(&rwb->wrsem);
  782. /* Then transfer the data directly to the media */
  783. ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
  784. }
  785. else
  786. {
  787. /* Buffer the data in the write buffer */
  788. rwb_semtake(&rwb->wrsem);
  789. ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
  790. rwb_semgive(&rwb->wrsem);
  791. }
  792. /* On success, return the number of blocks that we were requested to
  793. * write. This is for compatibility with the normal return of a block
  794. * driver write method
  795. */
  796. }
  797. else
  798. #endif /* CONFIG_DRVR_WRITEBUFFER */
  799. {
  800. /* No write buffer.. just pass the write operation through via the
  801. * flush callback.
  802. */
  803. ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
  804. }
  805. return (ssize_t)ret;
  806. }
  807. /****************************************************************************
  808. * Name: rwb_readbytes
  809. *
  810. * Description:
  811. * Character-oriented read
  812. *
  813. ****************************************************************************/
  814. #ifdef CONFIG_DRVR_READBYTES
  815. ssize_t rwb_readbytes(FAR struct rwbuffer_s *dev, off_t offset,
  816. size_t nbytes, FAR uint8_t *buffer)
  817. {
  818. /* Loop while there are bytes still be be read */
  819. /* Make sure that the sector containing the next bytes to transfer is in
  820. * memory.
  821. */
  822. /* How many bytes can be transfer from the in-memory data? */
  823. /* Transfer the bytes */
  824. /* Adjust counts and offsets for the next time through the loop */
  825. #warning Not Implemented
  826. return -ENOSYS;
  827. }
  828. #endif
  829. /****************************************************************************
  830. * Name: rwb_mediaremoved
  831. *
  832. * Description:
  833. * The following function is called when media is removed
  834. *
  835. ****************************************************************************/
  836. #ifdef CONFIG_DRVR_REMOVABLE
  837. int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
  838. {
  839. #ifdef CONFIG_DRVR_WRITEBUFFER
  840. if (rwb->wrmaxblocks > 0)
  841. {
  842. rwb_semtake(&rwb->wrsem);
  843. rwb_resetwrbuffer(rwb);
  844. rwb_semgive(&rwb->wrsem);
  845. }
  846. #endif
  847. #ifdef CONFIG_DRVR_READAHEAD
  848. if (rwb->rhmaxblocks > 0)
  849. {
  850. rwb_semtake(&rwb->rhsem);
  851. rwb_resetrhbuffer(rwb);
  852. rwb_semgive(&rwb->rhsem);
  853. }
  854. #endif
  855. return OK;
  856. }
  857. #endif
  858. /****************************************************************************
  859. * Name: rwb_invalidate
  860. *
  861. * Description:
  862. * Invalidate a region of the caches
  863. *
  864. ****************************************************************************/
  865. #ifdef CONFIG_DRVR_INVALIDATE
  866. int rwb_invalidate(FAR struct rwbuffer_s *rwb,
  867. off_t startblock, size_t blockcount)
  868. {
  869. int ret;
  870. #ifdef CONFIG_DRVR_WRITEBUFFER
  871. ret = rwb_invalidate_writebuffer(rwb, startblock, blockcount);
  872. if (ret < 0)
  873. {
  874. ferr("ERROR: rwb_invalidate_writebuffer failed: %d\n", ret);
  875. return ret;
  876. }
  877. #endif
  878. #ifdef CONFIG_DRVR_READAHEAD
  879. ret = rwb_invalidate_readahead(rwb, startblock, blockcount);
  880. if (ret < 0)
  881. {
  882. ferr("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
  883. return ret;
  884. }
  885. #endif
  886. return OK;
  887. }
  888. #endif
  889. /****************************************************************************
  890. * Name: rwb_flush
  891. *
  892. * Description:
  893. * Flush the write buffer
  894. *
  895. ****************************************************************************/
  896. #ifdef CONFIG_DRVR_WRITEBUFFER
  897. int rwb_flush(FAR struct rwbuffer_s *rwb)
  898. {
  899. rwb_semtake(&rwb->wrsem);
  900. rwb_wrcanceltimeout(rwb);
  901. rwb_wrflush(rwb);
  902. rwb_semgive(&rwb->wrsem);
  903. return OK;
  904. }
  905. #endif
  906. #endif /* CONFIG_DRVR_WRITEBUFFER || CONFIG_DRVR_READAHEAD */