diff --git a/boards/remote-revb/include/board.h b/boards/remote-revb/include/board.h index 619861cda7..7e1e212e99 100644 --- a/boards/remote-revb/include/board.h +++ b/boards/remote-revb/include/board.h @@ -110,6 +110,19 @@ #define CC1200_GPD2_GPIO GPIO_PB0 /** @} */ +/** + * @name Onboard micro-sd slot pin definitions + * @{ + */ +#define SDCARD_SPI_PARAM_SPI SPI_1 +#define SDCARD_SPI_PARAM_CS GPIO_PIN(0,7) +#define SDCARD_SPI_PARAM_CLK GPIO_PIN(2,4) +#define SDCARD_SPI_PARAM_MOSI GPIO_PIN(2,5) +#define SDCARD_SPI_PARAM_MISO GPIO_PIN(2,6) +#define SDCARD_SPI_PARAM_POWER GPIO_PIN(0,6) +#define SDCARD_SPI_PARAM_POWER_AH false +/** @} */ + #ifdef __cplusplus } /* end extern "C" */ #endif diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index e51c1100ad..d9f48c6291 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -137,6 +137,12 @@ ifneq (,$(filter rgbled,$(USEMODULE))) USEMODULE += color endif +ifneq (,$(filter sdcard_spi,$(USEMODULE))) + FEATURES_REQUIRED += periph_gpio + FEATURES_REQUIRED += periph_spi + USEMODULE += xtimer +endif + ifneq (,$(filter sht11,$(USEMODULE))) USEMODULE += xtimer endif diff --git a/drivers/Makefile.include b/drivers/Makefile.include index 1461d12e78..4838818340 100644 --- a/drivers/Makefile.include +++ b/drivers/Makefile.include @@ -88,3 +88,6 @@ endif ifneq (,$(filter lis3dh,$(USEMODULE))) USEMODULE_INCLUDES += $(RIOTBASE)/drivers/lis3dh/include endif +ifneq (,$(filter sdcard_spi,$(USEMODULE))) + USEMODULE_INCLUDES += $(RIOTBASE)/drivers/sdcard_spi/include +endif diff --git a/drivers/include/sdcard_spi.h b/drivers/include/sdcard_spi.h new file mode 100644 index 0000000000..8775892128 --- /dev/null +++ b/drivers/include/sdcard_spi.h @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup drivers_sdcard_spi SPI SD-Card driver + * @ingroup drivers + * @brief Driver for reading and writing sd-cards via spi interface. + * @{ + * + * @file + * @brief Public interface for the sdcard_spi driver. + * + * @author Michel Rottleuthner + */ + +#ifndef SDCARD_SPI_H +#define SDCARD_SPI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "periph/spi.h" +#include "periph/gpio.h" +#include "stdbool.h" + +#define SD_HC_BLOCK_SIZE (512) /**< size of a single block on SDHC cards */ +#define SDCARD_SPI_INIT_ERROR (-1) /**< returned on failed init */ +#define SDCARD_SPI_OK (0) /**< returned on successful init */ + +#define SD_SIZE_OF_OID 2 /**< OID (OEM/application ID field in CID reg) */ +#define SD_SIZE_OF_PNM 5 /**< PNM (product name field in CID reg) */ + +/** + * @brief CID register see section 5.2 in SD-Spec v5.00 + */ +struct { + uint8_t MID; /**< Manufacturer ID */ + char OID[SD_SIZE_OF_OID]; /**< OEM/Application ID*/ + char PNM[SD_SIZE_OF_PNM]; /**< Product name */ + uint8_t PRV; /**< Product revision */ + uint32_t PSN; /**< Product serial number */ + uint16_t MDT; /**< Manufacturing date */ + uint8_t CID_CRC; /**< CRC7 checksum */ +} typedef cid_t; + +/** + * @brief CSD register with csd structure version 1.0 + * see section 5.3.2 in SD-Spec v5.00 + */ +struct { + uint8_t CSD_STRUCTURE : 2; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t TAAC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t NSAC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t TRAN_SPEED : 8; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint16_t CCC : 12; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t READ_BL_LEN : 4; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t READ_BL_PARTIAL : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t WRITE_BLK_MISALIGN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t READ_BLK_MISALIGN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t DSR_IMP : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint16_t C_SIZE : 12; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t VDD_R_CURR_MIN : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t VDD_R_CURR_MAX : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t VDD_W_CURR_MIN : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t VDD_W_CURR_MAX : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t C_SIZE_MULT : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t ERASE_BLK_EN : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t SECTOR_SIZE : 7; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t WP_GRP_SIZE : 7; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t WP_GRP_ENABLE : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t R2W_FACTOR : 3; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t WRITE_BL_LEN : 4; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t WRITE_BL_PARTIAL : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t FILE_FORMAT_GRP : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t COPY : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t PERM_WRITE_PROTECT : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t TMP_WRITE_PROTECT : 1; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t FILE_FORMAT : 2; /**< see section 5.3.2 in SD-Spec v5.00 */ + uint8_t CSD_CRC : 8; /**< see section 5.3.2 in SD-Spec v5.00 */ +} typedef csd_v1_t; + +/** + * @brief CSD register with csd structure version 2.0 + * see section 5.3.3 in SD-Spec v5.00 + */ +struct { + uint8_t CSD_STRUCTURE : 2; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t TAAC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t NSAC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t TRAN_SPEED : 8; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint16_t CCC : 12; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t READ_BL_LEN : 4; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t READ_BL_PARTIAL : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t WRITE_BLK_MISALIGN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t READ_BLK_MISALIGN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t DSR_IMP : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint32_t C_SIZE : 22; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t ERASE_BLK_EN : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t SECTOR_SIZE : 7; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t WP_GRP_SIZE : 7; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t WP_GRP_ENABLE : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t R2W_FACTOR : 3; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t WRITE_BL_LEN : 4; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t WRITE_BL_PARTIAL : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t FILE_FORMAT_GRP : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t COPY : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t PERM_WRITE_PROTECT : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t TMP_WRITE_PROTECT : 1; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t FILE_FORMAT : 2; /**< see section 5.3.3 in SD-Spec v5.00 */ + uint8_t CSD_CRC : 8; /**< see section 5.3.3 in SD-Spec v5.00 */ +} typedef csd_v2_t; + +/** + * @brief CSD register (see section 5.3 in SD-Spec v5.00) + */ +union { + csd_v1_t v1; /**< see section 5.3.2 in SD-Spec v5.00 */ + csd_v2_t v2; /**< see section 5.3.3 in SD-Spec v5.00 */ +} typedef csd_t; + +/** + * @brief SD status register (see section 4.10.2 in SD-Spec v5.00) + */ +struct { + uint32_t SIZE_OF_PROTECTED_AREA : 32; /**< see section 4.10.2 in SD-Spec v5.00 */ + uint32_t SUS_ADDR : 22; /**< see section 4.10.2.12 in SD-Spec v5.00 */ + uint32_t VSC_AU_SIZE : 10; /**< see section 4.10.2.11 in SD-Spec v5.00 */ + uint16_t SD_CARD_TYPE : 16; /**< see section 4.10.2 in SD-Spec v5.00 */ + uint16_t ERASE_SIZE : 16; /**< see section 4.10.2.5 in SD-Spec v5.00 */ + uint8_t SPEED_CLASS : 8; /**< see section 4.10.2.2 in SD-Spec v5.00 */ + uint8_t PERFORMANCE_MOVE : 8; /**< see section 4.10.2.3 in SD-Spec v5.00 */ + uint8_t VIDEO_SPEED_CLASS : 8; /**< see section 4.10.2.10 in SD-Spec v5.00 */ + uint8_t ERASE_TIMEOUT : 6; /**< see section 4.10.2.6 in SD-Spec v5.00 */ + uint8_t ERASE_OFFSET : 2; /**< see section 4.10.2.7 in SD-Spec v5.00 */ + uint8_t UHS_SPEED_GRADE : 4; /**< see section 4.10.2.8 in SD-Spec v5.00 */ + uint8_t UHS_AU_SIZE : 4; /**< see section 4.10.2.9 in SD-Spec v5.00 */ + uint8_t AU_SIZE : 4; /**< see section 4.10.2.4 in SD-Spec v5.00 */ + uint8_t DAT_BUS_WIDTH : 2; /**< see section 4.10.2 in SD-Spec v5.00 */ + uint8_t SECURED_MODE : 1; /**< see section 4.10.2 in SD-Spec v5.00 */ +} typedef sd_status_t; + +/** + * @brief version type of SD-card + */ +typedef enum { + SD_V2, /**< SD version 2 */ + SD_V1, /**< SD version 1 */ + MMC_V3, /**< MMC version 3 */ + SD_UNKNOWN /**< SD-version unknown */ +} sd_version_t; + +/** + * @brief sdcard_spi r/w-operation return values + */ +typedef enum { + SD_RW_OK = 0, /**< no error */ + SD_RW_NO_TOKEN, /**< no token was received (on block read) */ + SD_RW_TIMEOUT, /**< cmd timed out (not-busy-state wasn't entered) */ + SD_RW_RX_TX_ERROR, /**< error while performing SPI read/write */ + SD_RW_WRITE_ERROR, /**< data-packet response indicates error */ + SD_RW_CRC_MISMATCH, /**< CRC-mismatch of received data */ + SD_RW_NOT_SUPPORTED /**< operation not supported on used card */ +} sd_rw_response_t; + +/** + * @brief sdcard_spi device params + */ +typedef struct { + spi_t spi_dev; /**< SPI bus used */ + gpio_t cs; /**< pin connected to the DAT3 sd pad */ + gpio_t clk; /**< pin connected to the CLK sd pad */ + gpio_t mosi; /**< pin connected to the CMD sd pad*/ + gpio_t miso; /**< pin connected to the DAT0 sd pad*/ + gpio_t power; /**< pin that controls sd power circuit*/ + bool power_act_high; /**< true if card power is enabled by 'power'-pin HIGH*/ +} sdcard_spi_params_t; + +/** + * @brief Device descriptor for sdcard_spi + */ +struct { + sdcard_spi_params_t params; /**< parameters for pin and spi config */ + bool use_block_addr; /**< true if block adressing (vs. byte adressing) is used */ + bool init_done; /**< set to true once the init procedure completed sucessfully */ + sd_version_t card_type; /**< version of SD-card */ + int csd_structure; /**< version of the CSD register structure */ + cid_t cid; /**< CID register */ + csd_t csd; /**< CSD register */ +} typedef sdcard_spi_t; + +/** + * @brief Initializes the sd-card with the given parameters in sdcard_spi_t structure. + * The init procedure also takes care of initializing the spi peripheral to master + * mode and performing all neccecary steps to set the sd-card to spi-mode. Reading + * the CID and CSD registers is also done within this routine and their + * values are copied to the given sdcard_spi_t struct. + * + * @param[out] card the device descriptor + * @param[in] params parameters for this device (pins and spi device are initialized by this driver) + * + * @return 0 if the card could be initialized successfully + * @return false if an error occured while initializing the card + */ +int sdcard_spi_init(sdcard_spi_t *card, const sdcard_spi_params_t *params); + +/** + * @brief Reads data blocks (usually multiples of 512 Bytes) from card to buffer. + * + * @param[in] card Initialized sd-card struct + * @param[in] blockaddr Start adress to read from. Independet of the actual adressing scheme of + * the used card the adress needs to be given as block address + * (e.g. 0, 1, 2... NOT: 0, 512... ). The driver takes care of mapping to + * byte adressing if needed. + * @param[out] data Buffer to store the read data in. The user is responsible for providing a + * suitable buffer size. + * @param[in] blocksize Size of data blocks. For now only 512 byte blocks are supported because + * only older (SDSC) cards support variable blocksizes anyway. + * With SDHC/SDXC-cards this is always fixed to 512 bytes. SDSC cards are + * automatically forced to use 512 byte as blocksize by the init procedure. + * @param[in] nblocks Number of blocks to read + * @param[out] state Contains information about the error state if something went wrong + * (if return value is lower than nblocks). + * + * @return number of sucessfully read blocks (0 if no block was read). + */ +int sdcard_spi_read_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize, + int nblocks, sd_rw_response_t *state); + +/** + * @brief Writes data blocks (usually multiples of 512 Bytes) from buffer to card. + * + * @param[in] card Initialized sd-card struct + * @param[in] blockaddr Start adress to read from. Independet of the actual adressing scheme of + * the used card the adress needs to be given as block address + * (e.g. 0, 1, 2... NOT: 0, 512... ). The driver takes care of mapping to + * byte adressing if needed. + * @param[out] data Buffer that contains the data to be sent. + * @param[in] blocksize Size of data blocks. For now only 512 byte blocks are supported because + * only older (SDSC) cards support variable blocksizes anyway. + * With SDHC/SDXC-cards this is always fixed to 512 bytes. SDSC cards are + * automatically forced to use 512 byte as blocksize by the init procedure. + * @param[in] nblocks Number of blocks to write + * @param[out] state Contains information about the error state if something went wrong + * (if return value is lower than nblocks). + * + * @return number of sucessfully written blocks (0 if no block was written). + */ +int sdcard_spi_write_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize, + int nblocks, sd_rw_response_t *state); + +/** + * @brief Gets the capacity of the card. + * + * @param[in] card Initialized sd-card struct + * + * @return capacity of the card in bytes + */ +uint64_t sdcard_spi_get_capacity(sdcard_spi_t *card); + +#ifdef __cplusplus +} +#endif + +#endif /* SDCARD_SPI_H */ +/** @} */ diff --git a/drivers/sdcard_spi/Makefile b/drivers/sdcard_spi/Makefile new file mode 100644 index 0000000000..f36db234e3 --- /dev/null +++ b/drivers/sdcard_spi/Makefile @@ -0,0 +1,3 @@ +MODULE = sdcard_spi + +include $(RIOTBASE)/Makefile.base diff --git a/drivers/sdcard_spi/include/sdcard_spi_internal.h b/drivers/sdcard_spi/include/sdcard_spi_internal.h new file mode 100644 index 0000000000..5ca30cfd20 --- /dev/null +++ b/drivers/sdcard_spi/include/sdcard_spi_internal.h @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sdcard_spi + * @{ + * + * @file + * @brief Interface for issuing commands on sd-cards via SPI. + * @details For details of the sd card standard and the spi mode refer to + * "SD Specifications Part 1 Physical Layer Simplified Specification". + * References to the sd specs in this file apply to Version 5.00 + * from August 10, 2016. For further details see + * https://www.sdcard.org/downloads/pls/pdf/part1_500.pdf. + * + * @author Michel Rottleuthner + */ + +#ifndef SDCARD_SPI_INTERNAL_H +#define SDCARD_SPI_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "periph/spi.h" +#include "periph/gpio.h" +#include "stdbool.h" +#include "sdcard_spi.h" + + +/* number of clocks that should be applied to the card on init + before taking furter actions (see sd spec. 6.4.1.1 Power Up Time of Card) */ +#define SD_POWERSEQUENCE_CLOCK_COUNT 74 + +#define SD_CARD_PREINIT_CLOCK_PERIOD_US 10 /* used to generate 100 kHz clock in init phase*/ +#define SD_CARD_WAIT_AFTER_POWER_UP_US 1000 + +/* R1 response bits (see sd spec. 7.3.2.1 Format R1) */ +#define SD_R1_RESPONSE_PARAM_ERROR (1<<6) +#define SD_R1_RESPONSE_ADDR_ERROR (1<<5) +#define SD_R1_RESPONSE_ERASE_SEQ_ERROR (1<<4) +#define SD_R1_RESPONSE_CMD_CRC_ERROR (1<<3) +#define SD_R1_RESPONSE_ILLEGAL_CMD_ERROR (1<<2) +#define SD_R1_RESPONSE_ERASE_RESET (1<<1) +#define SD_R1_RESPONSE_IN_IDLE_STATE (0x01) +#define SD_INVALID_R1_RESPONSE (1<<7) + +#define R1_VALID(X) (((X) >> 7) == 0) +#define R1_PARAM_ERR(X) ((((X) &SD_R1_RESPONSE_PARAM_ERROR) != 0)) +#define R1_ADDR_ERR(X) ((((X) &SD_R1_RESPONSE_ADDR_ERROR) != 0)) +#define R1_ERASE_ERR(X) ((((X) &SD_R1_RESPONSE_ERASE_SEQ_ERROR) != 0)) +#define R1_CMD_CRC_ERR(X) ((((X) &SD_R1_RESPONSE_CMD_CRC_ERROR) != 0)) +#define R1_ILL_CMD_ERR(X) ((((X) &SD_R1_RESPONSE_ILLEGAL_CMD_ERROR) != 0)) +#define R1_IDLE_BIT_SET(X) (((X) &SD_R1_RESPONSE_IN_IDLE_STATE) != 0) +#define R1_ERROR(X) (R1_PARAM_ERR(X) || R1_ADDR_ERR(X) || R1_ERASE_ERR(X) || \ + R1_CMD_CRC_ERR(X) || R1_ILL_CMD_ERR(X)) + +/* see sd spec. 7.3.3.1 Data Response Token */ +#define DATA_RESPONSE_IS_VALID(X) (((X) & 0x11) == 0x01) +#define DATA_RESPONSE_ACCEPTED(X) (((X) & 0x0E) == (1<<2)) +#define DATA_RESPONSE_CRC_ERR(X) (((X) & 0x0E) == 0x0A) +#define DATA_RESPONSE_WRITE_ERR(X) (((X) & 0x0E) == 0x0C) + +/* see sd spec. 5.1 OCR register */ +#define OCR_VOLTAGE_3_2_TO_3_3 (1L << 20) +#define OCR_VOLTAGE_3_3_TO_3_4 (1L << 21) + +/* card capacity status (CCS=0: the card is SDSD; CCS=1: card is SDHC or SDXC) */ +#define OCR_CCS (1L << 30) + +/* This bit is set to low if the card has not finished power up routine */ +#define OCR_POWER_UP_STATUS (1L << 31) + +/* to ensure the voltage range check on init is done properly you need to + define this according to your actual interface/wiring with the sd-card */ +#define SYSTEM_VOLTAGE (OCR_VOLTAGE_3_2_TO_3_3 | OCR_VOLTAGE_3_2_TO_3_3) + + +/* see sd spec. 7.3.1.3 Detailed Command Description */ +#define SD_CMD_PREFIX_MASK (1<<6) + +#define SD_CMD_0 0 /* Resets the SD Memory Card */ +#define SD_CMD_1 1 /* Sends host capacity support info and starts the cards init process */ +#define SD_CMD_8 8 /* Sends SD Card interface condition incl. host supply voltage info */ +#define SD_CMD_9 9 /* Asks the selected card to send its card-specific data (CSD) */ +#define SD_CMD_10 10 /* Asks the selected card to send its card identification (CID) */ +#define SD_CMD_12 12 /* Forces the card to stop transmission in Multiple Block Read Operation */ +#define SD_CMD_13 13 /* Sent as ACMD13 asks the card to send it's SD status */ + +#define SD_CMD_16 16 /* In case of SDSC Card, block length is set by this command */ +#define SD_CMD_17 17 /* Reads a block of the size selected by the SET_BLOCKLEN command */ +#define SD_CMD_18 18 /* Continuously transfers data blocks from card to host + until interrupted by a STOP_TRANSMISSION command */ +#define SD_CMD_24 24 /* Writes a block of the size selected by the SET_BLOCKLEN command */ +#define SD_CMD_25 25 /* Continuously writes blocks of data until 'Stop Tran'token is sent */ +#define SD_CMD_41 41 /* Reserved (used for ACMD41) */ +#define SD_CMD_55 55 /* Defines to the card that the next commmand is an application specific + command rather than a standard command */ +#define SD_CMD_58 58 /* Reads the OCR register of a card */ +#define SD_CMD_59 59 /* Turns the CRC option on or off. Argument: 1:on; 0:off */ + +#define SD_CMD_8_VHS_2_7_V_TO_3_6_V 0x01 +#define SD_CMD_8_CHECK_PATTERN 0xB5 +#define SD_CMD_NO_ARG 0x00000000 +#define SD_ACMD_41_ARG_HC 0x40000000 +#define SD_CMD_59_ARG_EN 0x00000001 +#define SD_CMD_59_ARG_DIS 0x00000000 + +/* see sd spec. 7.3.3 Control Tokens */ +#define SD_DATA_TOKEN_CMD_17_18_24 0xFE +#define SD_DATA_TOKEN_CMD_25 0xFC +#define SD_DATA_TOKEN_CMD_25_STOP 0xFD + +#define SD_SIZE_OF_CID_AND_CSD_REG 16 +#define SD_SIZE_OF_SD_STATUS 64 +#define SD_BLOCKS_FOR_REG_READ 1 +#define SD_GET_CSD_STRUCTURE(CSD_RAW_DATA) ((CSD_RAW_DATA)[0] >> 6) +#define SD_CSD_V1 0 +#define SD_CSD_V2 1 +#define SD_CSD_VUNSUPPORTED -1 + +/* the retry counters below are used as timeouts for specific actions. + The values may need some adjustments to either give the card more time to respond + to commands or to achieve a lower delay / avoid infinite blocking. */ +#define R1_POLLING_RETRY_CNT 1000000 +#define SD_DATA_TOKEN_RETRY_CNT 1000000 +#define INIT_CMD_RETRY_CNT 1000000 +#define INIT_CMD0_RETRY_CNT 3 +#define SD_WAIT_FOR_NOT_BUSY_CNT 1000000 /* use -1 for full blocking till the card isn't busy */ +#define SD_BLOCK_READ_CMD_RETRIES 10 /* only affects sending of cmd not whole transaction! */ +#define SD_BLOCK_WRITE_CMD_RETRIES 10 /* only affects sending of cmd not whole transaction! */ + +/* memory capacity in bytes = (C_SIZE+1) * SD_CSD_V2_C_SIZE_BLOCK_MULT * BLOCK_LEN */ +#define SD_CSD_V2_C_SIZE_BLOCK_MULT 1024 + +#define SD_CARD_SPI_MODE SPI_CONF_FIRST_RISING + +/* this speed setting is only used while the init procedure is performed */ +#define SD_CARD_SPI_SPEED_PREINIT SPI_SPEED_400KHZ + +/* after init procedure is finished the driver auto sets the card to this speed */ +#define SD_CARD_SPI_SPEED_POSTINIT SPI_SPEED_10MHZ + +#define SD_CARD_DUMMY_BYTE 0xFF + +#define SDCARD_SPI_IEC_KIBI (1024L) +#define SDCARD_SPI_SI_KILO (1000L) + +typedef enum { + SD_INIT_START, + SD_INIT_SPI_POWER_SEQ, + SD_INIT_SEND_CMD0, + SD_INIT_SEND_CMD8, + SD_INIT_CARD_UNKNOWN, + SD_INIT_SEND_ACMD41_HCS, + SD_INIT_SEND_ACMD41, + SD_INIT_SEND_CMD1, + SD_INIT_SEND_CMD58, + SD_INIT_SEND_CMD16, + SD_INIT_ENABLE_CRC, + SD_INIT_READ_CID, + SD_INIT_READ_CSD, + SD_INIT_SET_MAX_SPI_SPEED, + SD_INIT_FINISH +} sd_init_fsm_state_t; + +/** + * @brief Sends a cmd to the sd card. + * + * @param[in] card Initialized sd-card struct + * @param[in] sd_cmd_idx A supported sd-card command index for SPI-mode like defined in + * "7.3.1.3 Detailed Command Description" of sd spec. + * (for CMDX this parameter is simply the integer value X). + * @param[in] argument The argument for the given cmd. As described by "7.3.1.1 Command Format". + * This argument is transmitted byte wise with most significant byte first. + * @param[in] max_retry Specifies how often the command should be retried if an error occures. + * Use 0 to try only once, -1 to try forever, or n to retry n times. + * + * @return R1 response of the command if no (low-level) communication error occured + * @return SD_INVALID_R1_RESPONSE if either waiting for the card to enter + * not-busy-state timed out or spi communication failed + */ +char sdcard_spi_send_cmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry); + +/** + * @brief Sends an acmd to the sd card. ACMD consists of sending CMD55 + CMD + * + * @param[in] card Initialized sd-card struct + * @param[in] sd_cmd_idx A supported sd-card command index for SPI-mode like defined in + * "7.3.1.3 Detailed Command Description" of sd spec. + * (for ACMDX this parameter is simply the integer value X). + * @param[in] argument The argument for the given cmd. As described by "7.3.1.1 Command Format". + * This argument is transmitted byte wise with most significant byte first. + * @param[in] max_retry Specifies how often the command should be retried if an error occures. + * Use 0 to try only once, -1 to try forever, or n to retry n times. + * + * @return R1 response of the command if no (low-level) communication error occured + * @return SD_INVALID_R1_RESPONSE if either waiting for the card to enter + * not-busy-state timed out or spi communication failed + */ +char sdcard_spi_send_acmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry); + +/** + * @brief Gets the sector count of the card. + * + * @param[in] card Initialized sd-card struct + * + * @return number of available sectors + */ +uint32_t sdcard_spi_get_sector_count(sdcard_spi_t *card); + +/** + * @brief Gets the allocation unit size of the card. + * + * @param[in] card Initialized sd-card struct + * + * @return size of AU in bytes + */ +uint32_t sdcard_spi_get_au_size(sdcard_spi_t *card); + +/** + * @brief Gets the SD status of the card. + * + * @param[in] card Initialized sd-card struct + * @param[out] sd_status memory location where status struct is stored + * + * @return sd_status_t struct that contains all SD status information + */ +sd_rw_response_t sdcard_spi_read_sds(sdcard_spi_t *card, sd_status_t *sd_status); + + +#ifdef __cplusplus +} +#endif + +#endif /* SDCARD_SPI_INTERNAL_H */ +/** @} */ diff --git a/drivers/sdcard_spi/include/sdcard_spi_params.h b/drivers/sdcard_spi/include/sdcard_spi_params.h new file mode 100644 index 0000000000..0f46304054 --- /dev/null +++ b/drivers/sdcard_spi/include/sdcard_spi_params.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2017 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sdcard_spi + * @{ + * + * @file + * @brief Default parameters for sdcard_spi driver + * + * @author Michel Rottleuthner + */ + +#ifndef SDCARD_SPI_PARAMS_H +#define SDCARD_SPI_PARAMS_H + +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Set default configuration parameters for the sdcard_spi driver + * @{ + */ +#ifndef SDCARD_SPI_PARAM_SPI +#define SDCARD_SPI_PARAM_SPI (SPI_0) +#endif +#ifndef SDCARD_SPI_PARAM_CS +#define SDCARD_SPI_PARAM_CS (GPIO_PIN(2,4)) +#endif +#ifndef SDCARD_SPI_PARAM_CLK +#define SDCARD_SPI_PARAM_CLK (GPIO_PIN(2,5)) +#endif +#ifndef SDCARD_SPI_PARAM_MOSI +#define SDCARD_SPI_PARAM_MOSI (GPIO_PIN(2,6)) +#endif +#ifndef SDCARD_SPI_PARAM_MISO +#define SDCARD_SPI_PARAM_MISO (GPIO_PIN(2,7)) +#endif +#ifndef SDCARD_SPI_PARAM_POWER +#define SDCARD_SPI_PARAM_POWER (GPIO_UNDEF) +#endif +#ifndef SDCARD_SPI_PARAM_POWER_AH +/** treated as 'don't care' if SDCARD_SPI_PARAM_POWER is GPIO_UNDEF */ +#define SDCARD_SPI_PARAM_POWER_AH (true) +#endif +/** @} */ + +/** + * @brief sdcard_spi configuration + */ +static const sdcard_spi_params_t sdcard_spi_params[] = { + { + .spi_dev = SDCARD_SPI_PARAM_SPI, + .cs = SDCARD_SPI_PARAM_CS, + .clk = SDCARD_SPI_PARAM_CLK, + .mosi = SDCARD_SPI_PARAM_MOSI, + .miso = SDCARD_SPI_PARAM_MISO, + .power = SDCARD_SPI_PARAM_POWER, + .power_act_high = SDCARD_SPI_PARAM_POWER_AH + }, +}; +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* SDCARD_SPI_PARAMS_H */ +/** @} */ diff --git a/drivers/sdcard_spi/sdcard_spi.c b/drivers/sdcard_spi/sdcard_spi.c new file mode 100644 index 0000000000..ceeff4d204 --- /dev/null +++ b/drivers/sdcard_spi/sdcard_spi.c @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup drivers_sdcard_spi + * @{ + * + * @file + * @brief low level driver for accessing sd-cards via spi interface. + * + * @author Michel Rottleuthner + * + * @} + */ +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "sdcard_spi_internal.h" +#include "sdcard_spi.h" +#include "sdcard_spi_params.h" +#include "periph/spi.h" +#include "periph/gpio.h" +#include "xtimer.h" + +#include +#include +#include + +static inline void _select_card_spi(sdcard_spi_t *card); +static inline void _unselect_card_spi(sdcard_spi_t *card); +static inline char _wait_for_r1(sdcard_spi_t *card, int32_t max_retries); +static inline void _send_dummy_byte(sdcard_spi_t *card); +static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries); +static inline bool _wait_for_token(sdcard_spi_t *card, char token, int32_t max_retries); +static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_state_t state); +static sd_rw_response_t _read_cid(sdcard_spi_t *card); +static sd_rw_response_t _read_csd(sdcard_spi_t *card); +static sd_rw_response_t _read_data_packet(sdcard_spi_t *card, char token, char *data, int size); +static sd_rw_response_t _write_data_packet(sdcard_spi_t *card, char token, char *data, int size); + +/* CRC-7 (polynomial: x^7 + x^3 + 1) LSB of CRC-7 in a 8-bit variable is always 1*/ +static char _crc_7(const char *data, int n); + +/* CRC-16 (CRC-CCITT) (polynomial: x^16 + x^12 + x^5 + x^1) */ +static uint16_t _crc_16(const char *data, size_t n); + +/* use this transfer method instead of _transfer_bytes to force the use of 0xFF as dummy bytes */ +static inline int _transfer_bytes(sdcard_spi_t *card, char *out, char *in, unsigned int length); + +/* uses bitbanging for spi communication which allows to enable pull-up on the miso pin for +greater card compatibility on platforms that don't have a hw pull up installed */ +static inline int _sw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in); + +/* wrapper for default spi_transfer_byte function */ +static inline int _hw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in); + +/* function pointer to switch to hw spi mode after init sequence */ +static int (*_dyn_spi_rxtx_byte)(sdcard_spi_t *card, char out, char *in); + +int sdcard_spi_init(sdcard_spi_t *card, const sdcard_spi_params_t *params) +{ + sd_init_fsm_state_t state = SD_INIT_START; + memcpy(&card->params, params, sizeof(sdcard_spi_params_t)); + + do { + state = _init_sd_fsm_step(card, state); + } while (state != SD_INIT_FINISH); + + if (card->card_type != SD_UNKNOWN) { + card->init_done = true; + return SDCARD_SPI_OK; + } + card->init_done = false; + return SDCARD_SPI_INIT_ERROR; +} + +static sd_init_fsm_state_t _init_sd_fsm_step(sdcard_spi_t *card, sd_init_fsm_state_t state) +{ + switch (state) { + + case SD_INIT_START: + DEBUG("SD_INIT_START\n"); + + if ((gpio_init(card->params.mosi, GPIO_OUT) == 0) && + (gpio_init(card->params.clk, GPIO_OUT) == 0) && + (gpio_init(card->params.cs, GPIO_OUT) == 0) && + (gpio_init(card->params.miso, GPIO_IN) == 0) && + ( (card->params.power == GPIO_UNDEF) || + (gpio_init(card->params.power, GPIO_OUT) == 0)) ) { + + DEBUG("gpio_init(): [OK]\n"); + xtimer_init(); + return SD_INIT_SPI_POWER_SEQ; + } + + DEBUG("gpio_init(): [ERROR]\n"); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_SPI_POWER_SEQ: + DEBUG("SD_INIT_SPI_POWER_SEQ\n"); + + if (card->params.power != GPIO_UNDEF) { + gpio_write(card->params.power, card->params.power_act_high); + xtimer_usleep(SD_CARD_WAIT_AFTER_POWER_UP_US); + } + + gpio_set(card->params.mosi); + gpio_set(card->params.cs); /* unselect sdcard for power up sequence */ + + /* powersequence: perform at least 74 clockcycles with mosi_pin being high + * (same as sending dummy bytes with 0xFF) */ + for (int i = 0; i < SD_POWERSEQUENCE_CLOCK_COUNT; i += 1) { + gpio_set(card->params.clk); + xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2); + gpio_clear(card->params.clk); + xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2); + } + return SD_INIT_SEND_CMD0; + + case SD_INIT_SEND_CMD0: + DEBUG("SD_INIT_SEND_CMD0\n"); + + gpio_clear(card->params.mosi); + gpio_clear(card->params.cs); /* select sdcard for cmd0 */ + + /* use soft-spi to perform init command to allow use of internal pull-ups on miso */ + _dyn_spi_rxtx_byte = &_sw_spi_rxtx_byte; + + _select_card_spi(card); + char cmd0_r1 = sdcard_spi_send_cmd(card, SD_CMD_0, SD_CMD_NO_ARG, INIT_CMD0_RETRY_CNT); + if (R1_VALID(cmd0_r1) && !R1_ERROR(cmd0_r1) && R1_IDLE_BIT_SET(cmd0_r1)) { + DEBUG("CMD0: [OK]\n"); + _unselect_card_spi(card); + + if (spi_init_master(card->params.spi_dev, SD_CARD_SPI_MODE, + SD_CARD_SPI_SPEED_PREINIT) == 0) { + DEBUG("spi_init_master(): [OK]\n"); + + /* switch to hw spi since sd card is now in real spi mode */ + _dyn_spi_rxtx_byte = &_hw_spi_rxtx_byte; + return SD_INIT_ENABLE_CRC; + } + + DEBUG("spi_init_master(): [ERROR]\n"); + return SD_INIT_CARD_UNKNOWN; + } + + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_ENABLE_CRC: + DEBUG("SD_INIT_ENABLE_CRC\n"); + _select_card_spi(card); + char r1 = sdcard_spi_send_cmd(card, SD_CMD_59, SD_CMD_59_ARG_EN, INIT_CMD_RETRY_CNT); + if (R1_VALID(r1) && !R1_ERROR(r1)) { + DEBUG("CMD59: [OK]\n"); + _unselect_card_spi(card); + return SD_INIT_SEND_CMD8; + } + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_SEND_CMD8: + DEBUG("SD_INIT_SEND_CMD8\n"); + _select_card_spi(card); + int cmd8_arg = (SD_CMD_8_VHS_2_7_V_TO_3_6_V << 8) | SD_CMD_8_CHECK_PATTERN; + char cmd8_r1 = sdcard_spi_send_cmd(card, SD_CMD_8, cmd8_arg, INIT_CMD_RETRY_CNT); + + if (R1_VALID(cmd8_r1) && !R1_ERROR(cmd8_r1)) { + DEBUG("CMD8: [OK] --> reading remaining bytes for R7\n"); + + char r7[4]; + + if (_transfer_bytes(card, 0, &r7[0], sizeof(r7)) == sizeof(r7)) { + DEBUG("R7 response: 0x%02x 0x%02x 0x%02x 0x%02x\n", r7[0], r7[1], r7[2], r7[3]); + /* check if lower 12 bits (voltage range and check pattern) of response and arg + are equal to verify compatibility and communication is working properly */ + if (((r7[2] & 0x0F) == ((cmd8_arg >> 8) & 0x0F)) && + (r7[3] == (cmd8_arg & 0xFF))) { + DEBUG("CMD8: [R7 MATCH]\n"); + return SD_INIT_SEND_ACMD41_HCS; + } + + DEBUG("CMD8: [R7 MISMATCH]\n"); + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN;; + } + + DEBUG("CMD8: _transfer_bytes (R7): [ERROR]\n"); + return SD_INIT_CARD_UNKNOWN; + } + + DEBUG("CMD8: [ERROR / NO RESPONSE]\n"); + return SD_INIT_SEND_ACMD41; + + case SD_INIT_CARD_UNKNOWN: + DEBUG("SD_INIT_CARD_UNKNOWN\n"); + card->card_type = SD_UNKNOWN; + return SD_INIT_FINISH; + + case SD_INIT_SEND_ACMD41_HCS: + DEBUG("SD_INIT_SEND_ACMD41_HCS\n"); + int acmd41_hcs_retries = 0; + do { + char acmd41hcs_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_ACMD_41_ARG_HC, 0); + if (R1_VALID(acmd41hcs_r1) && !R1_ERROR(acmd41hcs_r1) && + !R1_IDLE_BIT_SET(acmd41hcs_r1)) { + DEBUG("ACMD41: [OK]\n"); + return SD_INIT_SEND_CMD58; + } + acmd41_hcs_retries++; + } while (INIT_CMD_RETRY_CNT < 0 || acmd41_hcs_retries <= INIT_CMD_RETRY_CNT);; + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_SEND_ACMD41: + DEBUG("SD_INIT_SEND_ACMD41\n"); + int acmd41_retries = 0; + do { + char acmd41_r1 = sdcard_spi_send_acmd(card, SD_CMD_41, SD_CMD_NO_ARG, 0); + if (R1_VALID(acmd41_r1) && !R1_ERROR(acmd41_r1) && !R1_IDLE_BIT_SET(acmd41_r1)) { + DEBUG("ACMD41: [OK]\n"); + card->use_block_addr = false; + card->card_type = SD_V1; + return SD_INIT_SEND_CMD16; + } + acmd41_retries++; + } while (INIT_CMD_RETRY_CNT < 0 || acmd41_retries <= INIT_CMD_RETRY_CNT); + + DEBUG("ACMD41: [ERROR]\n"); + return SD_INIT_SEND_CMD1; + + case SD_INIT_SEND_CMD1: + DEBUG("SD_INIT_SEND_CMD1\n"); + DEBUG("COULD TRY CMD1 (for MMC-card)-> currently not suported\n"); + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_SEND_CMD58: + DEBUG("SD_INIT_SEND_CMD58\n"); + char cmd58_r1 = sdcard_spi_send_cmd(card, SD_CMD_58, SD_CMD_NO_ARG, INIT_CMD_RETRY_CNT); + if (R1_VALID(cmd58_r1) && !R1_ERROR(cmd58_r1)) { + DEBUG("CMD58: [OK]\n"); + card->card_type = SD_V2; + + char r3[4]; + if (_transfer_bytes(card, 0, r3, sizeof(r3)) == sizeof(r3)) { + uint32_t ocr = ((uint32_t)r3[0] << (3 * 8)) | + ((uint32_t)r3[1] << (2 * 8)) | (r3[2] << 8) | r3[3]; + DEBUG("R3 RESPONSE: 0x%02x 0x%02x 0x%02x 0x%02x\n", r3[0], r3[1], r3[2], r3[3]); + DEBUG("OCR: 0x%"PRIx32"\n", ocr); + + if ((ocr & SYSTEM_VOLTAGE) != 0) { + DEBUG("OCR: SYS VOLTAGE SUPPORTED\n"); + + if ((ocr & OCR_POWER_UP_STATUS) != 0) { //if power up outine is finished + DEBUG("OCR: POWER UP ROUTINE FINISHED\n"); + if ((ocr & OCR_CCS) != 0) { //if sd card is sdhc + DEBUG("OCR: CARD TYPE IS SDHC (SD_V2 with block adressing)\n"); + card->use_block_addr = true; + _unselect_card_spi(card); + return SD_INIT_READ_CID; + } + + DEBUG("OCR: CARD TYPE IS SDSC (SD_v2 with byte adressing)\n"); + card->use_block_addr = false; + return SD_INIT_SEND_CMD16; + } + + DEBUG("OCR: POWER UP ROUTINE NOT FINISHED!\n"); + /* poll status till power up is finished */ + return SD_INIT_SEND_CMD58; + } + + DEBUG("OCR: SYS VOLTAGE NOT SUPPORTED!\n"); + } + + DEBUG("CMD58 response: [READ ERROR]\n"); + } + + DEBUG("CMD58: [ERROR]\n"); + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + + case SD_INIT_SEND_CMD16: + DEBUG("SD_INIT_SEND_CMD16\n"); + char r1_16 = sdcard_spi_send_cmd(card, SD_CMD_16, SD_HC_BLOCK_SIZE, INIT_CMD_RETRY_CNT); + if (R1_VALID(r1_16) && !R1_ERROR(r1_16)) { + DEBUG("CARD TYPE IS SDSC (SD_V1 with byte adressing)\n"); + _unselect_card_spi(card); + return SD_INIT_READ_CID; + } + else { + _unselect_card_spi(card); + return SD_INIT_CARD_UNKNOWN; + } + + case SD_INIT_READ_CID: + DEBUG("SD_INIT_READ_CID\n"); + if (_read_cid(card) == SD_RW_OK) { + return SD_INIT_READ_CSD; + } + else { + DEBUG("reading cid register failed!\n"); + return SD_INIT_CARD_UNKNOWN; + } + + case SD_INIT_READ_CSD: + DEBUG("SD_INIT_READ_CSD\n"); + if (_read_csd(card) == SD_RW_OK) { + if (card->csd_structure == SD_CSD_V1) { + DEBUG("csd_structure is version 1\n"); + } + else if (card->csd_structure == SD_CSD_V2) { + DEBUG("csd_structure is version 2\n"); + } + return SD_INIT_SET_MAX_SPI_SPEED; + } + else { + DEBUG("reading csd register failed!\n"); + return SD_INIT_CARD_UNKNOWN; + } + + case SD_INIT_SET_MAX_SPI_SPEED: + DEBUG("SD_INIT_SET_MAX_SPI_SPEED\n"); + if (spi_init_master(card->params.spi_dev, SD_CARD_SPI_MODE, + SD_CARD_SPI_SPEED_POSTINIT) == 0) { + DEBUG("SD_INIT_SET_MAX_SPI_SPEED: [OK]\n"); + return SD_INIT_FINISH; + } + else { + DEBUG("SD_INIT_SET_MAX_SPI_SPEED: [ERROR]\n"); + return SD_INIT_CARD_UNKNOWN; + } + + default: + DEBUG("SD-INIT-FSM REACHED INVALID STATE!\n"); + return SD_INIT_CARD_UNKNOWN; + + } +} + +static inline bool _wait_for_token(sdcard_spi_t *card, char token, int32_t max_retries) +{ + int tried = 0; + + do { + char read_byte = 0; + if (spi_transfer_byte(card->params.spi_dev, SD_CARD_DUMMY_BYTE, &read_byte) == 1) { + if (read_byte == token) { + DEBUG("_wait_for_token: [MATCH]\n"); + return true; + } + else { + DEBUG("_wait_for_token: [NO MATCH] (0x%02x)\n", read_byte); + } + } + else { + DEBUG("spi_transfer_byte: [FAILED]\n"); + return false; + } + + tried++; + } while ((max_retries < 0) || (tried <= max_retries)); + + return false; +} + +static inline void _send_dummy_byte(sdcard_spi_t *card) +{ + char read_byte; + + if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &read_byte) == 1) { + DEBUG("_send_dummy_byte:echo: 0x%02x\n", read_byte); + } + else { + DEBUG("_send_dummy_byte:_dyn_spi_rxtx_byte: [FAILED]\n"); + } +} + +static inline bool _wait_for_not_busy(sdcard_spi_t *card, int32_t max_retries) +{ + char read_byte; + int tried = 0; + + do { + if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &read_byte) == 1) { + if (read_byte == 0xFF) { + DEBUG("_wait_for_not_busy: [OK]\n"); + return true; + } + else { + DEBUG("_wait_for_not_busy: [BUSY]\n"); + } + } + else { + DEBUG("_wait_for_not_busy:_dyn_spi_rxtx_byte: [FAILED]\n"); + return false; + } + + tried++; + } while ((max_retries < 0) || (tried <= max_retries)); + + DEBUG("_wait_for_not_busy: [FAILED]\n"); + return false; +} + +static char _crc_7(const char *data, int n) +{ + char crc = 0; + + for (int i = 0; i < n; i++) { + char d = data[i]; + for (int j = 0; j < 8; j++) { + crc <<= 1; + if ((d & 0x80) ^ (crc & 0x80)) { + crc ^= 0x09; + } + d <<= 1; + } + } + return (crc << 1) | 1; +} + +static uint16_t _crc_16(const char *data, size_t n) +{ + uint16_t crc = 0; + + for (size_t i = 0; i < n; i++) { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= data[i]; + crc ^= (uint8_t)(crc & 0xFF) >> 4; + crc ^= crc << 12; + crc ^= (crc & 0xFF) << 5; + } + return crc; +} + +char sdcard_spi_send_cmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry) +{ + int try_cnt = 0; + char r1_resu; + char cmd_data[6]; + + cmd_data[0] = SD_CMD_PREFIX_MASK | sd_cmd_idx; + cmd_data[1] = argument >> (3 * 8); + cmd_data[2] = (argument >> (2 * 8)) & 0xFF; + cmd_data[3] = (argument >> 8) & 0xFF; + cmd_data[4] = argument & 0xFF; + cmd_data[5] = _crc_7(cmd_data, sizeof(cmd_data) - 1); + + char echo[sizeof(cmd_data)]; + + do { + DEBUG("sdcard_spi_send_cmd: CMD%02d (0x%08lx) (retry %d)\n", sd_cmd_idx, argument, try_cnt); + + if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) { + DEBUG("sdcard_spi_send_cmd: timeout while waiting for bus to be not busy!\n"); + r1_resu = SD_INVALID_R1_RESPONSE; + try_cnt++; + continue; + } + + if (_transfer_bytes(card, cmd_data, echo, sizeof(cmd_data)) != sizeof(cmd_data)) { + DEBUG("sdcard_spi_send_cmd: _transfer_bytes: send cmd [%d]: [ERROR]\n", sd_cmd_idx); + r1_resu = SD_INVALID_R1_RESPONSE; + try_cnt++; + continue; + } + + DEBUG("CMD%02d echo: ", sd_cmd_idx); + for (int i = 0; i < sizeof(echo); i++) { + DEBUG("0x%02X ", echo[i]); + } + DEBUG("\n"); + + /* received byte after cmd12 is a dummy byte and should be ignored */ + if (sd_cmd_idx == SD_CMD_12) { + _send_dummy_byte(card); + } + + r1_resu = _wait_for_r1(card, R1_POLLING_RETRY_CNT); + + if (R1_VALID(r1_resu)) { + break; + } + else { + DEBUG("sdcard_spi_send_cmd: R1_TIMEOUT (0x%02x)\n", r1_resu); + r1_resu = SD_INVALID_R1_RESPONSE; + } + try_cnt++; + + } while ((max_retry < 0) || (try_cnt <= max_retry)); + + return r1_resu; +} + +char sdcard_spi_send_acmd(sdcard_spi_t *card, char sd_cmd_idx, uint32_t argument, int32_t max_retry) +{ + int err_cnt = 0; + char r1_resu; + + do { + DEBUG("sdcard_spi_send_acmd: CMD%02d (0x%08lx)(retry %d)\n", sd_cmd_idx, argument, err_cnt); + r1_resu = sdcard_spi_send_cmd(card, SD_CMD_55, SD_CMD_NO_ARG, 0); + if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) { + r1_resu = sdcard_spi_send_cmd(card, sd_cmd_idx, argument, 0); + + if (R1_VALID(r1_resu) && !R1_ERROR(r1_resu)) { + return r1_resu; + } + else { + DEBUG("ACMD%02d: [ERROR / NO RESPONSE]\n", sd_cmd_idx); + err_cnt++; + } + } + else { + DEBUG("CMD55: [ERROR / NO RESPONSE]\n"); + err_cnt++; + } + + } while ((max_retry < 0) || (err_cnt <= max_retry)); + + DEBUG("sdcard_spi_send_acmd: [TIMEOUT]\n"); + return r1_resu; +} + +static inline char _wait_for_r1(sdcard_spi_t *card, int32_t max_retries) +{ + int tried = 0; + char r1; + + do { + if (_dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &r1) != 1) { + DEBUG("_wait_for_r1: _dyn_spi_rxtx_byte:[ERROR]\n"); + tried++; + continue; + } + else { + DEBUG("_wait_for_r1: r1=0x%02x\n", r1); + } + + if (R1_VALID(r1)) { + DEBUG("_wait_for_r1: R1_VALID\n"); + return r1; + } + tried++; + } while ((max_retries < 0) || (tried <= max_retries)); + + DEBUG("_wait_for_r1: [TIMEOUT]\n"); + return r1; +} + +void _select_card_spi(sdcard_spi_t *card) +{ + spi_acquire(card->params.spi_dev); + gpio_clear(card->params.cs); +} + +void _unselect_card_spi(sdcard_spi_t *card) +{ + gpio_set(card->params.cs); + spi_release(card->params.spi_dev); +} + +static inline int _sw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in){ + char rx = 0; + int i = 7; + for(; i >= 0; i--){ + if( ((out >> (i)) & 0x01) == 1){ + gpio_set(card->params.mosi); + }else{ + gpio_clear(card->params.mosi); + } + xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2); + gpio_set(card->params.clk); + rx = (rx | ((gpio_read(card->params.miso) > 0) << i)); + xtimer_usleep(SD_CARD_PREINIT_CLOCK_PERIOD_US/2); + gpio_clear(card->params.clk); + } + *in = rx; + return 1; +} + +static inline int _hw_spi_rxtx_byte(sdcard_spi_t *card, char out, char *in){ + return spi_transfer_byte(card->params.spi_dev, out, in); +} + +static inline int _transfer_bytes(sdcard_spi_t *card, char *out, char *in, unsigned int length){ + int trans_ret; + unsigned trans_bytes = 0; + char in_temp; + + for (trans_bytes = 0; trans_bytes < length; trans_bytes++) { + if (out != NULL) { + trans_ret = _dyn_spi_rxtx_byte(card, out[trans_bytes], &in_temp); + } + else { + trans_ret = _dyn_spi_rxtx_byte(card, SD_CARD_DUMMY_BYTE, &in_temp); + } + if (trans_ret < 0) { + return trans_ret; + } + if (in != NULL) { + in[trans_bytes] = in_temp; + } + } + + return trans_bytes; +} + +static sd_rw_response_t _read_data_packet(sdcard_spi_t *card, char token, char *data, int size) +{ + DEBUG("_read_data_packet: size: %d\n", size); + if (_wait_for_token(card, token, SD_DATA_TOKEN_RETRY_CNT) == true) { + DEBUG("_read_data_packet: [GOT TOKEN]\n"); + } + else { + DEBUG("_read_data_packet: [GOT NO TOKEN]\n"); + return SD_RW_NO_TOKEN; + } + + if (_transfer_bytes(card, NULL, data, size) == size) { + + DEBUG("_read_data_packet: data: "); + for (int i = 0; i < size; i++) { + DEBUG("0x%02X ", data[i]); + } + DEBUG("\n"); + + char crc_bytes[2]; + if (_transfer_bytes(card, 0, crc_bytes, sizeof(crc_bytes)) == sizeof(crc_bytes)) { + uint16_t data__crc_16 = (crc_bytes[0] << 8) | crc_bytes[1]; + + if (_crc_16(data, size) == data__crc_16) { + DEBUG("_read_data_packet: [OK]\n"); + return SD_RW_OK; + } + else { + DEBUG("_read_data_packet: [CRC_MISMATCH]\n"); + return SD_RW_CRC_MISMATCH; + } + } + + DEBUG("_read_data_packet: _transfer_bytes [RX_TX_ERROR] (while transmitting crc)\n"); + return SD_RW_RX_TX_ERROR; + } + + DEBUG("_read_data_packet: _transfer_bytes [RX_TX_ERROR] (while transmitting payload)\n"); + return SD_RW_RX_TX_ERROR; +} + +static inline int _read_blocks(sdcard_spi_t *card, int cmd_idx, int bladdr, char *data, int blsz, + int nbl, sd_rw_response_t *state) +{ + _select_card_spi(card); + int reads = 0; + + uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE); + char cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_READ_CMD_RETRIES); + + if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) { + DEBUG("_read_blocks: send CMD%d: [OK]\n", cmd_idx); + + for (int i = 0; i < nbl; i++) { + *state = _read_data_packet(card, SD_DATA_TOKEN_CMD_17_18_24, &(data[i * blsz]), blsz); + + if (*state != SD_RW_OK) { + DEBUG("_read_blocks: _read_data_packet: [FAILED]\n"); + _unselect_card_spi(card); + return reads; + } + else { + reads++; + } + } + + /* if this was a multi-block read */ + if (cmd_idx == SD_CMD_18) { + cmd_r1_resu = sdcard_spi_send_cmd(card, SD_CMD_12, 0, 1); + + if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) { + DEBUG("_read_blocks: read multi (%d) blocks [OK]\n", nbl); + *state = SD_RW_OK; + } + else { + DEBUG("_read_blocks: send CMD12: [RX_TX_ERROR]\n"); + *state = SD_RW_RX_TX_ERROR; + } + } + else { + DEBUG("_read_blocks: read single block [OK]\n"); + *state = SD_RW_OK; + } + } + else { + DEBUG("_read_blocks: send CMD%d: [RX_TX_ERROR]\n", cmd_idx); + *state = SD_RW_RX_TX_ERROR; + } + + _unselect_card_spi(card); + return reads; +} + +int sdcard_spi_read_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize, + int nblocks, sd_rw_response_t *state) +{ + if (nblocks > 1) { + return _read_blocks(card, SD_CMD_18, blockaddr, data, blocksize, nblocks, state); + } + else { + return _read_blocks(card, SD_CMD_17, blockaddr, data, blocksize, nblocks, state); + } +} + +static sd_rw_response_t _write_data_packet(sdcard_spi_t *card, char token, char *data, int size) +{ + + if (spi_transfer_byte(card->params.spi_dev, token, 0) == 1) { + + if (_transfer_bytes(card, data, 0, size) == size) { + + uint16_t data__crc_16 = _crc_16(data, size); + char crc[sizeof(uint16_t)] = { data__crc_16 >> 8, data__crc_16 & 0xFF }; + + if (_transfer_bytes(card, crc, 0, sizeof(crc)) == sizeof(crc)) { + + char data_response; + + if (spi_transfer_byte(card->params.spi_dev, SD_CARD_DUMMY_BYTE, + &data_response) == 1) { + + DEBUG("_write_data_packet: DATA_RESPONSE: 0x%02x\n", data_response); + + if (DATA_RESPONSE_IS_VALID(data_response)) { + + if (DATA_RESPONSE_ACCEPTED(data_response)) { + DEBUG("_write_data_packet: DATA_RESPONSE: [OK]\n"); + return SD_RW_OK; + } + else { + + if (DATA_RESPONSE_WRITE_ERR(data_response)) { + DEBUG("_write_data_packet: DATA_RESPONSE: [WRITE_ERROR]\n"); + } + + if (DATA_RESPONSE_CRC_ERR(data_response)) { + DEBUG("_write_data_packet: DATA_RESPONSE: [CRC_ERROR]\n"); + } + return SD_RW_WRITE_ERROR; + } + + } + else { + DEBUG("_write_data_packet: DATA_RESPONSE invalid\n"); + return SD_RW_RX_TX_ERROR; + } + + + } + else { + DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting data response)\n"); + return SD_RW_RX_TX_ERROR; + } + } + else { + DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting CRC16)\n"); + return SD_RW_RX_TX_ERROR; + } + } + else { + DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting payload)\n"); + return SD_RW_RX_TX_ERROR; + } + + } + else { + DEBUG("_write_data_packet: [RX_TX_ERROR] (while transmitting token)\n"); + return SD_RW_RX_TX_ERROR; + } +} + +static inline int _write_blocks(sdcard_spi_t *card, char cmd_idx, int bladdr, char *data, int blsz, + int nbl, sd_rw_response_t *state) +{ + _select_card_spi(card); + int written = 0; + + uint32_t addr = card->use_block_addr ? bladdr : (bladdr * SD_HC_BLOCK_SIZE); + char cmd_r1_resu = sdcard_spi_send_cmd(card, cmd_idx, addr, SD_BLOCK_WRITE_CMD_RETRIES); + + if (R1_VALID(cmd_r1_resu) && !R1_ERROR(cmd_r1_resu)) { + DEBUG("_write_blocks: send CMD%d: [OK]\n", cmd_idx); + + int token; + if (cmd_idx == SD_CMD_25) { + token = SD_DATA_TOKEN_CMD_25; + } + else { + token = SD_DATA_TOKEN_CMD_17_18_24; + } + + for (int i = 0; i < nbl; i++) { + sd_rw_response_t write_resu = _write_data_packet(card, token, &(data[i * blsz]), blsz); + if (write_resu != SD_RW_OK) { + DEBUG("_write_blocks: _write_data_packet: [FAILED]\n"); + _unselect_card_spi(card); + *state = write_resu; + return written; + } + if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) { + DEBUG("_write_blocks: _wait_for_not_busy: [FAILED]\n"); + _unselect_card_spi(card); + *state = SD_RW_TIMEOUT; + return written; + } + written++; + } + + /* if this is a multi-block write it is needed to issue a stop command*/ + if (cmd_idx == SD_CMD_25) { + char data_response; + if (spi_transfer_byte(card->params.spi_dev, SD_DATA_TOKEN_CMD_25_STOP, + &data_response) == 1) { + DEBUG("_write_blocks: write multi (%d) blocks: [OK]\n", nbl); + } + else { + DEBUG("_write_blocks: spi_transfer_byte: [FAILED] (SD_DATA_TOKEN_CMD_25_STOP)\n"); + _unselect_card_spi(card); + *state = SD_RW_RX_TX_ERROR; + } + _send_dummy_byte(card); //sd card needs dummy byte before we can wait for not-busy state + if (!_wait_for_not_busy(card, SD_WAIT_FOR_NOT_BUSY_CNT)) { + _unselect_card_spi(card); + *state = SD_RW_TIMEOUT; + } + } + else { + DEBUG("_write_blocks: write single block: [OK]\n"); + *state = SD_RW_OK; + } + + _unselect_card_spi(card); + return written; + } + else { + DEBUG("_write_blocks: sdcard_spi_send_cmd: SD_CMD_ERROR_NO_RESP\n"); + _unselect_card_spi(card); + *state = SD_RW_RX_TX_ERROR; + return written; + } +} + +int sdcard_spi_write_blocks(sdcard_spi_t *card, int blockaddr, char *data, int blocksize, + int nblocks, sd_rw_response_t *state) +{ + if (nblocks > 1) { + return _write_blocks(card, SD_CMD_25, blockaddr, data, blocksize, nblocks, state); + } + else { + return _write_blocks(card, SD_CMD_24, blockaddr, data, blocksize, nblocks, state); + } +} + +sd_rw_response_t _read_cid(sdcard_spi_t *card) +{ + char cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG]; + sd_rw_response_t state; + int nbl = _read_blocks(card, SD_CMD_10, 0, cid_raw_data, SD_SIZE_OF_CID_AND_CSD_REG, + SD_BLOCKS_FOR_REG_READ, &state); + + DEBUG("_read_cid: _read_blocks: nbl=%d state=%d\n", nbl, state); + DEBUG("_read_cid: cid_raw_data: "); + for (int i = 0; i < sizeof(cid_raw_data); i++) { + DEBUG("0x%02X ", cid_raw_data[i]); + } + DEBUG("\n"); + + char crc7 = _crc_7(&(cid_raw_data[0]), SD_SIZE_OF_CID_AND_CSD_REG - 1); + if (nbl == SD_BLOCKS_FOR_REG_READ) { + if (crc7 == cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1]) { + card->cid.MID = cid_raw_data[0]; + memcpy(&card->cid.OID[0], &cid_raw_data[1], SD_SIZE_OF_OID); + memcpy(&card->cid.PNM[0], &cid_raw_data[2], SD_SIZE_OF_PNM); + card->cid.PRV = cid_raw_data[8]; + memcpy((char *)&card->cid.PSN, &cid_raw_data[9], 4); + card->cid.MDT = (cid_raw_data[13]<<4) | cid_raw_data[14]; + card->cid.CID_CRC = cid_raw_data[15]; + DEBUG("_read_cid: [OK]\n"); + return SD_RW_OK; + } + else { + DEBUG("_read_cid: [SD_RW_CRC_MISMATCH] (data-crc: 0x%02x | calc-crc: 0x%02x)\n", + cid_raw_data[SD_SIZE_OF_CID_AND_CSD_REG - 1], crc7); + return SD_RW_CRC_MISMATCH; + } + } + return state; +} + +sd_rw_response_t _read_csd(sdcard_spi_t *card) +{ + char c[SD_SIZE_OF_CID_AND_CSD_REG]; + sd_rw_response_t state; + int read_resu = _read_blocks(card, SD_CMD_9, 0, c, SD_SIZE_OF_CID_AND_CSD_REG, + SD_BLOCKS_FOR_REG_READ, &state); + + DEBUG("_read_csd: _read_blocks: read_resu=%d state=%d\n", read_resu, state); + DEBUG("_read_csd: raw data: "); + for (int i = 0; i < sizeof(c); i++) { + DEBUG("0x%02X ", c[i]); + } + DEBUG("\n"); + + if (read_resu == SD_BLOCKS_FOR_REG_READ) { + if (_crc_7(c, SD_SIZE_OF_CID_AND_CSD_REG - 1) == c[SD_SIZE_OF_CID_AND_CSD_REG - 1]) { + if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V1) { + card->csd.v1.CSD_STRUCTURE = c[0]>>6; + card->csd.v1.TAAC = c[1]; + card->csd.v1.NSAC = c[2]; + card->csd.v1.TRAN_SPEED = c[3]; + card->csd.v1.CCC = (c[4]<<4) | ((c[5] & 0xF0)>>4); + card->csd.v1.READ_BL_LEN = (c[5] & 0x0F); + card->csd.v1.READ_BL_PARTIAL = (c[6] & (1<<7))>>7; + card->csd.v1.WRITE_BLK_MISALIGN = (c[6] & (1<<6))>>6; + card->csd.v1.READ_BLK_MISALIGN = (c[6] & (1<<5))>>5; + card->csd.v1.DSR_IMP = (c[6] & (1<<4))>>4; + card->csd.v1.C_SIZE = ((c[6] & 0x03)<<10) | (c[7]<<2) | (c[8]>>6); + card->csd.v1.VDD_R_CURR_MIN = (c[8] & 0x38)>>3; + card->csd.v1.VDD_R_CURR_MAX = (c[8] & 0x07); + card->csd.v1.VDD_W_CURR_MIN = (c[9] & 0xE0)>>5; + card->csd.v1.VDD_W_CURR_MAX = (c[9] & 0x1C)>>2; + card->csd.v1.C_SIZE_MULT = ((c[9] & 0x03)<<1) | (c[10]>>7); + card->csd.v1.ERASE_BLK_EN = (c[10] & (1<<6))>>6; + card->csd.v1.SECTOR_SIZE = ((c[10] & 0x3F)<<1) | (c[11]>>7); + card->csd.v1.WP_GRP_SIZE = (c[11] & 0x7F); + card->csd.v1.WP_GRP_ENABLE = c[12]>>7; + card->csd.v1.R2W_FACTOR = (c[12] & 0x1C)>>2; + card->csd.v1.WRITE_BL_LEN = (c[12] & 0x03)<<2 | (c[13]>>6); + card->csd.v1.WRITE_BL_PARTIAL = (c[13] & (1<<5))>>5; + card->csd.v1.FILE_FORMAT_GRP = (c[14] & (1<<7))>>7; + card->csd.v1.COPY = (c[14] & (1<<6))>>6; + card->csd.v1.PERM_WRITE_PROTECT = (c[14] & (1<<5))>>5; + card->csd.v1.TMP_WRITE_PROTECT = (c[14] & (1<<4))>>4; + card->csd.v1.FILE_FORMAT = (c[14] & 0x0C)>>2; + card->csd.v1.CSD_CRC = c[15]; + card->csd_structure = SD_CSD_V1; + return SD_RW_OK; + } + else if (SD_GET_CSD_STRUCTURE(c) == SD_CSD_V2) { + card->csd.v2.CSD_STRUCTURE = c[0]>>6; + card->csd.v2.TAAC = c[1]; + card->csd.v2.NSAC = c[2]; + card->csd.v2.TRAN_SPEED = c[3]; + card->csd.v2.CCC = (c[4]<<4) | ((c[5] & 0xF0)>>4); + card->csd.v2.READ_BL_LEN = (c[5] & 0x0F); + card->csd.v2.READ_BL_PARTIAL = (c[6] & (1<<7))>>7; + card->csd.v2.WRITE_BLK_MISALIGN = (c[6] & (1<<6))>>6; + card->csd.v2.READ_BLK_MISALIGN = (c[6] & (1<<5))>>5; + card->csd.v2.DSR_IMP = (c[6] & (1<<4))>>4; + card->csd.v2.C_SIZE = (((uint32_t)c[7] & 0x3F)<<16) + | (c[8]<<8) | c[9]; + card->csd.v2.ERASE_BLK_EN = (c[10] & (1<<6))>>6; + card->csd.v2.SECTOR_SIZE = (c[10] & 0x3F)<<1 | (c[11]>>7); + card->csd.v2.WP_GRP_SIZE = (c[11] & 0x7F); + card->csd.v2.WP_GRP_ENABLE = (c[12] & (1<<7))>> 7; + card->csd.v2.R2W_FACTOR = (c[12] & 0x1C)>> 2; + card->csd.v2.WRITE_BL_LEN = ((c[12] & 0x03)<<2) | (c[13]>>6); + card->csd.v2.WRITE_BL_PARTIAL = (c[13] & (1<<5))>>5; + card->csd.v2.FILE_FORMAT_GRP = (c[14] & (1<<7))>>7; + card->csd.v2.COPY = (c[14] & (1<<6))>>6; + card->csd.v2.PERM_WRITE_PROTECT = (c[14] & (1<<5))>>5; + card->csd.v2.TMP_WRITE_PROTECT = (c[14] & (1<<4))>>4; + card->csd.v2.FILE_FORMAT = (c[14] & 0x0C)>>2; + card->csd.v2.CSD_CRC = c[15]; + card->csd_structure = SD_CSD_V2; + return SD_RW_OK; + } + else { + return SD_RW_NOT_SUPPORTED; + } + + } + else { + return SD_RW_CRC_MISMATCH; + } + } + return state; +} + +sd_rw_response_t sdcard_spi_read_sds(sdcard_spi_t *card, sd_status_t *sd_status){ + _select_card_spi(card); + char sds_raw_data[SD_SIZE_OF_SD_STATUS]; + char r1_resu = sdcard_spi_send_cmd(card, SD_CMD_55, SD_CMD_NO_ARG, 0); + _unselect_card_spi(card); + if (R1_VALID(r1_resu)) { + if(!R1_ERROR(r1_resu)){ + + sd_rw_response_t state; + int nbl = _read_blocks(card, SD_CMD_13, 0, sds_raw_data, SD_SIZE_OF_SD_STATUS, + SD_BLOCKS_FOR_REG_READ, &state); + + DEBUG("sdcard_spi_read_sds: _read_blocks: nbl=%d state=%d\n", nbl, state); + DEBUG("sdcard_spi_read_sds: sds_raw_data: "); + for (int i = 0; i < sizeof(sds_raw_data); i++) { + DEBUG("0x%02X ", sds_raw_data[i]); + } + DEBUG("\n"); + + if (nbl == SD_BLOCKS_FOR_REG_READ) { + sd_status->DAT_BUS_WIDTH = sds_raw_data[0] >> 6; + sd_status->SECURED_MODE = (sds_raw_data[0] & (1<<5)) >> 5; + sd_status->SD_CARD_TYPE = (sds_raw_data[2] << 8) | sds_raw_data[3]; + sd_status->SIZE_OF_PROTECTED_AREA = ((uint32_t)sds_raw_data[4] << (3*8)) | + ((uint32_t)sds_raw_data[5] << (2*8)) | + (sds_raw_data[6] << 8 ) | + sds_raw_data[7]; + sd_status->SPEED_CLASS = sds_raw_data[8]; + sd_status->PERFORMANCE_MOVE = sds_raw_data[9]; + sd_status->AU_SIZE = sds_raw_data[10] >> 4; + sd_status->ERASE_SIZE = (sds_raw_data[11] << 8) | sds_raw_data[12]; + sd_status->ERASE_TIMEOUT = sds_raw_data[13] >> 2; + sd_status->ERASE_OFFSET = sds_raw_data[13] & 0x03; + sd_status->UHS_SPEED_GRADE = sds_raw_data[14] >> 4; + sd_status->UHS_AU_SIZE = sds_raw_data[14] & 0x0F; + sd_status->VIDEO_SPEED_CLASS = sds_raw_data[15]; + sd_status->VSC_AU_SIZE = ((sds_raw_data[16] & 0x03) << 8) + | sds_raw_data[17]; + sd_status->SUS_ADDR = (sds_raw_data[18] << 14) | + (sds_raw_data[19] << 6 ) | + (sds_raw_data[20] >> 2 ); + DEBUG("sdcard_spi_read_sds: [OK]\n"); + return SD_RW_OK; + } + return state; + } + return SD_RW_RX_TX_ERROR; + } + return SD_RW_TIMEOUT; +} + +uint64_t sdcard_spi_get_capacity(sdcard_spi_t *card) +{ + if (card->csd_structure == SD_CSD_V1) { + uint32_t block_len = (1 << card->csd.v1.READ_BL_LEN); + uint32_t mult = 1 << (card->csd.v1.C_SIZE_MULT + 2); + uint32_t blocknr = (card->csd.v1.C_SIZE + 1) * mult; + return blocknr * block_len; + } + else if (card->csd_structure == SD_CSD_V2) { + return (card->csd.v2.C_SIZE + 1) * (uint64_t)(SD_HC_BLOCK_SIZE << 10); + } + return 0; +} + +uint32_t sdcard_spi_get_sector_count(sdcard_spi_t *card) +{ + return sdcard_spi_get_capacity(card) / SD_HC_BLOCK_SIZE; +} + +uint32_t sdcard_spi_get_au_size(sdcard_spi_t *card) +{ + sd_status_t sds; + if(sdcard_spi_read_sds(card, &sds) == SD_RW_OK) { + if (sds.AU_SIZE < 0xB) { + return 1 << (13 + sds.AU_SIZE); /* sds->AU_SIZE = 1 maps to 16KB; 2 to 32KB etc.*/ + } + else if (sds.AU_SIZE == 0xB) { + return 12 * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI; /* 12 MB */ + } + else if (sds.AU_SIZE == 0xC) { + return 1 << (12 + sds.AU_SIZE); /* 16 MB */ + } + else if (sds.AU_SIZE == 0xD) { + return 24 * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI; /* 24 MB */ + } + else if (sds.AU_SIZE > 0xD) { + return 1 << (11 + sds.AU_SIZE); /* 32 MB or 64 MB */ + } + } + return 0; /* AU_SIZE is not defined by the card */ +} diff --git a/sys/auto_init/Makefile b/sys/auto_init/Makefile index ad7b0b1b7c..3328c84438 100644 --- a/sys/auto_init/Makefile +++ b/sys/auto_init/Makefile @@ -8,4 +8,8 @@ ifneq (,$(filter auto_init_saul,$(USEMODULE))) DIRS += saul endif +ifneq (,$(filter auto_init_storage,$(USEMODULE))) +DIRS += storage +endif + include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/auto_init.c b/sys/auto_init/auto_init.c index 5af0e8f005..d3142d0c3c 100644 --- a/sys/auto_init/auto_init.c +++ b/sys/auto_init/auto_init.c @@ -313,4 +313,15 @@ void auto_init(void) #endif #endif /* MODULE_AUTO_INIT_GNRC_RPL */ + +/* initialize storage devices */ +#ifdef MODULE_AUTO_INIT_STORAGE + DEBUG("auto_init STORAGE\n"); + +#ifdef MODULE_SDCARD_SPI + extern void auto_init_sdcard_spi(void); + auto_init_sdcard_spi(); +#endif + +#endif /* MODULE_AUTO_INIT_STORAGE */ } diff --git a/sys/auto_init/storage/Makefile b/sys/auto_init/storage/Makefile new file mode 100644 index 0000000000..80951d6fb3 --- /dev/null +++ b/sys/auto_init/storage/Makefile @@ -0,0 +1,3 @@ +MODULE = auto_init_storage + +include $(RIOTBASE)/Makefile.base diff --git a/sys/auto_init/storage/auto_init_sdcard_spi.c b/sys/auto_init/storage/auto_init_sdcard_spi.c new file mode 100644 index 0000000000..8c7dc3c25f --- /dev/null +++ b/sys/auto_init/storage/auto_init_sdcard_spi.c @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2017 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup auto_init_sdcard_spi + * @{ + * + * @file + * @brief Auto initialization for sd-cards connected over spi + * + * @author Michel Rottleuthner + */ + +#ifdef MODULE_SDCARD_SPI + +#include "sdcard_spi.h" +#include "sdcard_spi_params.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief number of used sd cards + * @{ + */ +#define SDCARD_SPI_NUM (sizeof(sdcard_spi_params) / sizeof(sdcard_spi_params[0])) +/** @} */ + +/** + * @brief Allocate memory for the device descriptors + * @{ + */ +sdcard_spi_t sdcard_spi_devs[SDCARD_SPI_NUM]; +/** @} */ + +void auto_init_sdcard_spi(void) +{ + for (int i = 0; i < SDCARD_SPI_NUM; i++) { + DEBUG("sdcard_spi_auto_init(): initializing device [%i]...\n", i); + int resu = sdcard_spi_init(&sdcard_spi_devs[i], &sdcard_spi_params[i]); + if(resu != 0){ + DEBUG("error initializing device [%i]\n", i); + } + } +} + +#else +typedef int dont_be_pedantic; +#endif /* MODULE_SDCARD_SPI */ +/** @} */ diff --git a/tests/driver_sdcard_spi/Makefile b/tests/driver_sdcard_spi/Makefile new file mode 100644 index 0000000000..b9cfac34ad --- /dev/null +++ b/tests/driver_sdcard_spi/Makefile @@ -0,0 +1,9 @@ +APPLICATION = driver_sdcard_spi +include ../Makefile.tests_common + +USEMODULE += sdcard_spi +USEMODULE += auto_init_storage +USEMODULE += fmt +USEMODULE += shell + +include $(RIOTBASE)/Makefile.include diff --git a/tests/driver_sdcard_spi/main.c b/tests/driver_sdcard_spi/main.c new file mode 100644 index 0000000000..24f5272b5c --- /dev/null +++ b/tests/driver_sdcard_spi/main.c @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2016 Michel Rottleuthner + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup tests + * @{ + * + * @file + * @brief Test application for the sd-card spi driver + * + * @author Michel Rottleuthner + * + * @} + */ +#include "shell.h" +#include "sdcard_spi.h" +#include "sdcard_spi_internal.h" +#include "sdcard_spi_params.h" +#include "fmt.h" +#include +#include +#include + +/* independent of what you specify in a r/w cmd this is the maximum number of blocks read at once. + If you call read with a bigger blockcount the read is performed in chunks*/ +#define MAX_BLOCKS_IN_BUFFER 4 +#define BLOCK_PRINT_BYTES_PER_LINE 16 +#define FIRST_PRINTABLE_ASCII_CHAR 0x20 +#define ASCII_UNPRINTABLE_REPLACEMENT "." + +/* this is provided by the sdcard_spi driver + * see sys/auto_init/storage/auto_init_sdcard_spi.c */ +extern sdcard_spi_t sdcard_spi_devs[sizeof(sdcard_spi_params) / sizeof(sdcard_spi_params[0])]; +sdcard_spi_t *card = &sdcard_spi_devs[0]; + +char buffer[SD_HC_BLOCK_SIZE * MAX_BLOCKS_IN_BUFFER]; + +static int _init(int argc, char **argv) +{ + printf("Initializing SD-card at SPI_%i...", sdcard_spi_params[0].spi_dev); + + if (sdcard_spi_init(card, &sdcard_spi_params[0]) != 0) { + puts("[FAILED]"); + #if ENABLE_DEBUG != 1 + puts("enable debugging in sdcard_spi.c for further information!"); + #endif + return -2; + } + puts("[OK]"); + return 0; +} + +static int _cid(int argc, char **argv) +{ + puts("----------------------------------------"); + printf("MID: %d\n", card->cid.MID); + printf("OID: %c%c\n", card->cid.OID[0], card->cid.OID[1]); + printf("PNM: %c%c%c%c%c\n", card->cid.PNM[0], card->cid.PNM[1], card->cid.PNM[2], + card->cid.PNM[3], card->cid.PNM[4]); + printf("PRV: %d\n", card->cid.PRV); + printf("PSN: %lu\n", card->cid.PSN); + printf("MDT: %d\n", card->cid.MDT); + printf("CRC: %d\n", card->cid.CID_CRC); + puts("----------------------------------------"); + return 0; +} + +static int _csd(int argc, char **argv) +{ + if (card->csd_structure == SD_CSD_V1) { + puts("CSD V1\n----------------------------------------"); + printf("CSD_STRUCTURE: 0x%0lx\n", (unsigned long)card->csd.v1.CSD_STRUCTURE); + printf("TAAC: 0x%0lx\n", (unsigned long)card->csd.v1.TAAC); + printf("NSAC: 0x%0lx\n", (unsigned long)card->csd.v1.NSAC); + printf("TRAN_SPEED: 0x%0lx\n", (unsigned long)card->csd.v1.TRAN_SPEED); + printf("CCC: 0x%0lx\n", (unsigned long)card->csd.v1.CCC); + printf("READ_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BL_LEN); + printf("READ_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BL_PARTIAL); + printf("WRITE_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BLK_MISALIGN); + printf("READ_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v1.READ_BLK_MISALIGN); + printf("DSR_IMP: 0x%0lx\n", (unsigned long)card->csd.v1.DSR_IMP); + printf("C_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.C_SIZE); + printf("VDD_R_CURR_MIN: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_R_CURR_MIN); + printf("VDD_R_CURR_MAX: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_R_CURR_MAX); + printf("VDD_W_CURR_MIN: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_W_CURR_MIN); + printf("VDD_W_CURR_MAX: 0x%0lx\n", (unsigned long)card->csd.v1.VDD_W_CURR_MAX); + printf("C_SIZE_MULT: 0x%0lx\n", (unsigned long)card->csd.v1.C_SIZE_MULT); + printf("ERASE_BLK_EN: 0x%0lx\n", (unsigned long)card->csd.v1.ERASE_BLK_EN); + printf("SECTOR_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.SECTOR_SIZE); + printf("WP_GRP_SIZE: 0x%0lx\n", (unsigned long)card->csd.v1.WP_GRP_SIZE); + printf("WP_GRP_ENABLE: 0x%0lx\n", (unsigned long)card->csd.v1.WP_GRP_ENABLE); + printf("R2W_FACTOR: 0x%0lx\n", (unsigned long)card->csd.v1.R2W_FACTOR); + printf("WRITE_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BL_LEN); + printf("WRITE_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v1.WRITE_BL_PARTIAL); + printf("FILE_FORMAT_GRP: 0x%0lx\n", (unsigned long)card->csd.v1.FILE_FORMAT_GRP); + printf("COPY: 0x%0lx\n", (unsigned long)card->csd.v1.COPY); + printf("PERM_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v1.PERM_WRITE_PROTECT); + printf("TMP_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v1.TMP_WRITE_PROTECT); + printf("FILE_FORMAT: 0x%0lx\n", (unsigned long)card->csd.v1.FILE_FORMAT); + printf("CRC: 0x%0lx\n", (unsigned long)card->csd.v1.CSD_CRC); + } + else if (card->csd_structure == SD_CSD_V2) { + puts("CSD V2:\n----------------------------------------"); + printf("CSD_STRUCTURE: 0x%0lx\n", (unsigned long)card->csd.v2.CSD_STRUCTURE); + printf("TAAC: 0x%0lx\n", (unsigned long)card->csd.v2.TAAC); + printf("NSAC: 0x%0lx\n", (unsigned long)card->csd.v2.NSAC); + printf("TRAN_SPEED: 0x%0lx\n", (unsigned long)card->csd.v2.TRAN_SPEED); + printf("CCC: 0x%0lx\n", (unsigned long)card->csd.v2.CCC); + printf("READ_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BL_LEN); + printf("READ_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BL_PARTIAL); + printf("WRITE_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BLK_MISALIGN); + printf("READ_BLK_MISALIGN: 0x%0lx\n", (unsigned long)card->csd.v2.READ_BLK_MISALIGN); + printf("DSR_IMP: 0x%0lx\n", (unsigned long)card->csd.v2.DSR_IMP); + printf("C_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.C_SIZE); + printf("ERASE_BLK_EN: 0x%0lx\n", (unsigned long)card->csd.v2.ERASE_BLK_EN); + printf("SECTOR_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.SECTOR_SIZE); + printf("WP_GRP_SIZE: 0x%0lx\n", (unsigned long)card->csd.v2.WP_GRP_SIZE); + printf("WP_GRP_ENABLE: 0x%0lx\n", (unsigned long)card->csd.v2.WP_GRP_ENABLE); + printf("R2W_FACTOR: 0x%0lx\n", (unsigned long)card->csd.v2.R2W_FACTOR); + printf("WRITE_BL_LEN: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BL_LEN); + printf("WRITE_BL_PARTIAL: 0x%0lx\n", (unsigned long)card->csd.v2.WRITE_BL_PARTIAL); + printf("FILE_FORMAT_GRP: 0x%0lx\n", (unsigned long)card->csd.v2.FILE_FORMAT_GRP); + printf("COPY: 0x%0lx\n", (unsigned long)card->csd.v2.COPY); + printf("PERM_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v2.PERM_WRITE_PROTECT); + printf("TMP_WRITE_PROTECT: 0x%0lx\n", (unsigned long)card->csd.v2.TMP_WRITE_PROTECT); + printf("FILE_FORMAT: 0x%0lx\n", (unsigned long)card->csd.v2.FILE_FORMAT); + printf("CRC: 0x%0lx\n", (unsigned long)card->csd.v2.CSD_CRC); + } + puts("----------------------------------------"); + return 0; +} + +static int _sds(int argc, char **argv) +{ + sd_status_t sds; + + if (sdcard_spi_read_sds(card, &sds) == SD_RW_OK) { + puts("SD status:\n----------------------------------------"); + printf("SIZE_OF_PROTECTED_AREA: 0x%0lx\n", (unsigned long)sds.SIZE_OF_PROTECTED_AREA); + printf("SUS_ADDR: 0x%0lx\n", (unsigned long)sds.SUS_ADDR); + printf("VSC_AU_SIZE: 0x%0lx\n", (unsigned long)sds.VSC_AU_SIZE); + printf("SD_CARD_TYPE: 0x%0lx\n", (unsigned long)sds.SD_CARD_TYPE); + printf("ERASE_SIZE: 0x%0lx\n", (unsigned long)sds.ERASE_SIZE); + printf("SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.SPEED_CLASS); + printf("PERFORMANCE_MOVE: 0x%0lx\n", (unsigned long)sds.PERFORMANCE_MOVE); + printf("VIDEO_SPEED_CLASS: 0x%0lx\n", (unsigned long)sds.VIDEO_SPEED_CLASS); + printf("ERASE_TIMEOUT: 0x%0lx\n", (unsigned long)sds.ERASE_TIMEOUT); + printf("ERASE_OFFSET: 0x%0lx\n", (unsigned long)sds.ERASE_OFFSET); + printf("UHS_SPEED_GRADE: 0x%0lx\n", (unsigned long)sds.UHS_SPEED_GRADE); + printf("UHS_AU_SIZE: 0x%0lx\n", (unsigned long)sds.UHS_AU_SIZE); + printf("AU_SIZE: 0x%0lx\n", (unsigned long)sds.AU_SIZE); + printf("DAT_BUS_WIDTH: 0x%0lx\n", (unsigned long)sds.DAT_BUS_WIDTH); + printf("SECURED_MODE: 0x%0lx\n", (unsigned long)sds.SECURED_MODE); + puts("----------------------------------------"); + return 0; + } + return -1; +} + +static int _size(int argc, char **argv) +{ + uint64_t bytes = sdcard_spi_get_capacity(card); + + uint32_t gib_int = bytes / (SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI); + uint32_t gib_frac = ( (((bytes/(SDCARD_SPI_IEC_KIBI * SDCARD_SPI_IEC_KIBI)) + - gib_int * SDCARD_SPI_IEC_KIBI) * SDCARD_SPI_SI_KILO) + / SDCARD_SPI_IEC_KIBI); + + uint32_t gb_int = bytes / (SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO); + uint32_t gb_frac = (bytes / (SDCARD_SPI_SI_KILO * SDCARD_SPI_SI_KILO)) + - (gb_int * SDCARD_SPI_SI_KILO); //[MB] + + puts("\nCard size: "); + //fflush(stdout); + print_u64_dec( bytes ); + printf(" bytes (%lu,%03lu GiB | %lu,%03lu GB)\n", gib_int, gib_frac, gb_int, gb_frac); + return 0; +} + +static int _read(int argc, char **argv) +{ + int blockaddr; + int cnt; + bool print_as_char = false; + + if ((argc == 3) || (argc == 4)) { + blockaddr = (uint32_t)atoi(argv[1]); + cnt = (uint32_t)atoi(argv[2]); + if (argc == 4 && (strcmp("-c", argv[3]) == 0)) { + print_as_char = true; + } + } + else { + printf("usage: %s blockaddr cnt [-c]\n", argv[0]); + return -1; + } + + int total_read = 0; + while (total_read < cnt) { + int chunk_blocks = cnt - total_read; + if (chunk_blocks > MAX_BLOCKS_IN_BUFFER) { + chunk_blocks = MAX_BLOCKS_IN_BUFFER; + } + sd_rw_response_t state; + int chunks_read = sdcard_spi_read_blocks(card, blockaddr + total_read, buffer, + SD_HC_BLOCK_SIZE, chunk_blocks, &state); + + if (state != SD_RW_OK) { + printf("read error %d (block %d/%d)\n", state, total_read + chunks_read, cnt); + return -1; + } + + for (int i = 0; i < chunk_blocks * SD_HC_BLOCK_SIZE; i++) { + + if ((i % SD_HC_BLOCK_SIZE) == 0) { + printf("BLOCK %d:\n", blockaddr + total_read + i / SD_HC_BLOCK_SIZE); + } + + if (print_as_char) { + if (buffer[i] >= FIRST_PRINTABLE_ASCII_CHAR) { + printf("%c", buffer[i]); + } + else { + printf(ASCII_UNPRINTABLE_REPLACEMENT); + } + } + else { + printf("%02x ", buffer[i]); + } + + if ((i % BLOCK_PRINT_BYTES_PER_LINE) == (BLOCK_PRINT_BYTES_PER_LINE - 1)) { + puts(""); /* line break after BLOCK_PRINT_BYTES_PER_LINE bytes */ + } + + if ((i % SD_HC_BLOCK_SIZE) == (SD_HC_BLOCK_SIZE - 1)) { + puts(""); /* empty line after each printed block */ + } + } + total_read += chunks_read; + } + return 0; +} + +static int _write(int argc, char **argv) +{ + int bladdr; + char *data; + int size; + bool repeat_data = false; + + if (argc == 3 || argc == 4) { + bladdr = (int)atoi(argv[1]); + data = argv[2]; + size = strlen(argv[2]); + printf("will write '%s' (%d chars) at start of block %d\n", data, size, bladdr); + if (argc == 4 && (strcmp("-r", argv[3]) == 0)) { + repeat_data = true; + puts("the rest of the block will be filled with copies of that string"); + } + else { + puts("the rest of the block will be filled with zeros"); + } + } + else { + printf("usage: %s blockaddr string [-r]\n", argv[0]); + return -1; + } + + if (size > SD_HC_BLOCK_SIZE) { + printf("maximum stringsize to write at once is %d ...aborting\n", SD_HC_BLOCK_SIZE); + return -1; + } + + /* copy data to a full-block-sized buffer an fill remaining block space according to -r param*/ + char buffer[SD_HC_BLOCK_SIZE]; + for (int i = 0; i < sizeof(buffer); i++) { + if (repeat_data || i < size) { + buffer[i] = data[i % size]; + } + else { + buffer[i] = 0; + } + } + + sd_rw_response_t state; + int chunks_written = sdcard_spi_write_blocks(card, bladdr, buffer, SD_HC_BLOCK_SIZE, 1, &state); + + if (state != SD_RW_OK) { + printf("write error %d (wrote %d/%d blocks)\n", state, chunks_written, 1); + return -1; + } + + printf("write block %d [OK]\n", bladdr); + return 0; +} + +static int _copy(int argc, char **argv) +{ + int src_block; + int dst_block; + char tmp_copy[SD_HC_BLOCK_SIZE]; + + if (argc != 3) { + printf("usage: %s src_block dst_block\n", argv[0]); + return -1; + } + + src_block = (uint32_t)atoi(argv[1]); + dst_block = (uint32_t)atoi(argv[2]); + + sd_rw_response_t rd_state; + sdcard_spi_read_blocks(card, src_block, tmp_copy, SD_HC_BLOCK_SIZE, 1, &rd_state); + + if (rd_state != SD_RW_OK) { + printf("read error %d (block %d)\n", rd_state, src_block); + return -1; + } + + sd_rw_response_t wr_state; + sdcard_spi_write_blocks(card, dst_block, tmp_copy, SD_HC_BLOCK_SIZE, 1, &wr_state); + + if (wr_state != SD_RW_OK) { + printf("write error %d (block %d)\n", wr_state, dst_block); + return -2; + } + + printf("copy block %d to %d [OK]\n", src_block, dst_block); + return 0; +} + +static int _sector_count(int argc, char **argv) +{ + printf("available sectors on card: %li\n", sdcard_spi_get_sector_count(card)); + return 0; +} + +static const shell_command_t shell_commands[] = { + { "init", "initializes default card", _init }, + { "cid", "print content of CID (Card IDentification) register", _cid }, + { "csd", "print content of CSD (Card-Specific Data) register", _csd }, + { "sds", "print SD status", _sds }, + { "size", "print card size", _size }, + { "sectors", "print sector count of card", _sector_count }, + { "read", "'read n m' reads m blocks beginning at block address n and prints the result. " + "Append -c option to print data readable chars", _read }, + { "write", "'write n data' writes data to block n. Append -r option to " + "repeatedly write data to coplete block", _write }, + { "copy", "'copy src dst' copies block src to block dst", _copy }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("SD-card spi driver test application"); + + card->init_done = false; + + puts("insert SD-card and use 'init' command to set card to spi mode"); + puts("WARNING: using 'write' or 'copy' commands WILL overwrite data on your sd-card and"); + puts("almost for sure corrupt existing filesystems, partitions and contained data!"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + return 0; +}