diff mbox

[26/48] SD overhaul

Message ID 1b6fa1dfd5acbe8abf31f23ec4eb13cd3393a4ec.1269617186.git.riku.voipio@nokia.com
State New
Headers show

Commit Message

Riku Voipio March 26, 2010, 4:06 p.m. UTC
From: Juha Riihimäki <juha.riihimaki@nokia.com>

- reset support
- add high capacity mmc support
- sd: improve mmc emulation
- make sd emulation work as mmc emulation as well
- handle SD CMD5 without error messages
- remove couple of unnecessary error messages
- Fix block count for OMAP3 MMC emulation

Signed-Off-By: Riku Voipio <riku.voipio@nokia.com>
Signed-Off-By: Juha Riihimäki <juha.riihimaki@nokia.com>

---
 hw/sd.c |  390 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 334 insertions(+), 56 deletions(-)
diff mbox

Patch

diff --git a/hw/sd.c b/hw/sd.c
index cc2839d..3a395e7 100644
--- a/hw/sd.c
+++ b/hw/sd.c
@@ -4,6 +4,7 @@ 
  *
  * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
  * Copyright (c) 2007 CodeSourcery
+ * Copyright (c) 2009 Nokia Corporation
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -75,6 +76,7 @@  struct SDState {
     uint8_t scr[8];
     uint8_t cid[16];
     uint8_t csd[16];
+    uint8_t ext_csd[512];
     uint16_t rca;
     uint32_t card_status;
     uint8_t sd_status[64];
@@ -89,7 +91,7 @@  struct SDState {
     int pwd_len;
     int function_group[6];
 
-    int spi;
+    int spi, mmc;
     int current_cmd;
     int blk_written;
     uint64_t data_start;
@@ -101,6 +103,7 @@  struct SDState {
     uint8_t *buf;
 
     int enable;
+    int buswidth, highspeed;
 };
 
 static void sd_set_status(SDState *sd)
@@ -131,9 +134,9 @@  static void sd_set_status(SDState *sd)
 }
 
 static const sd_cmd_type_t sd_cmd_type[64] = {
-    sd_bc,   sd_none, sd_bcr,  sd_bcr,  sd_none, sd_none, sd_none, sd_ac,
+    sd_bc,   sd_none, sd_bcr,  sd_bcr,  sd_none, sd_ac,   sd_none, sd_ac,
     sd_bcr,  sd_ac,   sd_ac,   sd_adtc, sd_ac,   sd_ac,   sd_none, sd_ac,
-    sd_ac,   sd_adtc, sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_ac,   sd_adtc, sd_adtc, sd_none, sd_adtc, sd_none, sd_none, sd_adtc,
     sd_adtc, sd_adtc, sd_adtc, sd_adtc, sd_ac,   sd_ac,   sd_adtc, sd_none,
     sd_ac,   sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
     sd_none, sd_none, sd_bc,   sd_none, sd_none, sd_none, sd_none, sd_none,
@@ -200,8 +203,8 @@  static void sd_set_ocr(SDState *sd)
 
 static void sd_set_scr(SDState *sd)
 {
-    sd->scr[0] = 0x00;		/* SCR Structure */
-    sd->scr[1] = 0x2f;		/* SD Security Support */
+    sd->scr[0] = 0x00; /* SCR v1.0, SD spec v1.0/1.01 */
+    sd->scr[1] = 0x25; /* erase=0, SD security v1.01, 1bit/4bit bus width */
     sd->scr[2] = 0x00;
     sd->scr[3] = 0x00;
     sd->scr[4] = 0x00;
@@ -212,7 +215,7 @@  static void sd_set_scr(SDState *sd)
 
 #define MID	0xaa
 #define OID	"XY"
-#define PNM	"QEMU!"
+#define PNM	"QEMU!!"
 #define PRV	0x01
 #define MDT_YR	2006
 #define MDT_MON	2
@@ -227,14 +230,23 @@  static void sd_set_cid(SDState *sd)
     sd->cid[5] = PNM[2];
     sd->cid[6] = PNM[3];
     sd->cid[7] = PNM[4];
-    sd->cid[8] = PRV;		/* Fake product revision (PRV) */
+    if (sd->mmc) {
+        sd->cid[8] = PNM[5];
+    } else {
+        sd->cid[8] = PRV;		/* Fake product revision (PRV) */
+    }
     sd->cid[9] = 0xde;		/* Fake serial number (PSN) */
     sd->cid[10] = 0xad;
     sd->cid[11] = 0xbe;
     sd->cid[12] = 0xef;
-    sd->cid[13] = 0x00 |	/* Manufacture date (MDT) */
-        ((MDT_YR - 2000) / 10);
-    sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
+    if (sd->mmc) {
+        sd->cid[13] = 0x55;
+        sd->cid[14] = ((MDT_MON) << 4) | (MDT_YR - 1997);
+    } else {
+        sd->cid[13] = 0x00 |	/* Manufacture date (MDT) */
+            ((MDT_YR - 2000) / 10);
+        sd->cid[14] = ((MDT_YR % 10) << 4) | MDT_MON;
+    }
     sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
 }
 
@@ -256,7 +268,12 @@  static void sd_set_csd(SDState *sd, uint64_t size)
     uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
 
     if (size <= 0x40000000) {	/* Standard Capacity SD */
-        sd->csd[0] = 0x00;	/* CSD structure */
+        if (sd->mmc) {
+            sd->csd[0] = 0x80 | /* CSD structure: v1.2 */
+                         0x0c;  /* MMC v3.x */
+        } else {
+            sd->csd[0] = 0x00;  /* CSD structure: v0 */
+        }
         sd->csd[1] = 0x26;	/* Data read access-time-1 */
         sd->csd[2] = 0x00;	/* Data read access-time-2 */
         sd->csd[3] = 0x5a;	/* Max. data transfer rate */
@@ -282,25 +299,52 @@  static void sd_set_csd(SDState *sd, uint64_t size)
         sd->csd[14] = 0x00;	/* File format group */
         sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
     } else {			/* SDHC */
-        size /= 512 * 1024;
-        size -= 1;
-        sd->csd[0] = 0x40;
+        if (sd->mmc) {
+            sd->csd[0] = 0x90; /* CSD structure v1.2, MMC v4.0/4.1 */
+        } else {
+            sd->csd[0] = 0x40; /* CSD structure v1 */
+        }
         sd->csd[1] = 0x0e;
         sd->csd[2] = 0x00;
         sd->csd[3] = 0x32;
         sd->csd[4] = 0x5b;
         sd->csd[5] = 0x59;
-        sd->csd[6] = 0x00;
-        sd->csd[7] = (size >> 16) & 0xff;
-        sd->csd[8] = (size >> 8) & 0xff;
-        sd->csd[9] = (size & 0xff);
-        sd->csd[10] = 0x7f;
+        if (sd->mmc) {
+            sd->csd[6] = 0x03;
+            sd->csd[7] = 0xff;
+            sd->csd[8] = 0xff;
+            sd->csd[9] = 0xff;
+            sd->csd[10] = 0xff;
+        } else {
+            size /= 512 * 1024;
+            size -= 1;
+            sd->csd[6] = 0x00;
+            sd->csd[7] = (size >> 16) & 0xff;
+            sd->csd[8] = (size >> 8) & 0xff;
+            sd->csd[9] = (size & 0xff);
+            sd->csd[10] = 0x7f;
+        }
         sd->csd[11] = 0x80;
         sd->csd[12] = 0x0a;
         sd->csd[13] = 0x40;
         sd->csd[14] = 0x00;
         sd->csd[15] = 0x00;
-        sd->ocr |= 1 << 30;	/* High Capacity SD Memort Card */
+        sd->ocr |= 1 << 30;	/* High Capacity SD Memory Card */
+        if (sd->mmc) {
+            size /= 512;
+            sd->buswidth = 1; /* 4bit mode */
+            sd->highspeed = 0;
+            memset(sd->ext_csd, 0, 512);
+            sd->ext_csd[183] = sd->buswidth;
+            sd->ext_csd[185] = sd->highspeed;
+            sd->ext_csd[192] = 0x03; /* EXT_CSD v3 */
+            sd->ext_csd[196] = 0x03; /* supports 26MHz and 52MHz */
+            sd->ext_csd[212] = (size & 0xff);
+            sd->ext_csd[213] = (size >> 8) & 0xff;
+            sd->ext_csd[214] = (size >> 16) & 0xff;
+            sd->ext_csd[215] = (size >> 24) & 0xff;
+            sd->ext_csd[217] = 0x00; /* sleep/awake timeout */
+        }
     }
 }
 
@@ -382,13 +426,13 @@  static void sd_response_r7_make(SDState *sd, uint8_t *response)
     response[3] = (sd->vhs >>  0) & 0xff;
 }
 
-static void sd_reset(SDState *sd, BlockDriverState *bdrv)
+void sd_reset(SDState *sd)
 {
     uint64_t size;
     uint64_t sect;
 
-    if (bdrv) {
-        bdrv_get_geometry(bdrv, &sect);
+    if (sd->bdrv) {
+        bdrv_get_geometry(sd->bdrv, &sect);
     } else {
         sect = 0;
     }
@@ -397,7 +441,7 @@  static void sd_reset(SDState *sd, BlockDriverState *bdrv)
     size = sect + 1;
 
     sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
-
+    
     sd->state = sd_idle_state;
     sd->rca = 0x0000;
     sd_set_ocr(sd);
@@ -407,11 +451,9 @@  static void sd_reset(SDState *sd, BlockDriverState *bdrv)
     sd_set_cardstatus(sd);
     sd_set_sdstatus(sd);
 
-    sd->bdrv = bdrv;
-
     if (sd->wp_groups)
         qemu_free(sd->wp_groups);
-    sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : 0;
+    sd->wp_switch = sd->bdrv ? bdrv_is_read_only(sd->bdrv) : 0;
     sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect);
     memset(sd->function_group, 0, sizeof(int) * 6);
     sd->erase_start = 0;
@@ -426,27 +468,121 @@  static void sd_cardchange(void *opaque)
     SDState *sd = opaque;
     qemu_set_irq(sd->inserted_cb, bdrv_is_inserted(sd->bdrv));
     if (bdrv_is_inserted(sd->bdrv)) {
-        sd_reset(sd, sd->bdrv);
+        sd_reset(sd);
         qemu_set_irq(sd->readonly_cb, sd->wp_switch);
     }
 }
 
+static void sd_save_state(QEMUFile *f, void *opaque)
+{
+    struct SDState *s = (struct SDState *)opaque;
+    int i;
+    uint32_t wpgc = (s->size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
+    char *filename;
+    
+    filename = qemu_mallocz(1024);
+    bdrv_get_backing_filename(s->bdrv, filename, 1024);
+    qemu_put_buffer(f, (uint8_t *)filename, 1024);
+    free(filename);
+    
+    qemu_put_sbe32(f, s->mode);
+    qemu_put_sbe32(f, s->state);
+    qemu_put_be32(f, s->ocr);
+    qemu_put_buffer(f, s->scr, sizeof(s->scr));
+    qemu_put_buffer(f, s->cid, sizeof(s->cid));
+    qemu_put_buffer(f, s->csd, sizeof(s->csd));
+    qemu_put_be16(f, s->rca);
+    qemu_put_be32(f, s->card_status);
+    qemu_put_buffer(f, s->sd_status, sizeof(s->sd_status));
+    qemu_put_be32(f, s->vhs);
+    for (i = 0; i < wpgc; i++)
+        qemu_put_sbe32(f, s->wp_groups[i]);
+    qemu_put_sbe32(f, s->blk_len);
+    qemu_put_be32(f, s->erase_start);
+    qemu_put_be32(f, s->erase_end);
+    qemu_put_buffer(f, s->pwd, sizeof(s->pwd));
+    qemu_put_sbe32(f, s->pwd_len);
+    for (i = 0; i < 6; i++)
+        qemu_put_sbe32(f, s->function_group[i]);
+    qemu_put_sbe32(f, s->current_cmd);
+    qemu_put_sbe32(f, s->blk_written);
+    qemu_put_be32(f, s->data_start);
+    qemu_put_be32(f, s->data_offset);
+    qemu_put_buffer(f, s->data, sizeof(s->data));
+    qemu_put_buffer(f, s->buf, 512);
+    qemu_put_sbe32(f, s->enable);
+}
+
+static int sd_load_state(QEMUFile *f, void *opaque, int version_id)
+{
+    struct SDState *s = (struct SDState *)opaque;
+    int i;
+    uint32_t wpgc = (s->size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
+    char *filename1, *filename2;
+    int result = 0;
+    
+    if (version_id)
+        return -EINVAL;
+    
+    filename1 = qemu_mallocz(1024);
+    filename2 = qemu_mallocz(1024);
+    bdrv_get_backing_filename(s->bdrv, filename1, 1024);
+    qemu_get_buffer(f, (uint8_t *)filename2, 1024);
+    if (!strcmp(filename1, filename2)) {
+        s->mode = qemu_get_sbe32(f);
+        s->state = qemu_get_sbe32(f);
+        s->ocr = qemu_get_be32(f);
+        qemu_get_buffer(f, s->scr, sizeof(s->scr));
+        qemu_get_buffer(f, s->cid, sizeof(s->cid));
+        qemu_get_buffer(f, s->csd, sizeof(s->csd));
+        s->rca = qemu_get_be16(f);
+        s->card_status = qemu_get_be32(f);
+        qemu_get_buffer(f, s->sd_status, sizeof(s->sd_status));
+        s->vhs = qemu_get_be32(f);
+        for (i = 0; i < wpgc; i++)
+            s->wp_groups[i] = qemu_get_sbe32(f);
+        s->blk_len = qemu_get_sbe32(f);
+        s->erase_start = qemu_get_be32(f);
+        s->erase_end = qemu_get_be32(f);
+        qemu_get_buffer(f, s->pwd, sizeof(s->pwd));
+        s->pwd_len = qemu_get_sbe32(f);
+        for (i = 0; i < 6; i++)
+            s->function_group[i] = qemu_get_sbe32(f);
+        s->current_cmd = qemu_get_sbe32(f);
+        s->blk_written = qemu_get_sbe32(f);
+        s->data_start = qemu_get_be32(f);
+        s->data_offset = qemu_get_be32(f);
+        qemu_get_buffer(f, s->data, sizeof(s->data));
+        qemu_get_buffer(f, s->buf, 512);
+        s->enable = qemu_get_sbe32(f);
+    } else 
+        result = -EINVAL;
+    free(filename2);
+    free(filename1);
+    return result;
+}
+
 /* We do not model the chip select pin, so allow the board to select
    whether card should be in SSI or MMC/SD mode.  It is also up to the
    board to ensure that ssi transfers only occur when the chip select
    is asserted.  */
-SDState *sd_init(BlockDriverState *bs, int is_spi)
+SDState *sd_init(BlockDriverState *bs, int is_spi, int is_mmc)
 {
     SDState *sd;
+    static int instance_number = 1;
 
     sd = (SDState *) qemu_mallocz(sizeof(SDState));
     sd->buf = qemu_memalign(512, 512);
     sd->spi = is_spi;
+    sd->mmc = is_mmc;
     sd->enable = 1;
-    sd_reset(sd, bs);
+    sd->bdrv = bs;
+    sd_reset(sd);
     if (sd->bdrv) {
         bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd);
     }
+    register_savevm("sd", instance_number++, 0,
+                    sd_save_state, sd_load_state, sd);
     return sd;
 }
 
@@ -524,6 +660,46 @@  static void sd_function_switch(SDState *sd, uint32_t arg)
     sd->data[66] = crc & 0xff;
 }
 
+static void mmc_function_switch(SDState *sd, uint32_t arg)
+{
+    enum {
+        cmd_set = 0,
+        set_bits,
+        clear_bits,
+        write_byte,
+        
+        unknown
+    } mode = (arg >> 24);
+    if (mode >= unknown) {
+        fprintf(stderr, "%s: unknown mode 0x%02x\n", __FUNCTION__, mode);
+    } else {
+        if (mode == cmd_set) {
+            fprintf(stderr, "%s: command set change not implemented!\n",
+                    __FUNCTION__);
+        } else {
+            uint8_t index = (arg >> 16) & 0xff;
+            /* ignore writes to read-only fields */
+            if (index != 192 && index != 196 &&
+                (index < 212 || index > 215)) {
+                uint8_t value = (arg >> 8) & 0xff;
+                switch (mode) {
+                    case set_bits:
+                        sd->ext_csd[index] |= value;
+                        break;
+                    case clear_bits:
+                        sd->ext_csd[index] &= ~value;
+                        break;
+                    case write_byte:
+                        sd->ext_csd[index] = value;
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+}
+
 static inline int sd_wp_addr(SDState *sd, uint32_t addr)
 {
     return sd->wp_groups[addr >>
@@ -617,15 +793,21 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
 
         default:
             sd->state = sd_idle_state;
-            sd_reset(sd, sd->bdrv);
+            sd_reset(sd);
             return sd->spi ? sd_r1 : sd_r0;
         }
         break;
 
     case 1:	/* CMD1:   SEND_OP_CMD */
+        if (sd->mmc) {
+            if (sd->state == sd_idle_state) {
+                sd->state = sd_ready_state;
+                return sd_r3;
+            }
+            break;
+         }
         if (!sd->spi)
             goto bad_cmd;
-
         sd->state = sd_transfer_state;
         return sd_r1;
 
@@ -647,8 +829,12 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
             goto bad_cmd;
         switch (sd->state) {
         case sd_identification_state:
-        case sd_standby_state:
             sd->state = sd_standby_state;
+        case sd_standby_state:
+            if (sd->mmc) {
+                sd->rca = req.arg >> 16;
+                return sd_r1;
+            }
             sd_set_rca(sd);
             return sd_r6;
 
@@ -662,14 +848,25 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
             goto bad_cmd;
         switch (sd->state) {
         case sd_standby_state:
-            break;
+            return sd_r0;
 
         default:
             break;
         }
         break;
 
-    case 5: /* CMD5: reserved for SDIO cards */
+    case 5: /* CMD5: reserved for SDIO cards / SLEEP_AWAKE (MMC) */
+        if (sd->mmc) {
+            if (sd->rca != rca) {
+                return sd_r0;
+            }
+            if (req.arg & (1 << 15)) {
+                sd->state = sd_transfer_state;
+            } else {
+                sd->state = sd_standby_state;
+            }
+            return sd_r1b;
+        }
         sd->card_status |= ILLEGAL_COMMAND;
         return sd_r0;
 
@@ -678,11 +875,16 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
             goto bad_cmd;
         switch (sd->mode) {
         case sd_data_transfer_mode:
-            sd_function_switch(sd, req.arg);
-            sd->state = sd_sendingdata_state;
-            sd->data_start = 0;
-            sd->data_offset = 0;
-            return sd_r1;
+            if (sd->mmc) {
+                mmc_function_switch(sd, req.arg);
+                return sd_r1b;
+            } else {
+                sd_function_switch(sd, req.arg);
+                sd->state = sd_sendingdata_state;
+                sd->data_start = 0;
+                sd->data_offset = 0;
+                return sd_r1;
+            }
 
         default:
             break;
@@ -727,22 +929,30 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
         }
         break;
 
-    case 8:	/* CMD8:   SEND_IF_COND */
-        /* Physical Layer Specification Version 2.00 command */
-        switch (sd->state) {
-        case sd_idle_state:
-            sd->vhs = 0;
+    case 8:	/* CMD8:   SEND_IF_COND / SEND_EXT_CSD (MMC) */
+        if (sd->mmc) {
+            sd->state = sd_sendingdata_state;
+            memcpy(sd->data, sd->ext_csd, 512);
+            sd->data_start = addr;
+            sd->data_offset = 0;
+            return sd_r1;
+        } else {
+            /* Physical Layer Specification Version 2.00 command */
+            switch (sd->state) {
+            case sd_idle_state:
+                sd->vhs = 0;
 
-            /* No response if not exactly one VHS bit is set.  */
-            if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
-                return sd->spi ? sd_r7 : sd_r0;
+                /* No response if not exactly one VHS bit is set.  */
+                if (!(req.arg >> 8) || (req.arg >> ffs(req.arg & ~0xff)))
+                    return sd->spi ? sd_r7 : sd_r0;
 
-            /* Accept.  */
-            sd->vhs = req.arg;
-            return sd_r7;
+                /* Accept.  */
+                sd->vhs = req.arg;
+                return sd_r7;
 
-        default:
-            break;
+            default:
+                break;
+            }
         }
         break;
 
@@ -901,8 +1111,33 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
             break;
         }
         break;
+            
+    /* Block write commands (Class 3) */
+    case 20: /* CMD20: WRITE_DAT_UNTIL_STOP */
+        if (sd->mmc) {
+            if (sd->state == sd_transfer_state) {
+                sd->state = sd_sendingdata_state;
+                sd->data_start = req.arg;
+                sd->data_offset = 0;
+                
+                if (sd->data_start + sd->blk_len > sd->size) {
+                    sd->card_status |= ADDRESS_ERROR;
+                }
+                return sd_r0;
+            }
+            break;
+        }
+        goto bad_cmd;
 
     /* Block write commands (Class 4) */
+    case 23: /* CMD23: SET_BLOCK_COUNT */
+        if (sd->mmc) {
+            sd->card_status |= ILLEGAL_COMMAND;
+            fprintf(stderr, "%s: CMD23 not implemented\n", __FUNCTION__);
+            return sd_r0;
+        }
+        goto bad_cmd;
+            
     case 24:	/* CMD24:  WRITE_SINGLE_BLOCK */
         if (sd->spi)
             goto unimplemented_cmd;
@@ -1042,6 +1277,11 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
 
     /* Erase commands (Class 5) */
     case 32:	/* CMD32:  ERASE_WR_BLK_START */
+    case 35: /* CMD35: ERASE_GROUP_START */
+        if ((req.cmd == 35 && !sd->mmc) ||
+            (req.cmd == 32 && sd->mmc)) {
+            goto bad_cmd;
+        }
         switch (sd->state) {
         case sd_transfer_state:
             sd->erase_start = req.arg;
@@ -1053,6 +1293,11 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
         break;
 
     case 33:	/* CMD33:  ERASE_WR_BLK_END */
+    case 36: /* CMD36: ERASE_GROUP_END */
+        if ((req.cmd == 36 && !sd->mmc) ||
+            (req.cmd == 33 && sd->mmc)) {
+            goto bad_cmd;
+        }
         switch (sd->state) {
         case sd_transfer_state:
             sd->erase_end = req.arg;
@@ -1081,6 +1326,17 @@  static sd_rsp_type_t sd_normal_command(SDState *sd,
             break;
         }
         break;
+            
+    /* Class 9 */
+    case 39: /* CMD39: FAST_IO */
+    case 40: /* CMD40: GO_IRQ_STATE */
+        if (sd->mmc) {
+            sd->card_status |= ILLEGAL_COMMAND;
+            fprintf(stderr, "%s: CMD%d not implemented\n",
+                    __FUNCTION__, req.cmd);
+            return sd_r0;
+        }
+        goto bad_cmd;
 
     /* Lock card commands (Class 7) */
     case 42:	/* CMD42:  LOCK_UNLOCK */
@@ -1336,8 +1592,8 @@  int sd_do_command(SDState *sd, SDRequest *req,
         int i;
         DPRINTF("Response:");
         for (i = 0; i < rsplen; i++)
-            printf(" %02x", response[i]);
-        printf(" state %d\n", sd->state);
+            fprintf(stderr, " %02x", response[i]);
+        fprintf(stderr, " state %d\n", sd->state);
     } else {
         DPRINTF("No response %d\n", sd->state);
     }
@@ -1434,6 +1690,11 @@  void sd_write_data(SDState *sd, uint8_t value)
         }
         break;
 
+    case 20: /* CMD20: WRITE_DAT_UNTIL_STOP */
+        if (!sd->mmc) {
+            goto unknown_command;
+        }
+        /* fall through */
     case 25:	/* CMD25:  WRITE_MULTIPLE_BLOCK */
         sd->data[sd->data_offset ++] = value;
         if (sd->data_offset >= sd->blk_len) {
@@ -1521,6 +1782,7 @@  void sd_write_data(SDState *sd, uint8_t value)
         break;
 
     default:
+    unknown_command:
         fprintf(stderr, "sd_write_data: unknown command\n");
         break;
     }
@@ -1536,7 +1798,7 @@  uint8_t sd_read_data(SDState *sd)
         return 0x00;
 
     if (sd->state != sd_sendingdata_state) {
-        fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
+        fprintf(stderr, "sd_read_data: not in Sending-Data state (state=%d)\n", sd->state);
         return 0x00;
     }
 
@@ -1553,6 +1815,16 @@  uint8_t sd_read_data(SDState *sd)
             sd->state = sd_transfer_state;
         break;
 
+    case 8: /* CMD8: SEND_EXT_CSD (MMC only) */
+        if (sd->mmc) {
+            ret = sd->data[sd->data_offset++];
+            if (sd->data_offset >= 512) {
+                sd->state = sd_transfer_state;
+            }
+        } else {
+            goto unknown_command;
+        }
+        break;
     case 9:	/* CMD9:   SEND_CSD */
     case 10:	/* CMD10:  SEND_CID */
         ret = sd->data[sd->data_offset ++];
@@ -1638,6 +1910,7 @@  uint8_t sd_read_data(SDState *sd)
         break;
 
     default:
+    unknown_command:
         fprintf(stderr, "sd_read_data: unknown command\n");
         return 0x00;
     }
@@ -1654,3 +1927,8 @@  void sd_enable(SDState *sd, int enable)
 {
     sd->enable = enable;
 }
+
+int sd_is_mmc(SDState *sd)
+{
+    return sd->mmc;
+}