rwbuffer.c 34 KB

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