sdspi: fix compatibility issue in multi block read

SDSPI driver optimized polling of the response tokens by requesting
two extra bytes on top of the block size (512) and CRC (2), and
checking whether these bytes contained the data response token or
not. In case the token was there, further polling would not need to
happen, thereby reducing latency between two consecutive blocks
transferred. However this caused compatibility issues when these two
extra bytes were sent after reading the final block. When
STOP_TRANSMISSION command was sent, these extra two bytes were
treated as part of the command, causing an invalid command error.

This fixes the logic by only requesting extra two bytes if the block
being read is not the final block. In addition to that, more strict
error checking is implemented for command response tokens.
This commit is contained in:
Ivan Grokhotkov 2018-03-12 15:47:06 +08:00
parent 229f67b816
commit 4a2489b99a

View file

@ -34,6 +34,8 @@
#define GPIO_UNUSED 0xff //!< Flag indicating that CD/WP is unused
/// Size of the buffer returned by get_block_buf
#define SDSPI_BLOCK_BUF_SIZE (SDSPI_MAX_DATA_LEN + 4)
/// Maximum number of dummy bytes between the request and response (minimum is 1)
#define SDSPI_RESPONSE_MAX_DELAY 8
/// Structure containing run time configuration for a single SD slot
@ -422,6 +424,30 @@ static esp_err_t start_command_default(int slot, int flags, sdspi_hw_cmd_t *cmd)
.rx_buffer = cmd
};
esp_err_t ret = spi_device_transmit(spi_handle(slot), &t);
if (cmd->cmd_index == MMC_STOP_TRANSMISSION) {
/* response is a stuff byte from previous transfer, ignore it */
cmd->r1 = 0xff;
}
int response_delay_bytes = SDSPI_RESPONSE_MAX_DELAY;
while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && response_delay_bytes-- > 0) {
spi_transaction_t* t = get_transaction(slot);
*t = (spi_transaction_t) {
.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA,
.length = 8,
};
t->tx_data[0] = 0xff;
ret = spi_device_transmit(spi_handle(slot), t);
uint8_t r1 = t->rx_data[0];
release_transaction(slot);
if (ret != ESP_OK) {
return ret;
}
cmd->r1 = r1;
}
if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) {
ESP_LOGD(TAG, "%s: no response token found", __func__);
return ESP_ERR_TIMEOUT;
}
return ret;
}
@ -564,6 +590,9 @@ static esp_err_t poll_data_token(int slot, spi_transaction_t* t,
* indicating the start of the next block. Actual scanning is done by
* setting pre_scan_data_ptr to point to these last 2 bytes, and setting
* pre_scan_data_size = 2, then going to step 2 to receive the next block.
* When the final block is being received, the number of extra bytes is 2
* (only for CRC), because we don't need to wait for start token of the
* next block, and some cards are getting confused by these two extra bytes.
*
* With this approach the delay between blocks of a multi-block transfer is
* ~95 microseconds, out of which 35 microseconds are spend doing the CRC check.
@ -575,9 +604,8 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
{
bool need_stop_command = rx_length > SDSPI_MAX_DATA_LEN;
spi_transaction_t* t_command = get_transaction(slot);
const int cmd_extra_bytes = 8;
*t_command = (spi_transaction_t) {
.length = (SDSPI_CMD_R1_SIZE + cmd_extra_bytes) * 8,
.length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8,
.tx_buffer = cmd,
.rx_buffer = cmd,
};
@ -588,7 +616,7 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
release_transaction(slot);
uint8_t* cmd_u8 = (uint8_t*) cmd;
size_t pre_scan_data_size = cmd_extra_bytes;
size_t pre_scan_data_size = SDSPI_RESPONSE_MAX_DELAY;
uint8_t* pre_scan_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE;
/* R1 response is delayed by 1-8 bytes from the request.
@ -640,7 +668,7 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
}
// receive actual data
const size_t receive_extra_bytes = 4;
const size_t receive_extra_bytes = (rx_length > SDSPI_MAX_DATA_LEN) ? 4 : 2;
memset(rx_data, 0xff, will_receive + receive_extra_bytes);
spi_transaction_t* t_data = get_transaction(slot);
*t_data = (spi_transaction_t) {
@ -695,6 +723,9 @@ static esp_err_t start_command_read_blocks(int slot, sdspi_hw_cmd_t *cmd,
if (ret != ESP_OK) {
return ret;
}
if (stop_cmd.r1 != 0) {
ESP_LOGD(TAG, "%s: STOP_TRANSMISSION response 0x%02x", __func__, stop_cmd.r1);
}
spi_transaction_t* t_poll = get_transaction(slot);
ret = poll_busy(slot, t_poll, cmd->timeout_ms);
release_transaction(slot);