| 
									
										
										
										
											2021-05-24 02:09:38 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: Apache-2.0 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <stddef.h>
 | 
					
						
							|  |  |  | #include <sys/param.h>
 | 
					
						
							|  |  |  | #include "esp_log.h"
 | 
					
						
							|  |  |  | #include "esp_heap_caps.h"
 | 
					
						
							|  |  |  | #include "driver/gpio.h"
 | 
					
						
							|  |  |  | #include "driver/sdmmc_defs.h"
 | 
					
						
							|  |  |  | #include "driver/sdspi_host.h"
 | 
					
						
							|  |  |  | #include "sdspi_private.h"
 | 
					
						
							|  |  |  | #include "sdspi_crc.h"
 | 
					
						
							|  |  |  | #include "esp_timer.h"
 | 
					
						
							| 
									
										
										
										
											2019-06-25 13:36:24 +08:00
										 |  |  | #include "freertos/FreeRTOS.h"
 | 
					
						
							|  |  |  | #include "freertos/semphr.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | #include "soc/soc_memory_layout.h"
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Max number of transactions in flight (used in start_command_write_blocks)
 | 
					
						
							|  |  |  | #define SDSPI_TRANSACTION_COUNT 4
 | 
					
						
							|  |  |  | #define SDSPI_MOSI_IDLE_VAL     0xff    //!< Data value which causes MOSI to stay high
 | 
					
						
							|  |  |  | #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
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * @brief Structure containing run time configuration for a single SD slot | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The slot info is referenced to by an sdspi_dev_handle_t (alias int). The handle may be the raw | 
					
						
							|  |  |  |  * pointer to the slot info itself (force converted to, new API in IDFv4.2), or the index of the | 
					
						
							|  |  |  |  * s_slot array (deprecated API). Returning the raw pointer to the caller instead of storing it | 
					
						
							|  |  |  |  * locally can save some static memory. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | typedef struct { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_host_device_t   host_id; //!< SPI host id.
 | 
					
						
							|  |  |  |     spi_device_handle_t spi_handle; //!< SPI device handle, used for transactions
 | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     uint8_t gpio_cs;            //!< CS GPIO, or GPIO_UNUSED
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     uint8_t gpio_cd;            //!< Card detect GPIO, or GPIO_UNUSED
 | 
					
						
							|  |  |  |     uint8_t gpio_wp;            //!< Write protect GPIO, or GPIO_UNUSED
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     uint8_t gpio_int;            //!< Write protect GPIO, or GPIO_UNUSED
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     /// Set to 1 if the higher layer has asked the card to enable CRC checks
 | 
					
						
							|  |  |  |     uint8_t data_crc_enabled : 1; | 
					
						
							|  |  |  |     /// Intermediate buffer used when application buffer is not in DMA memory;
 | 
					
						
							|  |  |  |     /// allocated on demand, SDSPI_BLOCK_BUF_SIZE bytes long. May be zero.
 | 
					
						
							|  |  |  |     uint8_t* block_buf; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     /// semaphore of gpio interrupt
 | 
					
						
							|  |  |  |     SemaphoreHandle_t   semphr_int; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } slot_info_t; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | // Reserved for old API to be back-compatible
 | 
					
						
							|  |  |  | static slot_info_t *s_slots[SOC_SPI_PERIPH_NUM] = {}; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | static const char *TAG = "sdspi_host"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static const bool use_polling = true; | 
					
						
							|  |  |  | static const bool no_use_polling = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | /// Functions to send out different kinds of commands
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         uint8_t *data, uint32_t rx_length, bool need_stop_command); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         const uint8_t *data, uint32_t tx_length, bool multi_block, bool stop_trans); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cmd_t *cmd); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | static esp_err_t shift_cmd_response(sdspi_hw_cmd_t *cmd, int sent_bytes); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-17 00:09:59 +09:00
										 |  |  | static esp_err_t poll_busy(slot_info_t *slot, int timeout_ms, bool polling); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | /// A few helper functions
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Map handle to pointer of slot information
 | 
					
						
							|  |  |  | static slot_info_t* get_slot_info(sdspi_dev_handle_t handle) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if ((uint32_t) handle < SOC_SPI_PERIPH_NUM) { | 
					
						
							|  |  |  |         return s_slots[handle]; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return (slot_info_t *) handle; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Store slot information (if possible) and return corresponding handle
 | 
					
						
							|  |  |  | static sdspi_dev_handle_t store_slot_info(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     /*
 | 
					
						
							|  |  |  |      * To be back-compatible, the first device of each bus will always be stored locally, and | 
					
						
							|  |  |  |      * referenced to by the handle `host_id`, otherwise the new API return the raw pointer to the | 
					
						
							|  |  |  |      * slot info as the handle, to save some static memory. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     if (s_slots[slot->host_id] == NULL) { | 
					
						
							|  |  |  |         s_slots[slot->host_id] = slot; | 
					
						
							|  |  |  |         return slot->host_id; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return (sdspi_dev_handle_t)slot; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Get the slot info for a specific handle, and remove the local reference (if exist).
 | 
					
						
							|  |  |  | static slot_info_t* remove_slot_info(sdspi_dev_handle_t handle) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if ((uint32_t) handle < SOC_SPI_PERIPH_NUM) { | 
					
						
							|  |  |  |         slot_info_t* slot = s_slots[handle]; | 
					
						
							|  |  |  |         s_slots[handle] = NULL; | 
					
						
							|  |  |  |         return slot; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         return (slot_info_t *) handle; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Set CS high for given slot
 | 
					
						
							|  |  |  | static void cs_high(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     if (slot->gpio_cs != GPIO_UNUSED) { | 
					
						
							|  |  |  |         gpio_set_level(slot->gpio_cs, 1); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Set CS low for given slot
 | 
					
						
							|  |  |  | static void cs_low(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     if (slot->gpio_cs != GPIO_UNUSED) { | 
					
						
							|  |  |  |         gpio_set_level(slot->gpio_cs, 0); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Return true if WP pin is configured and is low
 | 
					
						
							|  |  |  | static bool card_write_protected(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->gpio_wp == GPIO_UNUSED) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return gpio_get_level(slot->gpio_wp) == 0; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | /// Return true if CD pin is configured and is high
 | 
					
						
							|  |  |  | static bool card_missing(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->gpio_cd == GPIO_UNUSED) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return gpio_get_level(slot->gpio_cd) == 1; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Get pointer to a block of DMA memory, allocate if necessary.
 | 
					
						
							|  |  |  | /// This is used if the application provided buffer is not in DMA capable memory.
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t get_block_buf(slot_info_t *slot, uint8_t **out_buf) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->block_buf == NULL) { | 
					
						
							|  |  |  |         slot->block_buf = heap_caps_malloc(SDSPI_BLOCK_BUF_SIZE, MALLOC_CAP_DMA); | 
					
						
							|  |  |  |         if (slot->block_buf == NULL) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             return ESP_ERR_NO_MEM; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     *out_buf = slot->block_buf; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Clock out one byte (CS has to be high) to make the card release MISO
 | 
					
						
							|  |  |  | /// (clocking one bit would work as well, but that triggers a bug in SPI DMA)
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static void release_bus(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     spi_transaction_t t = { | 
					
						
							|  |  |  |         .flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA, | 
					
						
							|  |  |  |         .length = 8, | 
					
						
							|  |  |  |         .tx_data = {0xff} | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_device_polling_transmit(slot->spi_handle, &t); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     // don't care if this failed
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /// Clock out 80 cycles (10 bytes) before GO_IDLE command
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static void go_idle_clockout(slot_info_t *slot) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     //actually we need 10, declare 12 to meet requirement of RXDMA
 | 
					
						
							|  |  |  |     uint8_t data[12]; | 
					
						
							|  |  |  |     memset(data, 0xff, sizeof(data)); | 
					
						
							|  |  |  |     spi_transaction_t t = { | 
					
						
							|  |  |  |         .length = 10*8, | 
					
						
							|  |  |  |         .tx_buffer = data, | 
					
						
							|  |  |  |         .rx_buffer = data, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_device_polling_transmit(slot->spi_handle, &t); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     // don't care if this failed
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |  * (Re)Configure SPI device. Used to change clock speed. | 
					
						
							|  |  |  |  * @param slot Pointer to the slot to be configured | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |  * @param clock_speed_hz  clock speed, Hz | 
					
						
							|  |  |  |  * @return ESP_OK on success | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t configure_spi_dev(slot_info_t *slot, int clock_speed_hz) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->spi_handle) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         // Reinitializing
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_bus_remove_device(slot->spi_handle); | 
					
						
							|  |  |  |         slot->spi_handle = NULL; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  |     spi_device_interface_config_t devcfg = { | 
					
						
							|  |  |  |         .clock_speed_hz = clock_speed_hz, | 
					
						
							|  |  |  |         .mode = 0, | 
					
						
							|  |  |  |         // For SD cards, CS must stay low during the whole read/write operation,
 | 
					
						
							|  |  |  |         // rather than a single SPI transaction.
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         .spics_io_num = GPIO_NUM_NC, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .queue_size = SDSPI_TRANSACTION_COUNT, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     return spi_bus_add_device(slot->host_id, &devcfg, &slot->spi_handle); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-16 16:33:30 +07:00
										 |  |  | esp_err_t sdspi_host_init(void) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t deinit_slot(slot_info_t *slot) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     esp_err_t err = ESP_OK; | 
					
						
							|  |  |  |     if (slot->spi_handle) { | 
					
						
							|  |  |  |         spi_bus_remove_device(slot->spi_handle); | 
					
						
							|  |  |  |         slot->spi_handle = NULL; | 
					
						
							|  |  |  |         free(slot->block_buf); | 
					
						
							|  |  |  |         slot->block_buf = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     uint64_t pin_bit_mask = 0; | 
					
						
							|  |  |  |     if (slot->gpio_cs != GPIO_UNUSED) { | 
					
						
							|  |  |  |         pin_bit_mask |= BIT64(slot->gpio_cs); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (slot->gpio_cd != GPIO_UNUSED) { | 
					
						
							|  |  |  |         pin_bit_mask |= BIT64(slot->gpio_cd); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (slot->gpio_wp != GPIO_UNUSED) { | 
					
						
							|  |  |  |         pin_bit_mask |= BIT64(slot->gpio_wp); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (slot->gpio_int != GPIO_UNUSED) { | 
					
						
							|  |  |  |         pin_bit_mask |= BIT64(slot->gpio_int); | 
					
						
							|  |  |  |         gpio_intr_disable(slot->gpio_int); | 
					
						
							|  |  |  |         gpio_isr_handler_remove(slot->gpio_int); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     gpio_config_t config = { | 
					
						
							|  |  |  |         .pin_bit_mask = pin_bit_mask, | 
					
						
							|  |  |  |         .mode = GPIO_MODE_INPUT, | 
					
						
							| 
									
										
										
										
											2020-06-19 12:00:58 +08:00
										 |  |  |         .intr_type = GPIO_INTR_DISABLE, | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     if (pin_bit_mask != 0) { | 
					
						
							|  |  |  |         gpio_config(&config); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (slot->semphr_int) { | 
					
						
							|  |  |  |         vSemaphoreDelete(slot->semphr_int); | 
					
						
							|  |  |  |         slot->semphr_int = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     free(slot); | 
					
						
							|  |  |  |     return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | esp_err_t sdspi_host_remove_device(sdspi_dev_handle_t handle) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     //Get the slot info and remove the reference in the static memory (if used)
 | 
					
						
							|  |  |  |     slot_info_t* slot = remove_slot_info(handle); | 
					
						
							|  |  |  |     if (slot == NULL) { | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_ARG; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    deinit_slot(slot); | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //only the slots locally stored can be deinit in this function.
 | 
					
						
							| 
									
										
										
										
											2019-07-16 16:33:30 +07:00
										 |  |  | esp_err_t sdspi_host_deinit(void) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     for (size_t i = 0; i < sizeof(s_slots)/sizeof(s_slots[0]); ++i) { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot_info_t* slot = remove_slot_info(i); | 
					
						
							|  |  |  |         //slot isn't used, skip
 | 
					
						
							|  |  |  |         if (slot == NULL) continue; | 
					
						
							| 
									
										
										
										
											2019-09-28 16:47:50 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         deinit_slot(slot); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | esp_err_t sdspi_host_set_card_clk(sdspi_dev_handle_t handle, uint32_t freq_khz) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     slot_info_t *slot = get_slot_info(handle); | 
					
						
							|  |  |  |     if (slot == NULL) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         return ESP_ERR_INVALID_ARG; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-04 13:08:48 +08:00
										 |  |  |     ESP_LOGD(TAG, "Setting card clock to %"PRIu32" kHz", freq_khz); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     return configure_spi_dev(slot, freq_khz * 1000); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | static void gpio_intr(void* arg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     BaseType_t awoken = pdFALSE; | 
					
						
							|  |  |  |     slot_info_t* slot = (slot_info_t*)arg; | 
					
						
							|  |  |  |     xSemaphoreGiveFromISR(slot->semphr_int, &awoken); | 
					
						
							|  |  |  |     gpio_intr_disable(slot->gpio_int); | 
					
						
							|  |  |  |     if (awoken) { | 
					
						
							|  |  |  |         portYIELD_FROM_ISR(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | esp_err_t sdspi_host_init_device(const sdspi_device_config_t* slot_config, sdspi_dev_handle_t* out_handle) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     ESP_LOGD(TAG, "%s: SPI%d cs=%d cd=%d wp=%d", | 
					
						
							|  |  |  |              __func__, slot_config->host_id + 1, slot_config->gpio_cs, | 
					
						
							|  |  |  |              slot_config->gpio_cd, slot_config->gpio_wp); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     slot_info_t* slot = (slot_info_t*)malloc(sizeof(slot_info_t)); | 
					
						
							|  |  |  |     if (slot == NULL) { | 
					
						
							|  |  |  |         return ESP_ERR_NO_MEM; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     *slot = (slot_info_t) { | 
					
						
							|  |  |  |         .host_id = slot_config->host_id, | 
					
						
							|  |  |  |         .gpio_cs = slot_config->gpio_cs, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Attach the SD card to the SPI bus
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     esp_err_t ret = configure_spi_dev(slot, SDMMC_FREQ_PROBING * 1000); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     if (ret != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "spi_bus_add_device failed with rc=0x%x", ret); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         goto cleanup; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Configure CS pin
 | 
					
						
							|  |  |  |     gpio_config_t io_conf = { | 
					
						
							| 
									
										
										
										
											2020-06-19 12:00:58 +08:00
										 |  |  |         .intr_type = GPIO_INTR_DISABLE, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .mode = GPIO_MODE_OUTPUT, | 
					
						
							| 
									
										
										
										
											2019-11-18 13:42:22 +01:00
										 |  |  |         .pin_bit_mask = 1ULL << slot_config->gpio_cs, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     if (slot_config->gpio_cs != SDSPI_SLOT_NO_CS) { | 
					
						
							|  |  |  |         slot->gpio_cs = slot_config->gpio_cs; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         slot->gpio_cs = GPIO_UNUSED; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-07 17:45:23 +02:00
										 |  |  |     if (slot->gpio_cs != GPIO_UNUSED) { | 
					
						
							|  |  |  |         ret = gpio_config(&io_conf); | 
					
						
							|  |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "gpio_config (CS) failed with rc=0x%x", ret); | 
					
						
							|  |  |  |             goto cleanup; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         cs_high(slot); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Configure CD and WP pins
 | 
					
						
							|  |  |  |     io_conf = (gpio_config_t) { | 
					
						
							| 
									
										
										
										
											2020-06-19 12:00:58 +08:00
										 |  |  |         .intr_type = GPIO_INTR_DISABLE, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .mode = GPIO_MODE_INPUT, | 
					
						
							|  |  |  |         .pin_bit_mask = 0, | 
					
						
							|  |  |  |         .pull_up_en = true | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     if (slot_config->gpio_cd != SDSPI_SLOT_NO_CD) { | 
					
						
							| 
									
										
										
										
											2019-11-18 13:42:22 +01:00
										 |  |  |         io_conf.pin_bit_mask |= (1ULL << slot_config->gpio_cd); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_cd = slot_config->gpio_cd; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_cd = GPIO_UNUSED; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (slot_config->gpio_wp != SDSPI_SLOT_NO_WP) { | 
					
						
							| 
									
										
										
										
											2019-11-18 13:42:22 +01:00
										 |  |  |         io_conf.pin_bit_mask |= (1ULL << slot_config->gpio_wp); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_wp = slot_config->gpio_wp; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_wp = GPIO_UNUSED; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (io_conf.pin_bit_mask != 0) { | 
					
						
							|  |  |  |         ret = gpio_config(&io_conf); | 
					
						
							|  |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "gpio_config (CD/WP) failed with rc=0x%x", ret); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             goto cleanup; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (slot_config->gpio_int != SDSPI_SLOT_NO_INT) { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_int = slot_config->gpio_int; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         io_conf = (gpio_config_t) { | 
					
						
							|  |  |  |             .intr_type = GPIO_INTR_LOW_LEVEL, | 
					
						
							|  |  |  |             .mode = GPIO_MODE_INPUT, | 
					
						
							|  |  |  |             .pull_up_en = true, | 
					
						
							| 
									
										
										
										
											2019-11-18 13:42:22 +01:00
										 |  |  |             .pin_bit_mask = (1ULL << slot_config->gpio_int), | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         }; | 
					
						
							|  |  |  |         ret = gpio_config(&io_conf); | 
					
						
							|  |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "gpio_config (interrupt) failed with rc=0x%x", ret); | 
					
						
							|  |  |  |             goto cleanup; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->semphr_int = xSemaphoreCreateBinary(); | 
					
						
							|  |  |  |         if (slot->semphr_int == NULL) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             ret = ESP_ERR_NO_MEM; | 
					
						
							|  |  |  |             goto cleanup; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         gpio_intr_disable(slot->gpio_int); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         // 1. the interrupt is better to be disabled before the ISR is registered
 | 
					
						
							|  |  |  |         // 2. the semaphore MUST be initialized before the ISR is registered
 | 
					
						
							|  |  |  |         // 3. the gpio_int member should be filled before the ISR is registered
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = gpio_isr_handler_add(slot->gpio_int, &gpio_intr, slot); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             ESP_LOGE(TAG, "gpio_isr_handle_add failed with rc=0x%x", ret); | 
					
						
							|  |  |  |             goto cleanup; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         slot->gpio_int = GPIO_UNUSED; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     //Initialization finished, store the store information if possible
 | 
					
						
							|  |  |  |     //Then return corresponding handle
 | 
					
						
							|  |  |  |     *out_handle = store_slot_info(slot); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     return ESP_OK; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | cleanup: | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->semphr_int) { | 
					
						
							|  |  |  |         vSemaphoreDelete(slot->semphr_int); | 
					
						
							|  |  |  |         slot->semphr_int = NULL; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (slot->spi_handle) { | 
					
						
							|  |  |  |         spi_bus_remove_device(slot->spi_handle); | 
					
						
							|  |  |  |         slot->spi_handle = NULL; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     free(slot); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     return ret; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | esp_err_t sdspi_host_start_command(sdspi_dev_handle_t handle, sdspi_hw_cmd_t *cmd, void *data, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |                                    uint32_t data_size, int flags) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     slot_info_t *slot = get_slot_info(handle); | 
					
						
							|  |  |  |     if (slot == NULL) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         return ESP_ERR_INVALID_ARG; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (card_missing(slot)) { | 
					
						
							|  |  |  |         return ESP_ERR_NOT_FOUND; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // save some parts of cmd, as its contents will be overwritten
 | 
					
						
							|  |  |  |     int cmd_index = cmd->cmd_index; | 
					
						
							|  |  |  |     uint32_t cmd_arg; | 
					
						
							|  |  |  |     memcpy(&cmd_arg, cmd->arguments, sizeof(cmd_arg)); | 
					
						
							|  |  |  |     cmd_arg = __builtin_bswap32(cmd_arg); | 
					
						
							| 
									
										
										
										
											2022-08-04 13:08:48 +08:00
										 |  |  |     ESP_LOGV(TAG, "%s: slot=%i, CMD%d, arg=0x%08"PRIx32" flags=0x%x, data=%p, data_size=%"PRIu32" crc=0x%02x", | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |              __func__, handle, cmd_index, cmd_arg, flags, data, data_size, cmd->crc7); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-17 00:09:59 +09:00
										 |  |  |     spi_device_acquire_bus(slot->spi_handle, portMAX_DELAY); | 
					
						
							|  |  |  |     poll_busy(slot, 40, true); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     // For CMD0, clock out 80 cycles to help the card enter idle state,
 | 
					
						
							|  |  |  |     // *before* CS is asserted.
 | 
					
						
							|  |  |  |     if (cmd_index == MMC_GO_IDLE_STATE) { | 
					
						
							|  |  |  |         go_idle_clockout(slot); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     // actual transaction
 | 
					
						
							|  |  |  |     esp_err_t ret = ESP_OK; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     cs_low(slot); | 
					
						
							|  |  |  |     if (flags & SDSPI_CMD_FLAG_DATA) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         const bool multi_block = flags & SDSPI_CMD_FLAG_MULTI_BLK; | 
					
						
							|  |  |  |         //send stop transmission token only when multi-block write and non-SDIO mode
 | 
					
						
							|  |  |  |         const bool stop_transmission = multi_block && !(flags & SDSPI_CMD_FLAG_RSP_R5); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (flags & SDSPI_CMD_FLAG_WRITE) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             ret = start_command_write_blocks(slot, cmd, data, data_size, multi_block, stop_transmission); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             ret = start_command_read_blocks(slot, cmd, data, data_size, stop_transmission); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         ret = start_command_default(slot, flags, cmd); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     cs_high(slot); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     release_bus(slot); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_device_release_bus(slot->spi_handle); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (ret != ESP_OK) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "%s: cmd=%d error=0x%x", __func__, cmd_index, ret); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         // Update internal state when some commands are sent successfully
 | 
					
						
							|  |  |  |         if (cmd_index == SD_CRC_ON_OFF) { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |             slot->data_crc_enabled = (uint8_t) cmd_arg; | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "data CRC set=%d", slot->data_crc_enabled); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_default(slot_info_t *slot, int flags, sdspi_hw_cmd_t *cmd) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     size_t cmd_size = SDSPI_CMD_R1_SIZE; | 
					
						
							|  |  |  |     if ((flags & SDSPI_CMD_FLAG_RSP_R1) || | 
					
						
							| 
									
										
										
										
											2022-04-10 21:45:01 +02:00
										 |  |  |         (flags & SDSPI_CMD_FLAG_NORSP) || | 
					
						
							|  |  |  |         (flags & SDSPI_CMD_FLAG_RSP_R1B )) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         cmd_size = SDSPI_CMD_R1_SIZE; | 
					
						
							|  |  |  |     } else if (flags & SDSPI_CMD_FLAG_RSP_R2) { | 
					
						
							|  |  |  |         cmd_size = SDSPI_CMD_R2_SIZE; | 
					
						
							|  |  |  |     } else if (flags & SDSPI_CMD_FLAG_RSP_R3) { | 
					
						
							|  |  |  |         cmd_size = SDSPI_CMD_R3_SIZE; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     } else if (flags & SDSPI_CMD_FLAG_RSP_R4) { | 
					
						
							|  |  |  |         cmd_size = SDSPI_CMD_R4_SIZE; | 
					
						
							|  |  |  |     } else if (flags & SDSPI_CMD_FLAG_RSP_R5) { | 
					
						
							|  |  |  |         cmd_size = SDSPI_CMD_R5_SIZE; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } else if (flags & SDSPI_CMD_FLAG_RSP_R7) { | 
					
						
							|  |  |  |         cmd_size = SDSPI_CMD_R7_SIZE; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     //add extra clocks to avoid polling
 | 
					
						
							|  |  |  |     cmd_size += (SDSPI_NCR_MAX_SIZE-SDSPI_NCR_MIN_SIZE); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     spi_transaction_t t = { | 
					
						
							|  |  |  |         .flags = 0, | 
					
						
							|  |  |  |         .length = cmd_size * 8, | 
					
						
							|  |  |  |         .tx_buffer = cmd, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         .rx_buffer = cmd, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     if (cmd->cmd_index == MMC_STOP_TRANSMISSION) { | 
					
						
							|  |  |  |         /* response is a stuff byte from previous transfer, ignore it */ | 
					
						
							|  |  |  |         cmd->r1 = 0xff; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (ret != ESP_OK) { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ESP_LOGD(TAG, "%s: spi_device_polling_transmit returned 0x%x", __func__, ret); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (flags & SDSPI_CMD_FLAG_NORSP) { | 
					
						
							|  |  |  |         /* no (correct) response expected from the card, so skip polling loop */ | 
					
						
							|  |  |  |         ESP_LOGV(TAG, "%s: ignoring response byte", __func__); | 
					
						
							|  |  |  |         cmd->r1 = 0x00; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     // we have sent and received bytes with enough length.
 | 
					
						
							|  |  |  |     // now shift the response to match the offset of sdspi_hw_cmd_t
 | 
					
						
							|  |  |  |     ret = shift_cmd_response(cmd, cmd_size); | 
					
						
							|  |  |  |     if (ret != ESP_OK) return ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-10 21:45:01 +02:00
										 |  |  |     if (flags & SDSPI_CMD_FLAG_RSP_R1B) { | 
					
						
							|  |  |  |         ret = poll_busy(slot, cmd->timeout_ms, no_use_polling); | 
					
						
							|  |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wait until MISO goes high
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t poll_busy(slot_info_t *slot, int timeout_ms, bool polling) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     uint8_t t_rx; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_transaction_t t = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .tx_buffer = &t_rx, | 
					
						
							|  |  |  |         .flags = SPI_TRANS_USE_RXDATA,  //data stored in rx_data
 | 
					
						
							|  |  |  |         .length = 8, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_err_t ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 12:48:35 +08:00
										 |  |  |     int64_t t_end = esp_timer_get_time() + timeout_ms * 1000; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     int nonzero_count = 0; | 
					
						
							|  |  |  |     do { | 
					
						
							|  |  |  |         t_rx = SDSPI_MOSI_IDLE_VAL; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         t.rx_data[0] = 0; | 
					
						
							|  |  |  |         if (polling) { | 
					
						
							|  |  |  |             ret = spi_device_polling_transmit(slot->spi_handle, &t); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             ret = spi_device_transmit(slot->spi_handle, &t); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         if (t.rx_data[0] != 0) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             if (++nonzero_count == 2) { | 
					
						
							|  |  |  |                 return ESP_OK; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } while(esp_timer_get_time() < t_end); | 
					
						
							|  |  |  |     ESP_LOGD(TAG, "%s: timeout", __func__); | 
					
						
							|  |  |  |     return ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Wait for data token, reading 8 bytes at a time.
 | 
					
						
							|  |  |  | // If the token is found, write all subsequent bytes to extra_ptr,
 | 
					
						
							|  |  |  | // and store the number of bytes written to extra_size.
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t poll_data_token(slot_info_t *slot, uint8_t *extra_ptr, size_t *extra_size, int timeout_ms) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     uint8_t t_rx[8]; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_transaction_t t = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .tx_buffer = &t_rx, | 
					
						
							|  |  |  |         .rx_buffer = &t_rx, | 
					
						
							|  |  |  |         .length = sizeof(t_rx) * 8, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     esp_err_t ret; | 
					
						
							| 
									
										
										
										
											2020-11-17 12:48:35 +08:00
										 |  |  |     int64_t t_end = esp_timer_get_time() + timeout_ms * 1000; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     do { | 
					
						
							|  |  |  |         memset(t_rx, SDSPI_MOSI_IDLE_VAL, sizeof(t_rx)); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_polling_transmit(slot->spi_handle, &t); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         bool found = false; | 
					
						
							| 
									
										
										
										
											2020-11-17 12:48:35 +08:00
										 |  |  |         for (size_t byte_idx = 0; byte_idx < sizeof(t_rx); byte_idx++) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             uint8_t rd_data = t_rx[byte_idx]; | 
					
						
							|  |  |  |             if (rd_data == TOKEN_BLOCK_START) { | 
					
						
							|  |  |  |                 found = true; | 
					
						
							|  |  |  |                 memcpy(extra_ptr, t_rx + byte_idx + 1, sizeof(t_rx) - byte_idx - 1); | 
					
						
							|  |  |  |                 *extra_size = sizeof(t_rx) - byte_idx - 1; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (rd_data != 0xff && rd_data != 0) { | 
					
						
							|  |  |  |                 ESP_LOGD(TAG, "%s: received 0x%02x while waiting for data", | 
					
						
							|  |  |  |                         __func__, rd_data); | 
					
						
							|  |  |  |                 return ESP_ERR_INVALID_RESPONSE; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (found) { | 
					
						
							|  |  |  |             return ESP_OK; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } while (esp_timer_get_time() < t_end); | 
					
						
							|  |  |  |     ESP_LOGD(TAG, "%s: timeout", __func__); | 
					
						
							|  |  |  |     return ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | // the r1 respond could appear 1-8 clocks after the command token is sent
 | 
					
						
							|  |  |  | // this function search for r1 in the buffer after 1 clocks to max 8 clocks
 | 
					
						
							|  |  |  | // then shift the data after R1, to match the definition of sdspi_hw_cmd_t.
 | 
					
						
							|  |  |  | static esp_err_t shift_cmd_response(sdspi_hw_cmd_t* cmd, int sent_bytes) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     uint8_t* pr1 = &cmd->r1; | 
					
						
							|  |  |  |     int ncr_cnt = 1; | 
					
						
							|  |  |  |     while(true) { | 
					
						
							|  |  |  |         if ((*pr1 & SD_SPI_R1_NO_RESPONSE) == 0) break; | 
					
						
							|  |  |  |         pr1++; | 
					
						
							|  |  |  |         if (++ncr_cnt > 8) return ESP_ERR_NOT_FOUND; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     int copy_bytes = sent_bytes - SDSPI_CMD_SIZE - ncr_cnt; | 
					
						
							|  |  |  |     if (copy_bytes > 0) { | 
					
						
							|  |  |  |         memcpy(&cmd->r1, pr1, copy_bytes); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * Receiving one or more blocks of data happens as follows: | 
					
						
							|  |  |  |  * 1. send command + receive r1 response (SDSPI_CMD_R1_SIZE bytes total) | 
					
						
							|  |  |  |  * 2. keep receiving bytes until TOKEN_BLOCK_START is encountered (this may | 
					
						
							|  |  |  |  *    take a while, depending on card's read speed) | 
					
						
							|  |  |  |  * 3. receive up to SDSPI_MAX_DATA_LEN = 512 bytes of actual data | 
					
						
							|  |  |  |  * 4. receive 2 bytes of CRC | 
					
						
							|  |  |  |  * 5. for multi block transfers, go to step 2 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * These steps can be done separately, but that leads to a less than optimal | 
					
						
							|  |  |  |  * performance on large transfers because of delays between each step. | 
					
						
							|  |  |  |  * For example, if steps 3 and 4 are separate SPI transactions queued one after | 
					
						
							|  |  |  |  * another, there will be ~16 microseconds of dead time between end of step 3 | 
					
						
							|  |  |  |  * and the beginning of step 4. A delay between two blocking SPI transactions | 
					
						
							|  |  |  |  * in step 2 is even higher (~60 microseconds). | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * To improve read performance the following sequence is adopted: | 
					
						
							|  |  |  |  * 1. Do the first transfer: command + r1 response + 8 extra bytes. | 
					
						
							|  |  |  |  *    Set pre_scan_data_ptr to point to the 8 extra bytes, and set | 
					
						
							|  |  |  |  *    pre_scan_data_size to 8. | 
					
						
							|  |  |  |  * 2. Search pre_scan_data_size bytes for TOKEN_BLOCK_START. | 
					
						
							|  |  |  |  *    If found, the rest of the bytes contain part of the actual data. | 
					
						
							|  |  |  |  *    Store pointer to and size of that extra data as extra_data_{ptr,size}. | 
					
						
							|  |  |  |  *    If not found, fall back to polling for TOKEN_BLOCK_START, 8 bytes at a | 
					
						
							|  |  |  |  *    time (in poll_data_token function). Deal with extra data in the same way, | 
					
						
							|  |  |  |  *    by setting extra_data_{ptr,size}. | 
					
						
							|  |  |  |  * 3. Receive the remaining 512 - extra_data_size bytes, plus 4 extra bytes | 
					
						
							|  |  |  |  *    (i.e. 516 - extra_data_size). Of the 4 extra bytes, first two will capture | 
					
						
							|  |  |  |  *    the CRC value, and the other two will capture 0xff 0xfe sequence | 
					
						
							|  |  |  |  *    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. | 
					
						
							|  |  |  |  * Further speedup is possible by pipelining transfers and CRC checks, at an | 
					
						
							|  |  |  |  * expense of one extra temporary buffer. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_read_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         uint8_t *data, uint32_t rx_length, bool need_stop_command) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_transaction_t t_command = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .length = (SDSPI_CMD_R1_SIZE + SDSPI_RESPONSE_MAX_DELAY) * 8, | 
					
						
							|  |  |  |         .tx_buffer = cmd, | 
					
						
							|  |  |  |         .rx_buffer = cmd, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t_command); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     if (ret != ESP_OK) { | 
					
						
							|  |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     uint8_t* cmd_u8 = (uint8_t*) cmd; | 
					
						
							|  |  |  |     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.
 | 
					
						
							|  |  |  |      * This loop searches for the response and writes it to cmd->r1. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     while ((cmd->r1 & SD_SPI_R1_NO_RESPONSE) != 0 && pre_scan_data_size > 0) { | 
					
						
							|  |  |  |         cmd->r1 = *pre_scan_data_ptr; | 
					
						
							|  |  |  |         ++pre_scan_data_ptr; | 
					
						
							|  |  |  |         --pre_scan_data_size; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (cmd->r1 & SD_SPI_R1_NO_RESPONSE) { | 
					
						
							|  |  |  |         ESP_LOGD(TAG, "no response token found"); | 
					
						
							|  |  |  |         return ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (rx_length > 0) { | 
					
						
							|  |  |  |         size_t extra_data_size = 0; | 
					
						
							|  |  |  |         const uint8_t* extra_data_ptr = NULL; | 
					
						
							|  |  |  |         bool need_poll = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-17 12:48:35 +08:00
										 |  |  |         for (size_t i = 0; i < pre_scan_data_size; ++i) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             if (pre_scan_data_ptr[i] == TOKEN_BLOCK_START) { | 
					
						
							|  |  |  |                 extra_data_size = pre_scan_data_size - i - 1; | 
					
						
							|  |  |  |                 extra_data_ptr = pre_scan_data_ptr + i + 1; | 
					
						
							|  |  |  |                 need_poll = false; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (need_poll) { | 
					
						
							|  |  |  |             // Wait for data to be ready
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |             ret = poll_data_token(slot, cmd_u8 + SDSPI_CMD_R1_SIZE, &extra_data_size, cmd->timeout_ms); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             if (ret != ESP_OK) { | 
					
						
							|  |  |  |                 return ret; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (extra_data_size) { | 
					
						
							|  |  |  |                 extra_data_ptr = cmd_u8 + SDSPI_CMD_R1_SIZE; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Arrange RX buffer
 | 
					
						
							|  |  |  |         size_t will_receive = MIN(rx_length, SDSPI_MAX_DATA_LEN) - extra_data_size; | 
					
						
							|  |  |  |         uint8_t* rx_data; | 
					
						
							|  |  |  |         ret = get_block_buf(slot, &rx_data); | 
					
						
							|  |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // receive actual data
 | 
					
						
							|  |  |  |         const size_t receive_extra_bytes = (rx_length > SDSPI_MAX_DATA_LEN) ? 4 : 2; | 
					
						
							|  |  |  |         memset(rx_data, 0xff, will_receive + receive_extra_bytes); | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_transaction_t t_data = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             .length = (will_receive + receive_extra_bytes) * 8, | 
					
						
							|  |  |  |             .rx_buffer = rx_data, | 
					
						
							|  |  |  |             .tx_buffer = rx_data | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_transmit(slot->spi_handle, &t_data); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // CRC bytes need to be received even if CRC is not enabled
 | 
					
						
							|  |  |  |         uint16_t crc = UINT16_MAX; | 
					
						
							|  |  |  |         memcpy(&crc, rx_data + will_receive, sizeof(crc)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Bytes to scan for the start token
 | 
					
						
							|  |  |  |         pre_scan_data_size = receive_extra_bytes - sizeof(crc); | 
					
						
							|  |  |  |         pre_scan_data_ptr = rx_data + will_receive + sizeof(crc); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Copy data to the destination buffer
 | 
					
						
							|  |  |  |         memcpy(data + extra_data_size, rx_data, will_receive); | 
					
						
							|  |  |  |         if (extra_data_size) { | 
					
						
							|  |  |  |             memcpy(data, extra_data_ptr, extra_data_size); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // compute CRC of the received data
 | 
					
						
							|  |  |  |         uint16_t crc_of_data = 0; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         if (slot->data_crc_enabled) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             crc_of_data = sdspi_crc16(data, will_receive + extra_data_size); | 
					
						
							|  |  |  |             if (crc_of_data != crc) { | 
					
						
							|  |  |  |                 ESP_LOGE(TAG, "data CRC failed, got=0x%04x expected=0x%04x", crc_of_data, crc); | 
					
						
							|  |  |  |                 esp_log_buffer_hex(TAG, data, 16); | 
					
						
							|  |  |  |                 return ESP_ERR_INVALID_CRC; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         data += will_receive + extra_data_size; | 
					
						
							|  |  |  |         rx_length -= will_receive + extra_data_size; | 
					
						
							|  |  |  |         extra_data_size = 0; | 
					
						
							|  |  |  |         extra_data_ptr = NULL; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (need_stop_command) { | 
					
						
							|  |  |  |         // To end multi block transfer, send stop command and wait for the
 | 
					
						
							|  |  |  |         // card to process it
 | 
					
						
							|  |  |  |         sdspi_hw_cmd_t stop_cmd; | 
					
						
							|  |  |  |         make_hw_cmd(MMC_STOP_TRANSMISSION, 0, cmd->timeout_ms, &stop_cmd); | 
					
						
							| 
									
										
										
										
											2022-04-10 21:45:01 +02:00
										 |  |  |         ret = start_command_default(slot, SDSPI_CMD_FLAG_RSP_R1B, &stop_cmd); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (stop_cmd.r1 != 0) { | 
					
						
							|  |  |  |             ESP_LOGD(TAG, "%s: STOP_TRANSMISSION response 0x%02x", __func__, stop_cmd.r1); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = poll_busy(slot, cmd->timeout_ms, use_polling); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | /* For CMD53, we can send in byte mode, or block mode
 | 
					
						
							|  |  |  |  * The data start token is different, and cannot be determined by the length | 
					
						
							|  |  |  |  * That's why we need ``multi_block``. | 
					
						
							|  |  |  |  * It's also different that stop transmission token is not needed in the SDIO mode. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | static esp_err_t start_command_write_blocks(slot_info_t *slot, sdspi_hw_cmd_t *cmd, | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         const uint8_t *data, uint32_t tx_length, bool multi_block, bool stop_trans) | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | { | 
					
						
							|  |  |  |     if (card_write_protected(slot)) { | 
					
						
							|  |  |  |         ESP_LOGW(TAG, "%s: card write protected", __func__); | 
					
						
							|  |  |  |         return ESP_ERR_INVALID_STATE; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     // Send the minimum length that is sure to get the complete response
 | 
					
						
							|  |  |  |     // SD cards always return R1 (1bytes), SDIO returns R5 (2 bytes)
 | 
					
						
							|  |  |  |     const int send_bytes = SDSPI_CMD_R5_SIZE+SDSPI_NCR_MAX_SIZE-SDSPI_NCR_MIN_SIZE; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     spi_transaction_t t_command = { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         .length = send_bytes * 8, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         .tx_buffer = cmd, | 
					
						
							|  |  |  |         .rx_buffer = cmd, | 
					
						
							|  |  |  |     }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     esp_err_t ret = spi_device_polling_transmit(slot->spi_handle, &t_command); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     if (ret != ESP_OK) { | 
					
						
							|  |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     // check if command response valid
 | 
					
						
							|  |  |  |     ret = shift_cmd_response(cmd, send_bytes); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |     if (ret != ESP_OK) { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         ESP_LOGD(TAG, "%s: check_cmd_response returned 0x%x", __func__, ret); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         return ret; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     uint8_t start_token = multi_block ? | 
					
						
							|  |  |  |              TOKEN_BLOCK_START_WRITE_MULTI : TOKEN_BLOCK_START; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  | 
 | 
					
						
							|  |  |  |     while (tx_length > 0) { | 
					
						
							|  |  |  |         // Write block start token
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_transaction_t t_start_token = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             .length = sizeof(start_token) * 8, | 
					
						
							|  |  |  |             .tx_buffer = &start_token | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_polling_transmit(slot->spi_handle, &t_start_token); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Prepare data to be sent
 | 
					
						
							|  |  |  |         size_t will_send = MIN(tx_length, SDSPI_MAX_DATA_LEN); | 
					
						
							|  |  |  |         const uint8_t* tx_data = data; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         if (!esp_ptr_in_dram(tx_data)) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             // If the pointer can't be used with DMA, copy data into a new buffer
 | 
					
						
							|  |  |  |             uint8_t* tmp; | 
					
						
							|  |  |  |             ret = get_block_buf(slot, &tmp); | 
					
						
							|  |  |  |             if (ret != ESP_OK) { | 
					
						
							|  |  |  |                 return ret; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             memcpy(tmp, tx_data, will_send); | 
					
						
							|  |  |  |             tx_data = tmp; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Write data
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_transaction_t t_data = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             .length = will_send * 8, | 
					
						
							|  |  |  |             .tx_buffer = tx_data, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_transmit(slot->spi_handle, &t_data); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         // Write CRC and get the response in one transaction
 | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         uint16_t crc = sdspi_crc16(data, will_send); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         const int size_crc_response = sizeof(crc) + 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_transaction_t t_crc_rsp = { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             .length = size_crc_response * 8, | 
					
						
							|  |  |  |             .flags = SPI_TRANS_USE_TXDATA|SPI_TRANS_USE_RXDATA, | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         memset(t_crc_rsp.tx_data, 0xff, 4); | 
					
						
							|  |  |  |         memcpy(t_crc_rsp.tx_data, &crc, sizeof(crc)); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_polling_transmit(slot->spi_handle, &t_crc_rsp); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         uint8_t data_rsp = t_crc_rsp.rx_data[2]; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         if (!SD_SPI_DATA_RSP_VALID(data_rsp)) return ESP_ERR_INVALID_RESPONSE; | 
					
						
							|  |  |  |         switch (SD_SPI_DATA_RSP(data_rsp)) { | 
					
						
							|  |  |  |         case SD_SPI_DATA_ACCEPTED: | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         case SD_SPI_DATA_CRC_ERROR: | 
					
						
							|  |  |  |             return ESP_ERR_INVALID_CRC; | 
					
						
							|  |  |  |         case SD_SPI_DATA_WR_ERROR: | 
					
						
							|  |  |  |             return ESP_FAIL; | 
					
						
							|  |  |  |         default: | 
					
						
							|  |  |  |             return ESP_ERR_INVALID_RESPONSE; | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Wait for the card to finish writing data
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = poll_busy(slot, cmd->timeout_ms, no_use_polling); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tx_length -= will_send; | 
					
						
							|  |  |  |         data += will_send; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     if (stop_trans) { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         uint8_t stop_token[2] = { | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |             TOKEN_BLOCK_STOP_WRITE_MULTI, | 
					
						
							|  |  |  |             SDSPI_MOSI_IDLE_VAL | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         spi_transaction_t t_stop_token = { | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |             .length = sizeof(stop_token) * 8, | 
					
						
							|  |  |  |             .tx_buffer = &stop_token, | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = spi_device_polling_transmit(slot->spi_handle, &t_stop_token); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         ret = poll_busy(slot, cmd->timeout_ms, use_polling); | 
					
						
							| 
									
										
										
										
											2018-07-05 09:01:03 +10:00
										 |  |  |         if (ret != ESP_OK) { | 
					
						
							|  |  |  |             return ret; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | esp_err_t sdspi_host_io_int_enable(sdspi_dev_handle_t handle) | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | { | 
					
						
							|  |  |  |     //the pin and its interrupt is already initialized, nothing to do here.
 | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //the interrupt will give the semaphore and then disable itself
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  | esp_err_t sdspi_host_io_int_wait(sdspi_dev_handle_t handle, TickType_t timeout_ticks) | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     slot_info_t* slot = get_slot_info(handle); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     //skip the interrupt and semaphore if the gpio is already low.
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     if (gpio_get_level(slot->gpio_int)==0) return ESP_OK; | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     //clear the semaphore before wait
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     xSemaphoreTake(slot->semphr_int, 0); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     //enable the interrupt and wait for the semaphore
 | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |     gpio_intr_enable(slot->gpio_int); | 
					
						
							|  |  |  |     BaseType_t ret = xSemaphoreTake(slot->semphr_int, timeout_ticks); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |     if (ret == pdFALSE) { | 
					
						
							| 
									
										
										
										
											2018-09-03 23:55:22 +08:00
										 |  |  |         gpio_intr_disable(slot->gpio_int); | 
					
						
							| 
									
										
										
										
											2018-12-29 02:04:37 +08:00
										 |  |  |         return ESP_ERR_TIMEOUT; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return ESP_OK; | 
					
						
							|  |  |  | } |