diff --git a/Makefile b/Makefile
index 6c8092c395ce60ea16c806f71d2a1011ae067d4a..277dd02a7b10f04790c491ad40b99dc09f580df0 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@ SUBDIRS := miosix
 ## List here your source files (both .s, .c and .cpp)
 ##
 SRC :=                                  \
-main.cpp
+miosix/testsuite/testsuite.cpp
 
 ##
 ## List here additional static libraries with relative path
diff --git a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/disk.cpp b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/disk.cpp
index 970a5384ab13108567f8b4db564a45c39caefd75..26dffdab4ae93c42f9438670a4928bea5f5b05e6 100644
--- a/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/disk.cpp
+++ b/miosix/arch/cortexM3_stm32f2/common/interfaces-impl/disk.cpp
@@ -28,10 +28,13 @@
 #include "interfaces/disk.h"
 #include "interfaces/bsp.h"
 #include "interfaces/arch_registers.h"
+#include "kernel/scheduler/scheduler.h"
 #include "interfaces/delays.h"
 #include "kernel/kernel.h"
 #include <cstdio>
 #include <cstring>
+#include <utility>
+#include <miosix.h> //FIXME: remove
 
 //Note: enabling debugging might cause deadlock when using sleep() or reboot()
 //The bug won't be fixed because debugging is only useful for driver development
@@ -39,11 +42,80 @@
 //#define DBG iprintf
 #define DBG(x,...) ;
 ///\internal Debug macro, for errors only
-//#define DBGERR iprintf
-#define DBGERR(x,...) ;
+#define DBGERR iprintf
+//#define DBGERR(x,...) ;
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler
+ */
+void __attribute__((naked)) DMA2_Stream3_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _ZN6miosix18DMA2stream3irqImplEv");
+    restoreContext();
+}
+
+/**
+ * \internal
+ * SDIO interrupt handler
+ */
+void __attribute__((naked)) SDIO_IRQHandler()
+{
+    saveContext();
+    asm volatile("bl _ZN6miosix11SDIOirqImplEv");
+    restoreContext();
+}
 
 namespace miosix {
 
+static volatile bool transferError; ///< \internal DMA or SDIO transfer error
+static Thread *waiting;             ///< \internal Thread waiting for transfer
+static unsigned int dmaFlags;       ///< \internal DMA status flags
+static unsigned int sdioFlags;      ///< \internal SDIO status flags
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler actual implementation
+ */
+void __attribute__((used)) DMA2stream3irqImpl()
+{
+    dmaFlags=DMA2->LISR;
+    if(dmaFlags & (DMA_LISR_TEIF3 | DMA_LISR_DMEIF3 | DMA_LISR_FEIF3))
+        transferError=true;
+    
+    DMA2->LIFCR=DMA_LIFCR_CTCIF3  |
+                DMA_LIFCR_CTEIF3  |
+                DMA_LIFCR_CDMEIF3 |
+                DMA_LIFCR_CFEIF3;
+    
+    if(!waiting) return;
+    waiting->IRQwakeup();
+	if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+		Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
+/**
+ * \internal
+ * DMA2 Stream3 interrupt handler actual implementation
+ */
+void __attribute__((used)) SDIOirqImpl()
+{
+    sdioFlags=SDIO->STA;
+    if(sdioFlags & (SDIO_STA_STBITERR | SDIO_STA_RXOVERR  |
+                    SDIO_STA_TXUNDERR | SDIO_STA_DTIMEOUT | SDIO_STA_DCRCFAIL))
+        transferError=true;
+    
+    SDIO->ICR=0x7ff;//Clear flags
+    
+    if(!waiting) return;
+    waiting->IRQwakeup();
+	if(waiting->IRQgetPriority()>Thread::IRQgetCurrentThread()->IRQgetPriority())
+		Scheduler::IRQfindNextThread();
+    waiting=0;
+}
+
 /*
  * Operating voltage of device. It is sent to the SD card to check if it can
  * work at this voltage. Range *must* be within 28..36
@@ -781,6 +853,26 @@ unsigned char ClockController::retries=ClockController::MAX_RETRY;
 // Data send/receive functions
 //
 
+/**
+ * \internal
+ * Wait until the card is ready for data transfer.
+ * Can be called independently of the card being selected.
+ * \return true on success, false on failure
+ */
+bool waitForCardReady()
+{
+    for(int i=0;i<300;i++) //Timeout 1.5 second
+    {
+        CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16);
+        if(cr.validateR1Response()==false) return false;
+        //Bit 8 in R1 response means ready for data.
+        if(cr.getResponse() & (1<<8)) return true;
+        Thread::sleep(5);
+    }
+    DBGERR("Timeout waiting card ready\n");
+    return false;
+}
+
 /**
  * \internal
  * Receive a data block. The end of the data block must be told to the SDIO
@@ -879,26 +971,6 @@ static DataResult IRQsendDataBlock(const unsigned int *buffer, unsigned int size
     return DataResult(DataResult::Ok);
 }
 
-/**
- * \internal
- * Wait until the card is ready for data transfer.
- * Can be called independently of the card being selected.
- * \return true on success, false on failure
- */
-bool waitForCardReady()
-{
-    for(int i=0;i<300;i++) //Timeout 1.5 second
-    {
-        CmdResult cr=Command::send(Command::CMD13,Command::getRca()<<16);
-        if(cr.validateR1Response()==false) return false;
-        //Bit 8 in R1 response means ready for data.
-        if(cr.getResponse() & (1<<8)) return true;
-        Thread::sleep(5);
-    }
-    DBGERR("Timeout waiting card ready\n");
-    return false;
-}
-
 /**
  * \internal
  * Read a single block of 512 bytes from an SD/MMC card.
@@ -923,7 +995,6 @@ static bool singleBlockRead(unsigned char *buffer, unsigned int lba)
         // would cause a fifo overrun, so we disable interrupts.
         FastInterruptDisableLock dLock;
 
-        SDIO->DTIMER=1048576;
         SDIO->DLEN=512;
         //Block size 512 bytes, block data xfer, from card to controller
         SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
@@ -993,7 +1064,6 @@ static bool singleBlockWrite(const unsigned char *buffer, unsigned int lba)
         cr=Command::IRQsend(Command::CMD24,lba);
         if(cr.IRQvalidateR1Response())
         {
-            SDIO->DTIMER=1048576;
             SDIO->DLEN=512;
             //Block size 512 bytes, block data xfer, from controller to card
             SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DTEN;
@@ -1032,6 +1102,217 @@ static bool singleBlockWrite(const unsigned char *buffer, unsigned int lba)
     return true;
 }
 
+/**
+ * \internal
+ * Prints the errors that may occur during a DMA transfer
+ */
+void displayBlockTransferError()
+{
+    DBGERR("Block transfer error\n");
+    if(dmaFlags & DMA_LISR_TEIF3)     DBGERR("* DMA Transfer error\n");
+    if(dmaFlags & DMA_LISR_DMEIF3)    DBGERR("* DMA Direct mode error\n");
+    if(dmaFlags & DMA_LISR_FEIF3)     DBGERR("* DMA Fifo error\n");
+    if(sdioFlags & SDIO_STA_STBITERR) DBGERR("* SDIO Start bit error\n");
+    if(sdioFlags & SDIO_STA_RXOVERR)  DBGERR("* SDIO RX Overrun\n");
+    if(sdioFlags & SDIO_STA_TXUNDERR) DBGERR("* SDIO TX Underrun error\n");
+    if(sdioFlags & SDIO_STA_DCRCFAIL) DBGERR("* SDIO Data CRC fail\n");
+    if(sdioFlags & SDIO_STA_DTIMEOUT) DBGERR("* SDIO Data timeout\n");
+}
+
+/**
+ * \internal
+ * Contains initial common code between multipleBlockRead and multipleBlockWrite
+ * to clear interrupt and error flags, set the waiting thread and compute the
+ * memory transfer size based on buffer alignment
+ * \return the best DMA transfer size for a given buffer alignment 
+ */
+unsigned int dmaTransferCommonSetup(const unsigned char *buffer)
+{
+    //Clear both SDIO and DMA interrupt flags
+    SDIO->ICR=0x7ff;
+    DMA2->LIFCR=DMA_LIFCR_CTCIF3  |
+                DMA_LIFCR_CTEIF3  |
+                DMA_LIFCR_CDMEIF3 |
+                DMA_LIFCR_CFEIF3;
+    
+    transferError=false;
+    dmaFlags=sdioFlags=0;
+    waiting=Thread::getCurrentThread();
+    
+    //Select DMA transfer size based on buffer alignment. Best performance
+    //is achieved when the buffer is aligned on a 4 byte boundary
+    switch(reinterpret_cast<unsigned int>(buffer) & 0x3)
+    {
+        case 0:  return DMA_SxCR_MSIZE_1; //DMA reads 32bit at a time
+        case 2:  return DMA_SxCR_MSIZE_0; //DMA reads 16bit at a time
+        default: return 0; break;         //DMA reads  8bit at a time
+    }
+}
+
+/**
+ * \internal
+ * Read a given number of contiguous 512 byte blocks from an SD/MMC card.
+ * Card must be selected prior to calling this function.
+ * \param buffer, a buffer whose size is 512*nblk bytes
+ * \param nblk number of blocks to read.
+ * Due to hardware limitations must be between 0 and 32767.
+ * \param lba logical block address of the first block to read.
+ */
+static bool multipleBlockRead(unsigned char *buffer, int nblk, unsigned int lba)
+{
+    if(nblk<0 || nblk>32767) return false;
+    if(waitForCardReady()==false) return false;
+    
+    if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+    
+    unsigned int memoryTransferSize=dmaTransferCommonSetup(buffer);
+    
+    //Data transfer is considered complete once the DMA transfer complete
+    //interrupt occurs, that happens when the last data was written in the
+    //buffer. Both SDIO and DMA error interrupts are active to catch errors
+    SDIO->MASK=SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+               SDIO_MASK_RXOVERRIE  | //Interrupt on rx underrun
+               SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+               SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+               SDIO_MASK_DTIMEOUTIE;  //Interrupt on data timeout
+	DMA2_Stream3->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO);
+	DMA2_Stream3->M0AR=reinterpret_cast<unsigned int>(buffer);
+	//Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode
+    DMA2_Stream3->FCR=DMA_SxFCR_FEIE    | //Interrupt on fifo error
+                      DMA_SxFCR_DMDIS   | //Fifo enabled
+                      DMA_SxFCR_FTH_0;    //Take action if fifo half full
+	DMA2_Stream3->CR=DMA_SxCR_CHSEL_2   | //Channel 4 (SDIO)
+                     DMA_SxCR_PBURST_0  | //4-beat bursts read from SDIO
+                     DMA_SxCR_PL_0      | //Medium priority DMA stream
+                     memoryTransferSize | //RAM data size depends on alignment
+					 DMA_SxCR_PSIZE_1   | //Read 32bit at a time from SDIO
+				     DMA_SxCR_MINC      | //Increment RAM pointer
+			         0                  | //Peripheral to memory direction
+                     DMA_SxCR_PFCTRL    | //Peripheral is flow controller
+			         DMA_SxCR_TCIE      | //Interrupt on transfer complete
+                     DMA_SxCR_TEIE      | //Interrupt on transfer error
+                     DMA_SxCR_DMEIE     | //Interrupt on direct mode error
+			  	     DMA_SxCR_EN;         //Start the DMA
+    
+    SDIO->DLEN=nblk*512;
+    if(waiting==0)
+    {
+        DBGERR("Premature wakeup\n");
+        transferError=true;
+    }
+    CmdResult cr=Command::send(Command::CMD18,lba);
+    if(cr.validateR1Response())
+    {
+        //Block size 512 bytes, block data xfer, from card to controller
+        SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTDIR | SDIO_DCTRL_DTEN;
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            Thread::IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    } else transferError=true;
+    DMA2_Stream3->CR=0;
+    while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop
+    SDIO->DCTRL=0; //Disable data path state machine
+    SDIO->MASK=0;
+
+    cr=Command::send(Command::CMD12,0);
+    if(transferError || cr.validateR1Response()==false)
+    {
+        displayBlockTransferError();
+//        ClockController::IRQreduceClockSpeed();
+        return false;
+    }
+    return true;
+}
+
+/**
+ * \internal
+ * Write a given number of contiguous 512 byte blocks to an SD/MMC card.
+ * Card must be selected prior to calling this function.
+ * \param buffer, a buffer whose size is 512*nblk bytes
+ * \param nblk number of blocks to write.
+ * Due to hardware limitations must be between 0 and 32767.
+ * \param lba logical block address of the first block to write.
+ */
+static bool multipleBlockWrite(const unsigned char *buffer, int nblk,
+    unsigned int lba)
+{
+    if(nblk<0 || nblk>32767) return false;
+    if(waitForCardReady()==false) return false;
+    
+    if(cardType!=SDHC) lba*=512; // Convert to byte address if not SDHC
+    
+    unsigned int memoryTransferSize=dmaTransferCommonSetup(buffer);
+    
+    //Data transfer is considered complete once the SDIO transfer complete
+    //interrupt occurs, that happens when the last data was written to the SDIO
+    //Both SDIO and DMA error interrupts are active to catch errors
+    SDIO->MASK=SDIO_MASK_DATAENDIE  | //Interrupt on data end
+               SDIO_MASK_STBITERRIE | //Interrupt on start bit error
+               SDIO_MASK_RXOVERRIE  | //Interrupt on rx underrun
+               SDIO_MASK_TXUNDERRIE | //Interrupt on tx underrun
+               SDIO_MASK_DCRCFAILIE | //Interrupt on data CRC fail
+               SDIO_MASK_DTIMEOUTIE;  //Interrupt on data timeout
+	DMA2_Stream3->PAR=reinterpret_cast<unsigned int>(&SDIO->FIFO);
+	DMA2_Stream3->M0AR=reinterpret_cast<unsigned int>(buffer);
+	//Note: DMA2_Stream3->NDTR is don't care in peripheral flow control mode
+    DMA2_Stream3->FCR=/*DMA_SxFCR_FEIE    |*/ //Interrupt on fifo error
+                      DMA_SxFCR_DMDIS   | //Fifo enabled
+                      DMA_SxFCR_FTH_1   | //Take action if fifo full
+                      DMA_SxFCR_FTH_0;
+	DMA2_Stream3->CR=DMA_SxCR_CHSEL_2   | //Channel 4 (SDIO)
+                     DMA_SxCR_PBURST_0  | //4-beat bursts write to SDIO
+                     DMA_SxCR_PL_0      | //Medium priority DMA stream
+                     memoryTransferSize | //RAM data size depends on alignment
+					 DMA_SxCR_PSIZE_1   | //Write 32bit at a time to SDIO
+				     DMA_SxCR_MINC      | //Increment RAM pointer
+			         DMA_SxCR_DIR_0     | //Memory to peripheral direction
+                     DMA_SxCR_PFCTRL    | //Peripheral is flow controller
+                     DMA_SxCR_TEIE      | //Interrupt on transfer error
+                     DMA_SxCR_DMEIE     | //Interrupt on direct mode error
+			  	     DMA_SxCR_EN;         //Start the DMA
+    
+    SDIO->DLEN=nblk*512;
+    if(waiting==0)
+    {
+        DBGERR("Premature wakeup\n");
+        transferError=true;
+    }
+    CmdResult cr=Command::send(Command::CMD25,lba);
+    if(cr.validateR1Response())
+    {
+        //Block size 512 bytes, block data xfer, from card to controller
+        SDIO->DCTRL=(9<<4) | SDIO_DCTRL_DMAEN | SDIO_DCTRL_DTEN;
+        FastInterruptDisableLock dLock;
+        while(waiting)
+        {
+            Thread::IRQwait();
+            {
+                FastInterruptEnableLock eLock(dLock);
+                Thread::yield();
+            }
+        }
+    } else transferError=true;
+    DMA2_Stream3->CR=0;
+    while(DMA2_Stream3->CR & DMA_SxCR_EN) ; //DMA may take time to stop
+    SDIO->DCTRL=0; //Disable data path state machine
+    SDIO->MASK=0;
+
+    cr=Command::send(Command::CMD12,0);
+    if(transferError || cr.validateR1Response()==false)
+    {
+        displayBlockTransferError();
+//        ClockController::IRQreduceClockSpeed();
+        return false;
+    }
+    return true;
+}
+
 //
 // Class CardSelector
 //
@@ -1075,6 +1356,47 @@ private:
     bool success;
 };
 
+void fixmeTestFs()
+{
+    puts("----------");
+    const unsigned int sec=3250585;
+    unsigned char *b0=new unsigned char[4096];
+    unsigned char *b1=new unsigned char[4096];
+    unsigned char *b2=new unsigned char[4096];
+    memset(b1,'a',4096);
+    memset(b2,'b',4096);
+    int al=2;
+    int nb=7;
+    srand(getTick());
+    for(int i=0;i<512*nb;i++) b0[i+al]=rand() & 0xff;
+    
+    {
+        CardSelector selector;
+        if(selector.succeded()==false) puts("card select failed!");
+        if(multipleBlockWrite(b0+al,nb,sec)==false) puts("Fsbw failed!");
+        for(int i=0;i<nb;i++)
+            if(singleBlockRead(b1+512*i+al,sec+i)==false) puts("sbr failed!");
+        if(multipleBlockRead(b2+al,nb,sec)==false) puts("Fsbr failed!");
+        if(multipleBlockRead(b2+al,nb,sec)==false) puts("Fsbr failed!");
+    }
+    if(memcmp(b0+al,b1+al,512*nb)) puts("write wailed (buffers don't match)!");
+    if(memcmp(b1+al,b2+al,512*nb)) puts("buffer do not match!");
+    puts("Dump?");
+    char ans[16];
+    fgets(ans,16,stdin);
+    fgets(ans,16,stdin);
+    if(ans[0]=='y')
+    {
+        puts("------ b1 ------");
+        memDump((char*)b1+al,512*nb);
+        puts("------ b2 ------");
+        memDump((char*)b2+al,512*nb);
+    }
+    delete[] b0;
+    delete[] b1;
+    delete[] b2;
+}
+
 //
 // Initialization helper functions
 //
@@ -1088,7 +1410,9 @@ static void initSDIOPeripheral()
     {
         //Doing read-modify-write on RCC->APBENR2 and gpios, better be safe
         FastInterruptDisableLock lock;
-        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN;
+        RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN
+                      | RCC_AHB1ENR_GPIODEN
+                      | RCC_AHB1ENR_DMA2EN;
         RCC->APB2ENR |= RCC_APB2ENR_SDIOEN;
         sdD0::mode(Mode::ALTERNATE);
         sdD0::alternateFunction(12);
@@ -1103,7 +1427,9 @@ static void initSDIOPeripheral()
         sdCMD::mode(Mode::ALTERNATE);
         sdCMD::alternateFunction(12);
     }
-
+    NVIC_EnableIRQ(DMA2_Stream3_IRQn);
+    NVIC_EnableIRQ(SDIO_IRQn);
+    
     SDIO->POWER=0; //Power off state
     delayUs(1);
     SDIO->CLKCR=0;
@@ -1111,6 +1437,7 @@ static void initSDIOPeripheral()
     SDIO->DCTRL=0;
     SDIO->ICR=0xc007ff;
     SDIO->POWER=SDIO_POWER_PWRCTRL_1 | SDIO_POWER_PWRCTRL_0; //Power on state
+    SDIO->DTIMER=1048576;
     //This delay is particularly important: when setting the POWER register a
     //glitch on the CMD pin happens. This glitch has a fast fall time and a slow
     //rise time resembling an RC charge with a ~6us rise time. If the clock is
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index 7dddd5304c2cc639f9d57e6bee50665ed4a13e40..0675fb5edb3d8818b95d344dd513f6b64421cbde 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -13,14 +13,14 @@
 ## architecture
 ##
 #OPT_BOARD := lpc2138_miosix_board
-OPT_BOARD := stm32f103ze_stm3210e-eval
+#OPT_BOARD := stm32f103ze_stm3210e-eval
 #OPT_BOARD := stm32f103ve_mp3v2
 #OPT_BOARD := stm32f100rb_stm32vldiscovery
 #OPT_BOARD := stm32f103ve_strive_mini
 #OPT_BOARD := stm32f103ze_redbull_v2
 #OPT_BOARD := stm32f407vg_stm32f4discovery
 #OPT_BOARD := stm32f207ig_stm3220g-eval
-#OPT_BOARD := stm32f207zg_ethboard_v2
+OPT_BOARD := stm32f207zg_ethboard_v2
 #OPT_BOARD := stm32f207ze_als_camboard
 #OPT_BOARD := stm32l151_als_mainboard
 #OPT_BOARD := stm32f407vg_bitsboard
diff --git a/miosix/testsuite/testsuite.cpp b/miosix/testsuite/testsuite.cpp
index 61dcfbe73c6661b6824b57fdd347035d747ffc73..fc4f9fb95d1a02abaf8a3c3f20583b0620f7f9af 100644
--- a/miosix/testsuite/testsuite.cpp
+++ b/miosix/testsuite/testsuite.cpp
@@ -106,6 +106,10 @@ static void benchmark_4();
 static void exception_test();
 #endif //__NO_EXCEPTIONS
 
+namespace miosix {
+void fixmeTestFs();
+}
+
 //main(), calls all tests
 int main()
 {
@@ -124,6 +128,7 @@ int main()
         }
         switch(c)
         {
+            case '0': fixmeTestFs(); break;
             case 't':
                 //for(;;){ //Testing
                 ledOn();
diff --git a/miosix_np_2/nbproject/private/private.xml b/miosix_np_2/nbproject/private/private.xml
index 191d3a5c503f776d825b71c0eb46ddff1b5aa69a..e67bac43e71759e08a1e965bc199c26fa10b1520 100644
--- a/miosix_np_2/nbproject/private/private.xml
+++ b/miosix_np_2/nbproject/private/private.xml
@@ -5,7 +5,7 @@
     </code-assistance-data>
     <data xmlns="http://www.netbeans.org/ns/make-project-private/1">
         <activeConfTypeElem>0</activeConfTypeElem>
-        <activeConfIndexElem>7</activeConfIndexElem>
+        <activeConfIndexElem>9</activeConfIndexElem>
     </data>
     <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
 </project-private>