All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH] SecureDigital card emulation.
@ 2007-03-16 20:59 andrzej zaborowski
  2007-03-17 10:29 ` Paul Brook
  0 siblings, 1 reply; 3+ messages in thread
From: andrzej zaborowski @ 2007-03-16 20:59 UTC (permalink / raw)
  To: qemu-devel

[-- Attachment #1: Type: text/plain, Size: 330 bytes --]

Emulates an SD card with the full command set (no SPI mode though).
Should be more or less compliant with version 1.10 specification.

This is mostly the same code I used for the OMAP emulation a long time
ago, so it seems to be rather generic. I added the "-sd filename"
switch to indicate the card image to use.

Cheers,
Andrew

[-- Attachment #2: 0004-SecureDigital-card-emulation.txt --]
[-- Type: text/plain, Size: 52995 bytes --]

From 8c53791ec11ffda03de788d5b0cb8512918d8027 Mon Sep 17 00:00:00 2001
From: Andrzej Zaborowski <balrog@zabor.org>
Date: Fri, 16 Mar 2007 16:00:55 +0100
Subject: [PATCH] SecureDigital card emulation.

---
 hw/sd.c | 1519 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/sd.h |  129 ++++++
 vl.c    |   12 +-
 vl.h    |    2 +
 4 files changed, 1660 insertions(+), 2 deletions(-)

diff --git a/hw/sd.c b/hw/sd.c
new file mode 100644
index 0000000..37accde
--- /dev/null
+++ b/hw/sd.c
@@ -0,0 +1,1519 @@
+/* 
+ * SD Memory Card emulation as defined in the "SD Memory Card Physical
+ * layer specification, Version 1.10."
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "sd.h"
+
+struct sd_state_s {
+    enum {
+        sd_inactive,
+        sd_card_identification_mode,
+        sd_data_transfer_mode,
+    } mode;
+    enum {
+        sd_inactive_state = -1,
+        sd_idle_state = 0,
+        sd_ready_state,
+        sd_identification_state,
+        sd_standby_state,
+        sd_transfer_state,
+        sd_sendingdata_state,
+        sd_receivingdata_state,
+        sd_programming_state,
+        sd_disconnect_state,
+    } state;
+    uint32_t ocr;
+    uint8_t scr[8];
+    uint8_t cid[16];
+    uint8_t csd[16];
+    uint16_t rca;
+    uint32_t card_status;
+    uint8_t sd_status[64];
+    int wp_switch;
+    int *wp_groups;
+    uint32_t size;
+    int blk_len;
+    uint32_t erase_start;
+    uint32_t erase_end;
+    uint8_t pwd[16];
+    int pwd_len;
+    int function_group[6];
+
+    int current_cmd;
+    int blk_written;
+    uint32_t data_start;
+    uint32_t data_offset;
+    uint8_t data[512];
+    void (*readonly_cb)(void *, int);
+    void (*inserted_cb)(void *, int);
+    void *opaque;
+    BlockDriverState *bdrv;
+};
+
+static void sd_set_status(struct sd_state_s *sd)
+{
+    switch (sd->state) {
+    case sd_inactive_state:
+        sd->mode = sd_inactive;
+        break;
+
+    case sd_idle_state:
+    case sd_ready_state:
+    case sd_identification_state:
+        sd->mode = sd_card_identification_mode;
+        break;
+
+    case sd_standby_state:
+    case sd_transfer_state:
+    case sd_sendingdata_state:
+    case sd_receivingdata_state:
+    case sd_programming_state:
+    case sd_disconnect_state:
+        sd->mode = sd_data_transfer_mode;
+        break;
+    }
+
+    sd->card_status &= ~CURRENT_STATE;
+    sd->card_status |= sd->state << 9;
+}
+
+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_none, 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_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,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,
+    sd_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+const sd_cmd_type_t sd_acmd_type[64] = {
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_ac,   sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_adtc, sd_ac,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_bcr,  sd_ac,   sd_none, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_adtc, sd_none, sd_none, sd_none, sd_none,
+    sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
+};
+
+static const int sd_cmd_class[64] = {
+    0,  0,  0,  0,  0,  9, 10,  0,  0,  0,  0,  1,  0,  0,  0,  0,
+    2,  2,  2,  2,  3,  3,  3,  3,  4,  4,  4,  4,  6,  6,  6,  6,
+    5,  5, 10, 10, 10, 10,  5,  9,  9,  9,  7,  7,  7,  7,  7,  7,
+    7,  7, 10,  7,  9,  9,  9,  8,  8, 10,  8,  8,  8,  8,  8,  8,
+};
+
+static const sd_rsp_type_t sd_cmd_response[64] = {
+    sd_nore, sd_nore, sd_r2,   sd_r6,   sd_nore, sd_nore, sd_r1,   sd_r1b,
+    sd_nore, sd_r2,   sd_r2,   sd_nore, sd_r1b,  sd_r1,   sd_nore, sd_nore,
+    sd_r1,   sd_r1,   sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_r1,   sd_r1,   sd_r1,   sd_r1,   sd_r1b,  sd_r1b,  sd_r1,   sd_nore,
+    sd_r1,   sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore, sd_r1b,  sd_nore,
+    sd_nore, sd_nore, sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1,
+    sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+};
+
+static const sd_rsp_type_t sd_acmd_response[64] = {
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1,   sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1,   sd_nore, sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_r1,   sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_nore, sd_r3,   sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_r1,   sd_nore, sd_nore, sd_nore, sd_nore,
+    sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore, sd_nore,
+};
+
+static uint8_t sd_crc7(void *message, size_t width)
+{
+    int i, bit;
+    uint8_t shift_reg = 0x00;
+    uint8_t *msg = (uint8_t *) message;
+
+    for (i = 0; i < width; i ++, msg ++)
+        for (bit = 7; bit >= 0; bit --) {
+            shift_reg <<= 1;
+            if ((shift_reg >> 7) ^ ((*msg >> bit) & 1))
+                shift_reg ^= 0x89;
+        }
+
+    return shift_reg;
+}
+
+static uint16_t sd_crc16(void *message, size_t width)
+{
+    int i, bit;
+    uint16_t shift_reg = 0x0000;
+    uint16_t *msg = (uint16_t *) message;
+    width <<= 1;
+
+    for (i = 0; i < width; i ++, msg ++)
+        for (bit = 15; bit >= 0; bit --) {
+            shift_reg <<= 1;
+            if ((shift_reg >> 15) ^ ((*msg >> bit) & 1))
+                shift_reg ^= 0x1011;
+        }
+
+    return shift_reg;
+}
+
+static void sd_set_ocr(struct sd_state_s *sd)
+{
+    sd->ocr = 0x80fffff0;
+}
+
+static void sd_set_scr(struct sd_state_s *sd)
+{
+    sd->scr[0] = 0x00;		/* SCR Structure */
+    sd->scr[1] = 0x2f;		/* SD Security Support */
+    sd->scr[2] = 0x00;
+    sd->scr[3] = 0x00;
+    sd->scr[4] = 0x00;
+    sd->scr[5] = 0x00;
+    sd->scr[6] = 0x00;
+    sd->scr[7] = 0x00;
+}
+
+#define MID	0xaa
+#define OID	"XY"
+#define PNM	"QEMU!"
+#define PRV	0x01
+#define MDT_YR	2006
+#define MDT_MON	2
+
+static void sd_set_cid(struct sd_state_s *sd)
+{
+    sd->cid[0] = MID;		/* Fake card manufacturer ID (MID) */
+    sd->cid[1] = OID[0];	/* OEM/Application ID (OID) */
+    sd->cid[2] = OID[1];
+    sd->cid[3] = PNM[0];	/* Fake product name (PNM) */
+    sd->cid[4] = PNM[1];
+    sd->cid[5] = PNM[2];
+    sd->cid[6] = PNM[3];
+    sd->cid[7] = PNM[4];
+    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;
+    sd->cid[15] = (sd_crc7(sd->cid, 15) << 1) | 1;
+}
+
+#define HWBLOCK_SHIFT	9			/* 512 bytes */
+#define SECTOR_SHIFT	5			/* 16 kilobytes */
+#define WPGROUP_SHIFT	7			/* 2 megs */
+#define CMULT_SHIFT	9			/* 512 times HWBLOCK_SIZE */
+#define BLOCK_SIZE	(1 << (HWBLOCK_SHIFT))
+#define SECTOR_SIZE	(1 << (HWBLOCK_SHIFT + SECTOR_SHIFT))
+#define WPGROUP_SIZE	(1 << (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT))
+
+static const uint8_t sd_csd_rw_mask[16] = {
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0xfe,
+};
+
+static void sd_set_csd(struct sd_state_s *sd, uint32_t size)
+{
+    uint32_t csize = (size >> (CMULT_SHIFT + HWBLOCK_SHIFT)) - 1;
+    uint32_t sectsize = (1 << (SECTOR_SHIFT + 1)) - 1;
+    uint32_t wpsize = (1 << (WPGROUP_SHIFT + 1)) - 1;
+
+    sd->csd[0] = 0x00;		/* CSD structure */
+    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 */
+    sd->csd[4] = 0x5f;		/* Card Command Classes */
+    sd->csd[5] = 0x50 |		/* Max. read data block length */
+        HWBLOCK_SHIFT;
+    sd->csd[6] = 0xe0 |		/* Partial block for read allowed */
+        ((csize >> 10) & 0x03);
+    sd->csd[7] = 0x00 |		/* Device size */
+        ((csize >> 2) & 0xff);
+    sd->csd[8] = 0x3f |		/* Max. read current */
+        ((csize << 6) & 0xc0);
+    sd->csd[9] = 0xfc |		/* Max. write current */
+        ((CMULT_SHIFT - 2) >> 1);
+    sd->csd[10] = 0x40 |	/* Erase sector size */
+        (((CMULT_SHIFT - 2) << 7) & 0x80) | (sectsize >> 1);
+    sd->csd[11] = 0x00 |	/* Write protect group size */
+        ((sectsize << 7) & 0x80) | wpsize;
+    sd->csd[12] = 0x90 |	/* Write speed factor */
+        (HWBLOCK_SHIFT >> 2);
+    sd->csd[13] = 0x20 |	/* Max. write data block length */
+        ((HWBLOCK_SHIFT << 6) & 0xc0);
+    sd->csd[14] = 0x00;		/* File format group */
+    sd->csd[15] = (sd_crc7(sd->csd, 15) << 1) | 1;
+}
+
+static void sd_set_rca(struct sd_state_s *sd)
+{
+    sd->rca += 0x4567;
+}
+
+#define CARD_STATUS_A	0x02004100
+#define CARD_STATUS_B	0x00c01e00
+#define CARD_STATUS_C	0xfd39a028
+
+static void sd_set_cardstatus(struct sd_state_s *sd)
+{
+    sd->card_status = 0x00000100;
+}
+
+static void sd_set_sdstatus(struct sd_state_s *sd)
+{
+    memset(sd->sd_status, 0, 64);
+}
+
+static int sd_req_crc_validate(struct sd_request_s *req)
+{
+    uint8_t buffer[5];
+    buffer[0] = 0x40 | req->cmd;
+    buffer[1] = (req->arg >> 24) & 0xff;
+    buffer[2] = (req->arg >> 16) & 0xff;
+    buffer[3] = (req->arg >> 8) & 0xff;
+    buffer[4] = (req->arg >> 0) & 0xff;
+    return 0;
+    return sd_crc7(buffer, 5) != req->crc;	/* TODO */
+}
+
+void sd_response_r1_make(struct sd_state_s *sd,
+                union sd_response_u *response, uint32_t last_status)
+{
+    int buffer[5];
+    uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND;
+
+    response->r1.cmd = sd->current_cmd;
+    response->r1.status = (sd->card_status & ~mask) | (last_status & mask);
+    sd->card_status &= ~CARD_STATUS_C | APP_CMD;
+
+    buffer[0] = 0x00 | response->r1.cmd;
+    buffer[1] = (response->r1.status >> 24) & 0xff;
+    buffer[2] = (response->r1.status >> 16) & 0xff;
+    buffer[3] = (response->r1.status >> 8) & 0xff;
+    buffer[4] = (response->r1.status >> 0) & 0xff;
+    response->r1.crc = sd_crc7(buffer, 5);
+}
+
+void sd_response_r2_make(struct sd_state_s *sd, union sd_response_u *response)
+{
+    response->r2.reg[7] |= 0x01;
+}
+
+void sd_response_r3_make(struct sd_state_s *sd, union sd_response_u *response)
+{
+    response->r3.ocr_reg = sd->ocr;
+}
+
+void sd_response_r6_make(struct sd_state_s *sd, union sd_response_u *response)
+{
+    int buffer[5];
+
+    response->r6.cmd = sd->current_cmd;
+    response->r6.arg = sd->rca;
+    response->r6.status =
+        ((sd->card_status >> 8) & 0xc000) |
+        ((sd->card_status >> 6) & 0x2000) |
+        (sd->card_status & 0x1fff);
+
+    buffer[0] = 0x00 | response->r6.cmd;
+    buffer[1] = (response->r6.arg >> 8) & 0xff;
+    buffer[2] = (response->r6.arg >> 0) & 0xff;
+    buffer[3] = (response->r6.status >> 8) & 0xff;
+    buffer[4] = (response->r6.status >> 0) & 0xff;
+    response->r6.crc = sd_crc7(buffer, 5);
+}
+
+static void sd_reset(struct sd_state_s *sd, BlockDriverState *bdrv)
+{
+    uint32_t size;
+    uint64_t sect;
+
+    bdrv_get_geometry(bdrv, &sect);
+    sect <<= 9;
+
+    if (sect > 0x40000000)
+        size = 0x40000000;	/* 1 gig */
+    else
+        size = sect + 1;
+
+    sect = (size >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)) + 1;
+
+    sd->state = sd_idle_state;
+    sd->rca = 0x0000;
+    sd_set_ocr(sd);
+    sd_set_scr(sd);
+    sd_set_cid(sd);
+    sd_set_csd(sd, size);
+    sd_set_cardstatus(sd);
+    sd_set_sdstatus(sd);
+
+    sd->bdrv = bdrv;
+
+    sd->wp_switch = bdrv_is_read_only(bdrv);
+    sd->wp_groups = (int *) qemu_mallocz(sizeof(int) * sect);
+    memset(sd->wp_groups, 0, sizeof(int) * sect);
+    memset(sd->function_group, 0, sizeof(int) * 6);
+    sd->erase_start = 0;
+    sd->erase_end = 0;
+    sd->size = size;
+    sd->blk_len = 0x200;
+    sd->pwd_len = 0;
+}
+
+static void sd_cardchange(void *opaque)
+{
+    struct sd_state_s *sd = opaque;
+    if (sd->inserted_cb)
+        sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv));
+    if (bdrv_is_inserted(sd->bdrv)) {
+        sd_reset(sd, sd->bdrv);
+        if (sd->readonly_cb)
+            sd->readonly_cb(sd->opaque, sd->wp_switch);
+    }
+}
+
+struct sd_state_s *sd_init()
+{
+    struct sd_state_s *sd;
+    BlockDriverState *bs = bdrv_new("sd");
+    bdrv_set_type_hint(bs, BDRV_TYPE_FLOPPY);
+
+    if (sd_filename) {
+        if (bdrv_open(bs, sd_filename,
+                        snapshot ? BDRV_O_SNAPSHOT : 0) < 0)
+            fprintf(stderr, "%s: Couldn't open %s\n",
+                    __FUNCTION__, sd_filename);
+    }
+
+    sd = (struct sd_state_s *) qemu_mallocz(sizeof(struct sd_state_s));
+    sd_reset(sd, bs);
+    return sd;
+}
+
+void sd_set_cb(struct sd_state_s *sd, void *opaque,
+                void (*readonly_cb)(void *, int),
+                void (*inserted_cb)(void *, int))
+{
+    sd->opaque = opaque;
+    sd->readonly_cb = readonly_cb;
+    sd->inserted_cb = inserted_cb;
+    if (sd->readonly_cb)
+        sd->readonly_cb(sd->opaque, bdrv_is_read_only(sd->bdrv));
+    if (sd->inserted_cb)
+        sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv));
+    bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd);
+}
+
+static void sd_erase(struct sd_state_s *sd)
+{
+    int i, start, end;
+    if (!sd->erase_start || !sd->erase_end) {
+        sd->card_status |= ERASE_SEQ_ERROR;
+        return;
+    }
+
+    start = sd->erase_start >>
+            (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+    end = sd->erase_end >>
+            (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+    sd->erase_start = 0;
+    sd->erase_end = 0;
+    sd->csd[14] |= 0x40;
+
+    for (i = start; i <= end; i ++)
+        if (sd->wp_groups[i])
+            sd->card_status |= WP_ERASE_SKIP;
+}
+
+static uint32_t sd_wpbits(struct sd_state_s *sd, uint32_t addr)
+{
+    uint32_t i, wpnum;
+    uint32_t ret = 0;
+
+    wpnum = addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT);
+
+    for (i = 0; i < 32; i ++, wpnum ++, addr += WPGROUP_SIZE)
+        if (addr < sd->size && sd->wp_groups[wpnum])
+            ret |= (1 << i);
+
+    return ret;
+}
+
+static void sd_function_switch(struct sd_state_s *sd, uint32_t arg)
+{
+    int i, mode, new_func, crc;
+    mode = !!(arg & 0x80000000);
+
+    sd->data[0] = 0x00;		/* Maximum current consumption */
+    sd->data[1] = 0x01;
+    sd->data[2] = 0x80;		/* Supported group 6 functions */
+    sd->data[3] = 0x01;
+    sd->data[4] = 0x80;		/* Supported group 5 functions */
+    sd->data[5] = 0x01;
+    sd->data[6] = 0x80;		/* Supported group 4 functions */
+    sd->data[7] = 0x01;
+    sd->data[8] = 0x80;		/* Supported group 3 functions */
+    sd->data[9] = 0x01;
+    sd->data[10] = 0x80;	/* Supported group 2 functions */
+    sd->data[11] = 0x43;
+    sd->data[12] = 0x80;	/* Supported group 1 functions */
+    sd->data[13] = 0x03;
+    for (i = 0; i < 6; i ++) {
+        new_func = (arg >> (i * 4)) & 0x0f;
+        if (mode && new_func != 0x0f)
+            sd->function_group[i] = new_func;
+        sd->data[14 + (i >> 1)] = new_func << ((i * 4) & 4);
+    }
+    memset(&sd->data[17], 0, 47);
+    crc = sd_crc16(sd->data, 64);
+    sd->data[65] = crc >> 8;
+    sd->data[66] = crc & 0xff;
+}
+
+static inline int sd_wp_addr(struct sd_state_s *sd, uint32_t addr)
+{
+    return sd->wp_groups[addr >>
+            (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)];
+}
+
+static void sd_lock_command(struct sd_state_s *sd)
+{
+    int erase, lock, clr_pwd, set_pwd, pwd_len;
+    erase = !!(sd->data[0] & 0x08);
+    lock = sd->data[0] & 0x04;
+    clr_pwd = sd->data[0] & 0x02;
+    set_pwd = sd->data[0] & 0x01;
+
+    if (sd->blk_len > 1)
+        pwd_len = sd->data[1];
+    else
+        pwd_len = 0;
+
+    if (erase) {
+        if (!(sd->card_status & CARD_IS_LOCKED) || sd->blk_len > 1 ||
+                        set_pwd || clr_pwd || lock || sd->wp_switch ||
+                        (sd->csd[14] & 0x20)) {
+            sd->card_status |= LOCK_UNLOCK_FAILED;
+            return;
+        }
+        memset(sd->wp_groups, 0, sizeof(int) * (sd->size >>
+                        (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT)));
+        sd->csd[14] &= ~0x10;
+        sd->card_status &= ~CARD_IS_LOCKED;
+        sd->pwd_len = 0;
+        /* Erasing the entire card here! */
+        printf("SD: Card force-erased by CMD42\n");
+        return;
+    }
+
+    if (sd->blk_len < 2 + pwd_len ||
+                    pwd_len <= sd->pwd_len ||
+                    pwd_len > sd->pwd_len + 16) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    if (sd->pwd_len && memcmp(sd->pwd, sd->data + 2, sd->pwd_len)) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    pwd_len -= sd->pwd_len;
+    if ((pwd_len && !set_pwd) ||
+                    (clr_pwd && (set_pwd || lock)) ||
+                    (lock && !sd->pwd_len && !set_pwd) ||
+                    (!set_pwd && !clr_pwd &&
+                     (((sd->card_status & CARD_IS_LOCKED) && lock) ||
+                      (!(sd->card_status & CARD_IS_LOCKED) && !lock)))) {
+        sd->card_status |= LOCK_UNLOCK_FAILED;
+        return;
+    }
+
+    if (set_pwd) {
+        memcpy(sd->pwd, sd->data + 2 + sd->pwd_len, pwd_len);
+        sd->pwd_len = pwd_len;
+    }
+
+    if (clr_pwd) {
+        sd->pwd_len = 0;
+    }
+
+    if (lock)
+        sd->card_status |= CARD_IS_LOCKED;
+    else
+        sd->card_status &= ~CARD_IS_LOCKED;
+}
+
+static union sd_response_u sd_normal_command(struct sd_state_s *sd,
+                struct sd_request_s req)
+{
+    uint32_t rca = 0x0000;
+    union sd_response_u response;
+
+    if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+        rca = req.arg >> 16;
+
+    switch (req.cmd) {
+    /* Basic commands (Class 0 and Class 1) */
+    case 0:	/* CMD0:   GO_IDLE_STATE */
+        switch (sd->state) {
+        case sd_inactive_state:
+            return response;
+
+        default:
+            sd->state = sd_idle_state;
+            sd_reset(sd, sd->bdrv);
+            return response;
+        }
+        break;
+
+    case 2:	/* CMD2:   ALL_SEND_CID */
+        switch (sd->state) {
+        case sd_ready_state:
+            sd->state = sd_identification_state;
+            memcpy(response.r2.reg, sd->cid, 16);
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 3:	/* CMD3:   SEND_RELATIVE_ADDR */
+        switch (sd->state) {
+        case sd_identification_state:
+        case sd_standby_state:
+            sd->state = sd_standby_state;
+            sd_set_rca(sd);
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 4:	/* CMD4:   SEND_DSR */
+        switch (sd->state) {
+        case sd_standby_state:
+            break;
+
+        default:
+            break;
+        }
+        break;
+
+    case 6:	/* CMD6:   SWITCH_FUNCTION */
+        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 response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 7:	/* CMD7:   SELECT/DESELECT_CARD */
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return response;
+
+            sd->state = sd_transfer_state;
+            return response;
+
+        case sd_transfer_state:
+        case sd_sendingdata_state:
+            if (sd->rca == rca)
+                break;
+
+            sd->state = sd_standby_state;
+            return response;
+
+        case sd_disconnect_state:
+            if (sd->rca != rca)
+                return response;
+
+            sd->state = sd_programming_state;
+            return response;
+
+        case sd_programming_state:
+            if (sd->rca == rca)
+                break;
+
+            sd->state = sd_disconnect_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 9:	/* CMD9:   SEND_CSD */
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return response;
+
+            memcpy(response.r2.reg, sd->csd, sizeof(sd->csd));
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 10:	/* CMD10:  SEND_CID */
+        switch (sd->state) {
+        case sd_standby_state:
+            if (sd->rca != rca)
+                return response;
+
+            memcpy(response.r2.reg, sd->cid, sizeof(sd->cid));
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 11:	/* CMD11:  READ_DAT_UNTIL_STOP */
+        switch (sd->state) {
+        case 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 response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 12:	/* CMD12:  STOP_TRANSMISSION */
+        switch (sd->state) {
+        case sd_sendingdata_state:
+            sd->state = sd_transfer_state;
+            return response;
+
+        case sd_receivingdata_state:
+            sd->state = sd_programming_state;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 13:	/* CMD13:  SEND_STATUS */
+        switch (sd->mode) {
+        case sd_data_transfer_mode:
+            if (sd->rca != rca)
+                return response;
+
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 15:	/* CMD15:  GO_INACTIVE_STATE */
+        switch (sd->mode) {
+        case sd_data_transfer_mode:
+            if (sd->rca != rca)
+                return response;
+
+            sd->state = sd_inactive_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Block read commands (Classs 2) */
+    case 16:	/* CMD16:  SET_BLOCKLEN */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (req.arg > (1 << HWBLOCK_SHIFT))
+                sd->card_status |= BLOCK_LEN_ERROR;
+            else
+                sd->blk_len = req.arg;
+
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 17:	/* CMD17:  READ_SINGLE_BLOCK */
+        switch (sd->state) {
+        case 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 response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 18:	/* CMD18:  READ_MULTIPLE_BLOCK */
+        switch (sd->state) {
+        case 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 response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Block write commands (Class 4) */
+    case 24:	/* CMD24:  WRITE_SINGLE_BLOCK */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = req.arg;
+            sd->data_offset = 0;
+            sd->blk_written = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            if (sd_wp_addr(sd, sd->data_start))
+                sd->card_status |= WP_VIOLATION;
+            if (sd->csd[14] & 0x30)
+                sd->card_status |= WP_VIOLATION;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 25:	/* CMD25:  WRITE_MULTIPLE_BLOCK */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = req.arg;
+            sd->data_offset = 0;
+            sd->blk_written = 0;
+
+            if (sd->data_start + sd->blk_len > sd->size)
+                sd->card_status |= ADDRESS_ERROR;
+            if (sd_wp_addr(sd, sd->data_start))
+                sd->card_status |= WP_VIOLATION;
+            if (sd->csd[14] & 0x30)
+                sd->card_status |= WP_VIOLATION;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 26:	/* CMD26:  PROGRAM_CID */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 27:	/* CMD27:  PROGRAM_CSD */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Write protection (Class 6) */
+    case 28:	/* CMD28:  SET_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (req.arg >= sd->size) {
+                sd->card_status = ADDRESS_ERROR;
+                return response;
+            }
+
+            sd->state = sd_programming_state;
+            sd->wp_groups[req.arg >> (HWBLOCK_SHIFT +
+                            SECTOR_SHIFT + WPGROUP_SHIFT)] = 1;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 29:	/* CMD29:  CLR_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (req.arg >= sd->size) {
+                sd->card_status = ADDRESS_ERROR;
+                return response;
+            }
+
+            sd->state = sd_programming_state;
+            sd->wp_groups[req.arg >> (HWBLOCK_SHIFT +
+                            SECTOR_SHIFT + WPGROUP_SHIFT)] = 0;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 30:	/* CMD30:  SEND_WRITE_PROT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            *(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
+            sd->data_start = req.arg;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Erase commands (Class 5) */
+    case 32:	/* CMD32:  ERASE_WR_BLK_START */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->erase_start = req.arg;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 33:	/* CMD33:  ERASE_WR_BLK_END */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->erase_end = req.arg;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 38:	/* CMD38:  ERASE */
+        switch (sd->state) {
+        case sd_transfer_state:
+            if (sd->csd[14] & 0x30) {
+                sd->card_status |= WP_VIOLATION;
+                return response;
+            }
+
+            sd->state = sd_programming_state;
+            sd_erase(sd);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Lock card commands (Class 7) */
+    case 42:	/* CMD42:  LOCK_UNLOCK */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_receivingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    /* Application specific commands (Class 8) */
+    case 55:	/* CMD55:  APP_CMD */
+        if (sd->rca != rca)
+            return response;
+
+        sd->card_status |= APP_CMD;
+        return response;
+
+    case 56:	/* CMD56:  GEN_CMD */
+        printf("SD: GEN_CMD 0x%08x\n", req.arg);
+
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->data_offset = 0;
+            if (req.arg & 1)
+                sd->state = sd_sendingdata_state;
+            else
+                sd->state = sd_receivingdata_state;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        sd->card_status |= ILLEGAL_COMMAND;
+
+        printf("SD: Unknown CMD%i\n", req.cmd);
+        return response;
+    }
+
+    sd->card_status |= ILLEGAL_COMMAND;
+    printf("SD: CMD%i in a wrong state\n", req.cmd);
+    return response;
+}
+
+static union sd_response_u sd_app_command(struct sd_state_s *sd,
+        struct sd_request_s req) {
+    uint32_t rca;
+    union sd_response_u response;
+
+    if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
+        rca = req.arg >> 16;
+
+    switch (req.cmd) {
+    case 6:	/* ACMD6:  SET_BUS_WIDTH */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->sd_status[0] &= 0x3f;
+            sd->sd_status[0] |= (req.arg & 0x03) << 6;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 13:	/* ACMD13: SD_STATUS */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 22:	/* ACMD22: SEND_NUM_WR_BLOCKS */
+        switch (sd->state) {
+        case sd_transfer_state:
+            *(uint32_t *) sd->data = sd->blk_written;
+
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 23:	/* ACMD23: SET_WR_BLK_ERASE_COUNT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 41:	/* ACMD41: SD_APP_OP_COND */
+        switch (sd->state) {
+        case sd_idle_state:
+            /* We accept any voltage.  10000 V is nothing.  */
+            if (req.arg)
+                sd->state = sd_ready_state;
+
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 42:	/* ACMD42: SET_CLR_CARD_DETECT */
+        switch (sd->state) {
+        case sd_transfer_state:
+            /* Bringing in the 50KOhm pull-up resistor... Done.  */
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    case 51:	/* ACMD51: SEND_SCR */
+        switch (sd->state) {
+        case sd_transfer_state:
+            sd->state = sd_sendingdata_state;
+            sd->data_start = 0;
+            sd->data_offset = 0;
+            return response;
+
+        default:
+            break;
+        }
+        break;
+
+    default:
+        /* Fall back to standard commands.  */
+        sd->card_status &= ~APP_CMD;
+        return sd_normal_command(sd, req);
+    }
+
+    printf("SD: ACMD%i in a wrong state\n", req.cmd);
+    return response;
+}
+
+union sd_response_u sd_write_cmdline(struct sd_state_s *sd,
+        struct sd_request_s req, int *rsplen) {
+    uint32_t last_status = sd->card_status;
+    union sd_response_u response;
+    sd_rsp_type_t rtype;
+
+    if (!bdrv_is_inserted(sd->bdrv)) {
+        *rsplen = 0;
+        return response;
+    }
+
+    if (sd_req_crc_validate(&req)) {
+        sd->card_status &= ~COM_CRC_ERROR;
+        *rsplen = 0;
+        return response;
+    }
+
+    sd->card_status &= ~CARD_STATUS_B;
+    sd_set_status(sd);
+
+    if (last_status & CARD_IS_LOCKED)
+        if (((last_status & APP_CMD) &&
+                                (sd_acmd_type[req.cmd] == 0 ||
+                                 sd_acmd_type[req.cmd] == 7 ||
+                                 req.cmd == 41)) ||
+                        (!(last_status & APP_CMD) &&
+                         (sd_cmd_type[req.cmd] == 0 ||
+                          sd_cmd_type[req.cmd] == 7 ||
+                          req.cmd == 16 || req.cmd == 55))) {
+            sd->card_status |= ILLEGAL_COMMAND;
+            printf("SD: Card is locked\n");
+            return response;
+        }
+
+    if (last_status & APP_CMD)
+        response = sd_app_command(sd, req);
+    else
+        response = sd_normal_command(sd, req);
+
+    if (last_status & APP_CMD) {
+        if (sd->card_status & APP_CMD) {
+            rtype = sd_acmd_response[req.cmd];
+            sd->card_status &= ~APP_CMD;
+        } else
+            rtype = sd_cmd_response[req.cmd];
+    } else
+        rtype = sd_cmd_response[req.cmd];
+
+    sd->current_cmd = req.cmd;
+
+    switch (rtype) {
+    case sd_r1:
+    case sd_r1b:
+        sd_response_r1_make(sd, &response, last_status);
+        *rsplen = 48;
+        break;
+
+    case sd_r2:
+        sd_response_r2_make(sd, &response);
+        *rsplen = 128;
+        break;
+
+    case sd_r3:
+        sd_response_r3_make(sd, &response);
+        *rsplen = 48;
+        break;
+
+    case sd_r6:
+        sd_response_r6_make(sd, &response);
+        *rsplen = 48;
+        break;
+
+    case sd_nore:
+    default:
+        *rsplen = 0;
+        break;
+    }
+
+    if (sd->card_status & ILLEGAL_COMMAND)
+        *rsplen = 0;
+
+    return response;
+}
+
+/* No real need for 64 bit addresses here */
+static void sd_blk_read(BlockDriverState *bdrv,
+                void *data, uint32_t addr, uint32_t len)
+{
+    uint8_t buf[512];
+    uint32_t end = addr + len;
+
+    if (!bdrv || bdrv_read(bdrv, addr >> 9, buf, 1) == -1) {
+        printf("sd_blk_read: read error on host side\n");
+        return;
+    }
+
+    if (end > (addr & ~511) + 512) {
+        memcpy(data, buf + (addr & 511), 512 - (addr & 511));
+
+        if (bdrv_read(bdrv, end >> 9, buf, 1) == -1) {
+            printf("sd_blk_read: read error on host side\n");
+            return;
+        }
+        memcpy(data + 512 - (addr & 511), buf, end & 511);
+    } else
+        memcpy(data, buf + (addr & 511), len);
+}
+
+static void sd_blk_write(BlockDriverState *bdrv,
+                void *data, uint32_t addr, uint32_t len)
+{
+    uint8_t buf[512];
+    uint32_t end = addr + len;
+
+    if ((addr & 511) || len < 512)
+        if (!bdrv || bdrv_read(bdrv, addr >> 9, buf, 1) == -1) {
+            printf("sd_blk_write: read error on host side\n");
+            return;
+        }
+
+    if (end > (addr & ~511) + 512) {
+        memcpy(buf + (addr & 511), data, 512 - (addr & 511));
+        if (bdrv_write(bdrv, addr >> 9, buf, 1) == -1) {
+            printf("sd_blk_write: write error on host side\n");
+            return;
+        }
+
+        if (bdrv_read(bdrv, end >> 9, buf, 1) == -1) {
+            printf("sd_blk_write: read error on host side\n");
+            return;
+        }
+        memcpy(buf, data + 512 - (addr & 511), end & 511);
+        if (bdrv_write(bdrv, end >> 9, buf, 1) == -1)
+            printf("sd_blk_write: write error on host side\n");
+    } else {
+        memcpy(buf + (addr & 511), data, len);
+        if (!bdrv || bdrv_write(bdrv, addr >> 9, buf, 1) == -1)
+            printf("sd_blk_write: write error on host side\n");
+    }
+}
+
+#define BLK_READ_BLOCK(a, len)	sd_blk_read(sd->bdrv, sd->data, a, len)
+#define BLK_WRITE_BLOCK(a, len)	sd_blk_write(sd->bdrv, sd->data, a, len)
+#define APP_READ_BLOCK(a, len)	memset(sd->data, 0xec, len)
+#define APP_WRITE_BLOCK(a, len)
+
+void sd_write_datline(struct sd_state_s *sd, uint8_t value)
+{
+    int i;
+
+    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
+        return;
+
+    if (sd->state != sd_receivingdata_state) {
+        printf("sd_write_datline: not in Receiving-Data state\n");
+        return;
+    }
+
+    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+        return;
+
+    switch (sd->current_cmd) {
+    case 24:	/* CMD24:  WRITE_SINGLE_BLOCK */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->blk_written ++;
+            sd->csd[14] |= 0x40;
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 25:	/* CMD25:  WRITE_MULTIPLE_BLOCK */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            BLK_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->blk_written ++;
+            sd->data_start += sd->blk_len;
+            sd->data_offset = 0;
+            if (sd->data_start + sd->blk_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+            if (sd_wp_addr(sd, sd->data_start)) {
+                sd->card_status |= WP_VIOLATION;
+                break;
+            }
+            sd->csd[14] |= 0x40;
+
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_receivingdata_state;
+        }
+        break;
+
+    case 26:	/* CMD26:  PROGRAM_CID */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sizeof(sd->cid)) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            for (i = 0; i < sizeof(sd->cid); i ++)
+                if ((sd->cid[i] | 0x00) != sd->data[i])
+                    sd->card_status |= CID_CSD_OVERWRITE;
+
+            if (!(sd->card_status & CID_CSD_OVERWRITE))
+                for (i = 0; i < sizeof(sd->cid); i ++) {
+                    sd->cid[i] |= 0x00;
+                    sd->cid[i] &= sd->data[i];
+                }
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 27:	/* CMD27:  PROGRAM_CSD */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sizeof(sd->csd)) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            for (i = 0; i < sizeof(sd->csd); i ++)
+                if ((sd->csd[i] | sd_csd_rw_mask[i]) !=
+                    (sd->data[i] | sd_csd_rw_mask[i]))
+                    sd->card_status |= CID_CSD_OVERWRITE;
+
+            /* Copy flag (OTP) & Permanent write protect */
+            if (sd->csd[14] & ~sd->data[14] & 0x60)
+                sd->card_status |= CID_CSD_OVERWRITE;
+
+            if (!(sd->card_status & CID_CSD_OVERWRITE))
+                for (i = 0; i < sizeof(sd->csd); i ++) {
+                    sd->csd[i] |= sd_csd_rw_mask[i];
+                    sd->csd[i] &= sd->data[i];
+                }
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 42:	/* CMD42:  LOCK_UNLOCK */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            /* TODO: Check CRC before committing */
+            sd->state = sd_programming_state;
+            sd_lock_command(sd);
+            /* Bzzzzzzztt .... Operation complete.  */
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    case 56:	/* CMD56:  GEN_CMD */
+        sd->data[sd->data_offset ++] = value;
+        if (sd->data_offset >= sd->blk_len) {
+            APP_WRITE_BLOCK(sd->data_start, sd->data_offset);
+            sd->state = sd_transfer_state;
+        }
+        break;
+
+    default:
+        printf("sd_write_datline: unknown command\n");
+        break;
+    }
+}
+
+uint8_t sd_read_datline(struct sd_state_s *sd)
+{
+    /* TODO: Append CRCs */
+    uint8_t ret;
+
+    if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
+        return 0x00;
+
+    if (sd->state != sd_sendingdata_state) {
+        printf("sd_read_datline: not in Sending-Data state\n");
+        return 0x00;
+    }
+
+    if (sd->card_status & (ADDRESS_ERROR | WP_VIOLATION))
+        return 0x00;
+
+    switch (sd->current_cmd) {
+    case 6:	/* CMD6:   SWITCH_FUNCTION */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 64)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 11:	/* CMD11:  READ_DAT_UNTIL_STOP */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, sd->blk_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= sd->blk_len) {
+            sd->data_start += sd->blk_len;
+            sd->data_offset = 0;
+            if (sd->data_start + sd->blk_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+        }
+        break;
+
+    case 13:	/* ACMD13: SD_STATUS */
+        ret = sd->sd_status[sd->data_offset ++];
+
+        if (sd->data_offset >= sizeof(sd->sd_status))
+            sd->state = sd_transfer_state;
+        break;
+
+    case 17:	/* CMD17:  READ_SINGLE_BLOCK */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, sd->blk_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= sd->blk_len)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 18:	/* CMD18:  READ_MULTIPLE_BLOCK */
+        if (sd->data_offset == 0)
+            BLK_READ_BLOCK(sd->data_start, sd->blk_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= sd->blk_len) {
+            sd->data_start += sd->blk_len;
+            sd->data_offset = 0;
+            if (sd->data_start + sd->blk_len > sd->size) {
+                sd->card_status |= ADDRESS_ERROR;
+                break;
+            }
+        }
+        break;
+
+    case 22:	/* ACMD22: SEND_NUM_WR_BLOCKS */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 4)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 30:	/* CMD30:  SEND_WRITE_PROT */
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= 4)
+            sd->state = sd_transfer_state;
+        break;
+
+    case 51:	/* ACMD51: SEND_SCR */
+        ret = sd->scr[sd->data_offset ++];
+
+        if (sd->data_offset >= sizeof(sd->scr))
+            sd->state = sd_transfer_state;
+        break;
+
+    case 56:	/* CMD56:  GEN_CMD */
+        if (sd->data_offset == 0)
+            APP_READ_BLOCK(sd->data_start, sd->blk_len);
+        ret = sd->data[sd->data_offset ++];
+
+        if (sd->data_offset >= sd->blk_len)
+            sd->state = sd_transfer_state;
+        break;
+
+    default:
+        printf("sd_read_datline: unknown command\n");
+        return 0x00;
+    }
+
+    return ret;
+}
diff --git a/hw/sd.h b/hw/sd.h
new file mode 100644
index 0000000..a28df7f
--- /dev/null
+++ b/hw/sd.h
@@ -0,0 +1,129 @@
+/* 
+ * SD Memory Card emulation.  Mostly correct for MMC too.
+ *
+ * Copyright (c) 2006 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __hw_sd_h
+#define __hw_sd_h		1
+
+#include <vl.h>
+
+#define OUT_OF_RANGE		(1 << 31)
+#define ADDRESS_ERROR		(1 << 30)
+#define BLOCK_LEN_ERROR		(1 << 29)
+#define ERASE_SEQ_ERROR		(1 << 28)
+#define ERASE_PARAM		(1 << 27)
+#define WP_VIOLATION		(1 << 26)
+#define CARD_IS_LOCKED		(1 << 25)
+#define LOCK_UNLOCK_FAILED	(1 << 24)
+#define COM_CRC_ERROR		(1 << 23)
+#define ILLEGAL_COMMAND		(1 << 22)
+#define CARD_ECC_FAILED		(1 << 21)
+#define CC_ERROR		(1 << 20)
+#define SD_ERROR		(1 << 19)
+#define CID_CSD_OVERWRITE	(1 << 16)
+#define WP_ERASE_SKIP		(1 << 15)
+#define CARD_ECC_DISABLED	(1 << 14)
+#define ERASE_RESET		(1 << 13)
+#define CURRENT_STATE		(7 << 9)
+#define READY_FOR_DATA		(1 << 8)
+#define APP_CMD			(1 << 5)
+#define AKE_SEQ_ERROR		(1 << 3)
+
+typedef enum {
+    sd_none = -1,
+    sd_bc = 0,	/* broadcast -- no response */
+    sd_bcr,		/* broadcast with response */
+    sd_ac,		/* addressed -- no data transfer */
+    sd_adtc,	/* addressed with data transfer */
+} sd_cmd_type_t;
+
+typedef enum {
+    sd_nore = 0,	/* no response */
+    sd_r1,		/* normal response command */
+    sd_r2,		/* CID, CSD registers */
+    sd_r3,		/* OCR register */
+    sd_r6 = 6,	/* Published RCA response */
+    sd_r1b = -1,
+} sd_rsp_type_t;
+
+struct sd_request_s {
+    uint8_t cmd;
+    uint32_t arg;
+    uint8_t crc;
+};
+
+struct sd_response_none_s {
+};
+
+struct sd_response_r1_s {
+    uint8_t cmd;
+    uint32_t status;
+    uint8_t crc;
+};
+
+struct sd_response_r1b_s {
+    uint8_t cmd;
+    uint32_t status;
+    uint8_t crc;
+};
+
+struct sd_response_r2_s {
+    uint16_t reg[8];
+};
+
+struct sd_response_r3_s {
+    uint32_t ocr_reg;
+};
+
+struct sd_response_r6_s {
+    uint8_t cmd;
+    uint16_t arg;
+    uint16_t status;
+    uint8_t crc;
+};
+
+union sd_response_u {
+    struct sd_response_none_s none;
+    struct sd_response_r1_s r1;
+    struct sd_response_r1b_s r1b;
+    struct sd_response_r2_s r2;
+    struct sd_response_r3_s r3;
+    struct sd_response_r6_s r6;
+};
+
+struct sd_state_s;
+
+struct sd_state_s *sd_init(void);
+union sd_response_u sd_write_cmdline(struct sd_state_s *sd,
+                struct sd_request_s req, int *rsplen);
+void sd_write_datline(struct sd_state_s *sd, uint8_t value);
+uint8_t sd_read_datline(struct sd_state_s *sd);
+void sd_set_cb(struct sd_state_s *sd, void *opaque,
+                void (*readonly_cb)(void *, int),
+                void (*inserted_cb)(void *, int));
+
+#endif	/* __hw_sd_h */
diff --git a/vl.c b/vl.c
index d4e12ba..e5b53d2 100644
--- a/vl.c
+++ b/vl.c
@@ -184,6 +184,8 @@ const char *vnc_display;
 int acpi_enabled = 1;
 int fd_bootchk = 1;
 int no_reboot = 0;
+int snapshot = 0;
+const char *sd_filename = 0;
 int daemonize = 0;
 const char *option_rom[MAX_OPTION_ROMS];
 int nb_option_roms;
@@ -6405,6 +6407,7 @@ void help(void)
            "-hda/-hdb file  use 'file' as IDE hard disk 0/1 image\n"
            "-hdc/-hdd file  use 'file' as IDE hard disk 2/3 image\n"
            "-cdrom file     use 'file' as IDE cdrom image (cdrom is ide1 master)\n"
+           "-sd file        use 'file' as SecureDigital card image\n"
            "-boot [a|c|d|n] boot on floppy (a), hard disk (c), CD-ROM (d), or network (n)\n"
            "-snapshot       write to temporary files instead of disk image files\n"
 #ifdef CONFIG_SDL
@@ -6541,6 +6544,7 @@ enum {
     QEMU_OPTION_hdc,
     QEMU_OPTION_hdd,
     QEMU_OPTION_cdrom,
+    QEMU_OPTION_sd,
     QEMU_OPTION_boot,
     QEMU_OPTION_snapshot,
 #ifdef TARGET_I386
@@ -6617,6 +6621,7 @@ const QEMUOption qemu_options[] = {
     { "hdc", HAS_ARG, QEMU_OPTION_hdc },
     { "hdd", HAS_ARG, QEMU_OPTION_hdd },
     { "cdrom", HAS_ARG, QEMU_OPTION_cdrom },
+    { "sd", HAS_ARG, QEMU_OPTION_sd },
     { "boot", HAS_ARG, QEMU_OPTION_boot },
     { "snapshot", 0, QEMU_OPTION_snapshot },
 #ifdef TARGET_I386
@@ -6896,7 +6901,7 @@ int main(int argc, char **argv)
     const char *gdbstub_port;
 #endif
     int i, cdrom_index;
-    int snapshot, linux_boot;
+    int linux_boot;
     const char *initrd_filename;
     const char *hd_filename[MAX_DISKS], *fd_filename[MAX_FD];
     const char *kernel_filename, *kernel_cmdline;
@@ -7072,6 +7077,9 @@ int main(int argc, char **argv)
                         cdrom_index = -1;
                 }
                 break;
+            case QEMU_OPTION_sd:
+                sd_filename = optarg;
+                break;
             case QEMU_OPTION_snapshot:
                 snapshot = 1;
                 break;
@@ -7548,7 +7556,7 @@ int main(int argc, char **argv)
                 fd_table[i] = bdrv_new(buf);
                 bdrv_set_type_hint(fd_table[i], BDRV_TYPE_FLOPPY);
             }
-            if (fd_filename[i] != '\0') {
+            if (fd_filename[i][0] != '\0') {
                 if (bdrv_open(fd_table[i], fd_filename[i],
                               snapshot ? BDRV_O_SNAPSHOT : 0) < 0) {
                     fprintf(stderr, "qemu: could not open floppy disk image '%s'\n",
diff --git a/vl.h b/vl.h
index 563f437..cfafc08 100644
--- a/vl.h
+++ b/vl.h
@@ -156,6 +156,8 @@ extern int kqemu_allowed;
 extern int win2k_install_hack;
 extern int usb_enabled;
 extern int smp_cpus;
+extern int snapshot;
+extern const char *sd_filename;
 extern int no_quit;
 extern int semihosting_enabled;
 extern int autostart;
-- 
1.4.4.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [Qemu-devel] [PATCH] SecureDigital card emulation.
  2007-03-16 20:59 [Qemu-devel] [PATCH] SecureDigital card emulation andrzej zaborowski
@ 2007-03-17 10:29 ` Paul Brook
  2007-03-17 11:01   ` andrzej zaborowski
  0 siblings, 1 reply; 3+ messages in thread
From: Paul Brook @ 2007-03-17 10:29 UTC (permalink / raw)
  To: qemu-devel, balrogg

On Friday 16 March 2007 20:59, andrzej zaborowski wrote:
> Emulates an SD card with the full command set (no SPI mode though).
> Should be more or less compliant with version 1.10 specification.

I suspect your code only works on x86. Specifically things like:

> +struct sd_response_r1_s {
> +    uint8_t cmd;
> +    uint32_t status;
> +    uint8_t crc;
> +};

Will be a 12-byte struct on many hosts, and probably also breaks on big-endian 
hosts. AFAICS there's no way for the caller of sd_write_cmdline to figure out 
what type of response is returned (other than the length), so has to rely on 
doing a bitwise copy.

Paul

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [Qemu-devel] [PATCH] SecureDigital card emulation.
  2007-03-17 10:29 ` Paul Brook
@ 2007-03-17 11:01   ` andrzej zaborowski
  0 siblings, 0 replies; 3+ messages in thread
From: andrzej zaborowski @ 2007-03-17 11:01 UTC (permalink / raw)
  To: Paul Brook; +Cc: qemu-devel

Hi,

On 17/03/07, Paul Brook <paul@codesourcery.com> wrote:
> On Friday 16 March 2007 20:59, andrzej zaborowski wrote:
> > Emulates an SD card with the full command set (no SPI mode though).
> > Should be more or less compliant with version 1.10 specification.
>
> I suspect your code only works on x86. Specifically things like:
>
> > +struct sd_response_r1_s {
> > +    uint8_t cmd;
> > +    uint32_t status;
> > +    uint8_t crc;
> > +};
>
> Will be a 12-byte struct on many hosts, and probably also breaks on big-endian
> hosts. AFAICS there's no way for the caller of sd_write_cmdline to figure out
> what type of response is returned (other than the length), so has to rely on
> doing a bitwise copy.

The card is supposed to return the type of response matching the
command that was issued, so the the caller does know the type and
doesn't do bitwise copy. If the card is broken and returns a different
response then we get garbage, exactly like on the actual hardware.

Regards,
Andrew

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2007-03-17 11:02 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-03-16 20:59 [Qemu-devel] [PATCH] SecureDigital card emulation andrzej zaborowski
2007-03-17 10:29 ` Paul Brook
2007-03-17 11:01   ` andrzej zaborowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.