aboutsummaryrefslogtreecommitdiff
path: root/hw/sd/sd.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/sd/sd.c')
-rw-r--r--hw/sd/sd.c149
1 files changed, 64 insertions, 85 deletions
diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index 8517dbce8b..8b397effbc 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -739,11 +739,33 @@ void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert)
qemu_set_irq(insert, sd->blk ? blk_is_inserted(sd->blk) : 0);
}
+static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
+{
+ trace_sdcard_read_block(addr, len);
+ if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) {
+ fprintf(stderr, "sd_blk_read: read error on host side\n");
+ }
+}
+
+static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
+{
+ trace_sdcard_write_block(addr, len);
+ if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) {
+ fprintf(stderr, "sd_blk_write: write error on host side\n");
+ }
+}
+
+#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)
+
static void sd_erase(SDState *sd)
{
int i;
uint64_t erase_start = sd->erase_start;
uint64_t erase_end = sd->erase_end;
+ bool sdsc = true;
trace_sdcard_erase(sd->erase_start, sd->erase_end);
if (sd->erase_start == INVALID_ADDRESS
@@ -758,25 +780,30 @@ static void sd_erase(SDState *sd)
/* High capacity memory card: erase units are 512 byte blocks */
erase_start *= 512;
erase_end *= 512;
+ sdsc = false;
}
- if (sd->erase_start > sd->size || sd->erase_end > sd->size) {
+ if (erase_start > sd->size || erase_end > sd->size) {
sd->card_status |= OUT_OF_RANGE;
sd->erase_start = INVALID_ADDRESS;
sd->erase_end = INVALID_ADDRESS;
return;
}
- erase_start = sd_addr_to_wpnum(erase_start);
- erase_end = sd_addr_to_wpnum(erase_end);
sd->erase_start = INVALID_ADDRESS;
sd->erase_end = INVALID_ADDRESS;
sd->csd[14] |= 0x40;
- for (i = erase_start; i <= erase_end; i++) {
- assert(i < sd->wpgrps_size);
- if (test_bit(i, sd->wp_groups)) {
- sd->card_status |= WP_ERASE_SKIP;
+ /* Only SDSC cards support write protect groups */
+ if (sdsc) {
+ erase_start = sd_addr_to_wpnum(erase_start);
+ erase_end = sd_addr_to_wpnum(erase_end);
+
+ for (i = erase_start; i <= erase_end; i++) {
+ assert(i < sd->wpgrps_size);
+ if (test_bit(i, sd->wp_groups)) {
+ sd->card_status |= WP_ERASE_SKIP;
+ }
}
}
}
@@ -1136,8 +1163,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
case 13: /* CMD13: SEND_STATUS */
switch (sd->mode) {
case sd_data_transfer_mode:
- if (sd->rca != rca)
+ if (!sd->spi && sd->rca != rca) {
return sd_r0;
+ }
return sd_r1;
@@ -1181,24 +1209,6 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
break;
case 17: /* CMD17: READ_SINGLE_BLOCK */
- switch (sd->state) {
- case sd_transfer_state:
-
- if (addr + sd->blk_len > sd->size) {
- sd->card_status |= ADDRESS_ERROR;
- return sd_r1;
- }
-
- sd->state = sd_sendingdata_state;
- sd->data_start = addr;
- sd->data_offset = 0;
- return sd_r1;
-
- default:
- break;
- }
- break;
-
case 18: /* CMD18: READ_MULTIPLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
@@ -1245,41 +1255,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
/* Block write commands (Class 4) */
case 24: /* CMD24: WRITE_SINGLE_BLOCK */
- switch (sd->state) {
- case sd_transfer_state:
- /* Writing in SPI mode not implemented. */
- if (sd->spi)
- break;
-
- if (addr + sd->blk_len > sd->size) {
- sd->card_status |= ADDRESS_ERROR;
- return sd_r1;
- }
-
- sd->state = sd_receivingdata_state;
- sd->data_start = addr;
- sd->data_offset = 0;
- sd->blk_written = 0;
-
- if (sd_wp_addr(sd, sd->data_start)) {
- sd->card_status |= WP_VIOLATION;
- }
- if (sd->csd[14] & 0x30) {
- sd->card_status |= WP_VIOLATION;
- }
- return sd_r1;
-
- default:
- break;
- }
- break;
-
case 25: /* CMD25: WRITE_MULTIPLE_BLOCK */
switch (sd->state) {
case sd_transfer_state:
- /* Writing in SPI mode not implemented. */
- if (sd->spi)
- break;
if (addr + sd->blk_len > sd->size) {
sd->card_status |= ADDRESS_ERROR;
@@ -1291,8 +1269,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
sd->data_offset = 0;
sd->blk_written = 0;
- if (sd_wp_addr(sd, sd->data_start)) {
- sd->card_status |= WP_VIOLATION;
+ if (sd->size <= SDSC_MAX_CAPACITY) {
+ if (sd_wp_addr(sd, sd->data_start)) {
+ sd->card_status |= WP_VIOLATION;
+ }
}
if (sd->csd[14] & 0x30) {
sd->card_status |= WP_VIOLATION;
@@ -1334,6 +1314,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
/* Write protection (Class 6) */
case 28: /* CMD28: SET_WRITE_PROT */
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
+
switch (sd->state) {
case sd_transfer_state:
if (addr >= sd->size) {
@@ -1353,6 +1337,10 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
break;
case 29: /* CMD29: CLR_WRITE_PROT */
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
+
switch (sd->state) {
case sd_transfer_state:
if (addr >= sd->size) {
@@ -1372,13 +1360,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req)
break;
case 30: /* CMD30: SEND_WRITE_PROT */
+ if (sd->size > SDSC_MAX_CAPACITY) {
+ return sd_illegal;
+ }
+
switch (sd->state) {
case sd_transfer_state:
sd->state = sd_sendingdata_state;
*(uint32_t *) sd->data = sd_wpbits(sd, req.arg);
sd->data_start = addr;
sd->data_offset = 0;
- return sd_r1b;
+ return sd_r1;
default:
break;
@@ -1792,27 +1784,6 @@ send_response:
return rsplen;
}
-static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len)
-{
- trace_sdcard_read_block(addr, len);
- if (!sd->blk || blk_pread(sd->blk, addr, sd->data, len) < 0) {
- fprintf(stderr, "sd_blk_read: read error on host side\n");
- }
-}
-
-static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
-{
- trace_sdcard_write_block(addr, len);
- if (!sd->blk || blk_pwrite(sd->blk, addr, sd->data, len, 0) < 0) {
- fprintf(stderr, "sd_blk_write: write error on host side\n");
- }
-}
-
-#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)
-
void sd_write_byte(SDState *sd, uint8_t value)
{
int i;
@@ -1853,9 +1824,11 @@ void sd_write_byte(SDState *sd, uint8_t value)
sd->card_status |= ADDRESS_ERROR;
break;
}
- if (sd_wp_addr(sd, sd->data_start)) {
- sd->card_status |= WP_VIOLATION;
- break;
+ if (sd->size <= SDSC_MAX_CAPACITY) {
+ if (sd_wp_addr(sd, sd->data_start)) {
+ sd->card_status |= WP_VIOLATION;
+ break;
+ }
}
}
sd->data[sd->data_offset++] = value;
@@ -2087,6 +2060,11 @@ uint8_t sd_read_byte(SDState *sd)
return ret;
}
+static bool sd_receive_ready(SDState *sd)
+{
+ return sd->state == sd_receivingdata_state;
+}
+
static bool sd_data_ready(SDState *sd)
{
return sd->state == sd_sendingdata_state;
@@ -2197,6 +2175,7 @@ static void sd_class_init(ObjectClass *klass, void *data)
sc->do_command = sd_do_command;
sc->write_byte = sd_write_byte;
sc->read_byte = sd_read_byte;
+ sc->receive_ready = sd_receive_ready;
sc->data_ready = sd_data_ready;
sc->enable = sd_enable;
sc->get_inserted = sd_get_inserted;