/* Optimized SD Library for Teensy 3.X * Copyright (c) 2015, Paul Stoffregen, paul@pjrc.com * * Development of this SD library was funded by PJRC.COM, LLC by sales of * Teensy and Audio Adaptor boards. Please support PJRC's efforts to develop * open source software by purchasing genuine Teensy or other PJRC products. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice, development funding notice, and this permission * notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #if defined(__arm__) #include "SD_t3.h" #ifdef USE_TEENSY3_OPTIMIZED_CODE #define cache_t SDCache::cache_t #define sector_t SDClass::sector_t cache_t *SDCache::cache_list = NULL; cache_t SDCache::cache[SD_CACHE_SIZE]; #define CACHE_FLAG_HAS_DATA 1 #define CACHE_FLAG_IS_DIRTY 2 #define CACHE_FLAG_IS_FAT 4 //#define PRINT_SECTORS #ifdef PRINT_SECTORS static void print_sector(const void *data) { const uint8_t *p = (const uint8_t *)data; for (int i=0; i < 512; i++) { Serial.printf(" %02X", *p++); if ((i & 31) == 31) Serial.println(); } } #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, // of course no actual read occurs. This is the primary function // used to access the SD card. // sector_t * SDCache::read(uint32_t lba, bool is_fat) { sector_t *ret = NULL; //uint32_t slot=0, ucount=0; // the entire read operation, including all cache manipulation, // needs to be protected with exclusive access to the hardware. //Serial.printf("cache read: lba = %d\n", lba); SPI.beginTransaction(SD_SPI_SPEED); // does the cache already have the sector? cache_t *c = get(lba); if (c) { 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)) { c->flags = CACHE_FLAG_HAS_DATA; if (is_fat) c->flags |= CACHE_FLAG_IS_FAT; ret = &c->data; //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); } SPI.endTransaction(); //print_cache(); return ret; } // Read a whole 512 byte sector directly to memory. If the sector is // already cached, of course no actual read occurs and data is copied // from the cache. When the sector is not cached, it's transferred // directly from SD card to memory, bypassing the cache. // bool SDCache::read(uint32_t lba, void *buffer) { bool ret = true; SPI.beginTransaction(SD_SPI_SPEED); cache_t *c = get(lba, false); if (!c || !(c->flags & CACHE_FLAG_HAS_DATA)) { ret = SDClass::sd_read(lba, buffer); } SPI.endTransaction(); if (c) { if ((c->flags & CACHE_FLAG_HAS_DATA)) { memcpy(buffer, &c->data, 512); release(); return true; } release(); } return ret; } // locate a sector in the cache. cache_t * SDCache::get(uint32_t lba, bool allocate) { cache_t *c, *p=NULL, *last=NULL, *plast=NULL; // 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::init(void) { 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) { __disable_irq(); item->flags |= CACHE_FLAG_IS_DIRTY; __enable_irq(); } void SDCache::release(void) { //Serial.printf("cache release\n"); if (item) { __disable_irq(); item->usagecount--; //uint32_t ucount = item->usagecount; //uint32_t lba = item->lba; //Serial.printf("release %d, %d, slot %u\n", item->lba, item->usagecount, item-cache); __enable_irq(); item = NULL; } } #endif #endif