mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
sdmmc_io: support to print CIS information
Currently only ESP slaves can be parsed correctly.
This commit is contained in:
parent
53d1d211e7
commit
b98b4c3886
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user