From b98b4c38863f70e105e575f065d3ab7adbcc58cd Mon Sep 17 00:00:00 2001 From: "Michael (XIAO Xufeng)" Date: Fri, 12 Jul 2019 13:20:04 +0800 Subject: [PATCH] sdmmc_io: support to print CIS information Currently only ESP slaves can be parsed correctly. --- components/driver/include/driver/sdmmc_defs.h | 24 +- components/sdmmc/include/sdmmc_cmd.h | 49 +++ components/sdmmc/sdmmc_io.c | 278 ++++++++++++++++++ .../peripherals/sdio/host/main/app_main.c | 45 ++- 4 files changed, 383 insertions(+), 13 deletions(-) diff --git a/components/driver/include/driver/sdmmc_defs.h b/components/driver/include/driver/sdmmc_defs.h index dd4c426411..4f1e6ddc6b 100644 --- a/components/driver/include/driver/sdmmc_defs.h +++ b/components/driver/include/driver/sdmmc_defs.h @@ -458,15 +458,23 @@ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len) #define SD_IO_CIS_SIZE 0x17000 /* CIS tuple codes (based on PC Card 16) */ -#define SD_IO_CISTPL_NULL 0x00 -#define SD_IO_CISTPL_VERS_1 0x15 -#define SD_IO_CISTPL_MANFID 0x20 -#define SD_IO_CISTPL_FUNCID 0x21 -#define SD_IO_CISTPL_FUNCE 0x22 -#define SD_IO_CISTPL_END 0xff +#define CISTPL_CODE_NULL 0x00 +#define CISTPL_CODE_DEVICE 0x01 +#define CISTPL_CODE_CHKSUM 0x10 +#define CISTPL_CODE_VERS1 0x15 +#define CISTPL_CODE_ALTSTR 0x16 +#define CISTPL_CODE_CONFIG 0x1A +#define CISTPL_CODE_CFTABLE_ENTRY 0x1B +#define CISTPL_CODE_MANFID 0x20 +#define CISTPL_CODE_FUNCID 0x21 +#define TPLFID_FUNCTION_SDIO 0x0c +#define CISTPL_CODE_FUNCE 0x22 +#define CISTPL_CODE_VENDER_BEGIN 0x80 +#define CISTPL_CODE_VENDER_END 0x8F +#define CISTPL_CODE_SDIO_STD 0x91 +#define CISTPL_CODE_SDIO_EXT 0x92 +#define CISTPL_CODE_END 0xFF -/* CISTPL_FUNCID codes */ -#define TPLFID_FUNCTION_SDIO 0x0c /* Timing */ #define SDMMC_TIMING_LEGACY 0 diff --git a/components/sdmmc/include/sdmmc_cmd.h b/components/sdmmc/include/sdmmc_cmd.h index aa12a4477a..666f35560d 100644 --- a/components/sdmmc/include/sdmmc_cmd.h +++ b/components/sdmmc/include/sdmmc_cmd.h @@ -220,6 +220,55 @@ esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card); */ esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks); +/** + * Get the data of CIS region of a SDIO card. + * + * You may provide a buffer not sufficient to store all the CIS data. In this + * case, this functions store as much data into your buffer as possible. Also, + * this function will try to get and return the size required for you. + * + * @param card pointer to card information structure previously initialized + * using sdmmc_card_init + * @param out_buffer Output buffer of the CIS data + * @param buffer_size Size of the buffer. + * @param inout_cis_size Mandatory, pointer to a size, input and output. + * - input: Limitation of maximum searching range, should be 0 or larger than + * buffer_size. The function searches for CIS_CODE_END until this range. Set to + * 0 to search infinitely. + * - output: The size required to store all the CIS data, if CIS_CODE_END is found. + * + * @return + * - ESP_OK: on success + * - ESP_ERR_INVALID_RESPONSE: if the card does not (correctly) support CIS. + * - ESP_ERR_INVALID_SIZE: CIS_CODE_END found, but buffer_size is less than + * required size, which is stored in the inout_cis_size then. + * - ESP_ERR_NOT_FOUND: if the CIS_CODE_END not found. Increase input value of + * inout_cis_size or set it to 0, if you still want to search for the end; + * output value of inout_cis_size is invalid in this case. + * - and other error code return from sdmmc_io_read_bytes + */ +esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size); + +/** + * Parse and print the CIS information of a SDIO card. + * + * @note Not all the CIS codes and all kinds of tuples are supported. If you + * see some unresolved code, you can add the parsing of these code in + * sdmmc_io.c and contribute to the IDF through the Github repository. + * + * using sdmmc_card_init + * @param buffer Buffer to parse + * @param buffer_size Size of the buffer. + * @param fp File pointer to print to, set to NULL to print to stdout. + * + * @return + * - ESP_OK: on success + * - ESP_ERR_NOT_SUPPORTED: if the value from the card is not supported to be parsed. + * - ESP_ERR_INVALID_SIZE: if the CIS size fields are not correct. + */ +esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp); + + #ifdef __cplusplus } #endif diff --git a/components/sdmmc/sdmmc_io.c b/components/sdmmc/sdmmc_io.c index 7229f18983..e77623a17a 100644 --- a/components/sdmmc/sdmmc_io.c +++ b/components/sdmmc/sdmmc_io.c @@ -16,9 +16,50 @@ */ #include "sdmmc_common.h" +#include "esp_attr.h" + + +#define CIS_TUPLE(NAME) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, } +#define CIS_TUPLE_WITH_FUNC(NAME, FUNC) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), } + +#define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return ESP_ERR_INVALID_SIZE;} while(0) +#define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return ESP_ERR_NOT_SUPPORTED;} while(0) +#define CIS_GET_MINIMAL_SIZE 32 + +typedef esp_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp); + +typedef struct { + int code; + const char *name; + cis_tuple_info_func_t func; +} cis_tuple_t; static const char* TAG = "sdmmc_io"; +static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp); +static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp); +static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp); +static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp); + +static const cis_tuple_t cis_table[] = { + CIS_TUPLE(NULL), + CIS_TUPLE(DEVICE), + CIS_TUPLE(CHKSUM), + CIS_TUPLE(VERS1), + CIS_TUPLE(ALTSTR), + CIS_TUPLE(CONFIG), + CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry), + CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid), + CIS_TUPLE(FUNCID), + CIS_TUPLE(FUNCE), + CIS_TUPLE(VENDER_BEGIN), + CIS_TUPLE(VENDER_END), + CIS_TUPLE(SDIO_STD), + CIS_TUPLE(SDIO_EXT), + CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end), +}; + + esp_err_t sdmmc_io_reset(sdmmc_card_t* card) { uint8_t sdio_reset = CCCR_CTL_RES; @@ -352,3 +393,240 @@ esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks) } return (*card->host.io_int_wait)(card->host.slot, timeout_ticks); } + + +/* + * Print the CIS information of a CIS card, currently only ESP slave supported. + */ + +static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp) +{ + const cis_tuple_t* tuple = (const cis_tuple_t*)p; + uint8_t code = *(data++); + int size = *(data++); + if (tuple) { + fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size); + } else { + fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size); + } + for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++)); + fprintf(fp, "\n"); + return ESP_OK; +} + +static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp) +{ + const cis_tuple_t* tuple = (const cis_tuple_t*)p; + data++; + int size = *(data++); + fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size); + CIS_CHECK_SIZE(size, 4); + fprintf(fp, " MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2)); + return ESP_OK; +} + +static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp) +{ + const cis_tuple_t* tuple = (const cis_tuple_t*)p; + data++; + fprintf(fp, "TUPLE: %s\n", tuple->name); + return ESP_OK; +} + +static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp) +{ + const cis_tuple_t* tuple = (const cis_tuple_t*)p; + data++; + int size = *(data++); + fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size); + CIS_CHECK_SIZE(size, 2); + + CIS_CHECK_SIZE(size--, 1); + bool interface = data[0] & BIT(7); + bool def = data[0] & BIT(6); + int conf_ent_num = data[0] & 0x3F; + fprintf(fp, " INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num); + + if (interface) { + CIS_CHECK_SIZE(size--, 1); + fprintf(fp, " IF: %02X\n", *(data++)); + } + + CIS_CHECK_SIZE(size--, 1); + bool misc = data[0] & BIT(7); + int mem_space = (data[0] >> 5 )&(0x3); + bool irq = data[0] & BIT(4); + bool io_sp = data[0] & BIT(3); + bool timing = data[0] & BIT(2); + int power = data[0] & 3; + fprintf(fp, " FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power); + + CIS_CHECK_UNSUPPORTED(power == 0); //power descriptor is not handled yet + CIS_CHECK_UNSUPPORTED(!timing); //timing descriptor is not handled yet + CIS_CHECK_UNSUPPORTED(!io_sp); //io space descriptor is not handled yet + + if (irq) { + CIS_CHECK_SIZE(size--, 1); + bool mask = data[0] & BIT(4); + fprintf(fp, " IR: %02X, mask: %d, ",*(data++), mask); + if (mask) { + CIS_CHECK_SIZE(size, 2); + size-=2; + fprintf(fp, " IRQ: %02X %02X\n", data[0], data[1]); + data+=2; + } + } + + if (mem_space) { + CIS_CHECK_SIZE(size, 2); + size-=2; + CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet + int len = *(uint16_t*)data; + fprintf(fp, " LEN: %04X\n", len); + data+=2; + } + + CIS_CHECK_UNSUPPORTED(misc==0); //misc descriptor is not handled yet + return ESP_OK; +} + +static const cis_tuple_t* get_tuple(uint8_t code) +{ + for (int i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) { + if (code == cis_table[i].code) return &cis_table[i]; + } + return NULL; +} + +esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp) +{ + ESP_LOG_BUFFER_HEXDUMP("CIS", buffer, buffer_size, ESP_LOG_DEBUG); + if (!fp) fp = stdout; + + uint8_t* cis = buffer; + do { + const cis_tuple_t* tuple = get_tuple(cis[0]); + int size = cis[1]; + esp_err_t ret = ESP_OK; + if (tuple) { + ret = tuple->func(tuple, cis, fp); + } else { + ret = cis_tuple_func_default(NULL, cis, fp); + } + if (ret != ESP_OK) return ret; + cis += 2 + size; + if (tuple && tuple->code == CISTPL_CODE_END) break; + } while (cis < buffer + buffer_size) ; + return ESP_OK; +} + +/** + * Check tuples in the buffer. + * + * @param buf Buffer to check + * @param buffer_size Size of the buffer + * @param inout_cis_offset + * - input: the last cis_offset, relative to the beginning of the buf. -1 if + * this buffer begin with the tuple length, otherwise should be no smaller than + * zero. + * - output: when the end tuple found, output offset of the CISTPL_CODE_END + * byte + 1 (relative to the beginning of the buffer; when not found, output + * the address of next tuple code. + * + * @return true if found, false if haven't. + */ +static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset) +{ + int cis_offset = *inout_cis_offset; + if (cis_offset == -1) { + //the CIS code is checked in the last buffer, skip to next tuple + cis_offset += buf[0] + 2; + } + assert(cis_offset >= 0); + while (1) { + if (cis_offset < buffer_size) { + //A CIS code in the buffer, check it + if (buf[cis_offset] == CISTPL_CODE_END) { + *inout_cis_offset = cis_offset + 1; + return true; + } + } + if (cis_offset + 1 < buffer_size) { + cis_offset += buf[cis_offset+1] + 2; + } else { + break; + } + } + *inout_cis_offset = cis_offset; + return false; +} + +esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size) +{ + esp_err_t ret = ESP_OK; + WORD_ALIGNED_ATTR uint8_t buf[CIS_GET_MINIMAL_SIZE]; + + /* + * CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it + * from CCCR register. + */ + uint32_t addr; + ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3); + if (ret != ESP_OK) return ret; + //the sdmmc_io driver reads 4 bytes, the most significant byte is not the address. + addr &= 0xffffff; + if (addr < 0x1000 || addr > 0x17FFF) { + return ESP_ERR_INVALID_RESPONSE; + } + + /* + * To avoid reading too long, take the input value as limitation if + * existing. + */ + size_t max_reading = UINT32_MAX; + if (inout_cis_size && *inout_cis_size != 0) { + max_reading = *inout_cis_size; + } + + /* + * Parse the length while reading. If find the end tuple, or reaches the + * limitation, read no more and return both the data and the size already + * read. + */ + int buffer_offset = 0; + int cur_cis_offset = 0; + bool end_tuple_found = false; + do { + ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE); + if (ret != ESP_OK) return ret; + + //calculate relative to the beginning of the buffer + int offset = cur_cis_offset - buffer_offset; + bool finish = check_tuples_in_buffer(buf, CIS_GET_MINIMAL_SIZE, &offset); + + int remain_size = buffer_size - buffer_offset; + int copy_len; + if (finish) { + copy_len = MIN(offset, remain_size); + end_tuple_found = true; + } else { + copy_len = MIN(CIS_GET_MINIMAL_SIZE, remain_size); + } + if (copy_len > 0) { + memcpy(out_buffer + buffer_offset, buf, copy_len); + } + cur_cis_offset = buffer_offset + offset; + buffer_offset += CIS_GET_MINIMAL_SIZE; + } while (!end_tuple_found && buffer_offset < max_reading); + + if (end_tuple_found) { + *inout_cis_size = cur_cis_offset; + if (cur_cis_offset > buffer_size) { + return ESP_ERR_INVALID_SIZE; + } else { + return ESP_OK; + } + } else { + return ESP_ERR_NOT_FOUND; + } +} diff --git a/examples/peripherals/sdio/host/main/app_main.c b/examples/peripherals/sdio/host/main/app_main.c index 5015048f72..b0b62d15df 100644 --- a/examples/peripherals/sdio/host/main/app_main.c +++ b/examples/peripherals/sdio/host/main/app_main.c @@ -25,6 +25,7 @@ #include "driver/sdmmc_host.h" #include "driver/sdspi_host.h" + /* * For SDIO master-slave board, we have 3 pins controlling power of 3 different * slaves individially. We only enable one at a time. @@ -134,6 +135,36 @@ static void gpio_d2_set_high() } #endif +static esp_err_t print_sdio_cis_information(sdmmc_card_t* card) +{ + const size_t cis_buffer_size = 256; + uint8_t cis_buffer[cis_buffer_size]; + size_t cis_data_len = 1024; //specify maximum searching range to avoid infinite loop + esp_err_t ret = ESP_OK; + + ret = sdmmc_io_get_cis_data(card, cis_buffer, cis_buffer_size, &cis_data_len); + if (ret == ESP_ERR_INVALID_SIZE) { + int temp_buf_size = cis_data_len; + uint8_t* temp_buf = malloc(temp_buf_size); + assert(temp_buf); + + ESP_LOGW(TAG, "CIS data longer than expected, temporary buffer allocated."); + ret = sdmmc_io_get_cis_data(card, temp_buf, temp_buf_size, &cis_data_len); + ESP_ERROR_CHECK(ret); + + sdmmc_io_print_cis_info(temp_buf, cis_data_len, NULL); + + free(temp_buf); + } else if (ret == ESP_OK) { + sdmmc_io_print_cis_info(cis_buffer, cis_data_len, NULL); + } else { + //including ESP_ERR_NOT_FOUND + ESP_LOGE(TAG, "failed to get the entire CIS data."); + abort(); + } + return ESP_OK; +} + //host use this to initialize the slave card as well as SDIO registers esp_err_t slave_init(esp_slave_context_t *context) { @@ -164,10 +195,10 @@ esp_err_t slave_init(esp_slave_context_t *context) */ //slot_config.flags = SDMMC_SLOT_FLAG_INTERNAL_PULLUP; err = sdmmc_host_init(); - assert(err==ESP_OK); + ESP_ERROR_CHECK(err); err = sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config); - assert(err==ESP_OK); + ESP_ERROR_CHECK(err); #else //over SPI sdmmc_host_t config = SDSPI_HOST_DEFAULT(); @@ -179,12 +210,12 @@ esp_err_t slave_init(esp_slave_context_t *context) slot_config.gpio_int = SDIO_SLAVE_SLOT1_IOMUX_PIN_NUM_D1; err = gpio_install_isr_service(0); - assert(err == ESP_OK); + ESP_ERROR_CHECK(err); err = sdspi_host_init(); - assert(err==ESP_OK); + ESP_ERROR_CHECK(err); err = sdspi_host_init_slot(HSPI_HOST, &slot_config); - assert(err==ESP_OK); + ESP_ERROR_CHECK(err); ESP_LOGI(TAG, "Probe using SPI...\n"); //we have to pull up all the slave pins even when the pin is not used @@ -218,10 +249,14 @@ esp_err_t slave_init(esp_slave_context_t *context) *context = ESP_SLAVE_DEFAULT_CONTEXT(card); esp_err_t ret = esp_slave_init_io(context); + ESP_ERROR_CHECK(ret); + ret = print_sdio_cis_information(card); + ESP_ERROR_CHECK(ret); return ret; } + void slave_power_on() { #ifdef SLAVE_PWR_GPIO