-/*
+/*
* SD Memory Card emulation as defined in the "SD Memory Card Physical
* layer specification, Version 1.10."
*
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#include "hw.h"
+#include "block.h"
#include "sd.h"
//#define DEBUG_SD 1
#ifdef DEBUG_SD
-#define DPRINTF(fmt, args...) \
-do { printf("SD: " fmt , ##args); } while (0)
+#define DPRINTF(fmt, ...) \
+do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0)
#else
-#define DPRINTF(fmt, args...) do {} while(0)
+#define DPRINTF(fmt, ...) do {} while(0)
#endif
typedef enum {
sd_r2_s, /* CSD register */
sd_r3, /* OCR register */
sd_r6 = 6, /* Published RCA response */
+ sd_r7, /* Operating voltage */
sd_r1b = -1,
} sd_rsp_type_t;
uint16_t rca;
uint32_t card_status;
uint8_t sd_status[64];
+ uint32_t vhs;
int wp_switch;
int *wp_groups;
uint32_t size;
int pwd_len;
int function_group[6];
+ int spi;
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;
+ qemu_irq readonly_cb;
+ qemu_irq inserted_cb;
BlockDriverState *bdrv;
+ uint8_t *buf;
+
+ int enable;
};
static void sd_set_status(SDState *sd)
sd->card_status |= sd->state << 9;
}
-const sd_cmd_type_t sd_cmd_type[64] = {
+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_none, sd_ac, sd_ac, sd_adtc, sd_ac, 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_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_adtc, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none, sd_none,
};
-const sd_cmd_type_t sd_acmd_type[64] = {
+static 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,
static void sd_set_ocr(SDState *sd)
{
- sd->ocr = 0x80fffff0;
+ /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */
+ sd->ocr = 0x80ffff00;
}
static void sd_set_scr(SDState *sd)
#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] = {
memset(sd->sd_status, 0, 64);
}
-static int sd_req_crc_validate(struct sd_request_s *req)
+static int sd_req_crc_validate(SDRequest *req)
{
uint8_t buffer[5];
buffer[0] = 0x40 | req->cmd;
return sd_crc7(buffer, 5) != req->crc; /* TODO */
}
-void sd_response_r1_make(SDState *sd,
- uint8_t *response, uint32_t last_status)
+static void sd_response_r1_make(SDState *sd,
+ uint8_t *response, uint32_t last_status)
{
uint32_t mask = CARD_STATUS_B ^ ILLEGAL_COMMAND;
uint32_t status;
response[3] = (status >> 0) & 0xff;
}
-void sd_response_r3_make(SDState *sd, uint8_t *response)
+static void sd_response_r3_make(SDState *sd, uint8_t *response)
{
response[0] = (sd->ocr >> 24) & 0xff;
response[1] = (sd->ocr >> 16) & 0xff;
response[3] = (sd->ocr >> 0) & 0xff;
}
-void sd_response_r6_make(SDState *sd, uint8_t *response)
+static void sd_response_r6_make(SDState *sd, uint8_t *response)
{
uint16_t arg;
uint16_t status;
response[3] = status & 0xff;
}
+static void sd_response_r7_make(SDState *sd, uint8_t *response)
+{
+ response[0] = (sd->vhs >> 24) & 0xff;
+ response[1] = (sd->vhs >> 16) & 0xff;
+ response[2] = (sd->vhs >> 8) & 0xff;
+ response[3] = (sd->vhs >> 0) & 0xff;
+}
+
static void sd_reset(SDState *sd, BlockDriverState *bdrv)
{
uint32_t size;
uint64_t sect;
- bdrv_get_geometry(bdrv, §);
+ if (bdrv) {
+ bdrv_get_geometry(bdrv, §);
+ } else {
+ sect = 0;
+ }
sect <<= 9;
if (sect > 0x40000000)
sd->bdrv = bdrv;
- sd->wp_switch = bdrv_is_read_only(bdrv);
+ if (sd->wp_groups)
+ qemu_free(sd->wp_groups);
+ sd->wp_switch = bdrv ? bdrv_is_read_only(bdrv) : 0;
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;
static void sd_cardchange(void *opaque)
{
SDState *sd = opaque;
- if (sd->inserted_cb)
- sd->inserted_cb(sd->opaque, bdrv_is_inserted(sd->bdrv));
+ qemu_set_irq(sd->inserted_cb, 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);
+ qemu_set_irq(sd->readonly_cb, sd->wp_switch);
}
}
-SDState *sd_init(BlockDriverState *bs)
+/* 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;
sd = (SDState *) qemu_mallocz(sizeof(SDState));
+ sd->buf = qemu_memalign(512, 512);
+ sd->spi = is_spi;
+ sd->enable = 1;
sd_reset(sd, bs);
+ if (sd->bdrv) {
+ bdrv_set_change_cb(sd->bdrv, sd_cardchange, sd);
+ }
return sd;
}
-void sd_set_cb(SDState *sd, void *opaque,
- void (*readonly_cb)(void *, int),
- void (*inserted_cb)(void *, int))
+void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
{
- 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);
+ sd->readonly_cb = readonly;
+ sd->inserted_cb = insert;
+ qemu_set_irq(readonly, bdrv_is_read_only(sd->bdrv));
+ qemu_set_irq(insert, bdrv_is_inserted(sd->bdrv));
}
static void sd_erase(SDState *sd)
sd->card_status &= ~CARD_IS_LOCKED;
sd->pwd_len = 0;
/* Erasing the entire card here! */
- printf("SD: Card force-erased by CMD42\n");
+ fprintf(stderr, "SD: Card force-erased by CMD42\n");
return;
}
}
static sd_rsp_type_t sd_normal_command(SDState *sd,
- struct sd_request_s req)
+ SDRequest req)
{
uint32_t rca = 0x0000;
case 0: /* CMD0: GO_IDLE_STATE */
switch (sd->state) {
case sd_inactive_state:
- return sd_r0;
+ return sd->spi ? sd_r1 : sd_r0;
default:
sd->state = sd_idle_state;
sd_reset(sd, sd->bdrv);
- return sd_r0;
+ return sd->spi ? sd_r1 : sd_r0;
}
break;
+ case 1: /* CMD1: SEND_OP_CMD */
+ if (!sd->spi)
+ goto bad_cmd;
+
+ sd->state = sd_transfer_state;
+ return sd_r1;
+
case 2: /* CMD2: ALL_SEND_CID */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_ready_state:
sd->state = sd_identification_state;
break;
case 3: /* CMD3: SEND_RELATIVE_ADDR */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_identification_state:
case sd_standby_state:
break;
case 4: /* CMD4: SEND_DSR */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_standby_state:
break;
break;
case 6: /* CMD6: SWITCH_FUNCTION */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->mode) {
case sd_data_transfer_mode:
sd_function_switch(sd, req.arg);
break;
case 7: /* CMD7: SELECT/DESELECT_CARD */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_standby_state:
if (sd->rca != rca)
}
break;
+ case 8: /* CMD8: SEND_IF_COND */
+ /* 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;
+
+ /* Accept. */
+ sd->vhs = req.arg;
+ return sd_r7;
+
+ default:
+ break;
+ }
+ break;
+
case 9: /* CMD9: SEND_CSD */
switch (sd->state) {
case sd_standby_state:
return sd_r2_s;
+ case sd_transfer_state:
+ if (!sd->spi)
+ break;
+ sd->state = sd_sendingdata_state;
+ memcpy(sd->data, sd->csd, 16);
+ sd->data_start = req.arg;
+ sd->data_offset = 0;
+ return sd_r1;
+
default:
break;
}
return sd_r2_i;
+ case sd_transfer_state:
+ if (!sd->spi)
+ break;
+ sd->state = sd_sendingdata_state;
+ memcpy(sd->data, sd->cid, 16);
+ sd->data_start = req.arg;
+ sd->data_offset = 0;
+ return sd_r1;
+
default:
break;
}
break;
case 11: /* CMD11: READ_DAT_UNTIL_STOP */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_sendingdata_state;
break;
case 15: /* CMD15: GO_INACTIVE_STATE */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->mode) {
case sd_data_transfer_mode:
if (sd->rca != rca)
/* Block write commands (Class 4) */
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
switch (sd->state) {
case sd_transfer_state:
+ /* Writing in SPI mode not implemented. */
+ if (sd->spi)
+ break;
sd->state = sd_receivingdata_state;
sd->data_start = req.arg;
sd->data_offset = 0;
break;
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
switch (sd->state) {
case sd_transfer_state:
+ /* Writing in SPI mode not implemented. */
+ if (sd->spi)
+ break;
sd->state = sd_receivingdata_state;
sd->data_start = req.arg;
sd->data_offset = 0;
break;
case 26: /* CMD26: PROGRAM_CID */
+ if (sd->spi)
+ goto bad_cmd;
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_receivingdata_state;
break;
case 27: /* CMD27: PROGRAM_CSD */
+ if (sd->spi)
+ goto unimplemented_cmd;
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_receivingdata_state;
/* Lock card commands (Class 7) */
case 42: /* CMD42: LOCK_UNLOCK */
+ if (sd->spi)
+ goto unimplemented_cmd;
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_receivingdata_state;
return sd_r1;
case 56: /* CMD56: GEN_CMD */
- printf("SD: GEN_CMD 0x%08x\n", req.arg);
+ fprintf(stderr, "SD: GEN_CMD 0x%08x\n", req.arg);
switch (sd->state) {
case sd_transfer_state:
break;
default:
+ bad_cmd:
sd->card_status |= ILLEGAL_COMMAND;
- printf("SD: Unknown CMD%i\n", req.cmd);
+ fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd);
+ return sd_r0;
+
+ unimplemented_cmd:
+ /* Commands that are recognised but not yet implemented in SPI mode. */
+ sd->card_status |= ILLEGAL_COMMAND;
+ fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd);
return sd_r0;
}
sd->card_status |= ILLEGAL_COMMAND;
- printf("SD: CMD%i in a wrong state\n", req.cmd);
+ fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd);
return sd_r0;
}
static sd_rsp_type_t sd_app_command(SDState *sd,
- struct sd_request_s req) {
+ SDRequest req) {
uint32_t rca;
if (sd_cmd_type[req.cmd] == sd_ac || sd_cmd_type[req.cmd] == sd_adtc)
break;
case 41: /* ACMD41: SD_APP_OP_COND */
+ if (sd->spi) {
+ /* SEND_OP_CMD */
+ sd->state = sd_transfer_state;
+ return sd_r1;
+ }
switch (sd->state) {
case sd_idle_state:
/* We accept any voltage. 10000 V is nothing. */
return sd_normal_command(sd, req);
}
- printf("SD: ACMD%i in a wrong state\n", req.cmd);
+ fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd);
return sd_r0;
}
-int sd_do_command(SDState *sd, struct sd_request_s *req,
+int sd_do_command(SDState *sd, SDRequest *req,
uint8_t *response) {
uint32_t last_status = sd->card_status;
sd_rsp_type_t rtype;
int rsplen;
- if (!bdrv_is_inserted(sd->bdrv)) {
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable) {
return 0;
}
sd_cmd_class[req->cmd] == 7 ||
req->cmd == 16 || req->cmd == 55))) {
sd->card_status |= ILLEGAL_COMMAND;
- printf("SD: Card is locked\n");
+ fprintf(stderr, "SD: Card is locked\n");
return 0;
}
- if (last_status & APP_CMD)
+ if (last_status & APP_CMD) {
rtype = sd_app_command(sd, *req);
- else
+ sd->card_status &= ~APP_CMD;
+ } else
rtype = sd_normal_command(sd, *req);
sd->current_cmd = req->cmd;
case sd_r2_i:
memcpy(response, sd->cid, sizeof(sd->cid));
- response[7] |= 1;
rsplen = 16;
break;
case sd_r2_s:
memcpy(response, sd->csd, sizeof(sd->csd));
- response[7] |= 1;
rsplen = 16;
break;
rsplen = 4;
break;
+ case sd_r7:
+ sd_response_r7_make(sd, response);
+ rsplen = 4;
+ break;
+
case sd_r0:
default:
rsplen = 0;
}
/* No real need for 64 bit addresses here */
-static void sd_blk_read(BlockDriverState *bdrv,
- void *data, uint32_t addr, uint32_t len)
+static void sd_blk_read(SDState *sd, 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");
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_read: read error on host side\n");
return;
}
if (end > (addr & ~511) + 512) {
- memcpy(data, buf + (addr & 511), 512 - (addr & 511));
+ memcpy(sd->data, sd->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");
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "sd_blk_read: read error on host side\n");
return;
}
- memcpy(data + 512 - (addr & 511), buf, end & 511);
+ memcpy(sd->data + 512 - (addr & 511), sd->buf, end & 511);
} else
- memcpy(data, buf + (addr & 511), len);
+ memcpy(sd->data, sd->buf + (addr & 511), len);
}
-static void sd_blk_write(BlockDriverState *bdrv,
- void *data, uint32_t addr, uint32_t len)
+static void sd_blk_write(SDState *sd, 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");
+ if (!sd->bdrv || bdrv_read(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "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");
+ memcpy(sd->buf + (addr & 511), sd->data, 512 - (addr & 511));
+ if (bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "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");
+ if (bdrv_read(sd->bdrv, end >> 9, sd->buf, 1) == -1) {
+ fprintf(stderr, "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");
+ memcpy(sd->buf, sd->data + 512 - (addr & 511), end & 511);
+ if (bdrv_write(sd->bdrv, end >> 9, sd->buf, 1) == -1)
+ fprintf(stderr, "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");
+ memcpy(sd->buf + (addr & 511), sd->data, len);
+ if (!sd->bdrv || bdrv_write(sd->bdrv, addr >> 9, sd->buf, 1) == -1)
+ fprintf(stderr, "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 BLK_READ_BLOCK(a, len) sd_blk_read(sd, a, len)
+#define BLK_WRITE_BLOCK(a, len) sd_blk_write(sd, a, len)
#define APP_READ_BLOCK(a, len) memset(sd->data, 0xec, len)
#define APP_WRITE_BLOCK(a, len)
{
int i;
- if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
return;
if (sd->state != sd_receivingdata_state) {
- printf("sd_write_data: not in Receiving-Data state\n");
+ fprintf(stderr, "sd_write_data: not in Receiving-Data state\n");
return;
}
break;
default:
- printf("sd_write_data: unknown command\n");
+ fprintf(stderr, "sd_write_data: unknown command\n");
break;
}
}
/* TODO: Append CRCs */
uint8_t ret;
- if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv))
+ if (!sd->bdrv || !bdrv_is_inserted(sd->bdrv) || !sd->enable)
return 0x00;
if (sd->state != sd_sendingdata_state) {
- printf("sd_read_data: not in Sending-Data state\n");
+ fprintf(stderr, "sd_read_data: not in Sending-Data state\n");
return 0x00;
}
sd->state = sd_transfer_state;
break;
+ case 9: /* CMD9: SEND_CSD */
+ case 10: /* CMD10: SEND_CID */
+ ret = sd->data[sd->data_offset ++];
+
+ if (sd->data_offset >= 16)
+ 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);
break;
default:
- printf("sd_read_data: unknown command\n");
+ fprintf(stderr, "sd_read_data: unknown command\n");
return 0x00;
}
{
return sd->state == sd_sendingdata_state;
}
+
+void sd_enable(SDState *sd, int enable)
+{
+ sd->enable = enable;
+}