Accept FS#7134 - Sansa: external sd card support by Antonius Hellmann with some tweaks. All testers have given the green light. (Now for the RED ?? ;).

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13741 a1c6a512-1295-4272-9138-f99709370657
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 0b39430..fe7d5b5 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -59,7 +59,7 @@
 #include "fat.h"
 #include "mas.h"
 #include "eeprom_24cxx.h"
-#ifdef HAVE_MMC
+#if defined(HAVE_MMC) || defined(HAVE_HOTSWAP)
 #include "ata_mmc.h"
 #endif
 #if CONFIG_TUNER
@@ -1524,8 +1524,8 @@
 #endif
 
 #ifndef SIMULATOR
-#ifdef HAVE_MMC
-static bool dbg_mmc_info(void)
+#if defined(HAVE_MMC) || defined(HAVE_HOTSWAP)
+static bool dbg_card_info(void)
 {
     bool done = false;
     int currval = 0;
@@ -1548,7 +1548,7 @@
 
     while (!done)
     {
-        card = mmc_card_info(currval / 2);
+        card = card_get_info(currval / 2);
 
         line = 0;
         lcd_clear_display();
@@ -1564,29 +1564,29 @@
 
                 strncpy(card_name, ((unsigned char*)card->cid) + 3, 6);
                 snprintf(pbuf, sizeof(pbuf), "%s Rev %d.%d", card_name,
-                         (int) mmc_extract_bits(card->cid, 72, 4),
-                         (int) mmc_extract_bits(card->cid, 76, 4));
+                         (int) card_extract_bits(card->cid, 72, 4),
+                         (int) card_extract_bits(card->cid, 76, 4));
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "Prod: %d/%d",
-                         (int) mmc_extract_bits(card->cid, 112, 4),
-                         (int) mmc_extract_bits(card->cid, 116, 4) + 1997);
+                         (int) card_extract_bits(card->cid, 112, 4),
+                         (int) card_extract_bits(card->cid, 116, 4) + 1997);
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "Ser#: 0x%08lx",
-                         mmc_extract_bits(card->cid, 80, 32));
+                         card_extract_bits(card->cid, 80, 32));
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "M=%02x, O=%04x",
-                         (int) mmc_extract_bits(card->cid, 0, 8),
-                         (int) mmc_extract_bits(card->cid, 8, 16));
+                         (int) card_extract_bits(card->cid, 0, 8),
+                         (int) card_extract_bits(card->cid, 8, 16));
                 lcd_puts(0, line++, pbuf);
-                temp = mmc_extract_bits(card->csd, 2, 4);
+                temp = card_extract_bits(card->csd, 2, 4);
                 snprintf(pbuf, sizeof(pbuf), "MMC v%s", temp < 5 ?
                          spec_vers[temp] : "?.?");
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "Blocks: 0x%06lx", card->numblocks);
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "Blksz.: %d P:%c%c", card->blocksize,
-                         mmc_extract_bits(card->csd, 48, 1) ? 'R' : '-',
-                         mmc_extract_bits(card->csd, 106, 1) ? 'W' : '-');
+                         card_extract_bits(card->csd, 48, 1) ? 'R' : '-',
+                         card_extract_bits(card->csd, 106, 1) ? 'W' : '-');
                 lcd_puts(0, line++, pbuf);
             }
             else                  /* Technical details */
@@ -1606,12 +1606,12 @@
                 snprintf(pbuf, sizeof(pbuf), "R2W: *%d", card->r2w_factor);
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "IRmax: %d..%d mA",
-                         i_vmin[mmc_extract_bits(card->csd, 66, 3)],
-                         i_vmax[mmc_extract_bits(card->csd, 69, 3)]);
+                         i_vmin[card_extract_bits(card->csd, 66, 3)],
+                         i_vmax[card_extract_bits(card->csd, 69, 3)]);
                 lcd_puts(0, line++, pbuf);
                 snprintf(pbuf, sizeof(pbuf), "IWmax: %d..%d mA",
-                         i_vmin[mmc_extract_bits(card->csd, 72, 3)],
-                         i_vmax[mmc_extract_bits(card->csd, 75, 3)]);
+                         i_vmin[card_extract_bits(card->csd, 72, 3)],
+                         i_vmax[card_extract_bits(card->csd, 75, 3)]);
                 lcd_puts(0, line++, pbuf);
             }
         }
@@ -1642,7 +1642,7 @@
     action_signalscreenchange();
     return false;
 }
-#else /* !HAVE_MMC */
+#else /* !defined(HAVE_MMC) && !defined(HAVE_HOTSWAP) */
 static bool dbg_disk_info(void)
 {
     char buf[128];
@@ -1799,7 +1799,7 @@
     action_signalscreenchange();
     return false;
 }
-#endif /* !HAVE_MMC */
+#endif /* !defined(HAVE_MMC) && !defined(HAVE_HOTSWAP) */
 #endif /* !SIMULATOR */
 
 #ifdef HAVE_DIRCACHE
@@ -2278,8 +2278,10 @@
         { "View partitions", dbg_partitions },
 #endif
 #ifndef SIMULATOR
-#ifdef HAVE_MMC
-        { "View MMC info", dbg_mmc_info },
+#if defined(HAVE_MMC)
+        { "View MMC info", dbg_card_info },
+#elif defined(HAVE_HOTSWAP)
+        { "View microSD info", dbg_card_info },
 #else
         { "View disk info", dbg_disk_info },
 #endif
diff --git a/apps/main.c b/apps/main.c
index 3dfab06..2dd90ef 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -430,7 +430,8 @@
         /* enter USB mode early, before trying to mount */
         if (button_get_w_tmo(HZ/10) == SYS_USB_CONNECTED)
 #ifdef HAVE_MMC
-            if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED))
+            if (!mmc_touched() ||
+                (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED))
 #endif
             {
                 usb_screen();
diff --git a/apps/misc.c b/apps/misc.c
index 88ec73e..daa96b0 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -828,7 +828,8 @@
             if (callback != NULL)
                 callback(parameter);
 #ifdef HAVE_MMC
-            if (!mmc_touched() || (mmc_remove_request() == SYS_MMC_EXTRACTED))
+            if (!mmc_touched() ||
+                (mmc_remove_request() == SYS_HOTSWAP_EXTRACTED))
 #endif
             {
                 scrobbler_flush_cache();
diff --git a/apps/screens.c b/apps/screens.c
index d8e3121..aebb7bd 100644
--- a/apps/screens.c
+++ b/apps/screens.c
@@ -163,8 +163,8 @@
         queue_wait_w_tmo(&button_queue, &ev, HZ/2);
         switch (ev.id)
         {
-            case SYS_MMC_EXTRACTED:
-                return SYS_MMC_EXTRACTED;
+            case SYS_HOTSWAP_EXTRACTED:
+                return SYS_HOTSWAP_EXTRACTED;
 
             case SYS_USB_DISCONNECTED:
                 return SYS_USB_DISCONNECTED;
diff --git a/bootloader/main-pp.c b/bootloader/main-pp.c
index 49d7f0a..dd8b798 100644
--- a/bootloader/main-pp.c
+++ b/bootloader/main-pp.c
@@ -352,7 +352,7 @@
     unsigned long sum;
     
     /* Read header to find out how long the mi4 file is. */
-    ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET,
+    ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET,
                             PPMI_SECTORS, &ppmi_header);
     
     /* The first four characters at 0x80000 (sector 1024) should be PPMI*/
@@ -362,7 +362,7 @@
     printf("BL mi4 size: %x", ppmi_header.length);
     
     /* Read mi4 header of the OF */
-    ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS 
+    ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS 
                        + (ppmi_header.length/512), MI4_HEADER_SECTORS, &mi4header);
     
     /* We don't support encrypted mi4 files yet */
@@ -385,7 +385,7 @@
     printf("Binary type: %.4s", mi4header.type);
 
     /* Load firmware */
-    ata_read_sectors(pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS
+    ata_read_sectors(IF_MV2(0,) pinfo->start + PPMI_SECTOR_OFFSET + PPMI_SECTORS
                         + (ppmi_header.length/512) + MI4_HEADER_SECTORS,
                         (mi4header.mi4size-MI4_HEADER_SIZE)/512, buf);
 
@@ -404,7 +404,7 @@
         unsigned int i;
         /* check which known version we have */
         /* These are taken from the PPPS section, 0x00780240 */
-        ata_read_sectors(pinfo->start + 0x3C01, 1, block);
+        ata_read_sectors(IF_MV2(0,) pinfo->start + 0x3C01, 1, block);
         for (i=0; i<sizeof(OFDatabaseOffsets)/sizeof(*OFDatabaseOffsets); i++)
         {
             if (!memcmp(&block[0x40], OFDatabaseOffsets[i].version,
@@ -417,9 +417,9 @@
         }
         if (sector && offset)
         {
-            ata_read_sectors(sector, 1, block);
+            ata_read_sectors(IF_MV2(0,) sector, 1, block);
             block[offset] = 0;
-            ata_write_sectors(sector, 1, block);
+            ata_write_sectors(IF_MV2(0,) sector, 1, block);
         }
     }
     return EOK;
@@ -428,12 +428,14 @@
 
 void* main(void)
 {
+#ifndef SANSA_E200
     char buf[256];
+    unsigned short* identify_info;
+#endif
     int i;
     int btn;
     int rc;
     int num_partitions;
-    unsigned short* identify_info;
     struct partinfo* pinfo;
 #ifdef SANSA_E200
     int usb_retry = 0;
@@ -475,6 +477,7 @@
     printf(MODEL_NAME);
 
     i=ata_init();
+#ifndef SANSA_E200
     if (i==0) {
         identify_info=ata_get_identify();
         /* Show model */
@@ -489,8 +492,9 @@
     } else {
         error(EATA, i);
     }
+#endif
 
-    disk_init();
+    disk_init(IF_MV(0));
     num_partitions = disk_mount_all();
     if (num_partitions<=0)
     {
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 28ef38e..2a7bea3 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -116,6 +116,9 @@
 #endif /* HAVE_FLASH_DISK */
 #endif /* HAVE_MMC */
 drivers/fat.c
+#ifdef HAVE_HOTSWAP
+hotswap.c
+#endif
 #endif /* SIMULATOR */
 
 /* EEPROM */
diff --git a/firmware/common/dir.c b/firmware/common/dir.c
index bc44a53..0f46652 100644
--- a/firmware/common/dir.c
+++ b/firmware/common/dir.c
@@ -37,6 +37,9 @@
 #ifdef HAVE_MMC
 static const char* vol_names = "<MMC%d>";
 #define VOL_ENUM_POS 4 /* position of %d, to avoid runtime calculation */
+#elif defined(HAVE_HOTSWAP)
+static const char* vol_names = "<microSD%d>";
+#define VOL_ENUM_POS 8 /* position of %d, to avoid runtime calculation */
 #else
 static const char* vol_names = "<HD%d>";
 #define VOL_ENUM_POS 3
diff --git a/firmware/common/disk.c b/firmware/common/disk.c
index ebf8103..f491c9b 100644
--- a/firmware/common/disk.c
+++ b/firmware/common/disk.c
@@ -20,8 +20,8 @@
 #include "ata.h"
 #include "debug.h"
 #include "fat.h"
-#ifdef HAVE_MMC
-#include "ata_mmc.h"
+#ifdef HAVE_HOTSWAP
+#include "hotswap.h"
 #endif
 #include "file.h" /* for release_dirs() */
 #include "dir.h" /* for release_files() */
@@ -101,8 +101,8 @@
     int mounted;
     int i;
     
-#if defined(HAVE_MMC) && defined(HAVE_HOTSWAP)
-    mmc_enable_monitoring(false);
+#ifdef HAVE_MMC
+    card_enable_monitoring(false);
 #endif
 
     fat_init(); /* reset all mounted partitions */
@@ -110,13 +110,13 @@
         vol_drive[i] = -1; /* mark all as unassigned */
 
     mounted = disk_mount(0);
-#ifdef HAVE_MMC
-    if (mmc_detect()) /* for Ondio, only if card detected */
+#ifdef HAVE_HOTSWAP
+    if (card_detect())
     {
         mounted += disk_mount(1); /* try 2nd "drive", too */
     }
-#ifdef HAVE_HOTSWAP
-    mmc_enable_monitoring(true);
+#ifdef HAVE_MMC
+    card_enable_monitoring(true);
 #endif
 #endif
 
diff --git a/firmware/drivers/ata_mmc.c b/firmware/drivers/ata_mmc.c
index 847e282..66e60ea 100644
--- a/firmware/drivers/ata_mmc.c
+++ b/firmware/drivers/ata_mmc.c
@@ -397,26 +397,6 @@
     return 0;
 }
 
-/* helper function to extract n (<=32) bits from an arbitrary position.
-   counting from MSB to LSB */
-unsigned long mmc_extract_bits(
-    const unsigned long *p, /* the start of the bitfield array */
-    unsigned int start,     /* bit no. to start reading  */
-    unsigned int size)      /* how many bits to read */
-{
-    unsigned int long_index = start / 32;
-    unsigned int bit_index = start % 32;
-    unsigned long result;
-    
-    result = p[long_index] << bit_index;
-
-    if (bit_index + size > 32)    /* crossing longword boundary */
-        result |= p[long_index+1] >> (32 - bit_index);
-        
-    result >>= 32 - size;
-
-    return result;
-}
 
 static int initialize_card(int card_no)
 {
@@ -470,9 +450,9 @@
         return rc * 10 - 6;
 
     /* check block sizes */
-    card->block_exp = mmc_extract_bits(card->csd, 44, 4);
+    card->block_exp = card_extract_bits(card->csd, 44, 4);
     card->blocksize = 1 << card->block_exp;
-    if ((mmc_extract_bits(card->csd, 102, 4) != card->block_exp)
+    if ((card_extract_bits(card->csd, 102, 4) != card->block_exp)
         || card->blocksize > MAX_BLOCK_SIZE)
     {
         return -7;
@@ -486,16 +466,16 @@
     }
 
     /* max transmission speed, clock divider */
-    temp = mmc_extract_bits(card->csd, 29, 3);
+    temp = card_extract_bits(card->csd, 29, 3);
     temp = (temp > 3) ? 3 : temp;
-    card->speed = mantissa[mmc_extract_bits(card->csd, 25, 4)]
+    card->speed = mantissa[card_extract_bits(card->csd, 25, 4)]
                 * exponent[temp + 4];
     card->bitrate_register = (FREQ/4-1) / card->speed;
 
     /* NSAC, TSAC, read timeout */
-    card->nsac = 100 * mmc_extract_bits(card->csd, 16, 8);
-    card->tsac = mantissa[mmc_extract_bits(card->csd, 9, 4)];
-    temp = mmc_extract_bits(card->csd, 13, 3);
+    card->nsac = 100 * card_extract_bits(card->csd, 16, 8);
+    card->tsac = mantissa[card_extract_bits(card->csd, 9, 4)];
+    temp = card_extract_bits(card->csd, 13, 3);
     card->read_timeout = ((FREQ/4) / (card->bitrate_register + 1)
                          * card->tsac / exponent[9 - temp]
                          + (10 * card->nsac));
@@ -503,7 +483,7 @@
     card->tsac = card->tsac * exponent[temp] / 10;
 
     /* r2w_factor, write timeout */
-    card->r2w_factor = 1 << mmc_extract_bits(card->csd, 99, 3);
+    card->r2w_factor = 1 << card_extract_bits(card->csd, 99, 3);
     if (card->r2w_factor > 32)    /* dirty MMC spec violation */
     {
         card->read_timeout *= 4;  /* add safety factor */
@@ -513,8 +493,8 @@
         card->write_timeout = card->read_timeout * card->r2w_factor;
 
     /* card size */
-    card->numblocks = (mmc_extract_bits(card->csd, 54, 12) + 1)
-                      * (1 << (mmc_extract_bits(card->csd, 78, 3) + 2));
+    card->numblocks = (card_extract_bits(card->csd, 54, 12) + 1)
+            * (1 << (card_extract_bits(card->csd, 78, 3) + 2));
     card->size = card->numblocks * card->blocksize;
 
     /* switch to full speed */
@@ -993,12 +973,12 @@
                 break;
 
 #ifdef HAVE_HOTSWAP
-            case SYS_MMC_INSERTED:
+            case SYS_HOTSWAP_INSERTED:
                 disk_mount(1); /* mount MMC */
                 queue_broadcast(SYS_FS_CHANGED, 0);
                 break;
 
-            case SYS_MMC_EXTRACTED:
+            case SYS_HOTSWAP_EXTRACTED:
                 disk_unmount(1); /* release "by force" */
                 queue_broadcast(SYS_FS_CHANGED, 0);
                 break;
@@ -1097,11 +1077,11 @@
             {
                 if (current_status)
                 {
-                    queue_broadcast(SYS_MMC_INSERTED, 0);
+                    queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
                 }
                 else
                 {
-                    queue_broadcast(SYS_MMC_EXTRACTED, 0);
+                    queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
                     mmc_status = MMC_UNTOUCHED;
                     card_info[1].initialized = false;
                 }
diff --git a/firmware/export/ata_mmc.h b/firmware/export/ata_mmc.h
index a4e9f71..2361c43 100644
--- a/firmware/export/ata_mmc.h
+++ b/firmware/export/ata_mmc.h
@@ -18,34 +18,14 @@
  ****************************************************************************/
 #ifndef __ATA_MMC_H__
 #define __ATA_MMC_H__
-
-typedef struct
-{  
-    bool initialized;
-    unsigned char bitrate_register;
-    unsigned long read_timeout;   /* n * 8 clock cycles */
-    unsigned long write_timeout;  /* n * 8 clock cycles */
-
-    unsigned long ocr;            /* OCR register */
-    unsigned long csd[4];         /* CSD register, 16 bytes */
-    unsigned long cid[4];         /* CID register, 16 bytes */
-    unsigned long speed;          /* bit/s */
-    unsigned int nsac;            /* clock cycles */
-    unsigned long tsac;           /* n * 0.1 ns */
-    unsigned int r2w_factor;
-    unsigned long size;           /* size in bytes */
-    unsigned long numblocks;      /* size in flash blocks */
-    unsigned int blocksize;       /* block size in bytes */
-    unsigned int block_exp;       /* block size exponent */
-} tCardInfo;
+#include "hotswap.h"
 
 void mmc_enable_int_flash_clock(bool on);
 bool mmc_detect(void);
-unsigned long mmc_extract_bits(const unsigned long *p, unsigned int start,
-                               unsigned int size);
 tCardInfo *mmc_card_info(int card_no);
 bool mmc_touched(void);
 bool mmc_usb_active(int delayticks);
+
 #ifdef HAVE_HOTSWAP
 void mmc_enable_monitoring(bool on);
 #endif
diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h
index 71b1270..f970869 100644
--- a/firmware/export/config-e200.h
+++ b/firmware/export/config-e200.h
@@ -33,6 +33,11 @@
 /* define this if you have LCD enable function */
 #define HAVE_LCD_ENABLE
 
+#ifndef SIMULATOR
+#define HAVE_HOTSWAP
+#define HAVE_MULTIVOLUME
+#endif
+
 #define HAVE_BACKLIGHT_BRIGHTNESS
 /* Main LCD backlight brightness range and defaults */
 #define MIN_BRIGHTNESS_SETTING           1
diff --git a/firmware/export/config.h b/firmware/export/config.h
index 4652359..ffcbf68 100644
--- a/firmware/export/config.h
+++ b/firmware/export/config.h
@@ -247,7 +247,7 @@
 /* Enable the directory cache and tagcache in RAM if we have
  * plenty of RAM. Both features can be enabled independently. */
 #if ((defined(MEMORYSIZE) && (MEMORYSIZE > 8)) || MEM > 8) && \
- !defined(BOOTLOADER)
+ !defined(BOOTLOADER) && !defined(SANSA_E200)
 #define HAVE_DIRCACHE
 #ifdef HAVE_TAGCACHE
 #define HAVE_TC_RAMCACHE
diff --git a/firmware/export/hotswap.h b/firmware/export/hotswap.h
new file mode 100644
index 0000000..c6a657d
--- /dev/null
+++ b/firmware/export/hotswap.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2004 by Jens Arnold
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef __HOTSWAP_H__
+#define __HOTSWAP_H__
+
+typedef struct
+{  
+    bool initialized;
+    unsigned char bitrate_register;
+    unsigned long read_timeout;   /* n * 8 clock cycles */
+    unsigned long write_timeout;  /* n * 8 clock cycles */
+
+    unsigned long ocr;            /* OCR register */
+    unsigned long csd[4];         /* CSD register, 16 bytes */
+    unsigned long cid[4];         /* CID register, 16 bytes */
+    unsigned long speed;          /* bit/s */
+    unsigned int nsac;            /* clock cycles */
+    unsigned long tsac;           /* n * 0.1 ns */
+    unsigned int r2w_factor;
+    unsigned long size;           /* size in bytes */
+    unsigned long numblocks;      /* size in flash blocks */
+    unsigned int blocksize;       /* block size in bytes */
+    unsigned int block_exp;       /* block size exponent */
+} tCardInfo;
+
+#ifdef TARGET_TREE
+bool       card_detect(void);
+tCardInfo *card_get_info(int card_no);
+#else /* HAVE_MMC */
+#include "ata_mmc.h"
+#define card_detect            mmc_detect
+#define card_get_info          mmc_card_info
+#define card_touched           mmc_touched
+#define card_enable_monitoring mmc_enable_monitoring
+#endif
+
+/* helper function to extract n (<=32) bits from an arbitrary position.
+   counting from MSB to LSB */
+unsigned long card_extract_bits(
+        const unsigned long *p, /* the start of the bitfield array */
+        unsigned int start,     /* bit no. to start reading  */
+        unsigned int size);     /* how many bits to read */
+#endif
diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h
index 9e3a8d2..d5898a9 100644
--- a/firmware/export/kernel.h
+++ b/firmware/export/kernel.h
@@ -41,8 +41,8 @@
 #define SYS_USB_DISCONNECTED      ((SYS_EVENT | ((long)3 << 27)))
 #define SYS_USB_DISCONNECTED_ACK  ((SYS_EVENT | ((long)4 << 27)))
 #define SYS_TIMEOUT               ((SYS_EVENT | ((long)5 << 27)))
-#define SYS_MMC_INSERTED          ((SYS_EVENT | ((long)6 << 27)))
-#define SYS_MMC_EXTRACTED         ((SYS_EVENT | ((long)7 << 27)))
+#define SYS_HOTSWAP_INSERTED      ((SYS_EVENT | ((long)6 << 27)))
+#define SYS_HOTSWAP_EXTRACTED     ((SYS_EVENT | ((long)7 << 27)))
 #define SYS_POWEROFF              ((SYS_EVENT | ((long)8 << 27)))
 #define SYS_FS_CHANGED            ((SYS_EVENT | ((long)9 << 27)))
 #define SYS_CHARGER_CONNECTED     ((SYS_EVENT | ((long)10 << 27)))
diff --git a/firmware/export/pp5024.h b/firmware/export/pp5024.h
index e7758b9..5e2de17 100644
--- a/firmware/export/pp5024.h
+++ b/firmware/export/pp5024.h
@@ -24,7 +24,7 @@
 #include "pp5020.h"
 
 #undef GPIO_IRQ
-/* Ports A, ?? */
+/* Ports A, B, ?? */
 #define GPIO0_IRQ   (32+0)
 /* Ports F, H, ?? */
 #define GPIO1_IRQ   (32+1)
diff --git a/firmware/hotswap.c b/firmware/hotswap.c
new file mode 100644
index 0000000..5620edf
--- /dev/null
+++ b/firmware/hotswap.c
@@ -0,0 +1,58 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2004 by Jens Arnold
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdbool.h>
+#include "config.h"
+#ifdef TARGET_TREE
+#include "hotswap-target.h"
+#else
+#include "ata_mmc.h"
+#endif
+
+/* helper function to extract n (<=32) bits from an arbitrary position.
+   counting from MSB to LSB */
+unsigned long card_extract_bits(
+    const unsigned long *p, /* the start of the bitfield array */
+    unsigned int start,     /* bit no. to start reading  */
+    unsigned int size)      /* how many bits to read */
+{
+    unsigned int long_index = start / 32;
+    unsigned int bit_index = start % 32;
+    unsigned long result;
+    
+    result = p[long_index] << bit_index;
+
+    if (bit_index + size > 32)    /* crossing longword boundary */
+        result |= p[long_index+1] >> (32 - bit_index);
+        
+    result >>= 32 - size;
+
+    return result;
+}
+
+#ifdef TARGET_TREE
+bool card_detect(void)
+{
+    return card_detect_target();
+}
+
+tCardInfo *card_get_info(int card_no)
+{
+    return card_get_info_target(card_no);
+}
+#endif
diff --git a/firmware/include/dir.h b/firmware/include/dir.h
index c354082..020b24a 100644
--- a/firmware/include/dir.h
+++ b/firmware/include/dir.h
@@ -71,6 +71,10 @@
 #endif
 } DIR;
 
+#ifdef HAVE_HOTSWAP
+char *get_volume_name(int volume);
+#endif
+
 #ifndef DIRFUNCTIONS_DEFINED
 
 extern DIR* opendir(const char* name);
diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
index 8eccdfb..fea6773 100644
--- a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
+++ b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c
@@ -17,14 +17,16 @@
  *
  ****************************************************************************/
 #include "ata.h"
+#include "hotswap-target.h"
 #include "ata-target.h"
 #include "ata_idle_notify.h"
 #include "system.h"
 #include <string.h>
 #include "thread.h"
+#include "led.h"
+#include "disk.h"
 #include "pp5024.h"
-
-#define NOINLINE_ATTR __attribute__((noinline)) /* don't inline the loops */
+#include "panic.h"
 
 #define BLOCK_SIZE      (512)
 #define SECTOR_SIZE     (512)
@@ -48,6 +50,7 @@
 #define DATA_DONE       (1 << 12)
 #define CMD_DONE        (1 << 13)
 #define ERROR_BITS      (0x3f)
+#define READY_FOR_DATA  (1 << 8)
 #define FIFO_FULL       (1 << 7)
 #define FIFO_EMPTY      (1 << 6)
 
@@ -65,370 +68,481 @@
 #define FIFO_SIZE       16          /* FIFO is 16 words deep */
 
 /* SD Commands */
-#define GO_IDLE_STATE   0
-#define ALL_SEND_CID    2
-#define SEND_RELATIVE_ADDR  3
-#define SET_DSR         4
-#define SWITCH_FUNC     6
-#define SELECT_CARD     7
-#define DESELECT_CARD   7
-#define SEND_CSD        9
-#define SEND_CID        10
-#define STOP_TRANSMISSION   12
-#define SEND_STATUS     13
-#define GO_INACTIVE_STATE   15
-#define SET_BLOCKLEN    16
-#define READ_SINGLE_BLOCK   17
-#define READ_MULTIPLE_BLOCK 18
-#define WRITE_BLOCK     24
-#define WRITE_MULTIPLE_BLOCK    25
-#define ERASE_WR_BLK_START  32
-#define ERASE_WR_BLK_END    33
-#define ERASE           38
+#define GO_IDLE_STATE         0
+#define ALL_SEND_CID          2
+#define SEND_RELATIVE_ADDR    3
+#define SET_DSR               4
+#define SWITCH_FUNC           6
+#define SELECT_CARD           7
+#define DESELECT_CARD         7
+#define SEND_CSD              9
+#define SEND_CID             10
+#define STOP_TRANSMISSION    12
+#define SEND_STATUS          13
+#define GO_INACTIVE_STATE    15
+#define SET_BLOCKLEN         16
+#define READ_SINGLE_BLOCK    17
+#define READ_MULTIPLE_BLOCK  18
+#define SEND_NUM_WR_BLOCKS   22
+#define WRITE_BLOCK          24
+#define WRITE_MULTIPLE_BLOCK 25
+#define ERASE_WR_BLK_START   32
+#define ERASE_WR_BLK_END     33
+#define ERASE                38
+#define APP_CMD              55
+
+#define EC_POWER_UP             1 /* error code */
+#define EC_READ_TIMEOUT         2 /* error code */
+#define EC_WRITE_TIMEOUT        3 /* error code */
+#define EC_TRAN_SEL_BANK        4 /* error code */
+#define EC_TRAN_READ_ENTRY      5 /* error code */
+#define EC_TRAN_READ_EXIT       6 /* error code */
+#define EC_TRAN_WRITE_ENTRY     7 /* error code */
+#define EC_TRAN_WRITE_EXIT      8 /* error code */
+#define DO_PANIC               32 /* marker     */
+#define NO_PANIC                0 /* marker     */
+#define EC_COMMAND             10 /* error code */
+#define EC_FIFO_SEL_BANK_EMPTY 11 /* error code */
+#define EC_FIFO_SEL_BANK_DONE  12 /* error code */
+#define EC_FIFO_ENA_BANK_EMPTY 13 /* error code */
+#define EC_FIFO_READ_FULL      14 /* error code */
+#define EC_FIFO_WR_EMPTY       15 /* error code */
+#define EC_FIFO_WR_DONE        16 /* error code */
 
 /* Application Specific commands */
 #define SET_BUS_WIDTH   6
 #define SD_APP_OP_COND  41
 
-#define READ_TIMEOUT    5*HZ
-#define WRITE_TIMEOUT   0.5*HZ
-
-static unsigned short identify_info[SECTOR_SIZE];
+/* for compatibility */
 int ata_spinup_time = 0;
-long last_disk_activity = -1;
-static bool initialized = false;
 
-static unsigned char current_bank = 0; /* The bank that we are working with */
+long last_disk_activity = -1;
+
+static bool initialized = false;
+static int  sd1_status  = 0x00;  /* 0x00:inserted, 0x80:not inserted */
 
 static tSDCardInfo card_info[2];
+static tSDCardInfo *currcard; /* current active card */
 
-/* For multi volume support */
-static int current_card = 0;
-
-static struct mutex sd_mtx;
-
-static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x800)/sizeof(long)];
-
-static const char sd_thread_name[] = "sd";
+/* Shoot for around 75% usage */
+static long sd_stack [(DEFAULT_STACK_SIZE*2 + 0x1c0)/sizeof(long)];
+static const char         sd_thread_name[] = "ata/sd";
+static struct mutex       sd_mtx;
 static struct event_queue sd_queue;
 
+/* Posted when card plugged status has changed */
+#define SD_HOTSWAP    1
 
 /* Private Functions */
 
-bool sd_send_command(unsigned int cmd, unsigned long arg1, unsigned int arg2)
+static unsigned int check_time[10];
+
+static inline void sd_check_timeout(unsigned int timeout, int id)
 {
-    bool result = false;
-    do
+    if (USEC_TIMER > check_time[id] + timeout)
+        panicf("Error SDCard: %d", id);
+}
+
+static inline bool sd_poll_status(unsigned int trigger, unsigned int timeout,
+                                  int id)
+{
+    unsigned int t = USEC_TIMER;
+
+    while ((STATUS_REG & trigger) == 0)
+    {
+        if (USEC_TIMER > t + timeout)
+        {
+            if(id & DO_PANIC)
+                panicf("Error SDCard: %d", id & 31);
+
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool sd_command(unsigned int cmd, unsigned long arg1,
+                       unsigned int *response, unsigned int type)
+{
+    int i, words; /* Number of 16 bit words to read from RESPONSE_REG */
+    unsigned int data[9];
+
+    while (1)
     {
         CMD_REG0 = cmd;
         CMD_REG1 = (unsigned int)((arg1 & 0xffff0000) >> 16);
         CMD_REG2 = (unsigned int)((arg1 & 0xffff));
-        UNKNOWN = arg2;
-        while ((STATUS_REG & CMD_DONE) == 0)
-        {
-            /* Busy wait */
-        }
-        if ((STATUS_REG & ERROR_BITS) == 0)
-        {
-            result = true;
-        } 
-    } while ((STATUS_REG & ERROR_BITS) != 0);
-    return result;
-}
+        UNKNOWN  = type;
 
-void sd_read_response(unsigned int *response, int type)
-{
-    int i;
-    int words; /* Number of 16 bit words to read from RESPONSE_REG */
-    unsigned int response_from_card[9];
-    if(type == 2)
-    {
-        words = 9; /* R2 types are 8.5 16-bit words long */
-    } else {
-        words = 3;
+        sd_poll_status(CMD_DONE, 100000, EC_COMMAND | DO_PANIC);
+
+        if ((STATUS_REG & ERROR_BITS) == 0)
+            break;
+
+        priority_yield();
     }
 
+    if (cmd == GO_IDLE_STATE)  return true; /* no response here */
+
+    words = (type == 2) ? 9 : 3;
+
     for (i = 0; i < words; i++) /* RESPONSE_REG is read MSB first */
     {
-            response_from_card[i] = RESPONSE_REG; /* Read most significant 16-bit word */
+        data[i] = RESPONSE_REG; /* Read most significant 16-bit word */
     }
 
-    switch (type)
+    if (type == 2)
     {
-        case 1:
-            /* Response type 1 has the following structure:
-                Start bit
-                Transmission bit
-                Command index (6 bits)
-                Card Status (32 bits)
-                CRC7 (7 bits)
-                Stop bit
-            */
-            /* TODO: Sanity checks */
-            response[0] = ((response_from_card[0] & 0xff) << 24)
-                       + (response_from_card[1] << 8)
-                       + ((response_from_card[2] & 0xff00) >> 8);
-            break;
-        case 2:
-            /* Response type 2 has the following structure:
-                Start bit
-                Transmission bit
-                Reserved (6 bits)
-                CSD/CID register (127 bits)
-                Stop bit
-            */
-            response[3] = ((response_from_card[0]&0xff)<<24) +
-                           (response_from_card[1]<<8) +
-                           ((response_from_card[2]&0xff00)>>8);
-            response[2] = ((response_from_card[2]&0xff)<<24) +
-                           (response_from_card[3]<<8) +
-                           ((response_from_card[4]&0xff00)>>8);
-            response[1] = ((response_from_card[4]&0xff)<<24) +
-                           (response_from_card[5]<<8) +
-                           ((response_from_card[6]&0xff00)>>8);
-            response[0] = ((response_from_card[6]&0xff)<<24) +
-                           (response_from_card[7]<<8) +
-                           ((response_from_card[8]&0xff00)>>8);
-            break;
-        case 3:
-            /* Response type 3 has the following structure:
-                Start bit
-                Transmission bit
-                Reserved (6 bits)
-                OCR register (32 bits)
-                Reserved (7 bits)
-                Stop bit
-            */
-            response[0] = ((response_from_card[0] & 0xff) << 24)
-                       + (response_from_card[1] << 8)
-                       + ((response_from_card[2] & 0xff00) >> 8);
-        /* Types 4-6 not supported yet */
+        /* Response type 2 has the following structure:
+         * [135:135] Start Bit - '0'
+         * [134:134] Transmission bit - '0'
+         * [133:128] Reserved - '111111'
+         * [127:001] CID or CSD register including internal CRC7
+         * [000:000] End Bit - '1'
+         */
+        response[3] = (data[0]<<24) + (data[1]<<8) + ((data[2]&0xff00)>>8);
+        response[2] = (data[2]<<24) + (data[3]<<8) + ((data[4]&0xff00)>>8);
+        response[1] = (data[4]<<24) + (data[5]<<8) + ((data[6]&0xff00)>>8);
+        response[0] = (data[6]<<24) + (data[7]<<8) + ((data[8]&0xff00)>>8);
     }
-}
+    else
+    {
+        /* Response types 1, 1b, 3, 6 have the following structure:
+         * Types 4 and 5 are not supported.
+         *
+         *     [47] Start bit - '0'
+         *     [46] Transmission bit - '0'
+         *  [45:40] R1, R1b, R6: Command index
+         *          R3: Reserved - '111111'
+         *   [39:8] R1, R1b: Card Status
+         *          R3: OCR Register
+         *          R6: [31:16] RCA
+         *              [15: 0] Card Status Bits 23, 22, 19, 12:0
+         *                     [23] COM_CRC_ERROR
+         *                     [22] ILLEGAL_COMMAND
+         *                     [19] ERROR
+         *                   [12:9] CURRENT_STATE
+         *                      [8] READY_FOR_DATA
+         *                    [7:6]
+         *                      [5] APP_CMD
+         *                      [4]
+         *                      [3] AKE_SEQ_ERROR
+         *                      [2] Reserved
+         *                    [1:0] Reserved for test mode
+         *    [7:1] R1, R1b: CRC7
+         *          R3: Reserved - '1111111'
+         *      [0] End Bit - '1'
+         */
+        response[0] = (data[0]<<24) + (data[1]<<8) + ((data[2]&0xff00)>>8);
+    }
 
-bool sd_send_acommand(unsigned int cmd, unsigned long arg1, unsigned int arg2)
-{
-    unsigned int returncode;
-    if (sd_send_command(55, (card_info[current_card].rca)<<16, 1) == false)
-        return false;
-    sd_read_response(&returncode, 1);
-    if (sd_send_command(cmd, arg1, arg2) == false)
-        return false;
     return true;
 }
 
-void sd_wait_for_state(tSDCardInfo* card, unsigned int state)
+static void sd_wait_for_state(unsigned int state, unsigned int id)
 {
     unsigned int response = 0;
-    while(((response >> 9) & 0xf) != state)
+
+    check_time[id] = USEC_TIMER;
+
+    while (1)
     {
-        sd_send_command(SEND_STATUS, (card->rca) << 16, 1);
+        sd_command(SEND_STATUS, currcard->rca, &response, 1);
+        sd_check_timeout(0x80000, id);
+
+        if (((response >> 9) & 0xf) == state)
+            break;
+
         priority_yield();
-        sd_read_response(&response, 1);
-        /* TODO: Add a timeout and error handling */
     }
+
     SD_STATE_REG = state;
 }
 
-
-STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount)
-                NOINLINE_ATTR ICODE_ATTR;
-
-STATICIRAM void copy_read_sectors(unsigned char* buf, int wordcount)
+static inline void copy_read_sectors_fast(unsigned char **buf)
 {
-    unsigned int tmp = 0;
-
-    if ( (unsigned long)buf & 1)
-    {   /* not 16-bit aligned, copy byte by byte */
-        unsigned char* bufend = buf + wordcount*2;
-        do
-        {
-            tmp = DATA_REG;
-            *buf++ = tmp & 0xff;
-            *buf++ = tmp >> 8;
-        } while (buf < bufend); /* tail loop is faster */
-    }
-    else
-    {   /* 16-bit aligned, can do faster copy */
-        unsigned short* wbuf = (unsigned short*)buf;
-        unsigned short* wbufend = wbuf + wordcount;
-        do
-        {
-            *wbuf = DATA_REG;
-        } while (++wbuf < wbufend); /* tail loop is faster */
+    /* Copy one chunk of 16 words using best method for start alignment */
+    switch ( (intptr_t)*buf & 3 )
+    {
+    case 0:
+        asm volatile (
+            "ldmia  %[data], { r2-r9 }          \r\n"
+            "orr    r2, r2, r3, lsl #16         \r\n"
+            "orr    r4, r4, r5, lsl #16         \r\n"
+            "orr    r6, r6, r7, lsl #16         \r\n"
+            "orr    r8, r8, r9, lsl #16         \r\n"
+            "stmia  %[buf]!, { r2, r4, r6, r8 } \r\n"
+            "ldmia  %[data], { r2-r9 }          \r\n"
+            "orr    r2, r2, r3, lsl #16         \r\n"
+            "orr    r4, r4, r5, lsl #16         \r\n"
+            "orr    r6, r6, r7, lsl #16         \r\n"
+            "orr    r8, r8, r9, lsl #16         \r\n"
+            "stmia  %[buf]!, { r2, r4, r6, r8 } \r\n"
+            : [buf]"+&r"(*buf)
+            : [data]"r"(&DATA_REG)
+            : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9"
+        );
+        break;
+    case 1:
+        asm volatile (
+            "ldmia  %[data], { r2-r9 }          \r\n"
+            "orr    r3, r2, r3, lsl #16         \r\n"
+            "strb   r3, [%[buf]], #1            \r\n"
+            "mov    r3, r3, lsr #8              \r\n"
+            "strh   r3, [%[buf]], #2            \r\n"
+            "mov    r3, r3, lsr #16             \r\n"
+            "orr    r3, r3, r4, lsl #8          \r\n"
+            "orr    r3, r3, r5, lsl #24         \r\n"
+            "mov    r5, r5, lsr #8              \r\n"
+            "orr    r5, r5, r6, lsl #8          \r\n"
+            "orr    r5, r5, r7, lsl #24         \r\n"
+            "mov    r7, r7, lsr #8              \r\n"
+            "orr    r7, r7, r8, lsl #8          \r\n"
+            "orr    r7, r7, r9, lsl #24         \r\n"
+            "mov    r2, r9, lsr #8              \r\n"
+            "stmia  %[buf]!, { r3, r5, r7 }     \r\n"
+            "ldmia  %[data], { r3-r10 }         \r\n"
+            "orr    r2, r2, r3, lsl #8          \r\n"
+            "orr    r2, r2, r4, lsl #24         \r\n"
+            "mov    r4, r4, lsr #8              \r\n"
+            "orr    r4, r4, r5, lsl #8          \r\n"
+            "orr    r4, r4, r6, lsl #24         \r\n"
+            "mov    r6, r6, lsr #8              \r\n"
+            "orr    r6, r6, r7, lsl #8          \r\n"
+            "orr    r6, r6, r8, lsl #24         \r\n"
+            "mov    r8, r8, lsr #8              \r\n"
+            "orr    r8, r8, r9, lsl #8          \r\n"
+            "orr    r8, r8, r10, lsl #24        \r\n"
+            "mov    r10, r10, lsr #8            \r\n"
+            "stmia  %[buf]!, { r2, r4, r6, r8 } \r\n"
+            "strb   r10, [%[buf]], #1           \r\n"
+            : [buf]"+&r"(*buf)
+            : [data]"r"(&DATA_REG)
+            : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10"
+        );
+        break;
+    case 2:
+        asm volatile (
+            "ldmia  %[data], { r2-r9 }          \r\n"
+            "strh   r2, [%[buf]], #2            \r\n"
+            "orr    r3, r3, r4, lsl #16         \r\n"
+            "orr    r5, r5, r6, lsl #16         \r\n"
+            "orr    r7, r7, r8, lsl #16         \r\n"
+            "stmia  %[buf]!, { r3, r5, r7 }     \r\n"
+            "ldmia  %[data], { r2-r8, r10 }     \r\n"
+            "orr    r2, r9, r2, lsl #16         \r\n"
+            "orr    r3, r3, r4, lsl #16         \r\n"
+            "orr    r5, r5, r6, lsl #16         \r\n"
+            "orr    r7, r7, r8, lsl #16         \r\n"
+            "stmia  %[buf]!, { r2, r3, r5, r7 } \r\n"
+            "strh   r10, [%[buf]], #2           \r\n"
+            : [buf]"+&r"(*buf)
+            : [data]"r"(&DATA_REG)
+            : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10"
+        );
+        break;
+    case 3:
+        asm volatile (
+            "ldmia  %[data], { r2-r9 }          \r\n"
+            "orr    r3, r2, r3, lsl #16         \r\n"
+            "strb   r3, [%[buf]], #1            \r\n"
+            "mov    r3, r3, lsr #8              \r\n"
+            "orr    r3, r3, r4, lsl #24         \r\n"
+            "mov    r4, r4, lsr #8              \r\n"
+            "orr    r5, r4, r5, lsl #8          \r\n"
+            "orr    r5, r5, r6, lsl #24         \r\n"
+            "mov    r6, r6, lsr #8              \r\n"
+            "orr    r7, r6, r7, lsl #8          \r\n"
+            "orr    r7, r7, r8, lsl #24         \r\n"
+            "mov    r8, r8, lsr #8              \r\n"
+            "orr    r2, r8, r9, lsl #8          \r\n"
+            "stmia  %[buf]!, { r3, r5, r7 }     \r\n"
+            "ldmia  %[data], { r3-r10 }         \r\n"
+            "orr    r2, r2, r3, lsl #24         \r\n"
+            "mov    r3, r3, lsr #8              \r\n"
+            "orr    r4, r3, r4, lsl #8          \r\n"
+            "orr    r4, r4, r5, lsl #24         \r\n"
+            "mov    r5, r5, lsr #8              \r\n"
+            "orr    r6, r5, r6, lsl #8          \r\n"
+            "orr    r6, r6, r7, lsl #24         \r\n"
+            "mov    r7, r7, lsr #8              \r\n"
+            "orr    r8, r7, r8, lsl #8          \r\n"
+            "orr    r8, r8, r9, lsl #24         \r\n"
+            "mov    r9, r9, lsr #8              \r\n"
+            "orr    r10, r9, r10, lsl #8        \r\n"
+            "stmia  %[buf]!, { r2, r4, r6, r8 } \r\n"
+            "strh   r10, [%[buf]], #2           \r\n"
+            "mov    r10, r10, lsr #16           \r\n"
+            "strb   r10, [%[buf]], #1           \r\n"
+            : [buf]"+&r"(*buf)
+            : [data]"r"(&DATA_REG)
+            : "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10"
+        );
+        break;
     }
 }
 
-STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount)
-                NOINLINE_ATTR ICODE_ATTR;
+static inline void copy_read_sectors_slow(unsigned char** buf)
+{
+    int cnt = FIFO_SIZE;
+    int t;
 
-STATICIRAM void copy_write_sectors(const unsigned char* buf, int wordcount)
+    /* Copy one chunk of 16 words */
+    asm volatile (
+    "1:                                     \r\n"
+        "ldrh   %[t], [%[data]]             \r\n"
+        "strb   %[t], [%[buf]], #1          \r\n"
+        "mov    %[t], %[t], lsr #8          \r\n"
+        "strb   %[t], [%[buf]], #1          \r\n"
+        "subs   %[cnt], %[cnt], #1          \r\n"
+        "bgt    1b                          \r\n"
+        : [cnt]"+&r"(cnt), [buf]"+&r"(*buf),
+          [t]"=&r"(t)
+        : [data]"r"(&DATA_REG)
+    );
+}
+
+/* Writes have to be kept slow for now */
+static inline void copy_write_sectors(const unsigned char* buf)
 {
     unsigned short tmp = 0;
-    const unsigned char* bufend = buf + wordcount*2;
+    const unsigned char* bufend = buf + FIFO_SIZE*2;
+
     do
     {
-        tmp = (unsigned short) *buf++;
+        tmp  = (unsigned short) *buf++;
         tmp |= (unsigned short) *buf++ << 8;
         DATA_REG = tmp;
     } while (buf < bufend); /* tail loop is faster */
 }
 
-
-void sd_select_bank(unsigned char bank)
+static void sd_select_bank(unsigned char bank)
 {
     unsigned int response;
     unsigned char card_data[512];
     unsigned char* write_buf;
     int i;
-    tSDCardInfo *card = &card_info[0]; /* Bank selection will only be done on
-                                        the onboard flash */
-    if (current_bank != bank)
+
+    memset(card_data, 0, 512);
+    sd_wait_for_state(TRAN, EC_TRAN_SEL_BANK);
+    BLOCK_SIZE_REG = 512;
+    BLOCK_COUNT_REG = 1;
+    sd_command(35, 0, &response, 0x1c0d); /* CMD35 is vendor specific */
+    SD_STATE_REG = PRG;
+
+    card_data[0] = bank;
+
+    /* Write the card data */
+    write_buf = card_data;
+    for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE)
     {
-        memset(card_data, 0, 512);
-        sd_wait_for_state(card, TRAN);
-        BLOCK_SIZE_REG = 512;
-        BLOCK_COUNT_REG = 1;
-        sd_send_command(35, 0, 0x1c0d); /* CMD35 is vendor specific */
-        sd_read_response(&response, 1);
-        SD_STATE_REG = PRG;
+        /* Wait for the FIFO to empty */
+        sd_poll_status(FIFO_EMPTY, 10000, EC_FIFO_SEL_BANK_EMPTY | DO_PANIC);
 
-        card_data[0] = bank;
+        copy_write_sectors(write_buf); /* Copy one chunk of 16 words */
 
-        /* Write the card data */
-        write_buf = card_data;
-        for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE)
-        {
-            /* Wait for the FIFO to be empty */
-            while((STATUS_REG & FIFO_EMPTY) == 0) {} /* Erm... is this right? */
-
-            copy_write_sectors(write_buf, FIFO_SIZE);
-
-            write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */
-        }
-
-        while((STATUS_REG & DATA_DONE) == 0) {}
-        current_bank = bank;
+        write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */
     }
+
+    sd_poll_status(DATA_DONE, 10000, EC_FIFO_SEL_BANK_DONE | DO_PANIC);
+
+    currcard->current_bank = bank;
 }
 
-void sd_init_device(void)
+/* lock must already be aquired */
+static void sd_init_device(int card_no)
 {
 /* SD Protocol registers */
-    unsigned int dummy;
-    int i;
-
-    static unsigned int read_bl_len = 0;
-    static unsigned int c_size = 0;
-    static unsigned int c_size_mult = 0;
-    static unsigned long mult = 0;
-
+    unsigned int  i, dummy;
+    unsigned int  c_size = 0;
+    unsigned long c_mult = 0;
     unsigned char carddata[512];
     unsigned char *dataptr;
-    tSDCardInfo *card = &card_info[0]; /* Init onboard flash only */
-
-/* Initialise card data as blank */
-    card->initialized = false;
-    card->ocr = 0;
-    card->csd[0] = 0;
-    card->csd[1] = 0;
-    card->csd[2] = 0;
-    card->cid[0] = 0;
-    card->cid[1] = 0;
-    card->cid[2] = 0;
-    card->rca = 0;
-
-    card->capacity = 0;
-    card->numblocks = 0;
-    card->block_size = 0;
-    card->block_exp = 0;
 
 /* Enable and initialise controller */
-    GPIOG_ENABLE |= (0x3 << 5);
-    GPIOG_OUTPUT_EN |= (0x3 << 5);
-    GPIOG_OUTPUT_VAL |= (0x3 << 5);
-    outl(inl(0x70000088) & ~(0x4), 0x70000088);
-    outl(inl(0x7000008c) & ~(0x4), 0x7000008c);
-    outl(inl(0x70000080) | 0x4, 0x70000080);
-    outl(inl(0x70000084) | 0x4, 0x70000084);
     REG_1 = 6;
-    outl(inl(0x70000014) & ~(0x3ffff), 0x70000014);
-    outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014);
-    outl(0x1010, 0x70000034);
 
-    GPIOA_ENABLE |= (1 << 7);
-    GPIOA_OUTPUT_EN &= ~(1 << 7);
-    GPIOD_ENABLE |= (0x1f);
-    GPIOD_OUTPUT_EN |= (0x1f);
-    GPIOD_OUTPUT_VAL |= (0x1f);
+    currcard = &card_info[card_no];
+
+/* Initialise card data as blank */
+    memset(currcard, 0, sizeof(*currcard));
+
+    if (card_no == 0)
+    {
+        outl(inl(0x70000080) | 0x4, 0x70000080);
+
+        GPIOA_ENABLE     &= ~0x7a;
+        GPIOA_OUTPUT_EN  &= ~0x7a;
+        GPIOD_ENABLE     |=  0x1f;
+        GPIOD_OUTPUT_VAL |=  0x1f;
+        GPIOD_OUTPUT_EN  |=  0x1f;
+
+        outl((inl(0x70000014) & ~(0x3ffff)) | 0x255aa, 0x70000014);
+    }
+    else
+    {
+        outl(inl(0x70000080) & ~0x4, 0x70000080);
+
+        GPIOD_ENABLE     &= ~0x1f;
+        GPIOD_OUTPUT_EN  &= ~0x1f;
+        GPIOA_ENABLE     |=  0x7a;
+        GPIOA_OUTPUT_VAL |=  0x7a;
+        GPIOA_OUTPUT_EN  |=  0x7a;
+
+        outl(inl(0x70000014) & ~(0x3ffff), 0x70000014);
+    }
+
+    /* Init NAND */
+    REG_11 |=  (1 << 15);
+    REG_12 |=  (1 << 15);
+    REG_12 &= ~(3 << 12);
+    REG_12 |=  (1 << 13);
+    REG_11 &= ~(3 << 12);
+    REG_11 |=  (1 << 13);
+
     DEV_EN |= DEV_ATA; /* Enable controller */
     DEV_RS |= DEV_ATA; /* Reset controller */
     DEV_RS &=~DEV_ATA; /* Clear Reset */
 
-/* Init NAND */
-    REG_11 |= (1 << 15);
-    REG_12 |= (1 << 15);
-    REG_12 &= ~(3 << 12);
-    REG_12 |= (1 << 13);
-    REG_11 &= ~(3 << 12);
-    REG_11 |= (1 << 13);
-
     SD_STATE_REG = TRAN;
+
     REG_5 = 0xf;
-
-    sd_send_command(GO_IDLE_STATE, 0, 256);
-    while ((card->ocr & (1 << 31)) == 0) /* Loop until the card is powered up */
+    sd_command(GO_IDLE_STATE, 0, &dummy, 256);
+    check_time[EC_POWER_UP] = USEC_TIMER;
+    while ((currcard->ocr & (1 << 31)) == 0) /* until card is powered up */
     {
-        sd_send_acommand(SD_APP_OP_COND, 0x100000, 3);
-        sd_read_response(&(card->ocr), 3);
-
-        if (card->ocr == 0)
-        {
-            /* TODO: Handle failure */
-            while (1) {};
-        }
+        sd_command(APP_CMD, currcard->rca, &dummy, 1);
+        sd_command(SD_APP_OP_COND, 0x100000, &currcard->ocr, 3);
+        sd_check_timeout(5000000, EC_POWER_UP);
     }
 
-    sd_send_command(ALL_SEND_CID, 0, 2);
-    sd_read_response(card->cid, 2);
-    sd_send_command(SEND_RELATIVE_ADDR, 0, 1);
-    sd_read_response(&card->rca, 1);
-    card->rca >>= 16; /* The Relative Card Address is the top 16 bits of the
-                        32 bits returned.  Whenever it is used, it gets
-                        shifted left by 16 bits, so this step could possibly
-                        be skipped. */
+    sd_command(ALL_SEND_CID,         0, currcard->cid, 2);
+    sd_command(SEND_RELATIVE_ADDR,  0, &currcard->rca, 1);
+    sd_command(SEND_CSD, currcard->rca, currcard->csd, 2);
 
-    sd_send_command(SEND_CSD, card->rca << 16, 2);
-    sd_read_response(card->csd, 2);
-
-    /* Parse disk geometry */
     /* These calculations come from the Sandisk SD card product manual */
-    read_bl_len = ((card->csd[2] >> 16) & 0xf);
-    c_size = ((card->csd[2] & (0x3ff)) << 2) +
-        ((card->csd[1] & (0xc0000000)) >> 30);
-    c_size_mult = ((card->csd[1] >> 15) & 0x7);
-    mult = (1<<(c_size_mult + 2));
-    card->max_read_bl_len = (1<<read_bl_len);
-    card->block_size = BLOCK_SIZE;     /* Always use 512 byte blocks */
-    card->numblocks = (c_size + 1) * mult * (card->max_read_bl_len / 512);
-    card->capacity = card->numblocks * card->block_size;
+    c_size = ((currcard->csd[2] & 0x3ff) << 2) + (currcard->csd[1] >> 30) + 1;
+    c_mult = 4 << ((currcard->csd[1] >> 15) & 7);
+    currcard->max_read_bl_len = 1 << ((currcard->csd[2] >> 16) & 15);
+    currcard->block_size = BLOCK_SIZE;     /* Always use 512 byte blocks */
+    currcard->numblocks = c_size * c_mult * (currcard->max_read_bl_len / 512);
+    currcard->capacity = currcard->numblocks * currcard->block_size;
 
     REG_1 = 0;
-    sd_send_command(SELECT_CARD, card->rca << 16, 129);
-    sd_read_response(&dummy, 1); /* I don't think we use the result from this */
-    sd_send_acommand(SET_BUS_WIDTH, (card->rca << 16) | 2, 1);
-    sd_read_response(&dummy, 1); /* 4 bit wide bus */
-    sd_send_command(SET_BLOCKLEN, card->block_size, 1);
-    sd_read_response(&dummy, 1);
-    BLOCK_SIZE_REG = card->block_size;
+
+    sd_command(SELECT_CARD,   currcard->rca       , &dummy, 129);
+    sd_command(APP_CMD,       currcard->rca       , &dummy, 1);
+    sd_command(SET_BUS_WIDTH, currcard->rca | 2   , &dummy, 1); /* 4 bit */
+    sd_command(SET_BLOCKLEN,  currcard->block_size, &dummy, 1);
+    BLOCK_SIZE_REG = currcard->block_size;
 
     /* If this card is > 4Gb, then we need to enable bank switching */
-    if(card->numblocks >= BLOCKS_PER_BANK)
+    if(currcard->numblocks >= BLOCKS_PER_BANK)
     {
         SD_STATE_REG = TRAN;
         BLOCK_COUNT_REG = 1;
-        sd_send_command(SWITCH_FUNC, 0x80ffffef, 0x1c05);
-        sd_read_response(&dummy, 1);
+        sd_command(SWITCH_FUNC, 0x80ffffef, &dummy, 0x1c05);
         /* Read 512 bytes from the card.
         The first 512 bits contain the status information
         TODO: Do something useful with this! */
@@ -436,206 +550,216 @@
         for (i = 0; i < BLOCK_SIZE / 2; i += FIFO_SIZE)
         {
             /* Wait for the FIFO to be full */
-            while((STATUS_REG & FIFO_FULL) == 0) {}
-
-            copy_read_sectors(dataptr, FIFO_SIZE);
-
-            dataptr += (FIFO_SIZE*2); /* Advance one chunk of 16 words */
+            sd_poll_status(FIFO_FULL, 100000,
+                           EC_FIFO_ENA_BANK_EMPTY | DO_PANIC);
+            copy_read_sectors_slow(&dataptr);
         }
     }
-    spinlock_init(&sd_mtx);
+
+    currcard->initialized = true;
 }
 
 /* API Functions */
 
 void ata_led(bool onoff)
 {
-    (void)onoff;
+    led(onoff);
 }
 
-int ata_read_sectors(IF_MV2(int drive,)
-                     unsigned long start,
-                     int incount,
+int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount,
                      void* inbuf)
 {
-    int ret = 0;
-    long timeout;
-    int count;
-    void* buf;
-    long spinup_start;
+    int  ret = 0;
+    unsigned char *buf, *buf_end;
     unsigned int dummy;
-    unsigned int response;
-    unsigned int i;
-    tSDCardInfo *card = &card_info[current_card];
-
+    int bank;
+    
     /* TODO: Add DMA support. */
 
-#ifdef HAVE_MULTIVOLUME
-    (void)drive; /* unused for now */
-#endif
     spinlock_lock(&sd_mtx);
 
-    last_disk_activity = current_tick;
-    spinup_start = current_tick;
-
-    ata_enable(true);
     ata_led(true);
 
-    timeout = current_tick + READ_TIMEOUT;
-
-    /* TODO: Select device */
-    if(current_card == 0)
+    if (drive != 0 && (GPIOA_INPUT_VAL & 0x80) != 0)
     {
-        if(start >= BLOCKS_PER_BANK)
-        {
-            sd_select_bank(1);
-            start -= BLOCKS_PER_BANK;
-        } else {
-            sd_select_bank(0);
-        }
+        /* no external sd-card inserted */
+        ret = -9;
+        goto ata_read_error;
     }
 
-    buf = inbuf;
-    count = incount;
-    while (TIME_BEFORE(current_tick, timeout)) {
-        ret = 0;
-        last_disk_activity = current_tick;
+    if (&card_info[drive] != currcard || !card_info[drive].initialized)
+        sd_init_device(drive);
 
-        SD_STATE_REG = TRAN;
-        BLOCK_COUNT_REG = count;
-        sd_send_command(READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, 0x1c25);
-        sd_read_response(&dummy, 1);
-        /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */
+    last_disk_activity = current_tick;
 
-        for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE)
-        {
-            /* Wait for the FIFO to be full */
-            while((STATUS_REG & FIFO_FULL) == 0) {}
+    bank = start / BLOCKS_PER_BANK;
 
-            copy_read_sectors(buf, FIFO_SIZE);
+    if (currcard->current_bank != bank)
+        sd_select_bank(bank);
 
-            buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */
+    start -= bank * BLOCKS_PER_BANK;
 
-            /* TODO: Switch bank if necessary */
+    sd_wait_for_state(TRAN, EC_TRAN_READ_ENTRY);
+    BLOCK_COUNT_REG = incount;
+    sd_command(READ_MULTIPLE_BLOCK, start * BLOCK_SIZE, &dummy, 0x1c25);
+    /* TODO: Don't assume BLOCK_SIZE == SECTOR_SIZE */
 
-            last_disk_activity = current_tick;
-        }
-        udelay(75);
-        sd_send_command(STOP_TRANSMISSION, 0, 1);
-        sd_read_response(&dummy, 1);
+    buf_end = (unsigned char *)inbuf + incount * currcard->block_size;
+    for (buf = inbuf; buf < buf_end;)
+    {
+        /* Wait for the FIFO to be full */
+        sd_poll_status(FIFO_FULL, 0x80000, EC_FIFO_READ_FULL | DO_PANIC);
+        copy_read_sectors_fast(&buf); /* Copy one chunk of 16 words */
 
-        response = 0;
-        sd_wait_for_state(card, TRAN);
-        break;
+        /* TODO: Switch bank if necessary */
     }
+
+    last_disk_activity = current_tick;
+#if 0
+    udelay(75);
+#endif
+    sd_command(STOP_TRANSMISSION, 0, &dummy, 1);
+    sd_wait_for_state(TRAN, EC_TRAN_READ_EXIT);
+
+ata_read_error:
     ata_led(false);
-    ata_enable(false);
 
     spinlock_unlock(&sd_mtx);
 
     return ret;
 }
 
-
-int ata_write_sectors(IF_MV2(int drive,)
-                      unsigned long start,
-                      int count,
-                      const void* buf)
+int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count,
+                      const void* outbuf)
 {
 /* Write support is not finished yet */
-/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks 
+/* TODO: The standard suggests using ACMD23 prior to writing multiple blocks
    to improve performance */
     unsigned int response;
-    void const* write_buf;
+    void const* buf, *buf_end;
     int ret = 0;
-    unsigned int i;
-    long timeout;
-    tSDCardInfo *card = &card_info[current_card];
+    int bank;
 
     spinlock_lock(&sd_mtx);
-    ata_enable(true);
+
     ata_led(true);
-    if(current_card == 0)
+
+    if (drive != 0 && (GPIOA_INPUT_VAL & 0x80) != 0)
     {
-        if(start < BLOCKS_PER_BANK)
-        {
-            sd_select_bank(0);
-        } else {
-            sd_select_bank(1);
-            start -= BLOCKS_PER_BANK;
-        }
+        /* no external sd-card inserted */
+        ret = -9;
+        goto error;
     }
 
-retry:
-    sd_wait_for_state(card, TRAN);
+    if (&card_info[drive] != currcard || !card_info[drive].initialized)
+        sd_init_device(drive);
+
+    bank = start / BLOCKS_PER_BANK;
+
+    if (currcard->current_bank != bank)
+        sd_select_bank(bank);
+
+    start -= bank * BLOCKS_PER_BANK;
+
+    check_time[EC_WRITE_TIMEOUT] = USEC_TIMER;
+    sd_wait_for_state(TRAN, EC_TRAN_WRITE_ENTRY);
     BLOCK_COUNT_REG = count;
-    sd_send_command(WRITE_MULTIPLE_BLOCK, start * SECTOR_SIZE, 0x1c2d);
-    sd_read_response(&response, 1);
-    write_buf = buf;
-    for (i = 0; i < count * card->block_size / 2; i += FIFO_SIZE)
+    sd_command(WRITE_MULTIPLE_BLOCK, start * SECTOR_SIZE, &response, 0x1c2d);
+
+    buf_end = outbuf + count * currcard->block_size;
+    for (buf = outbuf; buf < buf_end; buf += 2 * FIFO_SIZE)
     {
-        if(i >= (count * card->block_size / 2)-FIFO_SIZE)
+        if (buf >= buf_end - 2 * FIFO_SIZE)
         {
             /* Set SD_STATE_REG to PRG for the last buffer fill */
             SD_STATE_REG = PRG;
         }
 
-        /* Wait for the FIFO to be empty */
-        while((STATUS_REG & FIFO_EMPTY) == 0) {}
-        /* Perhaps we could use bit 8 of card status (READY_FOR_DATA)? */
+        udelay(2); /* needed here (loop is too fast :-) */
 
-        copy_write_sectors(write_buf, FIFO_SIZE);
+        /* Wait for the FIFO to empty */
+        sd_poll_status(FIFO_EMPTY, 0x80000, EC_FIFO_WR_EMPTY | DO_PANIC);
 
-        write_buf += FIFO_SIZE*2; /* Advance one chunk of 16 words */
+        copy_write_sectors(buf); /* Copy one chunk of 16 words */
+
         /* TODO: Switch bank if necessary */
-
-        last_disk_activity = current_tick;
     }
 
-    timeout = current_tick + WRITE_TIMEOUT;
+    last_disk_activity = current_tick;
 
-    while((STATUS_REG & DATA_DONE) == 0) {
-        if(current_tick >= timeout)
-        {
-            sd_send_command(STOP_TRANSMISSION, 0, 1);
-            sd_read_response(&response, 1);
-            goto retry;
-        }
-    }
-    sd_send_command(STOP_TRANSMISSION, 0, 1);
-    sd_read_response(&response, 1);
+    sd_poll_status(DATA_DONE, 0x80000, EC_FIFO_WR_DONE | DO_PANIC);
+    sd_check_timeout(0x80000, EC_WRITE_TIMEOUT);
 
-    sd_wait_for_state(card, TRAN);
+    sd_command(STOP_TRANSMISSION, 0, &response, 1);
+    sd_wait_for_state(TRAN, EC_TRAN_WRITE_EXIT);
+
+  error:
     ata_led(false);
-    ata_enable(false);
     spinlock_unlock(&sd_mtx);
 
     return ret;
 }
 
+static void sd_thread(void) __attribute__((noreturn));
 static void sd_thread(void)
 {
     struct event ev;
     bool idle_notified = false;
     
-    while (1) {
+    while (1)
+    {
         queue_wait_w_tmo(&sd_queue, &ev, HZ);
+
         switch ( ev.id ) 
         {
-            default:
-                if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
-                {
-                    idle_notified = false;
-                }
-                else
-                {
-                    if (!idle_notified)
-                    {
-                        call_ata_idle_notifys(false);
-                        idle_notified = true;
-                    }
-                }
-                break;
+        case SD_HOTSWAP:
+        {
+            int status = 0;
+            enum { SD_UNMOUNTED = 0x1, SD_MOUNTED = 0x2 };
+
+            /* Delay on insert and remove to prevent reading state if it is
+               just bouncing back and forth while card is sliding - delay on
+               insert is also required for the card to stabilize and accept
+               commands */
+            sleep(HZ/10);
+
+            /* Lock to keep us from messing with this variable while an init
+               may be in progress */
+            spinlock_lock(&sd_mtx);
+            card_info[1].initialized = false;
+            spinlock_unlock(&sd_mtx);
+
+            /* Either unmount because the card was pulled or unmount and
+               remount if already mounted since multiple messages may be
+               generated for the same event - like someone inserting a new
+               card before anything detects the old one pulled :) */
+            if (disk_unmount(1) != 0)  /* release "by force" */
+                status |= SD_UNMOUNTED;
+
+            if (card_detect_target() && disk_mount(1) != 0) /* mount SD-CARD */
+                status |= SD_MOUNTED;
+
+            if (status & SD_UNMOUNTED)
+                queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0);
+
+            if (status & SD_MOUNTED)
+                queue_broadcast(SYS_HOTSWAP_INSERTED, 0);
+
+            if (status)
+                queue_broadcast(SYS_FS_CHANGED, 0);
+            break;
+            } /* SD_HOTSWAP */
+        case SYS_TIMEOUT:
+            if (TIME_BEFORE(current_tick, last_disk_activity+(3*HZ)))
+            {
+                idle_notified = false;
+            }
+            else if (!idle_notified)
+            {
+                call_ata_idle_notifys(false);
+                idle_notified = true;
+            }
+            break;
         }
     }
 }
@@ -648,7 +772,7 @@
 
 bool ata_disk_is_active(void)
 {
-  return 0;
+    return 0;
 }
 
 void ata_sleep(void)
@@ -662,12 +786,12 @@
 /* Hardware reset protocol as specified in chapter 9.1, ATA spec draft v5 */
 int ata_hard_reset(void)
 {
-  return 0;
+    return 0;
 }
 
 int ata_soft_reset(void)
 {
-  return 0;
+    return 0;
 }
 
 void ata_enable(bool on)
@@ -682,22 +806,113 @@
     }
 }
 
-unsigned short* ata_get_identify(void)
-{
-    return identify_info;
-}
-
 int ata_init(void)
 {
-    sd_init_device();
-    if ( !initialized ) 
+    ata_led(false);
+
+    /* NOTE: This init isn't dual core safe */
+    if (!initialized)
     {
-        queue_init(&sd_queue, true);
-        create_thread(sd_thread, sd_stack,
-                      sizeof(sd_stack), sd_thread_name IF_PRIO(, PRIORITY_SYSTEM)
-		      IF_COP(, CPU, false));
         initialized = true;
+
+        spinlock_init(&sd_mtx);
+
+        spinlock_lock(&sd_mtx);
+
+        /* init controller */
+        outl(inl(0x70000088) & ~(0x4), 0x70000088);
+        outl(inl(0x7000008c) & ~(0x4), 0x7000008c);
+        outl(inl(0x70000084) | 0x4, 0x70000084);
+        outl(0x1010, 0x70000034);
+
+        GPIOG_ENABLE     |= (0x3 << 5);
+        GPIOG_OUTPUT_EN  |= (0x3 << 5);
+        GPIOG_OUTPUT_VAL |= (0x3 << 5);
+
+        /* enable card detection port - mask interrupt first */
+        GPIOA_INT_EN     &= ~0x80;
+
+        GPIOA_OUTPUT_EN  &= ~0x80;
+        GPIOA_ENABLE     |=  0x80;
+
+        sd_init_device(0);
+
+        queue_init(&sd_queue, true);
+        create_thread(sd_thread, sd_stack, sizeof(sd_stack),
+            sd_thread_name IF_PRIO(, PRIORITY_SYSTEM) IF_COP(, CPU, false));
+
+        /* enable interupt for the mSD card */
+        sleep(HZ/10);
+
+        CPU_INT_EN = HI_MASK;
+        CPU_HI_INT_EN = GPIO0_MASK;
+
+        sd1_status = GPIOA_INPUT_VAL & 0x80;
+        GPIOA_INT_LEV = (GPIOA_INT_LEV & ~0x80) | (sd1_status ^ 0x80);
+
+        GPIOA_INT_CLR = 0x80;
+        GPIOA_INT_EN |= 0x80;
+
+        spinlock_unlock(&sd_mtx);
     }
 
     return 0;
 }
+
+/* move the sd-card info to mmc struct */
+tCardInfo *card_get_info_target(int card_no)
+{
+    int i, temp;
+    static tCardInfo card;
+    static const char mantissa[] = {  /* *10 */
+        0,  10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 };
+    static const int exponent[] = {  /* use varies */
+      1,10,100,1000,10000,100000,1000000,10000000,100000000,1000000000 };
+
+    card.initialized  = card_info[card_no].initialized;
+    card.ocr          = card_info[card_no].ocr;
+    for(i=0; i<4; i++)  card.csd[i] = card_info[card_no].csd[3-i];
+    for(i=0; i<4; i++)  card.cid[i] = card_info[card_no].cid[3-i];
+    card.numblocks    = card_info[card_no].numblocks;
+    card.blocksize    = card_info[card_no].block_size;
+    card.size         = card_info[card_no].capacity < 0xffffffff ?
+                        card_info[card_no].capacity : 0xffffffff;
+    card.block_exp    = card_info[card_no].block_exp;
+    temp              = card_extract_bits(card.csd, 29, 3);
+    card.speed        = mantissa[card_extract_bits(card.csd, 25, 4)]
+                      * exponent[temp > 2 ? 7 : temp + 4];
+    card.nsac         = 100 * card_extract_bits(card.csd, 16, 8);
+    temp              = card_extract_bits(card.csd, 13, 3);
+    card.tsac         = mantissa[card_extract_bits(card.csd, 9, 4)]
+                      * exponent[temp] / 10;
+    card.cid[0]       = htobe32(card.cid[0]); /* ascii chars here */
+    card.cid[1]       = htobe32(card.cid[1]); /* ascii chars here */
+    temp = *((char*)card.cid+13); /* adjust year<=>month, 1997 <=> 2000 */
+    *((char*)card.cid+13) = (unsigned char)((temp >> 4) | (temp << 4)) + 3;
+
+    return &card;
+}
+
+bool card_detect_target(void)
+{
+    /* 0x00:inserted, 0x80:not inserted */
+    return (GPIOA_INPUT_VAL & 0x80) == 0;
+}
+
+/* called on insertion/removal interrupt */
+void microsd_int(void)
+{
+    int status = GPIOA_INPUT_VAL & 0x80;
+    
+    GPIOA_INT_LEV = (GPIOA_INT_LEV & ~0x80) | (status ^ 0x80);
+    GPIOA_INT_CLR = 0x80;
+
+    if (status == sd1_status)
+        return;
+
+    sd1_status = status;
+
+    /* Take final state only - insert/remove is bouncy */
+    queue_remove_from_head(&sd_queue, SD_HOTSWAP);
+    queue_post(&sd_queue, SD_HOTSWAP, status);
+}
diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-target.h b/firmware/target/arm/sandisk/sansa-e200/ata-target.h
index dfdd3fe..61884e0 100644
--- a/firmware/target/arm/sandisk/sansa-e200/ata-target.h
+++ b/firmware/target/arm/sandisk/sansa-e200/ata-target.h
@@ -19,25 +19,4 @@
 #ifndef ATA_TARGET_H
 #define ATA_TARGET_H
 
-#include "inttypes.h"
-
-typedef struct
-{
-    bool initialized;
-
-    unsigned int ocr;            /* OCR register */
-    unsigned int csd[4];         /* CSD register */
-    unsigned int cid[4];         /* CID register */
-    unsigned int rca;
-
-    uint64_t capacity;           /* size in bytes */
-    unsigned long numblocks;     /* size in flash blocks */
-    unsigned int block_size;     /* block size in bytes */
-    unsigned int max_read_bl_len;/* max read data block length */
-    unsigned int block_exp;      /* block size exponent */
-} tSDCardInfo;
-
-tSDCardInfo *sd_card_info(int card_no);
-bool sd_touched(void);
-
 #endif
diff --git a/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h b/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h
new file mode 100644
index 0000000..95cb26d
--- /dev/null
+++ b/firmware/target/arm/sandisk/sansa-e200/hotswap-target.h
@@ -0,0 +1,45 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2007 by Antonius Hellmann
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#ifndef HOTSWAP_TARGET_H
+#define HOTSWAP_TARGET_H
+
+#include "inttypes.h"
+#include "hotswap.h"
+
+typedef struct
+{
+    bool initialized;
+
+    unsigned int ocr;            /* OCR register */
+    unsigned int csd[4];         /* CSD register */
+    unsigned int cid[4];         /* CID register */
+    unsigned int rca;
+
+    uint64_t capacity;           /* size in bytes */
+    unsigned long numblocks;     /* size in flash blocks */
+    unsigned int block_size;     /* block size in bytes */
+    unsigned int max_read_bl_len;/* max read data block length */
+    unsigned int block_exp;      /* block size exponent */
+    unsigned char current_bank;  /* The bank that we are working with */
+} tSDCardInfo;
+
+tCardInfo *card_get_info_target(int card_no);
+bool       card_detect_target(void);
+
+#endif
diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c
index ef0c62d..49ffdd3 100644
--- a/firmware/target/arm/system-pp502x.c
+++ b/firmware/target/arm/system-pp502x.c
@@ -56,8 +56,11 @@
 /* TODO: this should really be in the target tree, but moving it there caused
    crt0.S not to find it while linking */
 /* TODO: Even if it isn't in the target tree, this should be the default case */
+#ifdef SANSA_E200
 extern void button_int(void);
 extern void clickwheel_int(void);
+extern void microsd_int(void);
+#endif
 
 void irq(void)
 {
@@ -65,11 +68,15 @@
         if (CPU_INT_STAT & TIMER1_MASK) {
             TIMER1();
         }
-        else if (CPU_INT_STAT & TIMER2_MASK)
+        else if (CPU_INT_STAT & TIMER2_MASK) {
             TIMER2();
+        }
 #ifdef SANSA_E200
-        else if (CPU_HI_INT_STAT & GPIO1_MASK)
-        {
+        else if (CPU_HI_INT_STAT & GPIO0_MASK) {
+            if (GPIOA_INT_STAT & 0x80)
+                microsd_int();
+        }
+        else if (CPU_HI_INT_STAT & GPIO1_MASK) {
             if (GPIOF_INT_STAT & 0xff)
                 button_int();
             if (GPIOH_INT_STAT & 0xc0)
diff --git a/firmware/usb.c b/firmware/usb.c
index 217aaf8..9e9cb77 100644
--- a/firmware/usb.c
+++ b/firmware/usb.c
@@ -255,8 +255,8 @@
                 break;
 
 #ifdef HAVE_MMC
-            case SYS_MMC_INSERTED:
-            case SYS_MMC_EXTRACTED:
+            case SYS_HOTSWAP_INSERTED:
+            case SYS_HOTSWAP_EXTRACTED:
                 if(usb_state == USB_INSERTED)
                 {
                     usb_enable(false);