SD/MMC patches

- Various improvements for SD cards in SPI mode (Bin Meng)
 - Add Bin Meng as SD/MMC cards co-maintainer
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmAwU0gACgkQ4+MsLN6t
 wN6gUg//TIPzwJSZsJ36S8E0TIflSRRbCBmrvhQF0KeXkaImFx2wOzaeb7eGFhWS
 l12z5tCo6kYgeDzfmcKQdMkKjweP7C3ct/tSytTRdPiApaDvae/nHcxsBKrtHqDU
 otVC6kQ5cB/u/6uf9vDIRrBCqB4AiXrBnL5l/NzDgrUqHkmYbXOjk+K2Xy+2MkYw
 Vfwzdh50gdFgYDQW2nM9GfD1VRq5XAtzCNXjxQhwBZkQLQ8G7KLMVE8MjzFjw3Vt
 PDXzfD8zWZoHM3awC34imWnJC+br0h0NNQpTkRj5DBcYpYYwo4FkBmr9pE2LuwYG
 dma0TalP+/gnlbmJr+Wq9wChGkmPmuyHfBanbgEtmA5cuNB/YRfWPaHqCvvCXSvk
 4+UF0xgGQG3kuSUbeujVuRuak+/2a7f30fxK3EZ579L2TVbnUdloQoyVgoIr7xTd
 DWMsA0AjPNcvq4vA9myoFkj8/GWcT2jiYGGLQGIfnqcbMc2ii9E9wBibsAkuAnHb
 OvO6xdEWEui8LbTGJZ/PpMd68MWIcGQk65Su+53Ls5oPkOCoNNRBQ2eHMuObDDSm
 kNyQ5QgQed345U6W7gFp+krdOtBWbXM2X24eIo9lQ2U59kitdE1DPYgmTzO4SNSz
 3uM4GiNd3i13fZshH+vUZJrJ87PliCVsQfazIOeeVBFoms/Cz7o=
 =LijF
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sdmmc-20210220' into staging

SD/MMC patches

- Various improvements for SD cards in SPI mode (Bin Meng)
- Add Bin Meng as SD/MMC cards co-maintainer

# gpg: Signature made Sat 20 Feb 2021 00:09:44 GMT
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD  6BB2 E3E3 2C2C DEAD C0DE

* remotes/philmd-gitlab/tags/sdmmc-20210220:
  MAINTAINERS: Add Bin Meng as co-maintainer for SD/MMC cards
  hw/sd: sdhci: Simplify updating s->prnsts in sdhci_sdma_transfer_multi_blocks()
  hw/sd: sd: Bypass the RCA check for CMD13 in SPI mode
  hw/sd: sd: Skip write protect groups check in CMD24/25 for high capacity cards
  hw/sd: sd: Skip write protect groups check in sd_erase() for high capacity cards
  hw/sd: sd: Move the sd_block_{read, write} and macros ahead
  hw/sd: sd: Fix CMD30 response type
  hw/sd: sd: Only SDSC cards support CMD28/29/30
  hw/sd: sd: Fix address check in sd_erase()
  hw/sd: ssi-sd: Handle the rest commands with R1b response type
  hw/sd: ssi-sd: Fix STOP_TRANSMISSION (CMD12) response
  hw/sd: ssi-sd: Fix SEND_IF_COND (CMD8) response
  hw/sd: ssi-sd: Support multiple block write
  hw/sd: ssi-sd: Support single block write
  hw/sd: Introduce receive_ready() callback
  hw/sd: sd: Allow single/multiple block write for SPI mode
  hw/sd: sd: Remove duplicated codes in single/multiple block read/write
  hw/sd: ssi-sd: Support multiple block read

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-02-20 19:28:26 +00:00
commit a528b8c4c6
6 changed files with 199 additions and 109 deletions

View File

@ -1754,6 +1754,7 @@ F: hw/ssi/xilinx_*
SD (Secure Card)
M: Philippe Mathieu-Daudé <f4bug@amsat.org>
M: Bin Meng <bin.meng@windriver.com>
L: qemu-block@nongnu.org
S: Odd Fixes
F: include/hw/sd/sd*

View File

@ -160,6 +160,19 @@ void sdbus_read_data(SDBus *sdbus, void *buf, size_t length)
}
}
bool sdbus_receive_ready(SDBus *sdbus)
{
SDState *card = get_card(sdbus);
if (card) {
SDCardClass *sc = SD_CARD_GET_CLASS(card);
return sc->receive_ready(card);
}
return false;
}
bool sdbus_data_ready(SDBus *sdbus)
{
SDState *card = get_card(sdbus);

View File

@ -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;

View File

@ -596,9 +596,9 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
page_aligned = true;
}
s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
if (s->trnmod & SDHC_TRNS_READ) {
s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT |
SDHC_DAT_LINE_ACTIVE;
s->prnsts |= SDHC_DOING_READ;
while (s->blkcnt) {
if (s->data_count == 0) {
sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
@ -625,8 +625,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s)
}
}
} else {
s->prnsts |= SDHC_DOING_WRITE | SDHC_DATA_INHIBIT |
SDHC_DAT_LINE_ACTIVE;
s->prnsts |= SDHC_DOING_WRITE;
while (s->blkcnt) {
begin = s->data_count;
if (((boundary_count + begin) < block_size) && page_aligned) {

View File

@ -4,6 +4,11 @@
* Copyright (c) 2007-2009 CodeSourcery.
* Written by Paul Brook
*
* Copyright (c) 2021 Wind River Systems, Inc.
* Improved by Bin Meng <bin.meng@windriver.com>
*
* Validated with U-Boot v2021.01 and Linux v5.10 mmc_spi driver
*
* This code is licensed under the GNU GPL v2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
@ -43,6 +48,8 @@ typedef enum {
SSI_SD_DATA_START,
SSI_SD_DATA_READ,
SSI_SD_DATA_CRC16,
SSI_SD_DATA_WRITE,
SSI_SD_SKIP_CRC16,
} ssi_sd_mode;
struct ssi_sd_state {
@ -52,6 +59,8 @@ struct ssi_sd_state {
uint8_t cmdarg[4];
uint8_t response[5];
uint16_t crc16;
int32_t read_bytes;
int32_t write_bytes;
int32_t arglen;
int32_t response_pos;
int32_t stopping;
@ -78,37 +87,86 @@ OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD)
#define SSI_SDR_ADDRESS_ERROR 0x2000
#define SSI_SDR_PARAMETER_ERROR 0x4000
/* multiple block write */
#define SSI_TOKEN_MULTI_WRITE 0xfc
/* terminate multiple block write */
#define SSI_TOKEN_STOP_TRAN 0xfd
/* single block read/write, multiple block read */
#define SSI_TOKEN_SINGLE 0xfe
/* dummy value - don't care */
#define SSI_DUMMY 0xff
/* data accepted */
#define DATA_RESPONSE_ACCEPTED 0x05
static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
{
ssi_sd_state *s = SSI_SD(dev);
SDRequest request;
uint8_t longresp[16];
/* Special case: allow CMD12 (STOP TRANSMISSION) while reading data. */
if (s->mode == SSI_SD_DATA_READ && val == 0x4c) {
s->mode = SSI_SD_CMD;
/* There must be at least one byte delay before the card responds. */
s->stopping = 1;
/*
* Special case: allow CMD12 (STOP TRANSMISSION) while reading data.
*
* See "Physical Layer Specification Version 8.00" chapter 7.5.2.2,
* to avoid conflict between CMD12 response and next data block,
* timing of CMD12 should be controlled as follows:
*
* - CMD12 issued at the timing that end bit of CMD12 and end bit of
* data block is overlapped
* - CMD12 issued after one clock cycle after host receives a token
* (either Start Block token or Data Error token)
*
* We need to catch CMD12 in all of the data read states.
*/
if (s->mode >= SSI_SD_PREP_DATA && s->mode <= SSI_SD_DATA_CRC16) {
if (val == 0x4c) {
s->mode = SSI_SD_CMD;
/* There must be at least one byte delay before the card responds */
s->stopping = 1;
}
}
switch (s->mode) {
case SSI_SD_CMD:
if (val == SSI_DUMMY) {
switch (val) {
case SSI_DUMMY:
DPRINTF("NULL command\n");
return SSI_DUMMY;
break;
case SSI_TOKEN_SINGLE:
case SSI_TOKEN_MULTI_WRITE:
DPRINTF("Start write block\n");
s->mode = SSI_SD_DATA_WRITE;
return SSI_DUMMY;
case SSI_TOKEN_STOP_TRAN:
DPRINTF("Stop multiple write\n");
/* manually issue cmd12 to stop the transfer */
request.cmd = 12;
request.arg = 0;
s->arglen = sdbus_do_command(&s->sdbus, &request, longresp);
if (s->arglen <= 0) {
s->arglen = 1;
/* a zero value indicates the card is busy */
s->response[0] = 0;
DPRINTF("SD card busy\n");
} else {
s->arglen = 1;
/* a non-zero value indicates the card is ready */
s->response[0] = SSI_DUMMY;
}
return SSI_DUMMY;
}
s->cmd = val & 0x3f;
s->mode = SSI_SD_CMDARG;
s->arglen = 0;
return SSI_DUMMY;
case SSI_SD_CMDARG:
if (s->arglen == 4) {
SDRequest request;
uint8_t longresp[16];
/* FIXME: Check CRC. */
request.cmd = s->cmd;
request.arg = ldl_be_p(s->cmdarg);
@ -118,9 +176,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
s->arglen = 1;
s->response[0] = 4;
DPRINTF("SD command failed\n");
} else if (s->cmd == 58) {
/* CMD58 returns R3 response (OCR) */
DPRINTF("Returned OCR\n");
} else if (s->cmd == 8 || s->cmd == 58) {
/* CMD8/CMD58 returns R3/R7 response */
DPRINTF("Returned R3/R7\n");
s->arglen = 5;
s->response[0] = 1;
memcpy(&s->response[1], longresp, 4);
@ -136,6 +194,12 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
/* CMD13 returns a 2-byte statuse work. Other commands
only return the first byte. */
s->arglen = (s->cmd == 13) ? 2 : 1;
/* handle R1b */
if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) {
s->stopping = 1;
}
cardstatus = ldl_be_p(longresp);
status = 0;
if (((cardstatus >> 9) & 0xf) < 4)
@ -185,14 +249,15 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
s->mode = SSI_SD_RESPONSE;
return SSI_DUMMY;
case SSI_SD_RESPONSE:
if (s->stopping) {
s->stopping = 0;
return SSI_DUMMY;
}
if (s->response_pos < s->arglen) {
DPRINTF("Response 0x%02x\n", s->response[s->response_pos]);
return s->response[s->response_pos++];
}
if (s->stopping) {
s->stopping = 0;
s->mode = SSI_SD_CMD;
return SSI_DUMMY;
}
if (sdbus_data_ready(&s->sdbus)) {
DPRINTF("Data read\n");
s->mode = SSI_SD_DATA_START;
@ -212,8 +277,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
return SSI_TOKEN_SINGLE;
case SSI_SD_DATA_READ:
val = sdbus_read_byte(&s->sdbus);
s->read_bytes++;
s->crc16 = crc_ccitt_false(s->crc16, (uint8_t *)&val, 1);
if (!sdbus_data_ready(&s->sdbus)) {
if (!sdbus_data_ready(&s->sdbus) || s->read_bytes == 512) {
DPRINTF("Data read end\n");
s->mode = SSI_SD_DATA_CRC16;
}
@ -224,10 +290,36 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val)
s->response_pos++;
if (s->response_pos == 2) {
DPRINTF("CRC16 read end\n");
s->mode = SSI_SD_CMD;
if (s->read_bytes == 512 && s->cmd != 17) {
s->mode = SSI_SD_PREP_DATA;
} else {
s->mode = SSI_SD_CMD;
}
s->read_bytes = 0;
s->response_pos = 0;
}
return val;
case SSI_SD_DATA_WRITE:
sdbus_write_byte(&s->sdbus, val);
s->write_bytes++;
if (!sdbus_receive_ready(&s->sdbus) || s->write_bytes == 512) {
DPRINTF("Data write end\n");
s->mode = SSI_SD_SKIP_CRC16;
s->response_pos = 0;
}
return val;
case SSI_SD_SKIP_CRC16:
/* we don't verify the crc16 */
s->response_pos++;
if (s->response_pos == 2) {
DPRINTF("CRC16 receive end\n");
s->mode = SSI_SD_RESPONSE;
s->write_bytes = 0;
s->arglen = 1;
s->response[0] = DATA_RESPONSE_ACCEPTED;
s->response_pos = 0;
}
return SSI_DUMMY;
}
/* Should never happen. */
return SSI_DUMMY;
@ -237,7 +329,7 @@ static int ssi_sd_post_load(void *opaque, int version_id)
{
ssi_sd_state *s = (ssi_sd_state *)opaque;
if (s->mode > SSI_SD_DATA_CRC16) {
if (s->mode > SSI_SD_SKIP_CRC16) {
return -EINVAL;
}
if (s->mode == SSI_SD_CMDARG &&
@ -255,8 +347,8 @@ static int ssi_sd_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_ssi_sd = {
.name = "ssi_sd",
.version_id = 5,
.minimum_version_id = 5,
.version_id = 7,
.minimum_version_id = 7,
.post_load = ssi_sd_post_load,
.fields = (VMStateField []) {
VMSTATE_UINT32(mode, ssi_sd_state),
@ -264,6 +356,8 @@ static const VMStateDescription vmstate_ssi_sd = {
VMSTATE_UINT8_ARRAY(cmdarg, ssi_sd_state, 4),
VMSTATE_UINT8_ARRAY(response, ssi_sd_state, 5),
VMSTATE_UINT16(crc16, ssi_sd_state),
VMSTATE_INT32(read_bytes, ssi_sd_state),
VMSTATE_INT32(write_bytes, ssi_sd_state),
VMSTATE_INT32(arglen, ssi_sd_state),
VMSTATE_INT32(response_pos, ssi_sd_state),
VMSTATE_INT32(stopping, ssi_sd_state),
@ -316,6 +410,8 @@ static void ssi_sd_reset(DeviceState *dev)
memset(s->cmdarg, 0, sizeof(s->cmdarg));
memset(s->response, 0, sizeof(s->response));
s->crc16 = 0;
s->read_bytes = 0;
s->write_bytes = 0;
s->arglen = 0;
s->response_pos = 0;
s->stopping = 0;

View File

@ -116,6 +116,7 @@ struct SDCardClass {
* Return: byte value read
*/
uint8_t (*read_byte)(SDState *sd);
bool (*receive_ready)(SDState *sd);
bool (*data_ready)(SDState *sd);
void (*set_voltage)(SDState *sd, uint16_t millivolts);
uint8_t (*get_dat_lines)(SDState *sd);
@ -187,6 +188,7 @@ void sdbus_write_data(SDBus *sdbus, const void *buf, size_t length);
* Read multiple bytes of data on the data lines of a SD bus.
*/
void sdbus_read_data(SDBus *sdbus, void *buf, size_t length);
bool sdbus_receive_ready(SDBus *sd);
bool sdbus_data_ready(SDBus *sd);
bool sdbus_get_inserted(SDBus *sd);
bool sdbus_get_readonly(SDBus *sd);