|
|
|
|
|
|
|
|
#define cache_t SDCache::cache_t |
|
|
#define cache_t SDCache::cache_t |
|
|
#define sector_t SDClass::sector_t |
|
|
#define sector_t SDClass::sector_t |
|
|
|
|
|
|
|
|
|
|
|
cache_t *SDCache::cache_list = NULL; |
|
|
cache_t SDCache::cache[SD_CACHE_SIZE]; |
|
|
cache_t SDCache::cache[SD_CACHE_SIZE]; |
|
|
|
|
|
|
|
|
#define CACHE_FLAG_HAS_DATA 1 |
|
|
#define CACHE_FLAG_HAS_DATA 1 |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
void SDCache::print_cache(void) |
|
|
|
|
|
{ |
|
|
|
|
|
#if 0 |
|
|
|
|
|
const cache_t *end=cache+SD_CACHE_SIZE; |
|
|
|
|
|
for (cache_t *c = cache; c < end; c++) { |
|
|
|
|
|
Serial.printf(" cache index %u, lba= %u, ucount=%u, flags=%u\n", |
|
|
|
|
|
c - cache, c->lba, c->usagecount, c->flags); |
|
|
|
|
|
} |
|
|
|
|
|
Serial.print(" cache order:"); |
|
|
|
|
|
for (cache_t *c = cache_list; c; c = c->next) { |
|
|
|
|
|
Serial.printf(" %u ->", c - cache); |
|
|
|
|
|
} |
|
|
|
|
|
Serial.println(); |
|
|
|
|
|
#endif |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Read a sector into the cache. If the sector is already cached, |
|
|
// Read a sector into the cache. If the sector is already cached, |
|
|
// of course no actual read occurs. This is the primary function |
|
|
// of course no actual read occurs. This is the primary function |
|
|
// used to access the SD card. |
|
|
// used to access the SD card. |
|
|
|
|
|
|
|
|
//Serial.printf("cache read: lba = %d\n", lba); |
|
|
//Serial.printf("cache read: lba = %d\n", lba); |
|
|
SPI.beginTransaction(SD_SPI_SPEED); |
|
|
SPI.beginTransaction(SD_SPI_SPEED); |
|
|
// does the cache already have the sector? |
|
|
// does the cache already have the sector? |
|
|
cache_t *c = find(lba); |
|
|
|
|
|
|
|
|
cache_t *c = get(lba); |
|
|
if (c) { |
|
|
if (c) { |
|
|
ret = &c->data; |
|
|
|
|
|
} else { |
|
|
|
|
|
c = empty(); |
|
|
|
|
|
if (c != NULL) { |
|
|
|
|
|
// TODO: if dirty, write to SD card |
|
|
|
|
|
|
|
|
if (c->flags & CACHE_FLAG_HAS_DATA) { |
|
|
|
|
|
//Serial.printf(" cache hit, lba=%u\n", lba); |
|
|
|
|
|
ret = &c->data; |
|
|
|
|
|
} else { |
|
|
if (SDClass::sd_read(lba, &c->data)) { |
|
|
if (SDClass::sd_read(lba, &c->data)) { |
|
|
item = c; |
|
|
|
|
|
c->lba = lba; |
|
|
|
|
|
c->usagecount = 1; |
|
|
|
|
|
c->flags = CACHE_FLAG_HAS_DATA; |
|
|
c->flags = CACHE_FLAG_HAS_DATA; |
|
|
if (is_fat) c->flags |= CACHE_FLAG_IS_FAT; |
|
|
if (is_fat) c->flags |= CACHE_FLAG_IS_FAT; |
|
|
ret = &c->data; |
|
|
ret = &c->data; |
|
|
#ifdef PRINT_SECTORS |
|
|
|
|
|
Serial.printf("cache read %u\n", lba); |
|
|
|
|
|
print_sector(&c->data); |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
//Serial.printf(" cache miss, lba=%u\n", lba); |
|
|
|
|
|
} else { |
|
|
|
|
|
//Serial.printf(" cache miss: read error, lba=%u\n", lba); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
//Serial.printf(" cache full & all in use\n", lba); |
|
|
} |
|
|
} |
|
|
//if (c) slot = c - cache, ucount = c->usagecount; |
|
|
|
|
|
SPI.endTransaction(); |
|
|
SPI.endTransaction(); |
|
|
//if (ret) { |
|
|
|
|
|
//Serial.printf("read %u, %u, slot %u\n", lba, ucount, slot); |
|
|
|
|
|
//} else { |
|
|
|
|
|
//Serial.printf("read %u, FAIL\n", lba); |
|
|
|
|
|
//} |
|
|
|
|
|
|
|
|
//print_cache(); |
|
|
return ret; |
|
|
return ret; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool ret = true; |
|
|
bool ret = true; |
|
|
|
|
|
|
|
|
SPI.beginTransaction(SD_SPI_SPEED); |
|
|
SPI.beginTransaction(SD_SPI_SPEED); |
|
|
cache_t *c = find(lba); |
|
|
|
|
|
if (!c) ret = SDClass::sd_read(lba, buffer); |
|
|
|
|
|
|
|
|
cache_t *c = get(lba, false); |
|
|
|
|
|
if (!c || !(c->flags & CACHE_FLAG_HAS_DATA)) { |
|
|
|
|
|
ret = SDClass::sd_read(lba, buffer); |
|
|
|
|
|
} |
|
|
SPI.endTransaction(); |
|
|
SPI.endTransaction(); |
|
|
if (c) memcpy(buffer, &c->data, 512); |
|
|
|
|
|
|
|
|
if (c) { |
|
|
|
|
|
if ((c->flags & CACHE_FLAG_HAS_DATA)) { |
|
|
|
|
|
memcpy(buffer, &c->data, 512); |
|
|
|
|
|
release(); |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
release(); |
|
|
|
|
|
} |
|
|
return ret; |
|
|
return ret; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sector_t * SDCache::alloc(uint32_t lba) |
|
|
|
|
|
|
|
|
// locate a sector in the cache. |
|
|
|
|
|
cache_t * SDCache::get(uint32_t lba, bool allocate) |
|
|
{ |
|
|
{ |
|
|
|
|
|
cache_t *c, *p=NULL, *last=NULL, *plast=NULL; |
|
|
|
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void SDCache::priority(signed int n) |
|
|
|
|
|
{ |
|
|
|
|
|
if (!item) return; |
|
|
|
|
|
if (n > 0) { |
|
|
|
|
|
__disable_irq(); |
|
|
|
|
|
signed int pri = (int)(item->priority) + n; |
|
|
|
|
|
if (pri > 255) pri = 255; |
|
|
|
|
|
item->priority = pri; |
|
|
|
|
|
__enable_irq(); |
|
|
|
|
|
} else { |
|
|
|
|
|
__disable_irq(); |
|
|
|
|
|
signed int pri = (int)(item->priority) + n; |
|
|
|
|
|
if (pri < 0) pri = 0; |
|
|
|
|
|
item->priority = pri; |
|
|
|
|
|
__enable_irq(); |
|
|
|
|
|
|
|
|
// TODO: move initialization to a function called when the SD card is initialized |
|
|
|
|
|
if (cache_list == NULL) init(); |
|
|
|
|
|
// have we already acquired a cache entry? |
|
|
|
|
|
if (item) { |
|
|
|
|
|
// if it's the desired block, use it |
|
|
|
|
|
if (item->lba == lba) return item; |
|
|
|
|
|
// if not, release our hold on it |
|
|
|
|
|
release(); |
|
|
} |
|
|
} |
|
|
|
|
|
__disable_irq(); |
|
|
|
|
|
c = cache_list; |
|
|
|
|
|
do { |
|
|
|
|
|
if (c->lba == lba) { |
|
|
|
|
|
if (p) { |
|
|
|
|
|
p->next = c->next; |
|
|
|
|
|
c->next = cache_list; |
|
|
|
|
|
cache_list = c; |
|
|
|
|
|
} |
|
|
|
|
|
c->usagecount++; |
|
|
|
|
|
__enable_irq(); |
|
|
|
|
|
item = c; |
|
|
|
|
|
return item; |
|
|
|
|
|
} |
|
|
|
|
|
if (c->usagecount == 0) { |
|
|
|
|
|
plast = p; |
|
|
|
|
|
last = c; |
|
|
|
|
|
} |
|
|
|
|
|
p = c; |
|
|
|
|
|
c = c->next; |
|
|
|
|
|
} while (c); |
|
|
|
|
|
if (allocate && last) { |
|
|
|
|
|
if (plast) { |
|
|
|
|
|
plast->next = last->next; |
|
|
|
|
|
last->next = cache_list; |
|
|
|
|
|
cache_list = last; |
|
|
|
|
|
} |
|
|
|
|
|
last->usagecount = 1; |
|
|
|
|
|
// TODO: flush if dirty |
|
|
|
|
|
last->lba = lba; |
|
|
|
|
|
last->flags = 0; |
|
|
|
|
|
item = last; |
|
|
|
|
|
} |
|
|
|
|
|
__enable_irq(); |
|
|
|
|
|
return item; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void SDCache::priority(uint32_t lba, signed int n) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SDCache::init(void) |
|
|
{ |
|
|
{ |
|
|
// TODO: if any a specific sector is cached, adjust its priority |
|
|
|
|
|
|
|
|
cache_t *c = cache; |
|
|
|
|
|
cache_t *end = c + SD_CACHE_SIZE; |
|
|
|
|
|
//Serial.println("cache init"); |
|
|
|
|
|
__disable_irq(); |
|
|
|
|
|
do { |
|
|
|
|
|
c->lba = 0xFFFFFFFF; |
|
|
|
|
|
c->usagecount = 0; |
|
|
|
|
|
c->flags = 0; |
|
|
|
|
|
c->next = c + 1; |
|
|
|
|
|
c = c + 1; |
|
|
|
|
|
} while (c < end); |
|
|
|
|
|
c--; |
|
|
|
|
|
c->next = NULL; |
|
|
|
|
|
cache_list = cache; |
|
|
|
|
|
__enable_irq(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void SDCache::dirty(void) |
|
|
void SDCache::dirty(void) |
|
|
{ |
|
|
{ |
|
|
__disable_irq(); |
|
|
__disable_irq(); |
|
|
item->usagecount |= CACHE_FLAG_IS_DIRTY; |
|
|
|
|
|
|
|
|
item->flags |= CACHE_FLAG_IS_DIRTY; |
|
|
__enable_irq(); |
|
|
__enable_irq(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
cache_t * SDCache::find(uint32_t lba) |
|
|
|
|
|
{ |
|
|
|
|
|
//Serial.printf("SDCache::find, lba=%n\n", lba); |
|
|
|
|
|
// have we already acquired a cache entry? |
|
|
|
|
|
if (item) { |
|
|
|
|
|
//Serial.printf(" item exists, lba=%d\n", item->lba); |
|
|
|
|
|
// if it's the desired block, use it |
|
|
|
|
|
if (item->lba == lba) return item; |
|
|
|
|
|
// if not, release our hold on it |
|
|
|
|
|
//Serial.printf("cache find release\n"); |
|
|
|
|
|
item->usagecount--; |
|
|
|
|
|
item = NULL; |
|
|
|
|
|
} |
|
|
|
|
|
// does the cache already have the sector we want? |
|
|
|
|
|
const cache_t *end=cache+SD_CACHE_SIZE; |
|
|
|
|
|
for (cache_t *c = cache; c < end; c++) { |
|
|
|
|
|
if ((c->flags) && (c->lba == lba)) { |
|
|
|
|
|
//Serial.printf(" item found\n"); |
|
|
|
|
|
item = c; |
|
|
|
|
|
c->usagecount++; |
|
|
|
|
|
return c; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
//Serial.printf(" item not found\n"); |
|
|
|
|
|
// the desired sector isn't in the cache |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cache_t * SDCache::empty(void) |
|
|
|
|
|
{ |
|
|
|
|
|
const cache_t *end=cache+SD_CACHE_SIZE; |
|
|
|
|
|
cache_t *useme = NULL; |
|
|
|
|
|
uint32_t lowest_priority = 0xFF; |
|
|
|
|
|
|
|
|
|
|
|
for (cache_t *c = cache; c < end; c++) { |
|
|
|
|
|
if (c->usagecount == 0 && c->priority < lowest_priority) { |
|
|
|
|
|
useme = c; |
|
|
|
|
|
lowest_priority = c->priority; |
|
|
|
|
|
if (lowest_priority == 0) break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return useme; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#endif |
|
|
#endif |
|
|
#endif |
|
|
#endif |