1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147 |
- /****************************************************************************
- * drivers/rwbuffer.c
- *
- * Copyright (C) 2009, 2011, 2013-2014, 2017 Gregory Nutt. All rights
- * reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <string.h>
- #include <time.h>
- #include <assert.h>
- #include <semaphore.h>
- #include <errno.h>
- #include <debug.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/wqueue.h>
- #include <nuttx/drivers/rwbuffer.h>
- #if defined(CONFIG_DRVR_WRITEBUFFER) || defined(CONFIG_DRVR_READAHEAD)
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Configuration ************************************************************/
- #ifndef CONFIG_DRVR_WRDELAY
- # define CONFIG_DRVR_WRDELAY 350
- #endif
- #if !defined(CONFIG_SCHED_WORKQUEUE) && CONFIG_DRVR_WRDELAY != 0
- # error "Worker thread support is required (CONFIG_SCHED_WORKQUEUE)"
- #endif
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /****************************************************************************
- * Public Data
- ****************************************************************************/
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: rwb_semtake
- ****************************************************************************/
- static void rwb_semtake(sem_t *sem)
- {
- int ret;
- do
- {
- /* Take the semaphore (perhaps waiting) */
- ret = nxsem_wait(sem);
- /* The only case that an error should occur here is if the wait was
- * awakened by a signal.
- */
- DEBUGASSERT(ret == OK || ret == -EINTR);
- }
- while (ret == -EINTR);
- }
- /****************************************************************************
- * Name: rwb_semgive
- ****************************************************************************/
- #define rwb_semgive(s) nxsem_post(s)
- /****************************************************************************
- * Name: rwb_overlap
- ****************************************************************************/
- static inline bool rwb_overlap(off_t blockstart1, size_t nblocks1,
- off_t blockstart2, size_t nblocks2)
- {
- off_t blockend1 = blockstart1 + nblocks1 - 1;
- off_t blockend2 = blockstart2 + nblocks2 - 1;
- /* If the buffer 1 is wholly outside of buffer 2, return false */
- if ((blockend1 < blockstart2) || /* Wholly "below" */
- (blockstart1 > blockend2)) /* Wholly "above" */
- {
- return false;
- }
- else
- {
- return true;
- }
- }
- /****************************************************************************
- * Name: rwb_resetwrbuffer
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- static inline void rwb_resetwrbuffer(struct rwbuffer_s *rwb)
- {
- /* We assume that the caller holds the wrsem */
- rwb->wrnblocks = 0;
- rwb->wrblockstart = (off_t)-1;
- rwb->wrexpectedblock = (off_t)-1;
- }
- #endif
- /****************************************************************************
- * Name: rwb_wrflush
- *
- * Assumptions:
- * The caller holds the wrsem semaphore.
- *
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- static void rwb_wrflush(struct rwbuffer_s *rwb)
- {
- int ret;
- if (rwb->wrnblocks > 0)
- {
- finfo("Flushing: blockstart=0x%08lx nblocks=%d from buffer=%p\n",
- (long)rwb->wrblockstart, rwb->wrnblocks, rwb->wrbuffer);
- /* Flush cache. On success, the flush method will return the number
- * of blocks written. Anything other than the number requested is
- * an error.
- */
- ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
- if (ret != rwb->wrnblocks)
- {
- ferr("ERROR: Error flushing write buffer: %d\n", ret);
- }
- rwb_resetwrbuffer(rwb);
- }
- }
- #endif
- /****************************************************************************
- * Name: rwb_wrtimeout
- ****************************************************************************/
- #if defined(CONFIG_DRVR_WRITEBUFFER) && CONFIG_DRVR_WRDELAY != 0
- static void rwb_wrtimeout(FAR void *arg)
- {
- /* The following assumes that the size of a pointer is 4-bytes or less */
- FAR struct rwbuffer_s *rwb = (struct rwbuffer_s *)arg;
- DEBUGASSERT(rwb != NULL);
- finfo("Timeout!\n");
- /* If a timeout elapses with with write buffer activity, this watchdog
- * handler function will be evoked on the thread of execution of the
- * worker thread.
- */
- rwb_semtake(&rwb->wrsem);
- rwb_wrflush(rwb);
- rwb_semgive(&rwb->wrsem);
- }
- #endif
- /****************************************************************************
- * Name: rwb_wrstarttimeout
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
- {
- #if CONFIG_DRVR_WRDELAY != 0
- /* CONFIG_DRVR_WRDELAY provides the delay period in milliseconds. CLK_TCK
- * provides the clock tick of the system (frequency in Hz).
- */
- int ticks = MSEC2TICK(CONFIG_DRVR_WRDELAY);
- (void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
- #endif
- }
- #endif
- /****************************************************************************
- * Name: rwb_wrcanceltimeout
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb)
- {
- #if CONFIG_DRVR_WRDELAY != 0
- (void)work_cancel(LPWORK, &rwb->work);
- #endif
- }
- #endif
- /****************************************************************************
- * Name: rwb_writebuffer
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- static ssize_t rwb_writebuffer(FAR struct rwbuffer_s *rwb,
- off_t startblock, uint32_t nblocks,
- FAR const uint8_t *wrbuffer)
- {
- int ret;
- /* Write writebuffer Logic */
- rwb_wrcanceltimeout(rwb);
- /* First: Should we flush out our cache? We would do that if (1) we already
- * buffering blocks and the next block writing is not in the same sequence,
- * or (2) the number of blocks would exceed our allocated buffer capacity
- */
- if (((startblock != rwb->wrexpectedblock) && (rwb->wrnblocks)) ||
- ((rwb->wrnblocks + nblocks) > rwb->wrmaxblocks))
- {
- finfo("writebuffer miss, expected: %08x, given: %08x\n",
- rwb->wrexpectedblock, startblock);
- /* Flush the write buffer */
- ret = rwb->wrflush(rwb->dev, rwb->wrbuffer, rwb->wrblockstart, rwb->wrnblocks);
- if (ret < 0)
- {
- ferr("ERROR: Error writing multiple from cache: %d\n", -ret);
- return ret;
- }
- rwb_resetwrbuffer(rwb);
- }
- /* writebuffer is empty? Then initialize it */
- if (rwb->wrnblocks == 0)
- {
- finfo("Fresh cache starting at block: 0x%08x\n", startblock);
- rwb->wrblockstart = startblock;
- }
- /* Add data to cache */
- finfo("writebuffer: copying %d bytes from %p to %p\n",
- nblocks * rwb->blocksize, wrbuffer,
- &rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize]);
- memcpy(&rwb->wrbuffer[rwb->wrnblocks * rwb->blocksize],
- wrbuffer, nblocks * rwb->blocksize);
- rwb->wrnblocks += nblocks;
- rwb->wrexpectedblock = rwb->wrblockstart + rwb->wrnblocks;
- rwb_wrstarttimeout(rwb);
- return nblocks;
- }
- #endif
- /****************************************************************************
- * Name: rwb_resetrhbuffer
- ****************************************************************************/
- #ifdef CONFIG_DRVR_READAHEAD
- static inline void rwb_resetrhbuffer(struct rwbuffer_s *rwb)
- {
- /* We assume that the caller holds the readAheadBufferSemphore */
- rwb->rhnblocks = 0;
- rwb->rhblockstart = (off_t)-1;
- }
- #endif
- /****************************************************************************
- * Name: rwb_bufferread
- ****************************************************************************/
- #ifdef CONFIG_DRVR_READAHEAD
- static inline void
- rwb_bufferread(struct rwbuffer_s *rwb, off_t startblock,
- size_t nblocks, uint8_t **rdbuffer)
- {
- /* We assume that (1) the caller holds the readAheadBufferSemphore, and (2)
- * that the caller already knows that all of the blocks are in the
- * read-ahead buffer.
- */
- /* Convert the units from blocks to bytes */
- off_t blockoffset = startblock - rwb->rhblockstart;
- off_t byteoffset = rwb->blocksize * blockoffset;
- size_t nbytes = rwb->blocksize * nblocks;
- /* Get the byte address in the read-ahead buffer */
- uint8_t *rhbuffer = rwb->rhbuffer + byteoffset;
- /* Copy the data from the read-ahead buffer into the IO buffer */
- memcpy(*rdbuffer, rhbuffer, nbytes);
- /* Update the caller's copy for the next address */
- *rdbuffer += nbytes;
- }
- #endif
- /****************************************************************************
- * Name: rwb_rhreload
- ****************************************************************************/
- #ifdef CONFIG_DRVR_READAHEAD
- static int rwb_rhreload(struct rwbuffer_s *rwb, off_t startblock)
- {
- off_t endblock;
- size_t nblocks;
- int ret;
- /* Check for attempts to read beyond the end of the media */
- if (startblock >= rwb->nblocks)
- {
- return -ESPIPE;
- }
- /* Get the block number +1 of the last block that will fit in the
- * read-ahead buffer
- */
- endblock = startblock + rwb->rhmaxblocks;
- /* Make sure that we don't read past the end of the device */
- if (endblock > rwb->nblocks)
- {
- endblock = rwb->nblocks;
- }
- nblocks = endblock - startblock;
- /* Reset the read buffer */
- rwb_resetrhbuffer(rwb);
- /* Now perform the read */
- ret = rwb->rhreload(rwb->dev, rwb->rhbuffer, startblock, nblocks);
- if (ret == nblocks)
- {
- /* Update information about what is in the read-ahead buffer */
- rwb->rhnblocks = nblocks;
- rwb->rhblockstart = startblock;
- /* The return value is not the number of blocks we asked to be loaded. */
- return nblocks;
- }
- return -EIO;
- }
- #endif
- /****************************************************************************
- * Name: rwb_invalidate_writebuffer
- *
- * Description:
- * Invalidate a region of the write buffer
- *
- ****************************************************************************/
- #if defined(CONFIG_DRVR_WRITEBUFFER) && defined(CONFIG_DRVR_INVALIDATE)
- int rwb_invalidate_writebuffer(FAR struct rwbuffer_s *rwb,
- off_t startblock, size_t blockcount)
- {
- int ret = OK;
- /* Is there a write buffer? Is data saved in the write buffer? */
- if (rwb->wrmaxblocks > 0 && rwb->wrnblocks > 0)
- {
- off_t wrbend;
- off_t invend;
- finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
- rwb_semtake(&rwb->wrsem);
- /* Now there are five cases:
- *
- * 1. We invalidate nothing
- */
- wrbend = rwb->wrblockstart + rwb->wrnblocks;
- invend = startblock + blockcount;
- if (rwb->wrblockstart > invend || wrbend < startblock)
- {
- ret = OK;
- }
- /* 2. We invalidate the entire write buffer. */
- else if (rwb->wrblockstart >= startblock && wrbend <= invend)
- {
- rwb->wrnblocks = 0;
- ret = OK;
- }
- /* We are going to invalidate a subset of the write buffer. Three
- * more cases to consider:
- *
- * 2. We invalidate a portion in the middle of the write buffer
- */
- else if (rwb->wrblockstart < startblock && wrbend > invend)
- {
- uint8_t *src;
- off_t block;
- off_t offset;
- size_t nblocks;
- /* Write the blocks at the end of the media to hardware */
- nblocks = wrbend - invend;
- block = invend;
- offset = block - rwb->wrblockstart;
- src = rwb->wrbuffer + offset * rwb->blocksize;
- ret = rwb->wrflush(rwb->dev, src, block, nblocks);
- if (ret < 0)
- {
- ferr("ERROR: wrflush failed: %d\n", ret);
- }
- /* Keep the blocks at the beginning of the buffer up the
- * start of the invalidated region.
- */
- else
- {
- rwb->wrnblocks = startblock - rwb->wrblockstart;
- ret = OK;
- }
- }
- /* 3. We invalidate a portion at the end of the write buffer */
- else if (wrbend > startblock && wrbend <= invend)
- {
- rwb->wrnblocks = wrbend - startblock;
- ret = OK;
- }
- /* 4. We invalidate a portion at the beginning of the write buffer */
- else /* if (rwb->wrblockstart >= startblock && wrbend > invend) */
- {
- uint8_t *src;
- size_t ninval;
- size_t nkeep;
- DEBUGASSERT(rwb->wrblockstart >= startblock && wrbend > invend);
- /* Copy the data from the uninvalidated region to the beginning
- * of the write buffer.
- *
- * First calculate the source and destination of the transfer.
- */
- ninval = invend - rwb->wrblockstart;
- src = rwb->wrbuffer + ninval * rwb->blocksize;
- /* Calculate the number of blocks we are keeping. We keep
- * the ones that we don't invalidate.
- */
- nkeep = rwb->wrnblocks - ninval;
- /* Then move the data that we are keeping to the beginning
- * the write buffer.
- */
- memcpy(rwb->wrbuffer, src, nkeep * rwb->blocksize);
- /* Update the block info. The first block is now the one just
- * after the invalidation region and the number buffered blocks
- * is the number that we kept.
- */
- rwb->wrblockstart = invend;
- rwb->wrnblocks = nkeep;
- ret = OK;
- }
- rwb_semgive(&rwb->wrsem);
- }
- return ret;
- }
- #endif
- /****************************************************************************
- * Name: rwb_invalidate_readahead
- *
- * Description:
- * Invalidate a region of the read-ahead buffer
- *
- ****************************************************************************/
- #if defined(CONFIG_DRVR_READAHEAD) && defined(CONFIG_DRVR_INVALIDATE)
- int rwb_invalidate_readahead(FAR struct rwbuffer_s *rwb,
- off_t startblock, size_t blockcount)
- {
- int ret;
- if (rwb->rhmaxblocks > 0 && rwb->rhnblocks > 0)
- {
- off_t rhbend;
- off_t invend;
- finfo("startblock=%d blockcount=%p\n", startblock, blockcount);
- rwb_semtake(&rwb->rhsem);
- /* Now there are five cases:
- *
- * 1. We invalidate nothing
- */
- rhbend = rwb->rhblockstart + rwb->rhnblocks;
- invend = startblock + blockcount;
- if (rhbend <= startblock || rwb->rhblockstart >= invend)
- {
- ret = OK;
- }
- /* 2. We invalidate the entire read-ahead buffer. */
- else if (rwb->rhblockstart >= startblock && rhbend <= invend)
- {
- rwb->rhnblocks = 0;
- ret = OK;
- }
- /* We are going to invalidate a subset of the read-ahead buffer.
- * Three more cases to consider:
- *
- * 2. We invalidate a portion in the middle of the read-ahead buffer
- */
- else if (rwb->rhblockstart < startblock && rhbend > invend)
- {
- /* Keep the blocks at the beginning of the buffer up the
- * start of the invalidated region.
- */
- rwb->rhnblocks = startblock - rwb->rhblockstart;
- ret = OK;
- }
- /* 3. We invalidate a portion at the end of the read-ahead buffer */
- else if (rhbend > startblock && rhbend <= invend)
- {
- rwb->rhnblocks = rhbend - startblock;
- ret = OK;
- }
- /* 4. We invalidate a portion at the beginning of the write buffer */
- else /* if (rwb->rhblockstart >= startblock && rhbend > invend) */
- {
- uint8_t *src;
- size_t ninval;
- size_t nkeep;
- DEBUGASSERT(rwb->rhblockstart >= startblock && rhbend > invend);
- /* Copy the data from the uninvalidated region to the beginning
- * of the read buffer.
- *
- * First calculate the source and destination of the transfer.
- */
- ninval = invend - rwb->rhblockstart;
- src = rwb->rhbuffer + ninval * rwb->blocksize;
- /* Calculate the number of blocks we are keeping. We keep
- * the ones that we don't invalidate.
- */
- nkeep = rwb->rhnblocks - ninval;
- /* Then move the data that we are keeping to the beginning
- * the read buffer.
- */
- memmove(rwb->rhbuffer, src, nkeep * rwb->blocksize);
- /* Update the block info. The first block is now the one just
- * after the invalidation region and the number buffered blocks
- * is the number that we kept.
- */
- rwb->rhblockstart = invend;
- rwb->rhnblocks = nkeep;
- }
- rwb_semgive(&rwb->rhsem);
- }
- return ret;
- }
- #endif
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: rwb_initialize
- ****************************************************************************/
- int rwb_initialize(FAR struct rwbuffer_s *rwb)
- {
- uint32_t allocsize;
- /* Sanity checking */
- DEBUGASSERT(rwb != NULL);
- DEBUGASSERT(rwb->blocksize > 0);
- DEBUGASSERT(rwb->nblocks > 0);
- DEBUGASSERT(rwb->dev != NULL);
- /* Setup so that rwb_uninitialize can handle a failure */
- #ifdef CONFIG_DRVR_WRITEBUFFER
- DEBUGASSERT(rwb->wrflush != NULL);
- rwb->wrbuffer = NULL;
- #endif
- #ifdef CONFIG_DRVR_READAHEAD
- DEBUGASSERT(rwb->rhreload != NULL);
- rwb->rhbuffer = NULL;
- #endif
- #ifdef CONFIG_DRVR_WRITEBUFFER
- if (rwb->wrmaxblocks > 0)
- {
- finfo("Initialize the write buffer\n");
- /* Initialize the write buffer access semaphore */
- nxsem_init(&rwb->wrsem, 0, 1);
- /* Initialize write buffer parameters */
- rwb_resetwrbuffer(rwb);
- /* Allocate the write buffer */
- rwb->wrbuffer = NULL;
- if (rwb->wrmaxblocks > 0)
- {
- allocsize = rwb->wrmaxblocks * rwb->blocksize;
- rwb->wrbuffer = kmm_malloc(allocsize);
- if (!rwb->wrbuffer)
- {
- ferr("Write buffer kmm_malloc(%d) failed\n", allocsize);
- return -ENOMEM;
- }
- }
- finfo("Write buffer size: %d bytes\n", allocsize);
- }
- #endif /* CONFIG_DRVR_WRITEBUFFER */
- #ifdef CONFIG_DRVR_READAHEAD
- if (rwb->rhmaxblocks > 0)
- {
- finfo("Initialize the read-ahead buffer\n");
- /* Initialize the read-ahead buffer access semaphore */
- nxsem_init(&rwb->rhsem, 0, 1);
- /* Initialize read-ahead buffer parameters */
- rwb_resetrhbuffer(rwb);
- /* Allocate the read-ahead buffer */
- rwb->rhbuffer = NULL;
- if (rwb->rhmaxblocks > 0)
- {
- allocsize = rwb->rhmaxblocks * rwb->blocksize;
- rwb->rhbuffer = kmm_malloc(allocsize);
- if (!rwb->rhbuffer)
- {
- ferr("Read-ahead buffer kmm_malloc(%d) failed\n", allocsize);
- return -ENOMEM;
- }
- }
- finfo("Read-ahead buffer size: %d bytes\n", allocsize);
- }
- #endif /* CONFIG_DRVR_READAHEAD */
- return OK;
- }
- /****************************************************************************
- * Name: rwb_uninitialize
- ****************************************************************************/
- void rwb_uninitialize(FAR struct rwbuffer_s *rwb)
- {
- #ifdef CONFIG_DRVR_WRITEBUFFER
- if (rwb->wrmaxblocks > 0)
- {
- rwb_wrcanceltimeout(rwb);
- nxsem_destroy(&rwb->wrsem);
- if (rwb->wrbuffer)
- {
- kmm_free(rwb->wrbuffer);
- }
- }
- #endif
- #ifdef CONFIG_DRVR_READAHEAD
- if (rwb->rhmaxblocks > 0)
- {
- nxsem_destroy(&rwb->rhsem);
- if (rwb->rhbuffer)
- {
- kmm_free(rwb->rhbuffer);
- }
- }
- #endif
- }
- /****************************************************************************
- * Name: rwb_read_
- ****************************************************************************/
- static ssize_t rwb_read_(FAR struct rwbuffer_s *rwb, off_t startblock,
- size_t nblocks, FAR uint8_t *rdbuffer)
- {
- int ret = OK;
- #ifdef CONFIG_DRVR_READAHEAD
- if (rwb->rhmaxblocks > 0)
- {
- size_t remaining;
- /* Loop until we have read all of the requested blocks */
- rwb_semtake(&rwb->rhsem);
- for (remaining = nblocks; remaining > 0; )
- {
- /* Is there anything in the read-ahead buffer? */
- if (rwb->rhnblocks > 0)
- {
- off_t bufferend;
- /* How many blocks are available in this buffer? */
- bufferend = rwb->rhblockstart + rwb->rhnblocks;
- if (startblock >= rwb->rhblockstart && startblock < bufferend)
- {
- size_t rdblocks = bufferend - startblock;
- if (rdblocks > remaining)
- {
- rdblocks = remaining;
- }
- /* Then read the data from the read-ahead buffer */
- rwb_bufferread(rwb, startblock, rdblocks, &rdbuffer);
- startblock += rdblocks;
- remaining -= rdblocks;
- }
- }
- /* If we did not get all of the data from the buffer, then we have
- * to refill the buffer and try again.
- */
- if (remaining > 0)
- {
- ret = rwb_rhreload(rwb, startblock);
- if (ret < 0)
- {
- ferr("ERROR: Failed to fill the read-ahead buffer: %d\n", ret);
- rwb_semgive(&rwb->rhsem);
- return (ssize_t)ret;
- }
- }
- }
- /* On success, return the number of blocks that we were requested to
- * read. This is for compatibility with the normal return of a block
- * driver read method
- */
- rwb_semgive(&rwb->rhsem);
- ret = nblocks;
- }
- else
- #endif
- {
- /* No read-ahead buffering, (re)load the data directly into
- * the user buffer.
- */
- ret = rwb->rhreload(rwb->dev, rdbuffer, startblock, nblocks);
- }
- return (ssize_t)ret;
- }
- /****************************************************************************
- * Name: rwb_read
- ****************************************************************************/
- ssize_t rwb_read(FAR struct rwbuffer_s *rwb, off_t startblock,
- size_t nblocks, FAR uint8_t *rdbuffer)
- {
- int ret = OK;
- size_t readblocks = 0;
- finfo("startblock=%ld nblocks=%ld rdbuffer=%p\n",
- (long)startblock, (long)nblocks, rdbuffer);
- #ifdef CONFIG_DRVR_WRITEBUFFER
- /* If the new read data overlaps any part of the write buffer, we
- * directly copy write buffer to read buffer. This boost performance.
- */
- if (rwb->wrmaxblocks > 0)
- {
- /* If the write buffer overlaps the block(s) requested */
- rwb_semtake(&rwb->wrsem);
- if (rwb_overlap(rwb->wrblockstart, rwb->wrnblocks, startblock, nblocks))
- {
- size_t rdblocks = 0;
- size_t wrnpass = 0;
- if (rwb->wrblockstart > startblock)
- {
- rdblocks = rwb->wrblockstart - startblock;
- ret = rwb_read_(rwb, startblock, rdblocks, rdbuffer);
- if (ret < 0)
- {
- rwb_semgive(&rwb->wrsem);
- return (ssize_t)ret;
- }
- startblock += ret;
- nblocks -= ret;
- rdbuffer += ret * rwb->blocksize;
- readblocks += ret;
- }
- if (rwb->wrblockstart < startblock)
- {
- wrnpass = startblock - rwb->wrblockstart;
- }
- rdblocks = nblocks > (rwb->wrnblocks - wrnpass) ?
- (rwb->wrnblocks - wrnpass) : nblocks;
- memcpy(rdbuffer, &rwb->wrbuffer[wrnpass * rwb->blocksize],
- rdblocks * rwb->blocksize);
- startblock += rdblocks;
- nblocks -= rdblocks;
- rdbuffer += rdblocks * rwb->blocksize;
- readblocks += rdblocks;
- }
- rwb_semgive(&rwb->wrsem);
- }
- #endif
- ret = rwb_read_(rwb, startblock, nblocks, rdbuffer);
- if (ret < 0)
- {
- return ret;
- }
- return readblocks + ret;
- }
- /****************************************************************************
- * Name: rwb_write
- ****************************************************************************/
- ssize_t rwb_write(FAR struct rwbuffer_s *rwb, off_t startblock,
- size_t nblocks, FAR const uint8_t *wrbuffer)
- {
- int ret = OK;
- #ifdef CONFIG_DRVR_READAHEAD
- if (rwb->rhmaxblocks > 0)
- {
- /* If the new write data overlaps any part of the read buffer, then
- * flush the data from the read buffer. We could attempt some more
- * exotic handling -- but this simple logic is well-suited for simple
- * streaming applications.
- */
- rwb_semtake(&rwb->rhsem);
- if (rwb_overlap(rwb->rhblockstart, rwb->rhnblocks, startblock, nblocks))
- {
- #ifdef CONFIG_DRVR_INVALIDATE
- /* Just invalidate the read buffer startblock + nblocks data */
- ret = rwb_invalidate_readahead(rwb, startblock, nblocks);
- if (ret < 0)
- {
- ferr("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
- rwb_semgive(&rwb->rhsem);
- return (ssize_t)ret;
- }
- #else
- rwb_resetrhbuffer(rwb);
- #endif
- }
- rwb_semgive(&rwb->rhsem);
- }
- #endif
- #ifdef CONFIG_DRVR_WRITEBUFFER
- if (rwb->wrmaxblocks > 0)
- {
- finfo("startblock=%d wrbuffer=%p\n", startblock, wrbuffer);
- /* Use the block cache unless the buffer size is bigger than block cache */
- if (nblocks > rwb->wrmaxblocks)
- {
- /* First flush the cache */
- rwb_semtake(&rwb->wrsem);
- rwb_wrflush(rwb);
- rwb_semgive(&rwb->wrsem);
- /* Then transfer the data directly to the media */
- ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
- }
- else
- {
- /* Buffer the data in the write buffer */
- rwb_semtake(&rwb->wrsem);
- ret = rwb_writebuffer(rwb, startblock, nblocks, wrbuffer);
- rwb_semgive(&rwb->wrsem);
- }
- /* On success, return the number of blocks that we were requested to
- * write. This is for compatibility with the normal return of a block
- * driver write method
- */
- }
- else
- #endif /* CONFIG_DRVR_WRITEBUFFER */
- {
- /* No write buffer.. just pass the write operation through via the
- * flush callback.
- */
- ret = rwb->wrflush(rwb->dev, wrbuffer, startblock, nblocks);
- }
- return (ssize_t)ret;
- }
- /****************************************************************************
- * Name: rwb_readbytes
- *
- * Description:
- * Character-oriented read
- *
- ****************************************************************************/
- #ifdef CONFIG_DRVR_READBYTES
- ssize_t rwb_readbytes(FAR struct rwbuffer_s *dev, off_t offset,
- size_t nbytes, FAR uint8_t *buffer)
- {
- /* Loop while there are bytes still be be read */
- /* Make sure that the sector containing the next bytes to transfer is in
- * memory.
- */
- /* How many bytes can be transfer from the in-memory data? */
- /* Transfer the bytes */
- /* Adjust counts and offsets for the next time through the loop */
- #warning Not Implemented
- return -ENOSYS;
- }
- #endif
- /****************************************************************************
- * Name: rwb_mediaremoved
- *
- * Description:
- * The following function is called when media is removed
- *
- ****************************************************************************/
- #ifdef CONFIG_DRVR_REMOVABLE
- int rwb_mediaremoved(FAR struct rwbuffer_s *rwb)
- {
- #ifdef CONFIG_DRVR_WRITEBUFFER
- if (rwb->wrmaxblocks > 0)
- {
- rwb_semtake(&rwb->wrsem);
- rwb_resetwrbuffer(rwb);
- rwb_semgive(&rwb->wrsem);
- }
- #endif
- #ifdef CONFIG_DRVR_READAHEAD
- if (rwb->rhmaxblocks > 0)
- {
- rwb_semtake(&rwb->rhsem);
- rwb_resetrhbuffer(rwb);
- rwb_semgive(&rwb->rhsem);
- }
- #endif
- return OK;
- }
- #endif
- /****************************************************************************
- * Name: rwb_invalidate
- *
- * Description:
- * Invalidate a region of the caches
- *
- ****************************************************************************/
- #ifdef CONFIG_DRVR_INVALIDATE
- int rwb_invalidate(FAR struct rwbuffer_s *rwb,
- off_t startblock, size_t blockcount)
- {
- int ret;
- #ifdef CONFIG_DRVR_WRITEBUFFER
- ret = rwb_invalidate_writebuffer(rwb, startblock, blockcount);
- if (ret < 0)
- {
- ferr("ERROR: rwb_invalidate_writebuffer failed: %d\n", ret);
- return ret;
- }
- #endif
- #ifdef CONFIG_DRVR_READAHEAD
- ret = rwb_invalidate_readahead(rwb, startblock, blockcount);
- if (ret < 0)
- {
- ferr("ERROR: rwb_invalidate_readahead failed: %d\n", ret);
- return ret;
- }
- #endif
- return OK;
- }
- #endif
- /****************************************************************************
- * Name: rwb_flush
- *
- * Description:
- * Flush the write buffer
- *
- ****************************************************************************/
- #ifdef CONFIG_DRVR_WRITEBUFFER
- int rwb_flush(FAR struct rwbuffer_s *rwb)
- {
- rwb_semtake(&rwb->wrsem);
- rwb_wrcanceltimeout(rwb);
- rwb_wrflush(rwb);
- rwb_semgive(&rwb->wrsem);
- return OK;
- }
- #endif
- #endif /* CONFIG_DRVR_WRITEBUFFER || CONFIG_DRVR_READAHEAD */
|