A re-written SDFileSystem library with improved compatibility, CRC support, and card removal/replacement support.

Dependencies:   FATFileSystem

Dependents:   xadow_m0_SD_Hello roam_v1 roam_v2 Polytech_tours ... more

Revision:
13:635147efa748
Parent:
12:eebddab6eff2
Child:
15:c9e938f6934f
--- a/SDFileSystem.cpp	Fri Aug 15 17:54:13 2014 +0000
+++ b/SDFileSystem.cpp	Mon Aug 18 15:09:52 2014 +0000
@@ -25,6 +25,7 @@
     m_CardType = CARD_NONE;
     m_Crc = true;
     m_LargeFrames = false;
+    m_WriteValidation = true;
     m_Status = STA_NOINIT;
 
     //Configure the SPI bus
@@ -34,19 +35,19 @@
     if (cdtype == SWITCH_POS_NO) {
         m_Cd.mode(PullDown);
         m_CdAssert = 1;
-        m_Cd.fall(this, &SDFileSystem::checkSocket);
+        m_Cd.fall(this, &SDFileSystem::onCardRemoval);
     } else if (cdtype == SWITCH_POS_NC) {
         m_Cd.mode(PullDown);
         m_CdAssert = 0;
-        m_Cd.rise(this, &SDFileSystem::checkSocket);
+        m_Cd.rise(this, &SDFileSystem::onCardRemoval);
     } else if (cdtype == SWITCH_NEG_NO) {
         m_Cd.mode(PullUp);
         m_CdAssert = 0;
-        m_Cd.rise(this, &SDFileSystem::checkSocket);
+        m_Cd.rise(this, &SDFileSystem::onCardRemoval);
     } else {
         m_Cd.mode(PullUp);
         m_CdAssert = 1;
-        m_Cd.fall(this, &SDFileSystem::checkSocket);
+        m_Cd.fall(this, &SDFileSystem::onCardRemoval);
     }
 }
 
@@ -104,6 +105,18 @@
     m_LargeFrames = enabled;
 }
 
+bool SDFileSystem::write_validation()
+{
+    //Return whether or not write validation is enabled
+    return m_WriteValidation;
+}
+
+void SDFileSystem::write_validation(bool enabled)
+{
+    //Set whether or not write validation is enabled
+    m_WriteValidation = enabled;
+}
+
 int SDFileSystem::unmount()
 {
     //Unmount the filesystem
@@ -172,12 +185,12 @@
         }
 
         //Send ACMD41(0x40100000) repeatedly for up to 1 second to initialize the card
-        for (int i = 0; i < 1000; i++) {
+        m_Timer.start();
+        do {
             token = commandTransaction(ACMD41, 0x40100000);
-            if (token != 0x01)
-                break;
-            wait_ms(1);
-        }
+        } while (token == 0x01 && m_Timer.read_ms() < 1000);
+        m_Timer.stop();
+        m_Timer.reset();
 
         //Check if the card initialized
         if (token != 0x00) {
@@ -214,12 +227,12 @@
         }
 
         //Try to initialize the card using ACMD41(0x00100000) for 1 second
-        for (int i = 0; i < 1000; i++) {
+        m_Timer.start();
+        do {
             token = commandTransaction(ACMD41, 0x00100000);
-            if (token != 0x01)
-                break;
-            wait_ms(1);
-        }
+        } while (token == 0x01 && m_Timer.read_ms() < 1000);
+        m_Timer.stop();
+        m_Timer.reset();
 
         //Check if the card initialized
         if (token == 0x00) {
@@ -233,12 +246,12 @@
                 m_Spi.frequency(m_FREQ);
         } else {
             //Try to initialize the card using CMD1(0x00100000) for 1 second
-            for (int i = 0; i < 1000; i++) {
+            m_Timer.start();
+            do {
                 token = commandTransaction(CMD1, 0x00100000);
-                if (token != 0x01)
-                    break;
-                wait_ms(1);
-            }
+            } while (token == 0x01 && m_Timer.read_ms() < 1000);
+            m_Timer.stop();
+            m_Timer.reset();
 
             //Check if the card initialized
             if (token == 0x00) {
@@ -300,15 +313,10 @@
 
     //Read a single block, or multiple blocks
     if (count > 1) {
-        if (readBlocks((char*)buffer, sector, count))
-            return RES_OK;
+        return readBlocks((char*)buffer, sector, count) ? RES_OK : RES_ERROR;
     } else {
-        if (readBlock((char*)buffer, sector))
-            return RES_OK;
+        return readBlock((char*)buffer, sector) ? RES_OK : RES_ERROR;
     }
-
-    //The read operation failed
-    return RES_ERROR;
 }
 
 int SDFileSystem::disk_write(const uint8_t* buffer, uint64_t sector, uint8_t count)
@@ -323,15 +331,10 @@
 
     //Write a single block, or multiple blocks
     if (count > 1) {
-        if(writeBlocks((const char*)buffer, sector, count))
-            return RES_OK;
+        return writeBlocks((const char*)buffer, sector, count) ? RES_OK : RES_ERROR;
     } else {
-        if(writeBlock((const char*)buffer, sector))
-            return RES_OK;
+        return writeBlock((const char*)buffer, sector) ? RES_OK : RES_ERROR;
     }
-
-    //The write operation failed
-    return RES_ERROR;
 }
 
 int SDFileSystem::disk_sync()
@@ -354,11 +357,11 @@
     //Try to read the CSD register up to 3 times
     for (int f = 0; f < 3; f++) {
         //Select the card, and wait for ready
-        if (!select())
+        if(!select())
             break;
 
         //Send CMD9(0x00000000) to read the CSD register
-        if (command(CMD9, 0x00000000) == 0x00) {
+        if (writeCommand(CMD9, 0x00000000) == 0x00) {
             //Read the 16B CSD data block
             char csd[16];
             bool success = readData(csd, 16);
@@ -388,7 +391,13 @@
     return 0;
 }
 
-void SDFileSystem::checkSocket()
+void SDFileSystem::onCardRemoval()
+{
+    //Check the card socket
+    checkSocket();
+}
+
+inline void SDFileSystem::checkSocket()
 {
     //Check if a card is in the socket
     if (m_Cd == m_CdAssert) {
@@ -403,15 +412,18 @@
 
 inline bool SDFileSystem::waitReady(int timeout)
 {
-    //Wait for the specified timeout for the card to become ready
-    for (int i = 0; i < timeout; i++) {
-        if (m_Spi.write(0xFF) == 0xFF)
-            return true;
-        wait_ms(1);
-    }
+    char resp;
 
-    //We timed out
-    return false;
+    //Keep sending dummy clocks with DI held high until the card releases the DO line
+    m_Timer.start();
+    do {
+        resp = m_Spi.write(0xFF);
+    } while (resp == 0x00 && m_Timer.read_ms() < timeout);
+    m_Timer.stop();
+    m_Timer.reset();
+
+    //Return success/failure
+    return (resp > 0x00);
 }
 
 inline bool SDFileSystem::select()
@@ -444,31 +456,33 @@
 inline char SDFileSystem::commandTransaction(char cmd, unsigned int arg, unsigned int* resp)
 {
     //Select the card, and wait for ready
-    if (!select())
+    if(!select())
         return 0xFF;
 
     //Perform the command transaction
-    char token = command(cmd, arg, resp);
+    char token = writeCommand(cmd, arg, resp);
 
     //Deselect the card, and return the R1 response token
     deselect();
     return token;
 }
 
-char SDFileSystem::command(char cmd, unsigned int arg, unsigned int* resp)
+char SDFileSystem::writeCommand(char cmd, unsigned int arg, unsigned int* resp)
 {
     char token;
 
     //Try to send the command up to 3 times
     for (int f = 0; f < 3; f++) {
         //Send CMD55(0x00000000) prior to an application specific command
-        if (cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) {
-            token = command(CMD55, 0x00000000);
+        if (cmd == ACMD22 || cmd == ACMD23 || cmd == ACMD41 || cmd == ACMD42) {
+            token = writeCommand(CMD55, 0x00000000);
             if (token > 0x01)
                 return token;
 
-            //Some cards need a dummy byte between CMD55 and an ACMD
-            m_Spi.write(0xFF);
+            //Deselect and reselect the card between CMD55 and an ACMD
+            deselect();
+            if(!select())
+                return 0xFF;
         }
 
         //Prepare the command packet
@@ -535,15 +549,15 @@
     char token;
     unsigned short crc;
 
-    //Wait for up to 200ms for the start block token to arrive
-    for (int i = 0; i < 200; i++) {
+    //Wait for up to 500ms for a token to arrive
+    m_Timer.start();
+    do {
         token = m_Spi.write(0xFF);
-        if (token != 0xFF)
-            break;
-        wait_ms(1);
-    }
+    } while (token == 0xFF && m_Timer.read_ms() < 500);
+    m_Timer.stop();
+    m_Timer.reset();
 
-    //Make sure the token is valid
+    //Check if a valid start block token was received
     if (token != 0xFE)
         return false;
 
@@ -584,8 +598,9 @@
     //Calculate the CRC16 checksum for the data block (if enabled)
     unsigned short crc = (m_Crc) ? CRC16(buffer, 512) : 0xFFFF;
 
-    //Wait for the card to become ready
-    while (!m_Spi.write(0xFF));
+    //Wait for up to 500ms for the card to become ready
+    if (!waitReady(500))
+        return false;
 
     //Send the start block token
     m_Spi.write(token);
@@ -623,11 +638,11 @@
     //Try to read the block up to 3 times
     for (int f = 0; f < 3; f++) {
         //Select the card, and wait for ready
-        if (!select())
+        if(!select())
             break;
 
         //Send CMD17(block) to read a single block
-        if (command(CMD17, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) {
+        if (writeCommand(CMD17, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
             //Try to read the block, and deselect the card
             bool success = readData(buffer, 512);
             deselect();
@@ -651,14 +666,14 @@
     //Try to read each block up to 3 times
     for (int f = 0; f < 3;) {
         //Select the card, and wait for ready
-        if (!select())
+        if(!select())
             break;
 
         //Send CMD18(block) to read multiple blocks
-        if (command(CMD18, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) {
+        if (writeCommand(CMD18, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
             //Try to read all of the data blocks
             do {
-                //Read the next block and break on errors
+                //Read the next block, and break on errors
                 if (!readData(buffer, 512)) {
                     f++;
                     break;
@@ -671,19 +686,13 @@
             } while (--count);
 
             //Send CMD12(0x00000000) to stop the transmission
-            if (command(CMD12, 0x00000000) != 0x00) {
+            if (writeCommand(CMD12, 0x00000000) != 0x00) {
                 //The command failed, get out
                 break;
             }
 
-            //Only wait for CMD12 if the read was unsuccessful
-            if (count)
-                while (!m_Spi.write(0xFF));
-
-            //Deselect the card
+            //Deselect the card, and return if successful
             deselect();
-
-            //Return if successful
             if (count == 0)
                 return true;
         } else {
@@ -702,11 +711,11 @@
     //Try to write the block up to 3 times
     for (int f = 0; f < 3; f++) {
         //Select the card, and wait for ready
-        if (!select())
+        if(!select())
             break;
 
         //Send CMD24(block) to write a single block
-        if (command(CMD24, (m_CardType == CARD_SDHC) ? lba : lba * 512) == 0x00) {
+        if (writeCommand(CMD24, (m_CardType == CARD_SDHC) ? lba : lba << 9) == 0x00) {
             //Try to write the block, and deselect the card
             char token = writeData(buffer, 0xFE);
             deselect();
@@ -720,11 +729,13 @@
                 break;
             }
 
-            //Send CMD13(0x00000000) to verify that the programming was successful
-            unsigned int resp;
-            if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
-                //Some manner of unrecoverable write error occured during programming, get out
-                break;
+            //Send CMD13(0x00000000) to verify that the programming was successful if enabled
+            if (m_WriteValidation) {
+                unsigned int resp;
+                if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
+                    //Some manner of unrecoverable write error occured during programming, get out
+                    break;
+                }
             }
 
             //The data was written successfully
@@ -758,11 +769,11 @@
         }
 
         //Select the card, and wait for ready
-        if (!select())
+        if(!select())
             break;
 
         //Send CMD25(block) to write multiple blocks
-        if (command(CMD25, (m_CardType == CARD_SDHC) ? currentLba : currentLba * 512) == 0x00) {
+        if (writeCommand(CMD25, (m_CardType == CARD_SDHC) ? currentLba : currentLba << 9) == 0x00) {
             //Try to write all of the data blocks
             do {
                 //Write the next block and break on errors
@@ -777,45 +788,44 @@
                 f = 0;
             } while (--currentCount);
 
-            //Wait for the card to finish processing the last block
-            while (!m_Spi.write(0xFF));
+            //Wait for up to 500ms for the card to finish processing the last block
+            if (!waitReady(500))
+                break;
 
             //Finalize the transmission
             if (currentCount == 0) {
-                //Send the stop tran token
+                //Send the stop tran token, and deselect the card
                 m_Spi.write(0xFD);
-
-                //Wait for the programming to complete, and deselect the card
-                while (!m_Spi.write(0xFF));
                 deselect();
 
-                //Send CMD13(0x00000000) to verify that the programming was successful
-                unsigned int resp;
-                if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
-                    //Some manner of unrecoverable write error occured during programming, get out
-                    break;
+                //Send CMD13(0x00000000) to verify that the programming was successful if enabled
+                if (m_WriteValidation) {
+                    unsigned int resp;
+                    if (commandTransaction(CMD13, 0x00000000, &resp) != 0x00 || resp != 0x00) {
+                        //Some manner of unrecoverable write error occured during programming, get out
+                        break;
+                    }
                 }
 
                 //The data was written successfully
                 return true;
             } else {
                 //Send CMD12(0x00000000) to abort the transmission
-                if (command(CMD12, 0x00000000) != 0x00) {
+                if (writeCommand(CMD12, 0x00000000) != 0x00) {
                     //The command failed, get out
                     break;
                 }
 
-                //Wait for CMD12 to complete, and deselect the card
-                while (!m_Spi.write(0xFF));
+                //Deselect the card
                 deselect();
 
                 //Check the error token
                 if (token == 0x0A) {
                     //Determine the number of well written blocks if possible
                     unsigned int writtenBlocks = 0;
-                    if (m_CardType != CARD_MMC) {
+                    if (m_CardType != CARD_MMC && select()) {
                         //Send ACMD22(0x00000000) to get the number of well written blocks
-                        if (commandTransaction(ACMD22, 0x00000000) == 0x00) {
+                        if (writeCommand(ACMD22, 0x00000000) == 0x00) {
                             //Read the data
                             char acmdData[4];
                             if (readData(acmdData, 4)) {
@@ -826,10 +836,11 @@
                                 writtenBlocks |= acmdData[3];
                             }
                         }
+                        deselect();
                     }
 
                     //Roll back the variables based on the number of well written blocks
-                    currentBuffer = buffer + (writtenBlocks * 512);
+                    currentBuffer = buffer + (writtenBlocks << 9);
                     currentLba = lba + writtenBlocks;
                     currentCount = count - writtenBlocks;