Files
fdskey/FdsKey_bootloader/Core/Src/sdcard.c
2023-08-06 15:38:48 +04:00

715 lines
18 KiB
C

#include "sdcard.h"
#include "string.h"
static uint8_t sd_high_capacity;
static uint32_t sd_spi_speed;
static void SPI_transmit_receive(uint8_t* tx, uint8_t* rx, size_t buff_size)
{
HAL_SPI_TransmitReceive(&SD_SPI_PORT, tx, rx, buff_size, SD_TIMEOUT);
}
static void SPI_transmit(uint8_t* tx, size_t buff_size)
{
HAL_SPI_Transmit(&SD_SPI_PORT, tx, buff_size, SD_TIMEOUT);
}
static void SD_read_bytes(uint8_t *buff, size_t buff_size)
{
// make sure FF is transmitted during receive
uint8_t tx = 0xFF;
while (buff_size > 0)
{
SPI_transmit_receive(&tx, buff, 1);
buff++;
buff_size--;
}
}
static void SD_select()
{
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);
delay_us(10); // entry guard time for some SD cards
}
static void SD_unselect()
{
HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_SET);
delay_us(10); // exit guard time for some SD cards
}
static void SD_unselect_purge()
{
SD_unselect();
uint8_t tx = 0xFF;
SPI_transmit(&tx, 1);
}
static SD_RESULT SD_wait_not_busy()
{
uint32_t start_time = HAL_GetTick();
uint8_t busy;
do
{
if (HAL_GetTick() >= start_time + SD_TIMEOUT)
return SD_RES_BUSY_TIMEOUT;
SD_unselect();
SD_select();
SD_read_bytes(&busy, sizeof(busy));
} while (busy != 0xFF);
return SD_RES_OK;
}
static SD_RESULT SD_read_r1(uint8_t *r1)
{
/*
* R1: 0abcdefg
* |||||||+- 0th bit (g): Card is in idle state
* ||||||+-- 1th bit (f): Erase sequence cleared
* |||||+--- 2th bit (e): Illegal command detected
* ||||+---- 3th bit (d): CRC check error
* |||+----- 4th bit (c): Error in the sequence of erase commands
* ||+------ 5th bit (b): Misaligned address used in command
* |+------- 6th bit (a): Command argument outside allowed range
* +-------- 7th bit is always zero
*/
uint8_t tx = 0xFF;
int i = 0;
for (i = 0; i < SD_R1_ANSWER_RETRY_COUNT; i++)
{
SPI_transmit_receive(&tx, r1, sizeof(uint8_t));
if (!(*r1 & (1 << 7)))
return SD_RES_OK;
}
return SD_R1_FAILED;
}
static SD_RESULT SD_read_rx(uint8_t *rx, uint8_t x)
{
SD_RESULT r;
int i;
uint8_t tx = 0xFF;
r = SD_read_r1(rx);
if (r != SD_RES_OK)
return SD_R1_FAILED;
rx++;
for (i = 0; i < x; i++, rx++)
{
SPI_transmit_receive(&tx, rx, sizeof(uint8_t));
}
return SD_RES_OK;
}
static SD_RESULT SD_read_r3(uint8_t *r3)
{
return SD_read_rx(r3, 4);
}
static SD_RESULT SD_read_r7(uint8_t *r7)
{
return SD_read_rx(r7, 4);
}
static void SD_send_cmd(uint8_t command, uint32_t arg, uint8_t crc)
{
uint8_t cmd[] = { 0x40 | command, (arg >> 24) & 0xFF, (arg >> 16) & 0xFF, (arg >> 8) & 0xFF, arg & 0xFF, (crc << 1) | 1 };
SD_select();
SPI_transmit((uint8_t*) cmd, sizeof(cmd));
}
static SD_RESULT SD_wait_data_token()
{
uint8_t fb;
uint8_t tx = 0xFF; // make sure FF is transmitted during receive
uint32_t start_time = HAL_GetTick();
while (1)
{
SPI_transmit_receive(&tx, &fb, sizeof(fb));
if (fb == SD_DATA_TOKEN)
break;
if (fb != 0xFF)
return SD_RES_DATA_TOKEN_WRONG;
if (HAL_GetTick() >= start_time + SD_TIMEOUT)
return SD_RES_DATA_TOKEN_TIMEOUT;
}
return SD_RES_OK;
}
static SD_RESULT SD_init_app_op_cond(uint32_t arg)
{
SD_RESULT r;
uint8_t r1;
uint32_t start_time = HAL_GetTick();
while (1)
{
// CMD55 (APP_CMD) before any ACMD command
SD_send_cmd(55, 0, 0xFF);
r = SD_read_r1(&r1);
SD_unselect_purge();
if (r != SD_RES_OK)
return SD_RES_CMD55_R1_FAILED;
// ACMD41 - send operating condition
SD_send_cmd(41, arg, 0xFF);
r = SD_read_r1(&r1);
SD_unselect_purge();
if (r != SD_RES_OK)
return SD_RES_ACMD41_R1_FAILED;
if (r1 == 0x00)
return SD_RES_OK; // initialization finished
if (HAL_GetTick() >= start_time + SD_ACMD41_TIMEOUT)
return SD_RES_DATA_TOKEN_TIMEOUT;
HAL_Delay(1);
}
return SD_RES_ACMD41_TIMEOUT;
}
SD_RESULT SD_init()
{
SD_RESULT r;
uint8_t r1;
uint8_t r3[5];
uint8_t r7[5];
int i;
SD_select();
SD_unselect();
HAL_Delay(1);
// 80 clock pulses to enter SPI mode
uint8_t high = 0xFF;
for (i = 0; i < 10; i++)
{
SPI_transmit(&high, sizeof(high));
}
// CMD0 - reset card
for (i = 0; ; i++)
{
SD_send_cmd(0, 0x00000000, 0x4A);
r = SD_read_r1(&r1);
SD_unselect_purge();
if (r != SD_RES_OK)
return SD_RES_CMD0_R1_FAILED;
if (r1 == SD_R1_IDLE)
break;
if (i == SD_CMD0_RETRY_COUNT - 1)
return SD_RES_CMD0_COUNT_FAILED;
}
/*
* CMD8 - send interface condition
*
* 0th byte: R1 response
*
* 1st byte: Command version
* rrrrmnop (7th bit to 0th bit)
* ||||||||
* |||||+++-- 3th to 0th (m-p): Command version. It corresponds to the command version in the argument of the CMD8 command.
* ++++------ Reserved
*
* 2nd byte: Reserved and always 0
*
* 3nd byte: Reserved and always 0
*
* 4rd byte: Voltage acceptance
* rrrrrvwx (7th bit to 0th bit)
* ||||||||
* |||||+++-- 3th to 0th bits (x-u): Voltage acceptance (VHS). It echoes back the VHS field in the argument of the CMD8 command.
* ||||| 0b0001: The card supports 2.7-3.6V range.
* ||||| 0b0010: The card supports low voltage range 1.65-1.95V.
* ||||| 0b0100 and 0b1000: Reserved for future use.
* +++++----- Reserved
*
* 5th byte: Check pattern echo
* yzABCDEF (7th bit to 0th bit)
* ||||||||
* ++++++++-- 7th to 0th bits (F-y): Echo of check pattern. This field is an echo-back of the check pattern set in the argument of the CMD8 command.
*/
SD_send_cmd(8, 0x000001AA, 0x43); // request 2.7-3.63v
r = SD_read_r7(r7);
SD_unselect_purge();
if (r != SD_RES_OK)
return SD_RES_CMD8_R7_FAILED;
if (r7[0] & SD_R1_ILLEGAL_COMMAND)
{
// command not supported - old SD card version
sd_high_capacity = 0;
r = SD_init_app_op_cond(0);
if (r != SD_RES_OK)
return r;
} else if (r7[0] == SD_R1_IDLE)
{
// new version
// check that voltage accepted and echo byte
if (((r7[3] & 0x01) != 1) || (r7[4] != 0xAA))
return SD_RES_CMD8_VOLTAGE_FAILED;
r = SD_init_app_op_cond(0x40000000);
if (r != SD_RES_OK)
return r;
/*
* CMD58 - Read OCR
*
* 0th byte: R1 response
*
* 1st byte: OCR register [31:24]
* IJKrrrrP (7th bit to 0th bit)
* ||||||||
* |||||||+-- 0th bit (P): Switching to 1.8V Accepted (S18A).
* |||++++--- Reserved.
* ||+------- 5th (K) bits: UHS-II Card Status.
* |+-------- 6th (J) bits: Card Capacity Status (CCS).
* +--------- 7th (I) bits: Card power up status bit (busy).
*
*
* 2nd byte: OCR register [23:16]
* QRSTUVWX (7th bit to 0th bit)
* ||||||||
* |||||||+-- 0th bit (X): Voltage window 2.8-2.9V.
* ||||||+--- 1th bit (W): Voltage window 2.9-3.0V.
* |||||+---- 2th bit (V): Voltage window 3.0-3.1V.
* ||||+----- 3th bit (U): Voltage window 3.1-3.2V.
* |||+------ 4th bit (T): Voltage window 3.2-3.3V.
* ||+------- 5th bit (S): Voltage window 3.3-3.4V.
* |+-------- 6th bit (R): Voltage window 3.4-3.5V.
* +--------- 7th bit (Q): Voltage window 3.5-3.6V.
*
* 3rd byte: OCR register [15:8]
* Yrrrrrrr (7th bit to 0th bit)
* ||||||||
* |+++++++-- Reserved
* +--------- 7th bit (Y): Voltage window 2.7-2.8V.
*
* 4th byte: OCR register [7:0]
* grrrrrrr (7th bit to 0th bit)
* ||||||||
* |+++++++-- Reserved
* +--------- 7th bit (g): Reserved for Low Voltage Range.
*/
SD_send_cmd(58, 0x00000000, 0xFF);
r = SD_read_r3(r3);
SD_unselect_purge();
if (r != SD_RES_OK)
return SD_RES_CMD58_R3_FAILED;
if (r3[0] != 0)
return SD_RES_CMD58_GEN_FAILED;
// another voltage check
if (!(r3[2] & (0b00110000))) // 3.2-3.3V or 3.3-3.4V
return SD_RES_CMD58_VOLTAGE_FAILED;
sd_high_capacity = (r3[1] & 0x40) >> 6;
} else {
return SD_RES_CMD8_GEN_FAILED;
}
// set block length
SD_send_cmd(16, SD_BLOCK_LENGTH, 0xFF);
r = SD_read_r1(&r1);
SD_unselect_purge();
if (r != SD_RES_CMD16_R1_FAILED)
return r;
if (r1 != 0x00)
return SD_RES_CMD16_R1_NOT_NULL;
return SD_RES_OK;
}
// multiple init tries
SD_RESULT SD_init_tries()
{
SD_RESULT r;
int i;
for (i = 0; i < SD_INIT_TRIES; i++)
{
r = SD_init();
if (r == SD_RES_OK)
break;
}
return r;
}
// reduce SPI speed until SD card init is ok
SD_RESULT SD_init_try_speed()
{
SD_RESULT r;
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
sd_spi_speed = SPI_BAUDRATEPRESCALER_2;
HAL_SPI_Init(&SD_SPI_PORT);
r = SD_init_tries();
if (r == SD_RES_OK)
return r;
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
sd_spi_speed = SPI_BAUDRATEPRESCALER_4;
HAL_SPI_Init(&SD_SPI_PORT);
r = SD_init_tries();
if (r == SD_RES_OK)
return r;
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
sd_spi_speed = SPI_BAUDRATEPRESCALER_8;
HAL_SPI_Init(&SD_SPI_PORT);
r = SD_init_tries();
if (r == SD_RES_OK)
return r;
SD_SPI_PORT.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
sd_spi_speed = SPI_BAUDRATEPRESCALER_16;
HAL_SPI_Init(&SD_SPI_PORT);
return SD_init_tries();
}
uint32_t SD_get_spi_speed()
{
return sd_spi_speed;
}
SD_RESULT SD_read_single_block(uint32_t blockNum, uint8_t *buff)
{
SD_RESULT r;
uint8_t crc[2];
uint8_t r1;
if (!sd_high_capacity)
blockNum *= SD_BLOCK_LENGTH;
// CMD17 - send block
SD_send_cmd(17, blockNum, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD17_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD17_R1_NOT_NULL;
r = SD_wait_data_token();
if (r != SD_RES_OK)
return r;
SD_read_bytes(buff, SD_BLOCK_LENGTH);
SD_read_bytes(crc, 2);
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_write_single_block(uint32_t blockNum, const uint8_t *buff)
{
SD_RESULT r;
uint8_t r1;
SD_select();
if (!sd_high_capacity)
blockNum *= SD_BLOCK_LENGTH;
// CMD24 - write block
SD_send_cmd(24, blockNum, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD24_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD24_R1_NOT_NULL;
// send dummy bytes for NWR timing
uint8_t dummy = 0xFF;
SPI_transmit(&dummy, sizeof(dummy));
SPI_transmit(&dummy, sizeof(dummy));
// start token
uint8_t dataToken = SD_DATA_TOKEN;
uint8_t crc[2] = { 0xFF, 0xFF };
SPI_transmit(&dataToken, sizeof(dataToken));
SPI_transmit((uint8_t*)buff, SD_BLOCK_LENGTH);
SPI_transmit(crc, sizeof(crc));
/*
dataResp:
xxx0abc1
010 - Data accepted
101 - Data rejected due to CRC error
110 - Data rejected due to write error
*/
uint8_t dataResp;
SD_read_bytes(&dataResp, sizeof(dataResp));
if ((dataResp & 0x1F) != 0x05)
return SD_RES_CMD24_DATA_REJECTED;
r = SD_wait_not_busy();
if (r != SD_RES_OK)
return SD_RES_CMD24_BUSY_TIMEOUT;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_read_begin(uint32_t blockNum)
{
SD_RESULT r;
uint8_t r1;
SD_select();
if (!sd_high_capacity)
blockNum *= SD_BLOCK_LENGTH;
/* CMD18 (READ_MULTIPLE_BLOCK) command */
SD_send_cmd(18, blockNum, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD18_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD18_R1_NOT_NULL;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_read_data(uint8_t *buff)
{
SD_RESULT r;
uint8_t crc[2];
SD_select();
r = SD_wait_data_token();
if (r != SD_RES_OK)
return r;
SD_read_bytes(buff, SD_BLOCK_LENGTH);
SD_read_bytes(crc, 2);
SD_unselect_purge();
return HAL_OK;
}
SD_RESULT SD_read_end()
{
SD_RESULT r;
uint8_t r1;
SD_select();
// CMD12 - stop transmission
SD_send_cmd(12, 0x00000000, 0xFF);
/*
The received byte immediataly following CMD12 is a stuff byte, it should be
discarded before receive the response of the CMD12
*/
uint8_t stuffByte;
SD_read_bytes(&stuffByte, sizeof(stuffByte));
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD12_R1_FAILED;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_write_begin(uint32_t blockNum)
{
SD_RESULT r;
uint8_t r1;
SD_select();
if (!sd_high_capacity)
blockNum *= SD_BLOCK_LENGTH;
// CMD25 - write multiple blocks
SD_send_cmd(25, blockNum, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD25_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD25_R1_NOT_NULL;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_write_data(const uint8_t *buff)
{
SD_RESULT r;
SD_select();
uint8_t dataToken = SD_SEND_MULTIPLE_DATA_TOKEN;
uint8_t crc[2] = { 0xFF, 0xFF };
SPI_transmit(&dataToken, sizeof(dataToken));
SPI_transmit((uint8_t*)buff, SD_BLOCK_LENGTH);
SPI_transmit(crc, sizeof(crc));
/*
dataResp:
xxx0abc1
010 - Data accepted
101 - Data rejected due to CRC error
110 - Data rejected due to write error
*/
uint8_t dataResp;
SD_read_bytes(&dataResp, sizeof(dataResp));
if ((dataResp & 0x1F) != 0x05)
return SD_RES_WRITE_MULTI_DATA_REJECTED;
r = SD_wait_not_busy();
if (r != SD_RES_OK)
return SD_RES_WRITE_MULTI_BUSY_TIMEOUT;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_write_end()
{
SD_RESULT r;
SD_select();
uint8_t stopTran = SD_STOP_DATA_TOKEN; // stop transaction token for CMD25
SPI_transmit(&stopTran, sizeof(stopTran));
// skip one byte before readyng "busy"
// this is required by the spec and is necessary for some real SD-cards!
uint8_t skipByte;
SD_read_bytes(&skipByte, sizeof(skipByte));
r = SD_wait_not_busy();
if (r != SD_RES_OK)
return SD_RES_WRITE_MULTI_END_NOT_BUSY_TIMEOUT;
SD_unselect_purge();
return SD_RES_OK;
}
SD_RESULT SD_read_CSD(SD_CSD* csd)
{
SD_RESULT r;
uint8_t r1;
uint8_t csd_data[16];
uint8_t crc[2];
// CMD9 - read CSD register and SD card capacity
SD_send_cmd(9, 0x00000000, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD9_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD9_R1_NOT_NULL;
r = SD_wait_data_token();
if (r != SD_RES_OK)
return r;
SD_read_bytes(csd_data, sizeof(csd_data));
SD_read_bytes(crc, sizeof(crc));
SD_unselect_purge();
csd->CSDStruct = (csd_data[0] & 0xC0) >> 6;
csd->Reserved1 = csd_data[0] & 0x3F;
csd->TAAC = csd_data[1];
csd->NSAC = csd_data[2];
csd->MaxBusClkFrec = csd_data[3];
csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4);
csd->RdBlockLen = csd_data[5] & 0x0F;
csd->PartBlockRead = (csd_data[6] & 0x80) >> 7;
csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6;
csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5;
csd->DSRImpl = (csd_data[6] & 0x10) >> 4;
switch (csd->CSDStruct) {
case 0:
// CSD version 1
csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2);
csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) |
((csd_data[8] & 0xC0) >> 6);
csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3;
csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07);
csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5;
csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2;
csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) |
((csd_data[10] & 0x80) >> 7);
break;
case 1:
// CSD version 2
csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) |
((csd_data[7] & 0xC0) >> 6);
csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) |
csd_data[9];
csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8);
break;
default:
return SD_RES_INVALID_CSD_VERSION;
}
csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6;
csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7);
csd->WrProtectGrSize = (csd_data[11] & 0x7F);
csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7;
csd->Reserved2 = (csd_data[12] & 0x60) >> 5;
csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2;
csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6);
csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5;
csd->Reserved3 = (csd_data[13] & 0x1F);
csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7;
csd->CopyFlag = (csd_data[14] & 0x40) >> 6;
csd->PermWrProtect = (csd_data[14] & 0x20) >> 5;
csd->TempWrProtect = (csd_data[14] & 0x10) >> 4;
csd->FileFormat = (csd_data[14] & 0x0C) >> 2;
csd->Reserved4 = (csd_data[14] & 0x03);
csd->crc = (csd_data[15] & 0xFE) >> 1;
csd->Reserved5 = (csd_data[15] & 0x01);
return SD_RES_OK;
}
SD_RESULT SD_read_CID(SD_CID* cid)
{
SD_RESULT r;
uint8_t r1;
uint8_t cid_data[16];
uint8_t crc[2];
// CMD9 - read CSD register and SD card capacity
SD_send_cmd(10, 0x00000000, 0xFF);
r = SD_read_r1(&r1);
if (r != SD_RES_OK)
return SD_RES_CMD10_R1_FAILED;
if (r1 != 0x00)
return SD_RES_CMD10_R1_NOT_NULL;
r = SD_wait_data_token();
if (r != SD_RES_OK)
return r;
SD_read_bytes(cid_data, sizeof(cid_data));
SD_read_bytes(crc, sizeof(crc));
SD_unselect_purge();
cid->ManufacturerID = cid_data[0];
memcpy(cid->OEM_AppliID, cid_data + 1, 2);
cid->OEM_AppliID[2] = 0;
memcpy(cid->ProdName, cid_data + 3, 5);
cid->ProdName[5] = 0;
cid->ProdRev = cid_data[8];
cid->ProdSN = cid_data[9] << 24;
cid->ProdSN |= cid_data[10] << 16;
cid->ProdSN |= cid_data[11] << 8;
cid->ProdSN |= cid_data[12];
cid->Reserved1 = (cid_data[13] & 0xF0) >> 4;
cid->ManufactYear = (cid_data[13] & 0x0F) << 4;
cid->ManufactYear |= (cid_data[14] & 0xF0) >> 4;
cid->ManufactMonth = (cid_data[14] & 0x0F);
cid->CID_CRC = (cid_data[15] & 0xFE) >> 1;
cid->Reserved2 = 1;
return SD_RES_OK;
}
uint64_t SD_read_capacity()
{
SD_CSD csd;
SD_RESULT r;
r = SD_read_CSD(&csd);
if (r == SD_RES_OK)
{
if (sd_high_capacity)
return (uint64_t)(csd.version.v2.DeviceSize + 1) * SD_BLOCK_LENGTH * 1024;
else
return (uint64_t)(csd.version.v1.DeviceSize + 1) * (1UL << (csd.version.v1.DeviceSizeMul + 2)) * (1UL << csd.RdBlockLen);
}
return 0;
}