Merge branch 'master' into feature/cmake

This commit is contained in:
Angus Gratton 2018-08-30 18:52:31 +08:00 committed by Angus Gratton
commit 5fbfd20f10
35 changed files with 31787 additions and 1546 deletions

View file

@ -418,6 +418,23 @@ test_idf_monitor:
- ./run_test_idf_monitor.py
- pyenv global system
test_idf_size:
<<: *host_test_template
artifacts:
when: on_failure
paths:
- tools/test_idf_size/output
- tools/test_idf_size/.coverage
expire_in: 1 week
script:
- cd ${IDF_PATH}/tools/test_idf_size
- source /opt/pyenv/activate
- pyenv global 2.7.15
- ./test.sh
- pyenv global 3.4.8
- ./test.sh
- pyenv global system
test_esp_err_to_name_on_host:
<<: *host_test_template
artifacts:
@ -1212,6 +1229,24 @@ UT_010_04:
- UT_T1_RMT
- psram
UT_011_01:
<<: *unit_test_template
tags:
- ESP32_IDF
- EMMC
UT_011_02:
<<: *unit_test_template
tags:
- ESP32_IDF
- EMMC
UT_011_03:
<<: *unit_test_template
tags:
- ESP32_IDF
- EMMC
UT_601_01:
<<: *unit_test_template
tags:

View file

@ -91,6 +91,7 @@
/* SD mode R1 response type bits */
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
#define MMC_R1_SWITCH_ERROR (1<<7) /* switch command did not succeed */
/* SPI mode R1 response type bits */
#define SD_SPI_R1_IDLE_STATE (1<<0)
@ -131,6 +132,13 @@
#define EXT_CSD_STRUCTURE 194 /* RO */
#define EXT_CSD_CARD_TYPE 196 /* RO */
#define EXT_CSD_SEC_COUNT 212 /* RO */
#define EXT_CSD_PWR_CL_26_360 203 /* RO */
#define EXT_CSD_PWR_CL_52_360 202 /* RO */
#define EXT_CSD_PWR_CL_26_195 201 /* RO */
#define EXT_CSD_PWR_CL_52_195 200 /* RO */
#define EXT_CSD_POWER_CLASS 187 /* R/W */
#define EXT_CSD_CMD_SET 191 /* R/W */
#define EXT_CSD_S_CMD_SET 504 /* RO */
/* EXT_CSD field definitions */
#define EXT_CSD_CMD_SET_NORMAL (1U << 0)
@ -153,16 +161,19 @@
/* EXT_CSD_CARD_TYPE */
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
* 0x0B and 0x0F. */
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0)
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1)
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2)
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3)
#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */
#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */
#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) /* DDR, 1.8V or 3.3V I/O */
#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) /* DDR, 1.2V I/O */
#define EXT_CSD_CARD_TYPE_26M 0x01
#define EXT_CSD_CARD_TYPE_52M 0x03
#define EXT_CSD_CARD_TYPE_52M_V18 0x07
#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
/* EXT_CSD MMC */
#define EXT_CSD_MMC_SIZE 512
/* MMC_SWITCH access mode */
#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */
#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */
@ -447,5 +458,9 @@ static inline uint32_t MMC_RSP_BITS(uint32_t *src, int start, int len)
/* CISTPL_FUNCID codes */
#define TPLFID_FUNCTION_SDIO 0x0c
/* Timing */
#define SDMMC_TIMING_LEGACY 0
#define SDMMC_TIMING_HIGHSPEED 1
#define SDMMC_TIMING_MMC_DDR52 2
#endif //_SDMMC_DEFS_H_

View file

@ -33,13 +33,17 @@ extern "C" {
* Uses SDMMC peripheral, with 4-bit mode enabled, and max frequency set to 20MHz
*/
#define SDMMC_HOST_DEFAULT() {\
.flags = SDMMC_HOST_FLAG_4BIT, \
.flags = SDMMC_HOST_FLAG_8BIT | \
SDMMC_HOST_FLAG_4BIT | \
SDMMC_HOST_FLAG_1BIT | \
SDMMC_HOST_FLAG_DDR, \
.slot = SDMMC_HOST_SLOT_1, \
.max_freq_khz = SDMMC_FREQ_DEFAULT, \
.io_voltage = 3.3f, \
.init = &sdmmc_host_init, \
.set_bus_width = &sdmmc_host_set_bus_width, \
.get_bus_width = &sdmmc_host_get_slot_width, \
.set_bus_ddr_mode = &sdmmc_host_set_bus_ddr_mode, \
.set_card_clk = &sdmmc_host_set_card_clk, \
.do_transaction = &sdmmc_host_do_transaction, \
.deinit = &sdmmc_host_deinit, \
@ -150,6 +154,16 @@ size_t sdmmc_host_get_slot_width(int slot);
*/
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz);
/**
* @brief Enable or disable DDR mode of SD interface
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
* @param ddr_enabled enable or disable DDR mode
* @return
* - ESP_OK on success
* - ESP_ERR_NOT_SUPPORTED if DDR mode is not supported on this slot
*/
esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled);
/**
* @brief Send command to the card and get response
*

View file

@ -56,6 +56,13 @@ typedef struct {
int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */
} sdmmc_scr_t;
/**
* Decoded values of Extended Card Specific Data
*/
typedef struct {
uint8_t power_class; /*!< Power class used by the card */
} sdmmc_ext_csd_t;
/**
* SD/MMC command response buffer
*/
@ -103,6 +110,8 @@ typedef struct {
#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY)
#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX)
/* special flags */
#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */
/** @endcond */
esp_err_t error; /*!< error returned from transfer */
int timeout_ms; /*!< response timeout, in milliseconds */
@ -120,15 +129,19 @@ typedef struct {
#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */
#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */
#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */
#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */
int slot; /*!< slot number, to be passed to host functions */
int max_freq_khz; /*!< max frequency supported by the host */
#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */
#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */
#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */
#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */
#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */
float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */
esp_err_t (*init)(void); /*!< Host function to initialize the driver */
esp_err_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */
size_t (*get_bus_width)(int slot); /*!< host function to get bus width */
esp_err_t (*set_bus_ddr_mode)(int slot, bool ddr_enable); /*!< host function to set DDR mode */
esp_err_t (*set_card_clk)(int slot, uint32_t freq_khz); /*!< host function to set card clock frequency */
esp_err_t (*do_transaction)(int slot, sdmmc_command_t* cmdinfo); /*!< host function to do a transaction */
esp_err_t (*deinit)(void); /*!< host function to deinitialize the driver */
@ -146,11 +159,16 @@ typedef struct {
sdmmc_cid_t cid; /*!< decoded CID (Card IDentification) register value */
sdmmc_csd_t csd; /*!< decoded CSD (Card-Specific Data) register value */
sdmmc_scr_t scr; /*!< decoded SCR (SD card Configuration Register) value */
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
uint16_t rca; /*!< RCA (Relative Card Address) */
uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */
uint32_t num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */
uint32_t reserved : 27; /*!< Reserved for future expansion */
uint32_t log_bus_width : 2; /*!< log2(bus width supported by card) */
uint32_t is_ddr : 1; /*!< Card supports DDR mode */
uint32_t reserved : 23; /*!< Reserved for future expansion */
} sdmmc_card_t;

View file

@ -41,6 +41,7 @@ extern "C" {
.init = &sdspi_host_init, \
.set_bus_width = NULL, \
.get_bus_width = NULL, \
.set_bus_ddr_mode = NULL, \
.set_card_clk = &sdspi_host_set_card_clk, \
.do_transaction = &sdspi_host_do_transaction, \
.deinit = &sdspi_host_deinit, \

View file

@ -267,6 +267,9 @@ esp_err_t sdmmc_host_init()
SDMMC_INTMASK_RESP_ERR | SDMMC_INTMASK_HLE; //sdio is enabled only when use.
SDMMC.ctrl.int_enable = 1;
// Disable generation of Busy Clear Interrupt
SDMMC.cardthrctl.busy_clr_int_en = 0;
// Enable DMA
sdmmc_host_dma_init();
@ -336,7 +339,8 @@ esp_err_t sdmmc_host_init_slot(int slot, const sdmmc_slot_config_t* slot_config)
if (slot_width >= 4) {
configure_pin(pslot->d1_gpio);
configure_pin(pslot->d2_gpio);
//force pull-up D3 to make slave detect SD mode. connect to peripheral after width configuration.
// Force D3 high to make slave enter SD mode.
// Connect to peripheral after width configuration.
gpio_config_t gpio_conf = {
.pin_bit_mask = BIT(pslot->d3_gpio),
.mode = GPIO_MODE_OUTPUT ,
@ -445,10 +449,12 @@ esp_err_t sdmmc_host_set_bus_width(int slot, size_t width)
} else if (width == 4) {
SDMMC.ctype.card_width_8 &= ~mask;
SDMMC.ctype.card_width |= mask;
configure_pin(sdmmc_slot_info[slot].d3_gpio); // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
// D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set
configure_pin(sdmmc_slot_info[slot].d3_gpio);
} else if (width == 8) {
SDMMC.ctype.card_width_8 |= mask;
configure_pin(sdmmc_slot_info[slot].d3_gpio); // D3 was set to GPIO high to force slave into SD 1-bit mode, until 4-bit mode is set
// D3 was set to GPIO high to force slave into SD mode, until 4-bit mode is set
configure_pin(sdmmc_slot_info[slot].d3_gpio);
} else {
return ESP_ERR_INVALID_ARG;
}
@ -462,6 +468,28 @@ size_t sdmmc_host_get_slot_width(int slot)
return s_slot_width[slot];
}
esp_err_t sdmmc_host_set_bus_ddr_mode(int slot, bool ddr_enabled)
{
if (!(slot == 0 || slot == 1)) {
return ESP_ERR_INVALID_ARG;
}
if (s_slot_width[slot] == 8 && ddr_enabled) {
ESP_LOGW(TAG, "DDR mode with 8-bit bus width is not supported yet");
// requires reconfiguring controller clock for 2x card frequency
return ESP_ERR_NOT_SUPPORTED;
}
uint32_t mask = BIT(slot);
if (ddr_enabled) {
SDMMC.uhs.ddr |= mask;
SDMMC.emmc_ddr_reg |= mask;
} else {
SDMMC.uhs.ddr &= ~mask;
SDMMC.emmc_ddr_reg &= ~mask;
}
ESP_LOGD(TAG, "slot=%d ddr=%d", slot, ddr_enabled ? 1 : 0);
return ESP_OK;
}
static void sdmmc_host_dma_init()
{
SDMMC.ctrl.dma_enable = 1;
@ -501,6 +529,11 @@ void sdmmc_host_dma_resume()
SDMMC.pldmnd = 1;
}
bool sdmmc_host_card_busy()
{
return SDMMC.status.data_busy == 1;
}
esp_err_t sdmmc_host_io_int_enable(int slot)
{
configure_pin(sdmmc_slot_info[slot].d1_gpio);

View file

@ -38,6 +38,8 @@ void sdmmc_host_dma_stop();
void sdmmc_host_dma_resume();
bool sdmmc_host_card_busy();
esp_err_t sdmmc_host_transaction_handler_init();
void sdmmc_host_transaction_handler_deinit();

View file

@ -19,6 +19,7 @@
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "soc/sdmmc_periph.h"
#include "soc/soc_memory_layout.h"
#include "driver/sdmmc_types.h"
@ -80,6 +81,7 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
static void process_command_response(uint32_t status, sdmmc_command_t* cmd);
static void fill_dma_descriptors(size_t num_desc);
static size_t get_free_descriptors_count();
static bool wait_for_busy_cleared(int timeout_ms);
esp_err_t sdmmc_host_transaction_handler_init()
{
@ -165,6 +167,11 @@ esp_err_t sdmmc_host_do_transaction(int slot, sdmmc_command_t* cmdinfo)
break;
}
}
if (ret == ESP_OK && (cmdinfo->flags & SCF_WAIT_BUSY)) {
if (!wait_for_busy_cleared(cmdinfo->timeout_ms)) {
ret = ESP_ERR_TIMEOUT;
}
}
s_is_app_cmd = (ret == ESP_OK && cmdinfo->opcode == MMC_APP_CMD);
out:
@ -461,5 +468,23 @@ static esp_err_t process_events(sdmmc_event_t evt, sdmmc_command_t* cmd,
return ESP_OK;
}
static bool wait_for_busy_cleared(int timeout_ms)
{
if (timeout_ms == 0) {
return !sdmmc_host_card_busy();
}
/* It would have been nice to do this without polling, however the peripheral
* can only generate Busy Clear Interrupt for data write commands, and waiting
* for busy clear is mostly needed for other commands such as MMC_SWITCH.
*/
int timeout_ticks = (timeout_ms + portTICK_PERIOD_MS - 1) / portTICK_PERIOD_MS;
while (timeout_ticks-- > 0) {
if (!sdmmc_host_card_busy()) {
return true;
}
vTaskDelay(1);
}
return false;
}

View file

@ -1,4 +1,4 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,302 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
static const char* TAG = "sdmmc_common";
esp_err_t sdmmc_init_ocr(sdmmc_card_t* card)
{
esp_err_t err;
/* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage
* ranges the card can support. This step is skipped since 1.8V isn't
* supported on the ESP32.
*/
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
if ((card->ocr & SD_OCR_SDHC_CAP) != 0) {
host_ocr |= SD_OCR_SDHC_CAP;
}
/* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
/* If time-out, re-try send_op_cond as MMC */
if (err == ESP_ERR_TIMEOUT && !host_is_spi(card)) {
ESP_LOGD(TAG, "send_op_cond timeout, trying MMC");
card->is_mmc = 1;
err = sdmmc_send_cmd_send_op_cond(card, host_ocr, &card->ocr);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
if (host_is_spi(card)) {
err = sdmmc_send_cmd_read_ocr(card, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: read_ocr returned 0x%x", __func__, err);
return err;
}
}
ESP_LOGD(TAG, "host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr);
/* Clear all voltage bits in host's OCR which the card doesn't support.
* Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41
* response.
*/
host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK));
ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
return ESP_OK;
}
esp_err_t sdmmc_init_cid(sdmmc_card_t* card)
{
esp_err_t err;
sdmmc_csd_t csd;
sdmmc_response_t raw_cid;
if (!host_is_spi(card)) {
if (card->is_mem) {
err = sdmmc_send_cmd_all_send_cid(card, &raw_cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: all_send_cid returned 0x%x", __func__, err);
return err;
}
}
err = sdmmc_send_cmd_set_relative_addr(card, &card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_relative_addr returned 0x%x", __func__, err);
return err;
}
if (card->is_mmc) {
/* For MMC, need to know CSD to decode CID.
* But CSD can only be read in data transfer mode,
* and it is not possible to read CID in data transfer mode.
* Luckily at this point the RCA is set and the card is in data
* transfer mode, so we can get its CSD to decode the CID...
*/
err = sdmmc_send_cmd_send_csd(card, &csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
err = sdmmc_mmc_decode_cid(csd.mmc_ver, raw_cid, &card->cid);
} else {
err = sdmmc_decode_cid(raw_cid, &card->cid);
}
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
return err;
}
} else {
err = sdmmc_send_cmd_send_cid(card, &card->cid);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_cid returned 0x%x", __func__, err);
return err;
}
}
return ESP_OK;
}
esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
{
assert(card->is_mem);
/* Get and decode the contents of CSD register. Determine card capacity. */
esp_err_t err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sector_size + 1;
if (!(card->ocr & SD_OCR_SDHC_CAP) &&
card->csd.capacity > max_sdsc_capacity) {
ESP_LOGW(TAG, "%s: SDSC card reports capacity=%u. Limiting to %u.",
__func__, card->csd.capacity, max_sdsc_capacity);
card->csd.capacity = max_sdsc_capacity;
}
return ESP_OK;
}
esp_err_t sdmmc_init_select_card(sdmmc_card_t* card)
{
assert(!host_is_spi(card));
esp_err_t err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card)
{
esp_err_t err = ESP_ERR_NOT_SUPPORTED;
if (card->is_mem && !card->is_mmc) {
err = sdmmc_enable_hs_mode_and_check(card);
} else if (card->is_sdio) {
err = sdmmc_io_enable_hs_mode(card);
} else if (card->is_mmc){
err = sdmmc_mmc_enable_hs_mode(card);
}
if (err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "%s: host supports HS mode, but card doesn't", __func__);
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
} else if (err != ESP_OK) {
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card)
{
int bus_width = 1;
if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) &&
(card->log_bus_width == 2)) {
bus_width = 4;
} else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) &&
(card->log_bus_width == 3)) {
bus_width = 8;
}
ESP_LOGD(TAG, "%s: using %d-bit bus", __func__, bus_width);
if (bus_width > 1) {
esp_err_t err = (*card->host.set_bus_width)(card->host.slot, bus_width);
if (err != ESP_OK) {
ESP_LOGE(TAG, "host.set_bus_width failed (0x%x)", err);
return err;
}
}
return ESP_OK;
}
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
{
assert(card->max_freq_khz <= card->host.max_freq_khz);
/* Find highest frequency in the following list,
* which is below card->max_freq_khz.
*/
const uint32_t freq_values[] = {
SDMMC_FREQ_52M,
SDMMC_FREQ_HIGHSPEED,
SDMMC_FREQ_26M,
SDMMC_FREQ_DEFAULT
};
const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
uint32_t selected_freq = SDMMC_FREQ_PROBING;
for (int i = 0; i < n_freq_values; ++i) {
uint32_t freq = freq_values[i];
if (card->max_freq_khz >= freq) {
selected_freq = freq;
break;
}
}
ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
if (selected_freq > SDMMC_FREQ_PROBING) {
esp_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
return err;
}
}
if (card->is_ddr) {
if (card->host.set_bus_ddr_mode == NULL) {
ESP_LOGE(TAG, "host doesn't support DDR mode or voltage switching");
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "failed to switch bus to DDR mode (0x%x)", err);
return err;
}
}
return ESP_OK;
}
void sdmmc_flip_byte_order(uint32_t* response, size_t size)
{
assert(size % (2 * sizeof(uint32_t)) == 0);
const size_t n_words = size / sizeof(uint32_t);
for (int i = 0; i < n_words / 2; ++i) {
uint32_t left = __builtin_bswap32(response[i]);
uint32_t right = __builtin_bswap32(response[n_words - i - 1]);
response[i] = right;
response[n_words - i - 1] = left;
}
}
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
{
bool print_scr = false;
bool print_csd = false;
const char* type;
fprintf(stream, "Name: %s\n", card->cid.name);
if (card->is_sdio) {
type = "SDIO";
print_scr = true;
print_csd = true;
} else if (card->is_mmc) {
type = "MMC";
print_csd = true;
} else {
type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC";
}
fprintf(stream, "Type: %s\n", type);
if (card->max_freq_khz < 1000) {
fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
} else {
fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
card->is_ddr ? ", DDR" : "");
}
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
if (print_csd) {
fprintf(stream, "CSD: ver=%d, sector_size=%d, capacity=%d read_bl_len=%d\n",
card->csd.csd_ver,
card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
}
if (print_scr) {
fprintf(stream, "SCR: sd_spec=%d, bus_width=%d\n", card->scr.sd_spec, card->scr.bus_width);
}
}
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card)
{
const uint32_t width_1bit = SDMMC_HOST_FLAG_1BIT;
const uint32_t width_4bit = SDMMC_HOST_FLAG_4BIT;
const uint32_t width_8bit = SDMMC_HOST_FLAG_8BIT;
const uint32_t width_mask = width_1bit | width_4bit | width_8bit;
int slot_bit_width = card->host.get_bus_width(card->host.slot);
if (slot_bit_width == 1 &&
(card->host.flags & (width_4bit | width_8bit))) {
card->host.flags &= ~width_mask;
card->host.flags |= width_1bit;
} else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) {
if ((card->host.flags & width_4bit) == 0) {
ESP_LOGW(TAG, "slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode");
card->host.flags &= ~width_mask;
card->host.flags |= width_1bit;
} else {
card->host.flags &= ~width_mask;
card->host.flags |= width_4bit;
}
}
return ESP_OK;
}

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <string.h>
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/sdmmc_defs.h"
#include "driver/sdmmc_types.h"
#include "sdmmc_cmd.h"
#include "sys/param.h"
#include "soc/soc_memory_layout.h"
#define SDMMC_GO_IDLE_DELAY_MS 20
#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10
/* These delay values are mostly useful for cases when CD pin is not used, and
* the card is removed. In this case, SDMMC peripheral may not always return
* CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work
* as a safety net in such cases.
*/
#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands
#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands
/* Maximum retry/error count for SEND_OP_COND (CMD1).
* These are somewhat arbitrary, values originate from OpenBSD driver.
*/
#define SDMMC_SEND_OP_COND_MAX_RETRIES 100
#define SDMMC_SEND_OP_COND_MAX_ERRORS 3
/* Functions to send individual commands */
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd);
esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card);
esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr);
esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp);
esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid);
esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid);
esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca);
esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd);
esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp);
esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca);
esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr);
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width);
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status);
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable);
/* Higher level functions */
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card);
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
size_t start_block, size_t block_count);
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
size_t start_block, size_t block_count);
/* SD specific */
esp_err_t sdmmc_check_scr(sdmmc_card_t* card);
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr);
/* SDIO specific */
esp_err_t sdmmc_io_reset(sdmmc_card_t* card);
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp);
esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int function,
uint32_t reg, uint32_t arg, uint8_t *byte);
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int function,
uint32_t reg, int arg, void *data, size_t size);
/* MMC specific */
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen);
esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value);
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid);
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd);
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card);
/* Parts of card initialization flow */
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card);
esp_err_t sdmmc_init_select_card(sdmmc_card_t* card);
esp_err_t sdmmc_init_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_cid(sdmmc_card_t* card);
esp_err_t sdmmc_init_ocr(sdmmc_card_t* card);
esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card);
esp_err_t sdmmc_init_io(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_read_cid(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card);
esp_err_t sdmmc_init_card_hs_mode(sdmmc_card_t* card);
esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card);
/* Various helper functions */
static inline bool host_is_spi(const sdmmc_card_t* card)
{
return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0;
}
static inline uint32_t get_host_ocr(float voltage)
{
// TODO: report exact voltage to the card
// For now tell that the host has 2.8-3.6V voltage range
(void) voltage;
return SD_OCR_VOL_MASK;
}
void sdmmc_flip_byte_order(uint32_t* response, size_t size);
esp_err_t sdmmc_fix_host_flags(sdmmc_card_t* card);

View file

@ -0,0 +1,117 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
static const char* TAG = "sdmmc_init";
#define SDMMC_INIT_STEP(condition, function) \
do { \
if ((condition)) { \
esp_err_t err = (function)(card); \
if (err != ESP_OK) { \
ESP_LOGD(TAG, "%s: %s returned 0x%x", __func__, #function, err); \
return err; \
} \
} \
} while(0);
esp_err_t sdmmc_card_init(const sdmmc_host_t* config, sdmmc_card_t* card)
{
memset(card, 0, sizeof(*card));
memcpy(&card->host, config, sizeof(*config));
const bool is_spi = host_is_spi(card);
const bool always = true;
const bool io_supported = true;
/* Check if host flags are compatible with slot configuration. */
SDMMC_INIT_STEP(!is_spi, sdmmc_fix_host_flags);
/* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */
SDMMC_INIT_STEP(io_supported, sdmmc_io_reset);
/* GO_IDLE_STATE (CMD0) command resets the card */
SDMMC_INIT_STEP(always, sdmmc_send_cmd_go_idle_state);
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */
SDMMC_INIT_STEP(always, sdmmc_init_sd_if_cond);
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */
SDMMC_INIT_STEP(io_supported, sdmmc_init_io);
const bool is_mem = card->is_mem;
const bool is_sdio = !is_mem;
/* Enable CRC16 checks for data transfers in SPI mode */
SDMMC_INIT_STEP(is_spi, sdmmc_init_spi_crc);
/* Use SEND_OP_COND to set up card OCR */
SDMMC_INIT_STEP(is_mem, sdmmc_init_ocr);
const bool is_mmc = is_mem && card->is_mmc;
const bool is_sdmem = is_mem && !is_mmc;
ESP_LOGD(TAG, "%s: card type is %s", __func__,
is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD");
/* Read and decode the contents of CID register and assign RCA */
SDMMC_INIT_STEP(always, sdmmc_init_cid);
/* Read and decode the contents of CSD register */
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
/* Switch the card from stand-by mode to data transfer mode (not needed if
* SPI interface is used). This is needed to issue SET_BLOCKLEN and
* SEND_SCR commands.
*/
SDMMC_INIT_STEP(!is_spi, sdmmc_init_select_card);
/* SD memory cards:
* Set block len for SDSC cards to 512 bytes (same as SDHC)
* Read SCR
* Wait to enter data transfer state
*/
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_blocklen);
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_scr);
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_wait_data_ready);
/* MMC cards: read CXD */
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_read_ext_csd);
/* Try to switch card to HS mode if the card supports it.
* Set card->max_freq_khz value accordingly.
*/
SDMMC_INIT_STEP(always, sdmmc_init_card_hs_mode);
/* Set bus width. One call for every kind of card, then one for the host */
if (!is_spi) {
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_bus_width);
SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width);
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_bus_width);
SDMMC_INIT_STEP(always, sdmmc_init_host_bus_width);
}
/* Switch to the host to use card->max_freq_khz frequency. */
SDMMC_INIT_STEP(always, sdmmc_init_host_frequency);
/* Sanity check after switching the bus mode and frequency */
SDMMC_INIT_STEP(is_sdmem, sdmmc_check_scr);
/* TODO: add similar checks for eMMC and SDIO */
return ESP_OK;
}

352
components/sdmmc/sdmmc_io.c Normal file
View file

@ -0,0 +1,352 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
static const char* TAG = "sdmmc_io";
esp_err_t sdmmc_io_reset(sdmmc_card_t* card)
{
uint8_t sdio_reset = CCCR_CTL_RES;
esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CTL, SD_ARG_CMD52_WRITE, &sdio_reset);
if (err == ESP_ERR_TIMEOUT || (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED)) {
/* Non-IO cards are allowed to time out (in SD mode) or
* return "invalid command" error (in SPI mode).
*/
} else if (err == ESP_ERR_NOT_FOUND) {
ESP_LOGD(TAG, "%s: card not present", __func__);
return err;
} else if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: unexpected return: 0x%x", __func__, err );
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_io(sdmmc_card_t* card)
{
/* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card.
* Non-IO cards will not respond to this command.
*/
esp_err_t err = sdmmc_io_send_op_cond(card, 0, &card->ocr);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: io_send_op_cond (1) returned 0x%x; not IO card", __func__, err);
card->is_sdio = 0;
card->is_mem = 1;
} else {
card->is_sdio = 1;
if (card->ocr & SD_IO_OCR_MEM_PRESENT) {
ESP_LOGD(TAG, "%s: IO-only card", __func__);
card->is_mem = 0;
}
card->num_io_functions = SD_IO_OCR_NUM_FUNCTIONS(card->ocr);
ESP_LOGD(TAG, "%s: number of IO functions: %d", __func__, card->num_io_functions);
if (card->num_io_functions == 0) {
card->is_sdio = 0;
}
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
host_ocr &= card->ocr;
err = sdmmc_io_send_op_cond(card, host_ocr, &card->ocr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_send_op_cond (1) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_io_enable_int(card);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_enable_int failed (0x%x)", __func__, err);
}
}
return ESP_OK;
}
esp_err_t sdmmc_init_io_bus_width(sdmmc_card_t* card)
{
esp_err_t err;
card->log_bus_width = 0;
if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
uint8_t card_cap = 0;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_CARD_CAP,
SD_ARG_CMD52_READ, &card_cap);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read SD_IO_CCCR_CARD_CAP) returned 0x%0x", __func__, err);
return err;
}
ESP_LOGD(TAG, "IO card capabilities byte: %02x", card_cap);
if (!(card_cap & CCCR_CARD_CAP_LSC) ||
(card_cap & CCCR_CARD_CAP_4BLS)) {
// This card supports 4-bit bus mode
uint8_t bus_width = CCCR_BUS_WIDTH_4;
err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_BUS_WIDTH,
SD_ARG_CMD52_WRITE, &bus_width);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write SD_IO_CCCR_BUS_WIDTH) returned 0x%0x", __func__, err);
return err;
}
card->log_bus_width = 2;
}
}
return ESP_OK;
}
esp_err_t sdmmc_io_enable_hs_mode(sdmmc_card_t* card)
{
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
if (card->host.max_freq_khz <= card->max_freq_khz) {
/* Host is configured to use low frequency, don't attempt to switch */
card->max_freq_khz = card->host.max_freq_khz;
return ESP_OK;
}
/* For IO cards, do write + read operation on "High Speed" register,
* setting EHS bit. If both EHS and SHS read back as set, then HS mode
* has been enabled.
*/
uint8_t val = CCCR_HIGHSPEED_ENABLE;
esp_err_t err = sdmmc_io_rw_direct(card, 0, SD_IO_CCCR_HIGHSPEED,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &val);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_io_rw_direct returned 0x%x", __func__, err);
return err;
}
ESP_LOGD(TAG, "%s: CCCR_HIGHSPEED=0x%02x", __func__, val);
const uint8_t hs_mask = CCCR_HIGHSPEED_ENABLE | CCCR_HIGHSPEED_SUPPORT;
if ((val & hs_mask) != hs_mask) {
return ESP_ERR_NOT_SUPPORTED;
}
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
return ESP_OK;
}
esp_err_t sdmmc_io_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
{
esp_err_t err = ESP_OK;
sdmmc_command_t cmd = {
.flags = SCF_CMD_BCR | SCF_RSP_R4,
.arg = ocr,
.opcode = SD_IO_SEND_OP_COND
};
for (size_t i = 0; i < 100; i++) {
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
break;
}
if ((MMC_R4(cmd.response) & SD_IO_OCR_MEM_READY) ||
ocr == 0) {
break;
}
err = ESP_ERR_TIMEOUT;
vTaskDelay(SDMMC_IO_SEND_OP_COND_DELAY_MS / portTICK_PERIOD_MS);
}
if (err == ESP_OK && ocrp != NULL)
*ocrp = MMC_R4(cmd.response);
return err;
}
esp_err_t sdmmc_io_rw_direct(sdmmc_card_t* card, int func,
uint32_t reg, uint32_t arg, uint8_t *byte)
{
esp_err_t err;
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_DIRECT
};
arg |= (func & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD52_REG_MASK) << SD_ARG_CMD52_REG_SHIFT;
arg |= (*byte & SD_ARG_CMD52_DATA_MASK) << SD_ARG_CMD52_DATA_SHIFT;
cmd.arg = arg;
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGV(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
*byte = SD_R5_DATA(cmd.response);
return ESP_OK;
}
esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t *out_byte)
{
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr, SD_ARG_CMD52_READ, out_byte);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (read 0x%x) returned 0x%x", __func__, addr, ret);
}
return ret;
}
esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
uint32_t addr, uint8_t in_byte, uint8_t* out_byte)
{
uint8_t tmp_byte = in_byte;
esp_err_t ret = sdmmc_io_rw_direct(card, function, addr,
SD_ARG_CMD52_WRITE | SD_ARG_CMD52_EXCHANGE, &tmp_byte);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_io_rw_direct (write 0x%x) returned 0x%x", __func__, addr, ret);
return ret;
}
if (out_byte != NULL) {
*out_byte = tmp_byte;
}
return ESP_OK;
}
esp_err_t sdmmc_io_rw_extended(sdmmc_card_t* card, int func,
uint32_t reg, int arg, void *datap, size_t datalen)
{
esp_err_t err;
const size_t max_byte_transfer_size = 512;
sdmmc_command_t cmd = {
.flags = SCF_CMD_AC | SCF_RSP_R5,
.arg = 0,
.opcode = SD_IO_RW_EXTENDED,
.data = datap,
.datalen = datalen,
.blklen = max_byte_transfer_size /* TODO: read max block size from CIS */
};
uint32_t count; /* number of bytes or blocks, depending on transfer mode */
if (arg & SD_ARG_CMD53_BLOCK_MODE) {
if (cmd.datalen % cmd.blklen != 0) {
return ESP_ERR_INVALID_SIZE;
}
count = cmd.datalen / cmd.blklen;
} else {
if (datalen > max_byte_transfer_size) {
/* TODO: split into multiple operations? */
return ESP_ERR_INVALID_SIZE;
}
if (datalen == max_byte_transfer_size) {
count = 0; // See 5.3.1 SDIO simplifed spec
} else {
count = datalen;
}
cmd.blklen = datalen;
}
arg |= (func & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT;
arg |= (reg & SD_ARG_CMD53_REG_MASK) << SD_ARG_CMD53_REG_SHIFT;
arg |= (count & SD_ARG_CMD53_LENGTH_MASK) << SD_ARG_CMD53_LENGTH_SHIFT;
cmd.arg = arg;
if ((arg & SD_ARG_CMD53_WRITE) == 0) {
cmd.flags |= SCF_CMD_READ;
}
err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
/* host quirk: SDIO transfer with length not divisible by 4 bytes
* has to be split into two transfers: one with aligned length,
* the other one for the remaining 1-3 bytes.
*/
uint8_t *pc_dst = dst;
while (size > 0) {
size_t size_aligned = size & (~3);
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT,
pc_dst, will_transfer);
if (err != ESP_OK) {
return err;
}
pc_dst += will_transfer;
size -= will_transfer;
addr += will_transfer;
}
return ESP_OK;
}
esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
/* same host quirk as in sdmmc_io_read_bytes */
const uint8_t *pc_src = (const uint8_t*) src;
while (size > 0) {
size_t size_aligned = size & (~3);
size_t will_transfer = size_aligned > 0 ? size_aligned : size;
esp_err_t err = sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT,
(void*) pc_src, will_transfer);
if (err != ESP_OK) {
return err;
}
pc_src += will_transfer;
size -= will_transfer;
addr += will_transfer;
}
return ESP_OK;
}
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, void* dst, size_t size)
{
if (size % 4 != 0) {
return ESP_ERR_INVALID_SIZE;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
dst, size);
}
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
uint32_t addr, const void* src, size_t size)
{
if (size % 4 != 0) {
return ESP_ERR_INVALID_SIZE;
}
return sdmmc_io_rw_extended(card, function, addr,
SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT | SD_ARG_CMD53_BLOCK_MODE,
(void*) src, size);
}
esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card)
{
if (card->host.io_int_enable == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_enable)(card->host.slot);
}
esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks)
{
if (card->host.io_int_wait == NULL) {
return ESP_ERR_NOT_SUPPORTED;
}
return (*card->host.io_int_wait)(card->host.slot, timeout_ticks);
}

View file

@ -0,0 +1,233 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
#include "sdmmc_common.h"
static const char* TAG = "sdmmc_mmc";
esp_err_t sdmmc_init_mmc_read_ext_csd(sdmmc_card_t* card)
{
int card_type;
esp_err_t err = ESP_OK;
uint8_t* ext_csd = heap_caps_malloc(EXT_CSD_MMC_SIZE, MALLOC_CAP_DMA);
if (!ext_csd) {
ESP_LOGE(TAG, "%s: could not allocate ext_csd", __func__);
return ESP_ERR_NO_MEM;
}
uint32_t sectors = 0;
ESP_LOGD(TAG, "MMC version: %d", card->csd.mmc_ver);
if (card->csd.mmc_ver < MMC_CSD_MMCVER_4_0) {
err = ESP_ERR_NOT_SUPPORTED;
goto out;
}
/* read EXT_CSD */
err = sdmmc_mmc_send_ext_csd_data(card, ext_csd, EXT_CSD_MMC_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_ext_csd_data error 0x%x", __func__, err);
goto out;
}
card_type = ext_csd[EXT_CSD_CARD_TYPE];
card->is_ddr = 0;
if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) {
card->max_freq_khz = SDMMC_FREQ_52M;
if ((card->host.flags & SDMMC_HOST_FLAG_DDR) &&
card->host.max_freq_khz >= SDMMC_FREQ_26M &&
card->host.get_bus_width(card->host.slot) == 4) {
ESP_LOGD(TAG, "card and host support DDR mode");
card->is_ddr = 1;
}
} else if (card_type & EXT_CSD_CARD_TYPE_F_52M) {
card->max_freq_khz = SDMMC_FREQ_52M;
} else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
card->max_freq_khz = SDMMC_FREQ_26M;
} else {
ESP_LOGW(TAG, "%s: unknown CARD_TYPE 0x%x", __func__, card_type);
}
/* For MMC cards, use speed value from EXT_CSD */
card->csd.tr_speed = card->max_freq_khz * 1000;
ESP_LOGD(TAG, "MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr);
card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz);
if (card->host.flags & SDMMC_HOST_FLAG_8BIT) {
card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4;
card->log_bus_width = 3;
} else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) {
card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ?
EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f;
card->log_bus_width = 2;
} else {
card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode
card->log_bus_width = 0;
}
sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 )
| ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 );
if (sectors > (2u * 1024 * 1024 * 1024) / 512) {
card->csd.capacity = sectors;
}
out:
free(ext_csd);
return err;
}
esp_err_t sdmmc_init_mmc_bus_width(sdmmc_card_t* card)
{
esp_err_t err;
if (card->ext_csd.power_class != 0) {
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_CLASS, card->ext_csd.power_class);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't change power class (%d bit), 0x%x"
, __func__, card->ext_csd.power_class, err);
return err;
}
}
if (card->log_bus_width > 0) {
int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1;
int bus_width = 1;
if (card->log_bus_width == 2) {
if (card->is_ddr) {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR;
} else {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_4;
}
bus_width = 4;
} else if (card->log_bus_width == 3) {
if (card->is_ddr) {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR;
} else {
csd_bus_width_value = EXT_CSD_BUS_WIDTH_8;
}
bus_width = 8;
}
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_BUS_WIDTH, csd_bus_width_value);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: can't change bus width (%d bit), 0x%x",
__func__, bus_width, err);
return err;
}
}
return ESP_OK;
}
esp_err_t sdmmc_mmc_enable_hs_mode(sdmmc_card_t* card)
{
esp_err_t err;
if (card->max_freq_khz > SDMMC_FREQ_26M) {
/* switch to high speed timing */
err = sdmmc_mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x",
__func__, err);
return err;
}
}
return ESP_OK;
}
esp_err_t sdmmc_mmc_decode_cid(int mmc_ver, sdmmc_response_t resp, sdmmc_cid_t* out_cid)
{
if (mmc_ver == MMC_CSD_MMCVER_1_0 ||
mmc_ver == MMC_CSD_MMCVER_1_4) {
out_cid->mfg_id = MMC_CID_MID_V1(resp);
out_cid->oem_id = 0;
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
out_cid->revision = MMC_CID_REV_V1(resp);
out_cid->serial = MMC_CID_PSN_V1(resp);
out_cid->date = MMC_CID_MDT_V1(resp);
} else if (mmc_ver == MMC_CSD_MMCVER_2_0 ||
mmc_ver == MMC_CSD_MMCVER_3_1 ||
mmc_ver == MMC_CSD_MMCVER_4_0) {
out_cid->mfg_id = MMC_CID_MID_V2(resp);
out_cid->oem_id = MMC_CID_OID_V2(resp);
MMC_CID_PNM_V1_CPY(resp, out_cid->name);
out_cid->revision = 0;
out_cid->serial = MMC_CID_PSN_V1(resp);
out_cid->date = 0;
}
return ESP_OK;
}
esp_err_t sdmmc_mmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = MMC_CSD_CSDVER(response);
if (out_csd->csd_ver == MMC_CSD_CSDVER_1_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_2_0 ||
out_csd->csd_ver == MMC_CSD_CSDVER_EXT_CSD) {
out_csd->mmc_ver = MMC_CSD_MMCVER(response);
out_csd->capacity = MMC_CSD_CAPACITY(response);
out_csd->read_block_len = MMC_CSD_READ_BL_LEN(response);
} else {
ESP_LOGE(TAG, "unknown MMC CSD structure version 0x%x\n", out_csd->csd_ver);
return 1;
}
int read_bl_size = 1 << out_csd->read_block_len;
out_csd->sector_size = MIN(read_bl_size, 512);
if (out_csd->sector_size < read_bl_size) {
out_csd->capacity *= read_bl_size / out_csd->sector_size;
}
/* tr_speed will be determined when reading CXD */
out_csd->tr_speed = 0;
return ESP_OK;
}
esp_err_t sdmmc_mmc_send_ext_csd_data(sdmmc_card_t* card, void *out_data, size_t datalen)
{
assert(esp_ptr_dma_capable(out_data));
sdmmc_command_t cmd = {
.data = out_data,
.datalen = datalen,
.blklen = datalen,
.opcode = MMC_SEND_EXT_CSD,
.arg = 0,
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
};
return sdmmc_send_cmd(card, &cmd);
}
esp_err_t sdmmc_mmc_switch(sdmmc_card_t* card, uint8_t set, uint8_t index, uint8_t value)
{
sdmmc_command_t cmd = {
.opcode = MMC_SWITCH,
.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set,
.flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY,
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err == ESP_OK) {
//check response bit to see that switch was accepted
if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR)
err = ESP_ERR_INVALID_RESPONSE;
}
return err;
}

346
components/sdmmc/sdmmc_sd.c Normal file
View file

@ -0,0 +1,346 @@
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
* Adaptations to ESP-IDF Copyright (c) 2016-2018 Espressif Systems (Shanghai) PTE LTD
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "sdmmc_common.h"
static const char* TAG = "sdmmc_sd";
esp_err_t sdmmc_init_sd_if_cond(sdmmc_card_t* card)
{
/* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards.
* SD v1 and non-SD cards will not respond to this command.
*/
uint32_t host_ocr = get_host_ocr(card->host.io_voltage);
esp_err_t err = sdmmc_send_cmd_send_if_cond(card, host_ocr);
if (err == ESP_OK) {
ESP_LOGD(TAG, "SDHC/SDXC card");
host_ocr |= SD_OCR_SDHC_CAP;
} else if (err == ESP_ERR_TIMEOUT) {
ESP_LOGD(TAG, "CMD8 timeout; not an SD v2.00 card");
} else if (host_is_spi(card) && err == ESP_ERR_NOT_SUPPORTED) {
ESP_LOGD(TAG, "CMD8 rejected; not an SD v2.00 card");
} else {
ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
return err;
}
card->ocr = host_ocr;
return ESP_OK;
}
esp_err_t sdmmc_init_sd_blocklen(sdmmc_card_t* card)
{
/* SDSC cards support configurable data block lengths.
* We don't use this feature and set the block length to 512 bytes,
* same as the block length for SDHC cards.
*/
if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
esp_err_t err = sdmmc_send_cmd_set_blocklen(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: set_blocklen returned 0x%x", __func__, err);
return err;
}
}
return ESP_OK;
}
esp_err_t sdmmc_init_sd_scr(sdmmc_card_t* card)
{
esp_err_t err;
/* Get the contents of SCR register: bus width and the version of SD spec
* supported by the card.
* In SD mode, this is the first command which uses D0 line. Errors at
* this step usually indicate connection issue or lack of pull-up resistor.
*/
err = sdmmc_send_cmd_send_scr(card, &card->scr);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr (1) returned 0x%x", __func__, err);
return err;
}
if ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)
&& (card->host.flags & SDMMC_HOST_FLAG_4BIT)) {
card->log_bus_width = 2;
} else {
card->log_bus_width = 0;
}
return ESP_OK;
}
esp_err_t sdmmc_init_sd_bus_width(sdmmc_card_t* card)
{
int width = 1;
if (card->log_bus_width == 2) {
width = 4;
} else if (card->log_bus_width == 3) {
width = 8;
}
esp_err_t err = sdmmc_send_cmd_set_bus_width(card, width);
if (err != ESP_OK) {
ESP_LOGE(TAG, "set_bus_width failed (0x%x)", err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_init_sd_wait_data_ready(sdmmc_card_t* card)
{
/* Wait for the card to be ready for data transfers */
uint32_t status = 0;
uint32_t count = 0;
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
// TODO: add some timeout here
esp_err_t err = sdmmc_send_cmd_send_status(card, &status);
if (err != ESP_OK) {
return err;
}
if (++count % 16 == 0) {
ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
}
}
return ESP_OK;
}
esp_err_t sdmmc_send_cmd_switch_func(sdmmc_card_t* card,
uint32_t mode, uint32_t group, uint32_t function,
sdmmc_switch_func_rsp_t* resp)
{
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return ESP_ERR_NOT_SUPPORTED;
}
if (group == 0 ||
group > SD_SFUNC_GROUP_MAX ||
function > SD_SFUNC_FUNC_MAX) {
return ESP_ERR_INVALID_ARG;
}
if (mode > 1) {
return ESP_ERR_INVALID_ARG;
}
uint32_t group_shift = (group - 1) << 2;
/* all functions which should not be affected are set to 0xf (no change) */
uint32_t other_func_mask = (0x00ffffff & ~(0xf << group_shift));
uint32_t func_val = (function << group_shift) | other_func_mask;
sdmmc_command_t cmd = {
.opcode = MMC_SWITCH,
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
.blklen = sizeof(sdmmc_switch_func_rsp_t),
.data = resp->data,
.datalen = sizeof(sdmmc_switch_func_rsp_t),
.arg = (!!mode << 31) | func_val
};
esp_err_t err = sdmmc_send_cmd(card, &cmd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
return err;
}
sdmmc_flip_byte_order(resp->data, sizeof(sdmmc_switch_func_rsp_t));
uint32_t resp_ver = SD_SFUNC_VER(resp->data);
if (resp_ver == 0) {
/* busy response is never sent */
} else if (resp_ver == 1) {
if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) {
ESP_LOGD(TAG, "%s: response indicates function %d:%d is busy",
__func__, group, function);
return ESP_ERR_INVALID_STATE;
}
} else {
ESP_LOGD(TAG, "%s: got an invalid version of SWITCH_FUNC response: 0x%02x",
__func__, resp_ver);
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
esp_err_t sdmmc_enable_hs_mode(sdmmc_card_t* card)
{
/* This will determine if the card supports SWITCH_FUNC command,
* and high speed mode. If the cards supports both, this will enable
* high speed mode at the card side.
*/
if (card->scr.sd_spec < SCR_SD_SPEC_VER_1_10 ||
((card->csd.card_command_class & SD_CSD_CCC_SWITCH) == 0)) {
return ESP_ERR_NOT_SUPPORTED;
}
sdmmc_switch_func_rsp_t* response = (sdmmc_switch_func_rsp_t*)
heap_caps_malloc(sizeof(*response), MALLOC_CAP_DMA);
if (response == NULL) {
return ESP_ERR_NO_MEM;
}
esp_err_t err = sdmmc_send_cmd_switch_func(card, 0, SD_ACCESS_MODE, 0, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (1) returned 0x%x", __func__, err);
goto out;
}
uint32_t supported_mask = SD_SFUNC_SUPPORTED(response->data, 1);
if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) {
err = ESP_ERR_NOT_SUPPORTED;
goto out;
}
err = sdmmc_send_cmd_switch_func(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response);
if (err != ESP_OK) {
ESP_LOGD(TAG, "%s: sdmmc_send_cmd_switch_func (2) returned 0x%x", __func__, err);
goto out;
}
out:
free(response);
return err;
}
esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
{
/* All cards should support at least default speed */
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
if (card->host.max_freq_khz <= card->max_freq_khz) {
/* Host is configured to use low frequency, don't attempt to switch */
card->max_freq_khz = card->host.max_freq_khz;
return ESP_OK;
}
/* Try to enabled HS mode */
esp_err_t err = sdmmc_enable_hs_mode(card);
if (err != ESP_OK) {
return err;
}
/* HS mode has been enabled on the card.
* Read CSD again, it should now indicate that the card supports
* 50MHz clock.
* Since SEND_CSD is allowed only in standby mode, and the card is
* currently in data transfer more, deselect the card first, then
* get the CSD, then select the card again.
*/
err = sdmmc_send_cmd_select_card(card, 0);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (1) returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_send_csd(card, &card->csd);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_csd returned 0x%x", __func__, err);
return err;
}
err = sdmmc_send_cmd_select_card(card, card->rca);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: select_card (2) returned 0x%x", __func__, err);
return err;
}
if (card->csd.tr_speed != 50000000) {
ESP_LOGW(TAG, "unexpected: after enabling HS mode, tr_speed=%d", card->csd.tr_speed);
return ESP_ERR_NOT_SUPPORTED;
}
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
return ESP_OK;
}
esp_err_t sdmmc_check_scr(sdmmc_card_t* card)
{
/* If frequency switch has been performed, read SCR register one more time
* and compare the result with the previous one. Use this simple check as
* an indicator of potential signal integrity issues.
*/
sdmmc_scr_t scr_tmp;
esp_err_t err = sdmmc_send_cmd_send_scr(card, &scr_tmp);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
return err;
}
if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) {
ESP_LOGE(TAG, "got corrupted data after increasing clock frequency");
return ESP_ERR_INVALID_RESPONSE;
}
return ESP_OK;
}
esp_err_t sdmmc_init_spi_crc(sdmmc_card_t* card)
{
/* In SD mode, CRC checks of data transfers are mandatory and performed
* by the hardware. In SPI mode, CRC16 of data transfers is optional and
* needs to be enabled.
*/
assert(host_is_spi(card));
esp_err_t err = sdmmc_send_cmd_crc_on_off(card, true);
if (err != ESP_OK) {
ESP_LOGE(TAG, "%s: sdmmc_send_cmd_crc_on_off returned 0x%x", __func__, err);
return err;
}
return ESP_OK;
}
esp_err_t sdmmc_decode_cid(sdmmc_response_t resp, sdmmc_cid_t* out_cid)
{
out_cid->mfg_id = SD_CID_MID(resp);
out_cid->oem_id = SD_CID_OID(resp);
SD_CID_PNM_CPY(resp, out_cid->name);
out_cid->revision = SD_CID_REV(resp);
out_cid->serial = SD_CID_PSN(resp);
out_cid->date = SD_CID_MDT(resp);
return ESP_OK;
}
esp_err_t sdmmc_decode_csd(sdmmc_response_t response, sdmmc_csd_t* out_csd)
{
out_csd->csd_ver = SD_CSD_CSDVER(response);
switch (out_csd->csd_ver) {
case SD_CSD_CSDVER_2_0:
out_csd->capacity = SD_CSD_V2_CAPACITY(response);
out_csd->read_block_len = SD_CSD_V2_BL_LEN;
break;
case SD_CSD_CSDVER_1_0:
out_csd->capacity = SD_CSD_CAPACITY(response);
out_csd->read_block_len = SD_CSD_READ_BL_LEN(response);
break;
default:
ESP_LOGE(TAG, "unknown SD CSD structure version 0x%x", out_csd->csd_ver);
return ESP_ERR_NOT_SUPPORTED;
}
out_csd->card_command_class = SD_CSD_CCC(response);
int read_bl_size = 1 << out_csd->read_block_len;
out_csd->sector_size = MIN(read_bl_size, 512);
if (out_csd->sector_size < read_bl_size) {
out_csd->capacity *= read_bl_size / out_csd->sector_size;
}
int speed = SD_CSD_SPEED(response);
if (speed == SD_CSD_SPEED_50_MHZ) {
out_csd->tr_speed = 50000000;
} else {
out_csd->tr_speed = 25000000;
}
return ESP_OK;
}
esp_err_t sdmmc_decode_scr(uint32_t *raw_scr, sdmmc_scr_t* out_scr)
{
sdmmc_response_t resp = { 0 };
resp[1] = __builtin_bswap32(raw_scr[0]);
resp[0] = __builtin_bswap32(raw_scr[1]);
int ver = SCR_STRUCTURE(resp);
if (ver != 0) {
return ESP_ERR_NOT_SUPPORTED;
}
out_scr->sd_spec = SCR_SD_SPEC(resp);
out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
return ESP_OK;
}

View file

@ -28,6 +28,47 @@
#include <sys/time.h>
#include <unistd.h>
// Can't test eMMC (slot 0) and PSRAM together
#ifndef CONFIG_SPIRAM_SUPPORT
#define WITH_EMMC_TEST
#endif
/* power supply enable pin */
#define SD_TEST_BOARD_VSEL_EN_GPIO 27
/* power supply voltage select pin */
#define SD_TEST_BOARD_VSEL_GPIO 26
#define SD_TEST_BOARD_VSEL_3V3 1
#define SD_TEST_BOARD_VSEL_1V8 0
/* time to wait for reset / power-on */
#define SD_TEST_BOARD_PWR_RST_DELAY_MS 5
#define SD_TEST_BOARD_PWR_ON_DELAY_MS 50
/* gpio which is not connected to actual CD pin, used to simulate CD behavior */
#define CD_WP_TEST_GPIO 18
static void sd_test_board_power_on()
{
gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, SD_TEST_BOARD_VSEL_3V3);
gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_OUTPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
usleep(SD_TEST_BOARD_PWR_RST_DELAY_MS * 1000);
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 1);
usleep(SD_TEST_BOARD_PWR_ON_DELAY_MS * 1000);
}
static void sd_test_board_power_off()
{
gpio_set_level(SD_TEST_BOARD_VSEL_EN_GPIO, 0);
gpio_set_direction(SD_TEST_BOARD_VSEL_GPIO, GPIO_MODE_INPUT);
gpio_set_level(SD_TEST_BOARD_VSEL_GPIO, 0);
gpio_set_direction(SD_TEST_BOARD_VSEL_EN_GPIO, GPIO_MODE_INPUT);
}
TEST_CASE("MMC_RSP_BITS", "[sd]")
{
uint32_t data[2] = { 0x01234567, 0x89abcdef };
@ -38,54 +79,49 @@ TEST_CASE("MMC_RSP_BITS", "[sd]")
TEST_ASSERT_EQUAL_HEX32(0x11, MMC_RSP_BITS(data, 59, 5));
}
TEST_CASE("can probe SD (4-bit)", "[sd][test_env=UT_T1_SDMODE]")
static void probe_sd(int slot, int width, int freq_khz, int ddr)
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
config.slot = slot;
config.max_freq_khz = freq_khz;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
sdmmc_card_print_info(stdout, card);
TEST_ESP_OK(sdmmc_host_deinit());
free(card);
}
TEST_CASE("can probe SD (1-bit)", "[sd][test_env=UT_T1_SDMODE]")
{
//the card DAT3 should be connected to high in SD 1-bit mode
//do it by our own GPIO.
gpio_config_t conf = {
.pin_bit_mask = GPIO_SEL_13,
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 1,
.pull_down_en = 0,
.intr_type = GPIO_INTR_DISABLE,
};
gpio_config(&conf);
gpio_set_level(GPIO_NUM_13, 1);
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
if (width == 1) {
config.flags = SDMMC_HOST_FLAG_1BIT;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
slot_config.width = 1;
} else if (width == 4) {
config.flags &= ~SDMMC_HOST_FLAG_8BIT;
slot_config.width = 4;
} else {
assert(!ddr && "host driver does not support 8-line DDR mode yet");
}
if (!ddr) {
config.flags &= ~SDMMC_HOST_FLAG_DDR;
}
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
sdmmc_card_print_info(stdout, card);
uint8_t* buffer = heap_caps_malloc(512, MALLOC_CAP_DMA);
TEST_ESP_OK(sdmmc_read_sectors(card, buffer, 0, 1));
free(buffer);
TEST_ESP_OK(sdmmc_host_deinit());
free(card);
sd_test_board_power_off();
}
TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE]")
static void probe_spi(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int pin_cs)
{
sd_test_board_power_on();
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = pin_miso;
slot_config.gpio_mosi = pin_mosi;
slot_config.gpio_sck = pin_sck;
slot_config.gpio_cs = pin_cs;
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_slot(config.slot, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
@ -94,8 +130,63 @@ TEST_CASE("can probe SD(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE]")
sdmmc_card_print_info(stdout, card);
TEST_ESP_OK(sdspi_host_deinit());
free(card);
sd_test_board_power_off();
}
TEST_CASE("probe SD, slot 1, 4-bit", "[sd][test_env=UT_T1_SDMODE]")
{
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, 0);
}
TEST_CASE("probe SD, slot 1, 1-bit", "[sd][test_env=UT_T1_SDMODE]")
{
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_1, 1, SDMMC_FREQ_HIGHSPEED, 0);
}
#ifdef WITH_EMMC_TEST
TEST_CASE("probe eMMC, slot 0, 4-bit, DDR", "[sd][test_env=EMMC]")
{
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 1);
}
TEST_CASE("probe eMMC, slot 0, 8-bit", "[sd][test_env=EMMC]")
{
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 8, SDMMC_FREQ_HIGHSPEED, 0);
}
#endif // WITH_EMMC_TEST
TEST_CASE("probe SD, slot 0, 4-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 4, SDMMC_FREQ_HIGHSPEED, 0);
}
TEST_CASE("probe SD, slot 0, 1-bit", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_PROBING, 0);
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_DEFAULT, 0);
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, 0);
}
TEST_CASE("probe SD in SPI mode, slot 1", "[sd][test_env=UT_T1_SPIMODE]")
{
probe_spi(SDMMC_FREQ_DEFAULT, 2, 15, 14, 13);
}
TEST_CASE("probe SD in SPI mode, slot 0", "[sd][test_env=UT_T1_SDCARD][ignore]")
{
probe_spi(SDMMC_FREQ_DEFAULT, 7, 11, 6, 10);
}
// Fill buffer pointed to by 'dst' with 'count' 32-bit ints generated
// from 'rand' with the starting value of 'seed'
static void fill_buffer(uint32_t seed, uint8_t* dst, size_t count) {
@ -177,13 +268,20 @@ static void read_write_test(sdmmc_card_t* card)
do_single_write_read_test(card, card->csd.capacity/2, 128, 1);
}
TEST_CASE("can write and read back blocks", "[sd][test_env=UT_T1_SDMODE]")
void test_sd_rw_blocks(int slot, int width)
{
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
config.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
TEST_ESP_OK(sdmmc_host_init());
config.slot = slot;
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
if (width != 0) {
slot_config.width = width;
}
if (slot_config.width == 8) {
config.flags &= ~SDMMC_HOST_FLAG_DDR;
}
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(slot, &slot_config));
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
TEST_ASSERT_NOT_NULL(card);
TEST_ESP_OK(sdmmc_card_init(&config, card));
@ -192,8 +290,32 @@ TEST_CASE("can write and read back blocks", "[sd][test_env=UT_T1_SDMODE]")
TEST_ESP_OK(sdmmc_host_deinit());
}
TEST_CASE("can write and read back blocks(using SPI)", "[sdspi][test_env=UT_T1_SPIMODE]")
TEST_CASE("SDMMC read/write test (SD slot 1)", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
test_sd_rw_blocks(1, 4);
sd_test_board_power_off();
}
#ifdef WITH_EMMC_TEST
TEST_CASE("SDMMC read/write test (eMMC slot 0, 4 line DDR)", "[sd][test_env=EMMC]")
{
sd_test_board_power_on();
test_sd_rw_blocks(0, 4);
sd_test_board_power_off();
}
TEST_CASE("SDMMC read/write test (eMMC slot 0, 8 line)", "[sd][test_env=EMMC]")
{
sd_test_board_power_on();
test_sd_rw_blocks(0, 8);
sd_test_board_power_off();
}
#endif // WITH_EMMC_TEST
TEST_CASE("SDMMC read/write test (SD slot 1, in SPI mode)", "[sdspi][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
TEST_ESP_OK(sdspi_host_init());
@ -204,10 +326,12 @@ TEST_CASE("can write and read back blocks(using SPI)", "[sdspi][test_env=UT_T1_S
read_write_test(card);
free(card);
TEST_ESP_OK(sdspi_host_deinit());
sd_test_board_power_off();
}
TEST_CASE("reads and writes with an unaligned buffer", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
TEST_ESP_OK(sdmmc_host_init());
@ -240,6 +364,7 @@ TEST_CASE("reads and writes with an unaligned buffer", "[sd][test_env=UT_T1_SDMO
free(buffer);
free(card);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
static void test_cd_input(int gpio_cd_num, const sdmmc_host_t* config)
@ -255,43 +380,45 @@ static void test_cd_input(int gpio_cd_num, const sdmmc_host_t* config)
// Check that card initialization fails if CD is high
REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_cd_num));
usleep(100);
usleep(1000);
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, sdmmc_card_init(config, card));
// Check that card initialization succeeds if CD is low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_cd_num));
usleep(100);
usleep(1000);
TEST_ESP_OK(sdmmc_card_init(config, card));
free(card);
}
TEST_CASE("CD input works in SD mode", "[sd][test_env=UT_T1_SDMODE][ignore]")
TEST_CASE("CD input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
const int gpio_cd_num = 5;
slot_config.gpio_cd = gpio_cd_num;
slot_config.gpio_cd = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
test_cd_input(gpio_cd_num, &config);
test_cd_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
TEST_CASE("CD input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
const int gpio_cd_num = 5;
slot_config.gpio_cd = gpio_cd_num;
slot_config.gpio_cd = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_slot(config.slot, &slot_config));
test_cd_input(gpio_cd_num, &config);
test_cd_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdspi_host_deinit());
sd_test_board_power_off();
}
static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
@ -313,12 +440,12 @@ static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
// Check that card write succeeds if WP is high
REG_WRITE(GPIO_OUT_W1TS_REG, BIT(gpio_wp_num));
usleep(100);
usleep(1000);
TEST_ESP_OK(sdmmc_write_sectors(card, &data, 0, 1));
// Check that write fails if WP is low
REG_WRITE(GPIO_OUT_W1TC_REG, BIT(gpio_wp_num));
usleep(100);
usleep(1000);
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, sdmmc_write_sectors(card, &data, 0, 1));
// ...but reads still work
TEST_ESP_OK(sdmmc_read_sectors(card, &data, 0, 1));
@ -327,30 +454,32 @@ static void test_wp_input(int gpio_wp_num, const sdmmc_host_t* config)
free(card);
}
TEST_CASE("WP input works in SD mode", "[sd][test_env=UT_T1_SDMODE][ignore]")
TEST_CASE("WP input works in SD mode", "[sd][test_env=UT_T1_SDMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
const int gpio_wp_num = 5;
slot_config.gpio_wp = gpio_wp_num;
slot_config.gpio_wp = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdmmc_host_init());
TEST_ESP_OK(sdmmc_host_init_slot(SDMMC_HOST_SLOT_1, &slot_config));
test_wp_input(gpio_wp_num, &config);
test_wp_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdmmc_host_deinit());
sd_test_board_power_off();
}
TEST_CASE("WP input works in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
{
sd_test_board_power_on();
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
const int gpio_wp_num = 5;
slot_config.gpio_wp = gpio_wp_num;
slot_config.gpio_wp = CD_WP_TEST_GPIO;
TEST_ESP_OK(sdspi_host_init());
TEST_ESP_OK(sdspi_host_init_slot(config.slot, &slot_config));
test_wp_input(gpio_wp_num, &config);
test_wp_input(CD_WP_TEST_GPIO, &config);
TEST_ESP_OK(sdspi_host_deinit());
sd_test_board_power_off();
}

View file

@ -71,6 +71,7 @@
#define SDMMC_INTMASK_EBE BIT(15)
#define SDMMC_INTMASK_ACD BIT(14)
#define SDMMC_INTMASK_SBE BIT(13)
#define SDMMC_INTMASK_BCI BIT(13)
#define SDMMC_INTMASK_HLE BIT(12)
#define SDMMC_INTMASK_FRUN BIT(11)
#define SDMMC_INTMASK_HTO BIT(10)

View file

@ -283,7 +283,12 @@ typedef volatile struct {
uint32_t usrid; ///< user ID
uint32_t verid; ///< IP block version
uint32_t hcon; ///< compile-time IP configuration
uint32_t uhs; ///< TBD
union {
struct {
uint32_t voltage: 16; ///< voltage control for slots; no-op on ESP32.
uint32_t ddr: 16; ///< bit N enables DDR mode for card N
};
} uhs; ///< UHS related settings
union {
struct {
@ -348,7 +353,16 @@ typedef volatile struct {
uint32_t bufaddrl; ///< unused
uint32_t bufaddru; ///< unused
uint32_t reserved_a8[22];
uint32_t cardthrctl;
union {
struct {
uint32_t read_thr_en : 1; ///< initiate transfer only if FIFO has more space than the read threshold
uint32_t busy_clr_int_en : 1; ///< enable generation of busy clear interrupts
uint32_t write_thr_en : 1; ///< equivalent of read_thr_en for writes
uint32_t reserved1 : 13;
uint32_t card_threshold : 12; ///< threshold value for reads/writes, in bytes
};
uint32_t val;
} cardthrctl;
uint32_t back_end_power;
uint32_t uhs_reg_ext;
uint32_t emmc_ddr_reg;

View file

@ -39,12 +39,25 @@ Pin mappings of these slots are given in the following table:
| WP | any input via GPIO matrix |
+--------+---------------------------+
Card Detect and Write Protect signals can be routed to arbitrary pins using GPIO matrix. To use these pins, set ``gpio_cd`` and ``gpio_wp`` members of :cpp:class:`sdmmc_slot_config_t` structure when calling :cpp:func:`sdmmc_host_init_slot`. Note that it is not advised to specify Card Detect pin when working with SDIO cards, because in ESP32 card detect signal can also trigger SDIO slave interrupt.
Card Detect and Write Protect signals can be routed to arbitrary pins using GPIO matrix. To use these pins, set ``gpio_cd`` and ``gpio_wp`` members of :cpp:class:`sdmmc_slot_config_t` structure before calling :cpp:func:`sdmmc_host_init_slot`. Note that it is not advised to specify Card Detect pin when working with SDIO cards, because in ESP32 card detect signal can also trigger SDIO slave interrupt.
.. warning::
Pins used by slot 0 (``HS1_*``) are also used to connect SPI flash chip in ESP-WROOM32 and ESP32-WROVER modules. These pins can not be shared between SD card and SPI flash. If you need to use Slot 0, connect SPI flash to different pins and set Efuses accordingly.
Supported speed modes
---------------------
SDMMC Host driver supports the following speed modes:
- Default Speed (20MHz), 4-line/1-line (with SD cards), and 8-line (with 3.3V eMMC).
- High Speed (40MHz), 4-line/1-line (with SD cards), and 8-line (with 3.3V eMMC)
- High Speed DDR (40MHz), 4-line (with 3.3V eMMC)
Not supported at present are:
- High Speed DDR mode, 8-line eMMC
- UHS-I 1.8V modes, 4-line SD cards
Using the SDMMC Host driver
---------------------------
@ -53,6 +66,22 @@ Of all the funtions listed below, only :cpp:func:`sdmmc_host_init`, :cpp:func:`s
Other functions, such as :cpp:func:`sdmmc_host_set_bus_width`, :cpp:func:`sdmmc_host_set_card_clk`, and :cpp:func:`sdmmc_host_do_transaction` will be called by the SD/MMC protocol layer via function pointers in :cpp:class:`sdmmc_host_t` structure.
Configuring bus width and frequency
-----------------------------------
With the default initializers for :cpp:class:`sdmmc_host_t` and :cpp:class:`sdmmc_slot_config_t` (:c:macro:`SDMMC_HOST_DEFAULT` and :c:macro:`SDMMC_SLOT_CONFIG_DEFAULT`), SDMMC Host driver will attempt to use widest bus supported by the card (4 lines for SD, 8 lines for eMMC) and 20MHz frequency.
In designs where communication at 40MHz frequency can be achieved, it is possible to increase the bus frequency to by changing ``max_freq_khz`` field of :cpp:class:`sdmmc_host_t`::
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
To configure bus width, set ``width`` field of :cpp:class:`sdmmc_slot_config_t`. For example, to set 1-line mode::
sdmmc_slot_config_t slot = SDMMC_SLOT_CONFIG_DEFAULT();
slot.width = 1;
See also
--------

View file

@ -4,7 +4,7 @@ SD/SDIO/MMC Driver
Overview
--------
SD/SDIO/MMC driver currently supports SD memory and IO cards. Support for MCC/eMMC cards will be added in the future. This protocol level driver builds on top of SDMMC and SD SPI host drivers.
SD/SDIO/MMC driver currently supports SD memory, SDIO cards, and eMMC chips. This protocol level driver builds on top of SDMMC and SD SPI host drivers.
SDMMC and SD SPI host drivers (``driver/sdmmc_host.h``) provide APIs to send commands to the slave device(s), send and receive data, and handle error conditions on the bus.
@ -34,6 +34,14 @@ Usage with SD memory cards
3. To read and write sectors of the card, use :cpp:func:`sdmmc_read_sectors` and :cpp:func:`sdmmc_write_sectors`, passing the pointer to card information structure (``card``).
4. When card is not used anymore, call the host driver function to disable the host peripheral and free resources allocated by the driver (e.g. :cpp:func:`sdmmc_host_deinit`).
Usage with eMMC chips
^^^^^^^^^^^^^^^^^^^^^
From the perspective of the protocol layer, eMMC memory chips behave the same way as SD memory cards. Because of similarity of the protocol, even though eMMC are chips don't have the "card" form factor, same terminology is used as for SD cards (`sdmmc_card_t`, `sdmmc_card_init`). Note that eMMC chips can not be used over SPI, therefore are incompatible with SD SPI host driver.
To initialize eMMC memory and do read/write operations, follow the steps listed above for SD cards.
Usage with SDIO cards
^^^^^^^^^^^^^^^^^^^^^

View file

@ -28,9 +28,9 @@ if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
# When running on local machine execute the following before running this script
# > export TEST_FW_PATH='~/esp/esp-idf/tools/tiny-test-fw'
# > make print_flash_cmd | tail -n 1 > build/download.config
# > make app bootloader
# > make print_flash_cmd | tail -n 1 > build/download.config
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
import TinyFW
import IDF
@ -39,7 +39,13 @@ import IDF
expath = os.path.dirname(os.path.realpath(__file__))
client = imp.load_source("client", expath + "/scripts/test.py")
@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
# Due to connectivity issues (between runner host and DUT) in the runner environment,
# some of the `advanced_tests` are ignored. These tests are intended for verifying
# the expected limits of the http_server capabilities, and implement sending and receiving
# of large HTTP packets and malformed requests, running multiple parallel sessions, etc.
# It is advised that all these tests be run locally, when making changes or adding new
# features to this component.
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_http_server_advanced(env, extra_data):
# Acquire DUT
dut1 = env.get_dut("http_server", "examples/protocols/http_server/advanced_tests")
@ -47,22 +53,24 @@ def test_examples_protocol_http_server_advanced(env, extra_data):
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "tests.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("http_server_bin_size", bin_size//1024)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size/1024))
IDF.check_performance("http_server_bin_size", bin_size/1024)
# Upload binary and start testing
print "Starting http_server advanced test app"
dut1.start_app()
# Parse IP address of STA
print "Waiting to connect with AP"
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
print "Leak Tests..."
# Expected Leak test Logs
dut1.expect("Leak Test Started...");
dut1.expect("Leak Test Passed");
#print "Leak Tests..."
## Expected Leak test Logs
#dut1.expect("Leak Test Started...", timeout=15);
#dut1.expect("Leak Test Passed", timeout=15);
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Started HTTP server on port: (\d+)"))[0]
result = dut1.expect(re.compile(r"(?:[\s\S]*)Max URI handlers: (\d+)(?:[\s\S]*)Max Open Sessions: (\d+)(?:[\s\S]*)Max Header Length: (\d+)(?:[\s\S]*)Max URI Length: (\d+)(?:[\s\S]*)Max Stack Size: (\d+)"))
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Started HTTP server on port: (\d+)"), timeout=15)[0]
result = dut1.expect(re.compile(r"(?:[\s\S]*)Max URI handlers: (\d+)(?:[\s\S]*)Max Open Sessions: (\d+)(?:[\s\S]*)Max Header Length: (\d+)(?:[\s\S]*)Max URI Length: (\d+)(?:[\s\S]*)Max Stack Size: (\d+)"), timeout=15)
max_uri_handlers = int(result[0])
max_sessions = int(result[1])
max_hdr_len = int(result[2])
@ -72,111 +80,114 @@ def test_examples_protocol_http_server_advanced(env, extra_data):
print "Got IP : " + got_ip
print "Got Port : " + got_port
print "Handler Tests..."
# Expected Handler Test Logs
dut1.expect("Test: Register Max URI handlers")
dut1.expect("Success")
dut1.expect("Test: Register Max URI + 1 handlers")
dut1.expect("no slots left for registering handler")
dut1.expect("Success")
dut1.expect("Test: Unregister 0th handler")
dut1.expect("Success")
dut1.expect("Test: Again unregister 0th handler not registered")
dut1.expect("handler 0 with method 1 not found")
dut1.expect("Success")
dut1.expect("Test: Register back 0th handler")
dut1.expect("Success")
dut1.expect("Test: Register 0th handler again after registering")
dut1.expect("handler 0 with method 1 already registered")
dut1.expect("Success")
dut1.expect("Test: Register 1 more handler")
dut1.expect("no slots left for registering handler")
dut1.expect("Success")
dut1.expect("Test: Unregister all handlers")
dut1.expect("Success")
dut1.expect("Registering basic handlers")
dut1.expect("Success")
#print "Handler Tests..."
## Expected Handler Test Logs
#dut1.expect("Test: Register Max URI handlers", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Register Max URI + 1 handlers", timeout=15)
#dut1.expect("no slots left for registering handler", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Unregister 0th handler", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Again unregister 0th handler not registered", timeout=15)
#dut1.expect("handler 0 with method 1 not found", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Register back 0th handler", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Register 0th handler again after registering", timeout=15)
#dut1.expect("handler 0 with method 1 already registered", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Test: Register 1 more handler", timeout=15)
#dut1.expect("no slots left for registering handler", timeout=15)
#dut1.expect("Success")
#dut1.expect("Test: Unregister all handlers", timeout=15)
#dut1.expect("Success", timeout=15)
#dut1.expect("Registering basic handlers", timeout=15)
#dut1.expect("Success", timeout=15)
# Run test script
# If failed raise appropriate exception
print "Basic HTTP Client Tests..."
if not client.get_hello(got_ip, got_port):
raise RuntimeError
inital_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: (\d+)"))[0])
if inital_stack < 0.1*max_stack_size:
print "More than 90% of stack being used on server start"
raise RuntimeError
if not client.post_hello(got_ip, got_port):
raise RuntimeError
if not client.put_hello(got_ip, got_port):
raise RuntimeError
if not client.post_echo(got_ip, got_port):
raise RuntimeError
if not client.get_echo(got_ip, got_port):
raise RuntimeError
if not client.put_echo(got_ip, got_port):
raise RuntimeError
if not client.get_hello_type(got_ip, got_port):
raise RuntimeError
if not client.get_hello_status(got_ip, got_port):
raise RuntimeError
if not client.get_false_uri(got_ip, got_port):
raise RuntimeError
print "Error code tests..."
if not client.code_500_server_error_test(got_ip, got_port):
raise RuntimeError
if not client.code_501_method_not_impl(got_ip, got_port):
raise RuntimeError
if not client.code_505_version_not_supported(got_ip, got_port):
raise RuntimeError
if not client.code_400_bad_request(got_ip, got_port):
raise RuntimeError
if not client.code_404_not_found(got_ip, got_port):
raise RuntimeError
if not client.code_405_method_not_allowed(got_ip, got_port):
raise RuntimeError
if not client.code_408_req_timeout(got_ip, got_port):
raise RuntimeError
if not client.code_414_uri_too_long(got_ip, got_port, max_uri_len):
raise RuntimeError
if not client.code_431_hdr_too_long(got_ip, got_port, max_hdr_len):
raise RuntimeError
if not client.test_upgrade_not_supported(got_ip, got_port):
raise RuntimeError
failed = False
print "Sessions and Context Tests..."
if not client.parallel_sessions_adder(got_ip, got_port, max_sessions):
raise RuntimeError
if not client.leftover_data_test(got_ip, got_port):
raise RuntimeError
if not client.async_response_test(got_ip, got_port):
raise RuntimeError
if not client.spillover_session(got_ip, got_port, max_sessions):
raise RuntimeError
print "Ignoring failure"
if not client.parallel_sessions_adder(got_ip, got_port, max_sessions):
print "Ignoring failure"
if not client.leftover_data_test(got_ip, got_port):
failed = True
if not client.async_response_test(got_ip, got_port):
failed = True
if not client.recv_timeout_test(got_ip, got_port):
raise RuntimeError
# May timeout in case requests are sent slower than responses are read.
# Instead use httperf stress test
#if not client.pipeline_test(got_ip, got_port, max_sessions):
# raise RuntimeError
failed = True
test_size = 50*1024 # 50KB
if not client.packet_size_limit_test(got_ip, got_port, test_size):
raise RuntimeError
print "Ignoring failure"
print "Getting initial stack usage..."
if not client.get_hello(got_ip, got_port):
raise RuntimeError
failed = True
final_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: (\d+)"))[0])
inital_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: (\d+)"), timeout=15)[0])
if inital_stack < 0.1*max_stack_size:
print "More than 90% of stack being used on server start"
failed = True
print "Basic HTTP Client Tests..."
if not client.get_hello(got_ip, got_port):
failed = True
if not client.post_hello(got_ip, got_port):
failed = True
if not client.put_hello(got_ip, got_port):
failed = True
if not client.post_echo(got_ip, got_port):
failed = True
if not client.get_echo(got_ip, got_port):
failed = True
if not client.put_echo(got_ip, got_port):
failed = True
if not client.get_hello_type(got_ip, got_port):
failed = True
if not client.get_hello_status(got_ip, got_port):
failed = True
if not client.get_false_uri(got_ip, got_port):
failed = True
print "Error code tests..."
if not client.code_500_server_error_test(got_ip, got_port):
failed = True
if not client.code_501_method_not_impl(got_ip, got_port):
failed = True
if not client.code_505_version_not_supported(got_ip, got_port):
failed = True
if not client.code_400_bad_request(got_ip, got_port):
failed = True
if not client.code_404_not_found(got_ip, got_port):
failed = True
if not client.code_405_method_not_allowed(got_ip, got_port):
failed = True
if not client.code_408_req_timeout(got_ip, got_port):
failed = True
if not client.code_414_uri_too_long(got_ip, got_port, max_uri_len):
print "Ignoring failure"
if not client.code_431_hdr_too_long(got_ip, got_port, max_hdr_len):
print "Ignoring failure"
if not client.test_upgrade_not_supported(got_ip, got_port):
failed = True
print "Getting final stack usage..."
if not client.get_hello(got_ip, got_port):
failed = True
final_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: (\d+)"), timeout=15)[0])
if final_stack < 0.05*max_stack_size:
print "More than 95% of stack got used during tests"
failed = True
if failed:
raise RuntimeError
if __name__ == '__main__':

View file

@ -398,6 +398,11 @@ httpd_handle_t test_httpd_start()
pre_start_mem = esp_get_free_heap_size();
httpd_handle_t hd;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
config.server_port = 1234;
/* This check should be a part of http_server */
config.max_open_sockets = (CONFIG_LWIP_MAX_SOCKETS - 3);
if (httpd_start(&hd, &config) == ESP_OK) {
ESP_LOGI(TAG, "Started HTTP server on port: %d", config.server_port);
ESP_LOGI(TAG, "Max URI handlers: %d", config.max_uri_handlers);
@ -495,10 +500,10 @@ bool leak_test(void)
httpd_handle_t start_tests()
{
leak_test();
// leak_test();
httpd_handle_t hd = test_httpd_start();
if (hd) {
test_handler_limit(hd);
// test_handler_limit(hd);
register_basic_handlers(hd);
}
return hd;

View file

@ -130,11 +130,11 @@
import threading
from multiprocessing import Process, Queue
import socket
import time
import argparse
import requests
import signal
import httplib
import sys
import string
import random
@ -144,8 +144,24 @@ _verbose_ = False
class Session:
def __init__(self, addr, port):
self.client = socket.create_connection((addr, int(port)))
self.client.settimeout(30)
self.client.settimeout(15)
self.target = addr
self.status = 0
self.encoding = ''
self.content_type = ''
self.content_len = 0
def send_err_check(self, request, data=None):
rval = True
try:
self.client.sendall(request);
if data:
self.client.sendall(data)
except socket.error as err:
self.client.close()
print "Socket Error in send :", err
rval = False
return rval
def send_get(self, path, headers=None):
request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
@ -153,7 +169,7 @@ class Session:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\n\r\n"
self.client.send(request);
return self.send_err_check(request)
def send_put(self, path, data, headers=None):
request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
@ -161,8 +177,7 @@ class Session:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
return self.send_err_check(request, data)
def send_post(self, path, data, headers=None):
request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
@ -170,10 +185,10 @@ class Session:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
return self.send_err_check(request, data)
def read_resp_hdrs(self):
try:
state = 'nothing'
resp_read = ''
while True:
@ -190,7 +205,7 @@ class Session:
state = 'nothing'
resp_read += char
if state == 'second_lf':
break;
break
# Handle first line
line_hdrs = resp_read.splitlines()
line_comp = line_hdrs[0].split()
@ -211,13 +226,17 @@ class Session:
if len(line_comp) == 2:
headers[line_comp[0]] = line_comp[1].lstrip()
return headers
except socket.error as err:
self.client.close()
print "Socket Error in recv :", err
return None
def read_resp_data(self):
try:
read_data = ''
if self.encoding != 'chunked':
while len(read_data) != self.content_len:
read_data += self.client.recv(self.content_len)
self.content_len = 0
else:
chunk_data_buf = ''
while (True):
@ -231,11 +250,16 @@ class Session:
chunk_len = int(chunk_data_buf, 16)
# Keep adding to contents
self.content_len += chunk_len
read_data += self.client.recv(chunk_len)
rem_len = chunk_len
while (rem_len):
new_data = self.client.recv(rem_len)
read_data += new_data
rem_len -= len(new_data)
chunk_data_buf = ''
# Fetch remaining CRLF
if self.client.recv(2) != "\r\n":
# Error in packet
print "Error in chunked data"
return None
if not chunk_len:
# If last chunk
@ -246,6 +270,10 @@ class Session:
# character to chunked data buffer
chunk_data_buf += read_ch
return read_data
except socket.error as err:
self.client.close()
print "Socket Error in recv :", err
return None
def close(self):
self.client.close()
@ -259,11 +287,12 @@ def test_val(text, expected, received):
return False
return True
class myThread (threading.Thread):
class adder_thread (threading.Thread):
def __init__(self, id, dut, port):
threading.Thread.__init__(self)
self.id = id
self.dut = dut
self.depth = 3
self.session = Session(dut, port)
def run(self):
@ -272,24 +301,21 @@ class myThread (threading.Thread):
# Pipeline 3 requests
if (_verbose_):
print " Thread: Using adder start " + str(self.id)
self.session.send_post('/adder', str(self.id));
time.sleep(1)
self.session.send_post('/adder', str(self.id));
time.sleep(1)
self.session.send_post('/adder', str(self.id));
time.sleep(1)
self.session.read_resp_hdrs()
self.response.append(self.session.read_resp_data())
self.session.read_resp_hdrs()
self.response.append(self.session.read_resp_data())
for _ in range(self.depth):
self.session.send_post('/adder', str(self.id))
time.sleep(2)
for _ in range(self.depth):
self.session.read_resp_hdrs()
self.response.append(self.session.read_resp_data())
def adder_result(self):
if len(self.response) != self.depth:
print "Error : missing response packets"
return False
for i in range(len(self.response)):
# print self.response[i]
if not test_val("thread" + str(self.id) + ": response[" + str(i) + "]",
if not test_val("Thread" + str(self.id) + " response[" + str(i) + "]",
str(self.id * (i + 1)), str(self.response[i])):
return False
return True
@ -300,94 +326,136 @@ class myThread (threading.Thread):
def get_hello(dut, port):
# GET /hello should return 'Hello World!'
print "[test] GET /hello returns 'Hello World!' =>",
r = requests.get("http://" + dut + ":" + port + "/hello")
if not test_val("status_code", 200, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("GET", "/hello")
resp = conn.getresponse()
if not test_val("status_code", 200, resp.status):
conn.close()
return False
if not test_val("data", "Hello World!", r.text):
if not test_val("data", "Hello World!", resp.read()):
conn.close()
return False
if not test_val("data", "application/json", r.headers['Content-Type']):
return False
print "Success"
return True
def post_hello(dut, port):
# PUT /hello returns 405'
print "[test] PUT /hello returns 405' =>",
r = requests.put("http://" + dut + ":" + port + "/hello", data="Hello")
if not test_val("status_code", 405, r.status_code):
if not test_val("data", "application/json", resp.getheader('Content-Type')):
conn.close()
return False
print "Success"
conn.close()
return True
def put_hello(dut, port):
# POST /hello returns 405'
print "[test] POST /hello returns 404' =>",
r = requests.post("http://" + dut + ":" + port + "/hello", data="Hello")
if not test_val("status_code", 405, r.status_code):
# PUT /hello returns 405'
print "[test] PUT /hello returns 405' =>",
conn = httplib.HTTPConnection(dut, int(port))
conn.request("PUT", "/hello", "Hello")
resp = conn.getresponse()
if not test_val("status_code", 405, resp.status):
conn.close()
return False
print "Success"
conn.close()
return True
def post_hello(dut, port):
# POST /hello returns 405'
print "[test] POST /hello returns 404' =>",
conn = httplib.HTTPConnection(dut, int(port))
conn.request("POST", "/hello", "Hello")
resp = conn.getresponse()
if not test_val("status_code", 405, resp.status):
conn.close()
return False
print "Success"
conn.close()
return True
def post_echo(dut, port):
# POST /echo echoes data'
print "[test] POST /echo echoes data' =>",
r = requests.post("http://" + dut + ":" + port + "/echo", data="Hello")
if not test_val("status_code", 200, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("POST", "/echo", "Hello")
resp = conn.getresponse()
if not test_val("status_code", 200, resp.status):
conn.close()
return False
if not test_val("data", "Hello", r.text):
if not test_val("data", "Hello", resp.read()):
conn.close()
return False
print "Success"
conn.close()
return True
def put_echo(dut, port):
# POST /echo echoes data'
# PUT /echo echoes data'
print "[test] PUT /echo echoes data' =>",
r = requests.put("http://" + dut + ":" + port + "/echo", data="Hello")
if not test_val("status_code", 200, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("PUT", "/echo", "Hello")
resp = conn.getresponse()
if not test_val("status_code", 200, resp.status):
conn.close()
return False
if not test_val("data", "Hello", r.text):
if not test_val("data", "Hello", resp.read()):
conn.close()
return False
print "Success"
conn.close()
return True
def get_echo(dut, port):
# GET /echo returns 404'
print "[test] GET /echo returns 405' =>",
r = requests.get("http://" + dut + ":" + port + "/echo")
if not test_val("status_code", 405, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("GET", "/echo")
resp = conn.getresponse()
if not test_val("status_code", 405, resp.status):
conn.close()
return False
print "Success"
conn.close()
return True
def get_hello_type(dut, port):
# GET /hello/type_html returns text/html as Content-Type'
print "[test] GET /hello/type_html has Content-Type of text/html =>",
r = requests.get("http://" + dut + ":" + port + "/hello/type_html")
if not test_val("status_code", 200, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("GET", "/hello/type_html")
resp = conn.getresponse()
if not test_val("status_code", 200, resp.status):
conn.close()
return False
if not test_val("data", "Hello World!", r.text):
if not test_val("data", "Hello World!", resp.read()):
conn.close()
return False
if not test_val("data", "text/html", r.headers['Content-Type']):
if not test_val("data", "text/html", resp.getheader('Content-Type')):
conn.close()
return False
print "Success"
conn.close()
return True
def get_hello_status(dut, port):
# GET /hello/status_500 returns status 500'
print "[test] GET /hello/status_500 returns status 500 =>",
r = requests.get("http://" + dut + ":" + port + "/hello/status_500")
if not test_val("status_code", 500, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("GET", "/hello/status_500")
resp = conn.getresponse()
if not test_val("status_code", 500, resp.status):
conn.close()
return False
print "Success"
conn.close()
return True
def get_false_uri(dut, port):
# GET /false_uri returns status 404'
print "[test] GET /false_uri returns status 404 =>",
r = requests.get("http://" + dut + ":" + port + "/false_uri")
if not test_val("status_code", 404, r.status_code):
conn = httplib.HTTPConnection(dut, int(port))
conn.request("GET", "/false_uri")
resp = conn.getresponse()
if not test_val("status_code", 404, resp.status):
conn.close()
return False
print "Success"
conn.close()
return True
def parallel_sessions_adder(dut, port, max_sessions):
@ -396,7 +464,7 @@ def parallel_sessions_adder(dut, port, max_sessions):
t = []
# Create all sessions
for i in xrange(max_sessions):
t.append(myThread(i * 2, dut, port))
t.append(adder_thread(i, dut, port))
for i in xrange(len(t)):
t[i].start()
@ -406,8 +474,7 @@ def parallel_sessions_adder(dut, port, max_sessions):
res = True
for i in xrange(len(t)):
if not t[i].adder_result():
if not test_val("Thread" + str(i) + "Failed", "True", "False"):
if not test_val("Thread" + str(i) + " Failed", t[i].adder_result(), True):
res = False
t[i].close()
if (res):
@ -436,32 +503,30 @@ def async_response_test(dut, port):
def leftover_data_test(dut, port):
# Leftover data in POST is purged (valid and invalid URIs)
print "[test] Leftover data in POST is purged (valid and invalid URIs) =>",
s = Session(dut, port)
s = httplib.HTTPConnection(dut + ":" + port)
s.send_post('/leftover_data',
"abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
s.read_resp_hdrs()
if not test_val("Partial data", "abcdefghij", s.read_resp_data()):
s.request("POST", url='/leftover_data', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
resp = s.getresponse()
if not test_val("Partial data", "abcdefghij", resp.read()):
s.close()
return False
s.send_get('/hello')
s.read_resp_hdrs()
if not test_val("Hello World Data", "Hello World!", s.read_resp_data()):
s.request("GET", url='/hello')
resp = s.getresponse()
if not test_val("Hello World Data", "Hello World!", resp.read()):
s.close()
return False
s.send_post('/false_uri',
"abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
s.read_resp_hdrs()
if not test_val("False URI Status", str(404), str(s.status)):
s.request("POST", url='/false_uri', body="abcdefghijklmnopqrstuvwxyz\r\nabcdefghijklmnopqrstuvwxyz")
resp = s.getresponse()
if not test_val("False URI Status", str(404), str(resp.status)):
s.close()
return False
s.read_resp_data()
resp.read()
s.send_get('/hello')
s.read_resp_hdrs()
if not test_val("Hello World Data", "Hello World!", s.read_resp_data()):
s.request("GET", url='/hello')
resp = s.getresponse()
if not test_val("Hello World Data", "Hello World!", resp.read()):
s.close()
return False
@ -469,122 +534,86 @@ def leftover_data_test(dut, port):
print "Success"
return True
def timeout_handler(signum, frame):
raise Exception("Timeout")
def spillover_session(dut, port, max):
# Session max_sessions + 1 is rejected
print "[test] Session max_sessions (" + str(max) + ") + 1 is rejected =>",
def spillover_session(dut, port, max_sess):
# Session max_sess_sessions + 1 is rejected
print "[test] Session max_sess_sessions (" + str(max_sess) + ") + 1 is rejected =>",
s = []
# Register a timeout callback
signal.signal(signal.SIGALRM, timeout_handler)
for i in xrange(max + 1):
_verbose_ = True
for i in xrange(max_sess + 1):
if (_verbose_):
print "Executing " + str(i)
a = Session(dut, port)
a.send_get('/hello')
try:
# Check for response timeout
signal.alarm(5)
a.read_resp_hdrs()
a.read_resp_data()
signal.alarm(0)
# Control reaches here only if connection was established
a = httplib.HTTPConnection(dut + ":" + port)
a.request("GET", url='/hello')
resp = a.getresponse()
if not test_val("Connection " + str(i), "Hello World!", resp.read()):
a.close()
break
s.append(a)
except Exception, msg:
except:
if (_verbose_):
print str(msg) + ": Connection " + str(i) + " rejected"
print "Connection " + str(i) + " rejected"
a.close()
break
# Close open connections
for a in s:
a.close()
# Check if number of connections is equal to max
print ["Fail","Success"][len(s) == max]
return (len(s) == max)
# Check if number of connections is equal to max_sess
print ["Fail","Success"][len(s) == max_sess]
return (len(s) == max_sess)
def recv_timeout_test(dut, port):
print "[test] Timeout occurs if partial packet sent =>",
signal.signal(signal.SIGALRM, timeout_handler)
s = Session(dut, port)
s.client.send("GE")
try:
signal.alarm(15)
s.client.sendall("GE")
s.read_resp_hdrs()
resp = s.read_resp_data()
signal.alarm(0)
if not test_val("Request Timeout", "Server closed this connection", resp):
s.close()
return False
except:
s.close()
return False
s.close()
print "Success"
return True
def pipeline_test(dut, port, max_sess):
print "[test] Pipelining test =>",
s = [Session(dut, port) for _ in range(max_sess)]
path = "/echo"
pipeline_depth = 10
header = "POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: "
s[0].client.send(header)
for i in range(1, max_sess):
for j in range(pipeline_depth):
data = str(i) + ":" + str(j)
request = header + str(len(data)) + "\r\n\r\n" + data
s[i].client.send(request)
s[0].client.send(str(len("0:0")) + "\r\n\r\n" + "0:0")
for j in range(1, pipeline_depth):
data = "0:" + str(j)
request = header + str(len(data)) + "\r\n\r\n" + data
s[0].client.send(request)
res = True
for i in range(max_sess):
#time.sleep(1)
for j in range(pipeline_depth):
s[i].read_resp_hdrs()
echo_data = s[i].read_resp_data()
if (_verbose_):
print "[" + str(i) + "][" + str(j) + "] = " + echo_data
if not test_val("Echo Data", str(i) + ":" + str(j), echo_data):
res = False
s[i].close()
#for i in range(max_sess):
#s[i].close()
if (res):
print "Success"
return res
def packet_size_limit_test(dut, port, test_size):
print "[test] LWIP send size limit test =>",
s = Session(dut, port)
print "[test] send size limit test =>",
retry = 5
while (retry):
retry -= 1
print "data size = ", test_size
s = httplib.HTTPConnection(dut + ":" + port)
random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(test_size))
path = "/echo"
s.send_post(path, random_data)
s.read_resp_hdrs()
resp = s.read_resp_data()
s.request("POST", url=path, body=random_data)
resp = s.getresponse()
if not test_val("Error", "200", str(resp.status)):
if test_val("Error", "408", str(resp.status)):
print "Data too large to be allocated"
test_size = test_size/10
else:
print "Unexpected error"
s.close()
print "Retry..."
continue
resp = resp.read()
result = (resp == random_data)
if not result:
test_val("Data size", str(len(random_data)), str(len(resp)))
s.close()
return False
print "Success"
print "Retry..."
continue
s.close()
print "Success"
return True
print "Failed"
return False
def code_500_server_error_test(dut, port):
print "[test] 500 Server Error test =>",
s = Session(dut, port)
s.client.send("abcdefgh\0")
s.client.sendall("abcdefgh\0")
s.read_resp_hdrs()
resp = s.read_resp_data()
# Presently server sends back 400 Bad Request
@ -602,7 +631,7 @@ def code_501_method_not_impl(dut, port):
print "[test] 501 Method Not Implemented =>",
s = Session(dut, port)
path = "/hello"
s.client.send("ABC " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.client.sendall("ABC " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
# Presently server sends back 400 Bad Request
@ -620,7 +649,7 @@ def code_505_version_not_supported(dut, port):
print "[test] 505 Version Not Supported =>",
s = Session(dut, port)
path = "/hello"
s.client.send("GET " + path + " HTTP/2.0\r\nHost: " + dut + "\r\n\r\n")
s.client.sendall("GET " + path + " HTTP/2.0\r\nHost: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
if not test_val("Server Error", "505", s.status):
@ -634,7 +663,7 @@ def code_400_bad_request(dut, port):
print "[test] 400 Bad Request =>",
s = Session(dut, port)
path = "/hello"
s.client.send("XYZ " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.client.sendall("XYZ " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
if not test_val("Client Error", "400", s.status):
@ -648,7 +677,7 @@ def code_404_not_found(dut, port):
print "[test] 404 Not Found =>",
s = Session(dut, port)
path = "/dummy"
s.client.send("GET " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.client.sendall("GET " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
if not test_val("Client Error", "404", s.status):
@ -662,7 +691,7 @@ def code_405_method_not_allowed(dut, port):
print "[test] 405 Method Not Allowed =>",
s = Session(dut, port)
path = "/hello"
s.client.send("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.client.sendall("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
if not test_val("Client Error", "405", s.status):
@ -674,20 +703,13 @@ def code_405_method_not_allowed(dut, port):
def code_408_req_timeout(dut, port):
print "[test] 408 Request Timeout =>",
signal.signal(signal.SIGALRM, timeout_handler)
s = Session(dut, port)
s.client.send("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 10\r\n\r\nABCD")
try:
signal.alarm(15)
s.client.sendall("POST /echo HTTP/1.1\r\nHost: " + dut + "\r\nContent-Length: 10\r\n\r\nABCD")
s.read_resp_hdrs()
resp = s.read_resp_data()
signal.alarm(0)
if not test_val("Client Error", "408", s.status):
s.close()
return False
except:
s.close()
return False
s.close()
print "Success"
return True
@ -696,7 +718,7 @@ def code_411_length_required(dut, port):
print "[test] 411 Length Required =>",
s = Session(dut, port)
path = "/echo"
s.client.send("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n")
s.client.sendall("POST " + path + " HTTP/1.1\r\nHost: " + dut + "\r\nContent-Type: text/plain\r\nTransfer-Encoding: chunked\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
# Presently server sends back 400 Bad Request
@ -715,11 +737,11 @@ def send_getx_uri_len(dut, port, length):
method = "GET "
version = " HTTP/1.1\r\n"
path = "/"+"x"*(length - len(method) - len(version) - len("/"))
s.client.send(method)
s.client.sendall(method)
time.sleep(1)
s.client.send(path)
s.client.sendall(path)
time.sleep(1)
s.client.send(version + "Host: " + dut + "\r\n\r\n")
s.client.sendall(version + "Host: " + dut + "\r\n\r\n")
s.read_resp_hdrs()
resp = s.read_resp_data()
s.close()
@ -743,9 +765,9 @@ def send_postx_hdr_len(dut, port, length):
custom_hdr_field = "\r\nCustom: "
custom_hdr_val = "x"*(length - len(host) - len(custom_hdr_field) - len("\r\n\r\n") + len("0"))
request = "POST " + path + " HTTP/1.1\r\n" + host + custom_hdr_field + custom_hdr_val + "\r\n\r\n"
s.client.send(request[:length/2])
s.client.sendall(request[:length/2])
time.sleep(1)
s.client.send(request[length/2:])
s.client.sendall(request[length/2:])
hdr = s.read_resp_hdrs()
resp = s.read_resp_data()
s.close()
@ -768,7 +790,7 @@ def test_upgrade_not_supported(dut, port):
print "[test] Upgrade Not Supported =>",
s = Session(dut, port)
path = "/hello"
s.client.send("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n");
s.client.sendall("OPTIONS * HTTP/1.1\r\nHost:" + dut + "\r\nUpgrade: TLS/1.0\r\nConnection: Upgrade\r\n\r\n");
s.read_resp_hdrs()
resp = s.read_resp_data()
if not test_val("Client Error", "200", s.status):
@ -831,10 +853,6 @@ if __name__ == '__main__':
async_response_test(dut, port)
spillover_session(dut, port, max_sessions)
recv_timeout_test(dut, port)
# May timeout in case requests are sent slower than responses are read.
# Instead use httperf stress test
pipeline_test(dut, port, max_sessions)
packet_size_limit_test(dut, port, 50*1024)
get_hello(dut, port)

View file

@ -28,9 +28,9 @@ if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
# When running on local machine execute the following before running this script
# > export TEST_FW_PATH='~/esp/esp-idf/tools/tiny-test-fw'
# > make print_flash_cmd | tail -n 1 > build/download.config
# > make app bootloader
# > make print_flash_cmd | tail -n 1 > build/download.config
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
import TinyFW
import IDF
@ -47,21 +47,23 @@ def test_examples_protocol_http_server_persistence(env, extra_data):
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "persistent_sockets.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("http_server_bin_size", bin_size//1024)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size/1024))
IDF.check_performance("http_server_bin_size", bin_size/1024)
# Upload binary and start testing
print "Starting http_server persistance test app"
dut1.start_app()
# Parse IP address of STA
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"))[0]
print "Waiting to connect with AP"
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=120)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"), timeout=30)[0]
print "Got IP : " + got_ip
print "Got Port : " + got_port
# Expected Logs
dut1.expect("Registering URI handlers")
dut1.expect("Registering URI handlers", timeout=30)
# Run test script
conn = client.start_session(got_ip, got_port)
@ -72,9 +74,9 @@ def test_examples_protocol_http_server_persistence(env, extra_data):
num = random.randint(0,100)
client.putreq(conn, "/adder", str(num))
visitor += 1
dut1.expect("/adder visitor count = " + str(visitor))
dut1.expect("/adder PUT handler read " + str(num))
dut1.expect("PUT allocating new session")
dut1.expect("/adder visitor count = " + str(visitor), timeout=30)
dut1.expect("/adder PUT handler read " + str(num), timeout=30)
dut1.expect("PUT allocating new session", timeout=30)
# Retest PUT request and change session context value
num = random.randint(0,100)
@ -82,11 +84,11 @@ def test_examples_protocol_http_server_persistence(env, extra_data):
client.putreq(conn, "/adder", str(num))
visitor += 1
adder += num
dut1.expect("/adder visitor count = " + str(visitor))
dut1.expect("/adder PUT handler read " + str(num))
dut1.expect("/adder visitor count = " + str(visitor), timeout=30)
dut1.expect("/adder PUT handler read " + str(num), timeout=30)
try:
# Re allocation shouldn't happen
dut1.expect("PUT allocating new session")
dut1.expect("PUT allocating new session", timeout=30)
# Not expected
raise RuntimeError
except:
@ -100,21 +102,21 @@ def test_examples_protocol_http_server_persistence(env, extra_data):
client.postreq(conn, "/adder", str(num))
visitor += 1
adder += num
dut1.expect("/adder visitor count = " + str(visitor))
dut1.expect("/adder handler read " + str(num))
dut1.expect("/adder visitor count = " + str(visitor), timeout=30)
dut1.expect("/adder handler read " + str(num), timeout=30)
# Test GET request and session persistence
print "Matching final sum :", adder
if client.getreq(conn, "/adder") != str(adder):
raise RuntimeError
visitor += 1
dut1.expect("/adder visitor count = " + str(visitor))
dut1.expect("/adder GET handler send " + str(adder))
dut1.expect("/adder visitor count = " + str(visitor), timeout=30)
dut1.expect("/adder GET handler send " + str(adder), timeout=30)
print "Ending session"
# Close connection and check for invocation of context "Free" function
client.end_session(conn)
dut1.expect("/adder Free Context function called")
dut1.expect("/adder Free Context function called", timeout=30)
print "Validating user context data"
# Start another session to check user context data
@ -122,11 +124,11 @@ def test_examples_protocol_http_server_persistence(env, extra_data):
num = random.randint(0,100)
client.putreq(conn, "/adder", str(num))
visitor += 1
dut1.expect("/adder visitor count = " + str(visitor))
dut1.expect("/adder PUT handler read " + str(num))
dut1.expect("PUT allocating new session")
dut1.expect("/adder visitor count = " + str(visitor), timeout=30)
dut1.expect("/adder PUT handler read " + str(num), timeout=30)
dut1.expect("PUT allocating new session", timeout=30)
client.end_session(conn)
dut1.expect("/adder Free Context function called")
dut1.expect("/adder Free Context function called", timeout=30)
if __name__ == '__main__':
test_examples_protocol_http_server_persistence()

View file

@ -28,9 +28,9 @@ if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
# When running on local machine execute the following before running this script
# > export TEST_FW_PATH='~/esp/esp-idf/tools/tiny-test-fw'
# > make print_flash_cmd | tail -n 1 > build/download.config
# > make app bootloader
# > make print_flash_cmd | tail -n 1 > build/download.config
# > export TEST_FW_PATH=~/esp/esp-idf/tools/tiny-test-fw
import TinyFW
import IDF
@ -39,7 +39,7 @@ import IDF
expath = os.path.dirname(os.path.realpath(__file__))
client = imp.load_source("client", expath + "/scripts/client.py")
@IDF.idf_example_test(env_tag="Example_WIFI", ignore=True)
@IDF.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_http_server_simple(env, extra_data):
# Acquire DUT
dut1 = env.get_dut("http_server", "examples/protocols/http_server/simple")
@ -47,21 +47,23 @@ def test_examples_protocol_http_server_simple(env, extra_data):
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "simple.bin")
bin_size = os.path.getsize(binary_file)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size//1024))
IDF.check_performance("http_server_bin_size", bin_size//1024)
IDF.log_performance("http_server_bin_size", "{}KB".format(bin_size/1024))
IDF.check_performance("http_server_bin_size", bin_size/1024)
# Upload binary and start testing
print "Starting http_server simple test app"
dut1.start_app()
# Parse IP address of STA
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"))[0]
print "Waiting to connect with AP"
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Got IP: (\d+.\d+.\d+.\d+)"), timeout=120)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Starting server on port: (\d+)"), timeout=30)[0]
print "Got IP : " + got_ip
print "Got Port : " + got_port
# Expected Logs
dut1.expect("Registering URI handlers")
dut1.expect("Registering URI handlers", timeout=30)
# Run test script
# If failed raise appropriate exception
@ -70,24 +72,24 @@ def test_examples_protocol_http_server_simple(env, extra_data):
raise RuntimeError
# Acquire host IP. Need a way to check it
host_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Found header => Host: (\d+.\d+.\d+.\d+)"))[0]
host_ip = dut1.expect(re.compile(r"(?:[\s\S]*)Found header => Host: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
# Match additional headers sent in the request
dut1.expect("Found header => Test-Header-2: Test-Value-2")
dut1.expect("Found header => Test-Header-1: Test-Value-1")
dut1.expect("Found URL query parameter => query1=value1")
dut1.expect("Found URL query parameter => query3=value3")
dut1.expect("Found URL query parameter => query2=value2")
dut1.expect("Request headers lost")
dut1.expect("Found header => Test-Header-2: Test-Value-2", timeout=30)
dut1.expect("Found header => Test-Header-1: Test-Value-1", timeout=30)
dut1.expect("Found URL query parameter => query1=value1", timeout=30)
dut1.expect("Found URL query parameter => query3=value3", timeout=30)
dut1.expect("Found URL query parameter => query2=value2", timeout=30)
dut1.expect("Request headers lost", timeout=30)
print "Test /ctrl PUT handler and realtime handler de/registration"
if not client.test_put_handler(got_ip, got_port):
raise RuntimeError
dut1.expect("Unregistering /hello and /echo URIs")
dut1.expect("Registering /hello and /echo URIs")
dut1.expect("Unregistering /hello and /echo URIs", timeout=30)
dut1.expect("Registering /hello and /echo URIs", timeout=30)
# Generate random data of 10KB
random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(1024*10))
random_data = ''.join(string.printable[random.randint(0,len(string.printable))-1] for _ in range(10*1024))
print "Test /echo POST handler with random data"
if not client.test_post_handler(got_ip, got_port, random_data):
raise RuntimeError
@ -96,19 +98,19 @@ def test_examples_protocol_http_server_simple(env, extra_data):
print "Test /hello with custom query : " + query
if not client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("Found URL query => " + query)
dut1.expect("Found URL query => " + query, timeout=30)
query = "abcd+1234%20xyz"
print "Test /hello with custom query : " + query
if not client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("Found URL query => " + query)
dut1.expect("Found URL query => " + query, timeout=30)
query = "abcd\nyz"
print "Test /hello with invalid query"
if client.test_custom_uri_query(got_ip, got_port, query):
raise RuntimeError
dut1.expect("400 Bad Request - Server unable to understand request due to invalid syntax")
dut1.expect("400 Bad Request - Server unable to understand request due to invalid syntax", timeout=30)
if __name__ == '__main__':
test_examples_protocol_http_server_simple()

View file

@ -14,117 +14,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import socket
import httplib
import argparse
class Session:
def __init__(self, addr, port):
self.client = socket.create_connection((addr, int(port)))
self.target = addr
def send_get(self, path, headers=None):
request = "GET " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\n\r\n"
self.client.send(request);
def send_put(self, path, data, headers=None):
request = "PUT " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
def send_post(self, path, data, headers=None):
request = "POST " + path + " HTTP/1.1\r\nHost: " + self.target
if headers:
for field, value in headers.iteritems():
request += "\r\n"+field+": "+value
request += "\r\nContent-Length: " + str(len(data)) +"\r\n\r\n"
self.client.send(request)
self.client.send(data)
def read_resp_hdrs(self):
state = 'nothing'
resp_read = ''
while True:
char = self.client.recv(1)
if char == '\r' and state == 'nothing':
state = 'first_cr'
elif char == '\n' and state == 'first_cr':
state = 'first_lf'
elif char == '\r' and state == 'first_lf':
state = 'second_cr'
elif char == '\n' and state == 'second_cr':
state = 'second_lf'
else:
state = 'nothing'
resp_read += char
if state == 'second_lf':
break;
# Handle first line
line_hdrs = resp_read.splitlines()
line_comp = line_hdrs[0].split()
self.status = line_comp[1]
del line_hdrs[0]
self.encoding = ''
self.content_type = ''
headers = dict()
# Process other headers
for h in range(len(line_hdrs)):
line_comp = line_hdrs[h].split(':')
if line_comp[0] == 'Content-Length':
self.content_len = int(line_comp[1])
if line_comp[0] == 'Content-Type':
self.content_type = line_comp[1].lstrip()
if line_comp[0] == 'Transfer-Encoding':
self.encoding = line_comp[1].lstrip()
if len(line_comp) == 2:
headers[line_comp[0]] = line_comp[1].lstrip()
return headers
def read_resp_data(self):
read_data = ''
if self.encoding != 'chunked':
while len(read_data) != self.content_len:
read_data += self.client.recv(self.content_len)
self.content_len = 0
else:
chunk_data_buf = ''
while (True):
# Read one character into temp buffer
read_ch = self.client.recv(1)
# Check CRLF
if (read_ch == '\r'):
read_ch = self.client.recv(1)
if (read_ch == '\n'):
# If CRLF decode length of chunk
chunk_len = int(chunk_data_buf, 16)
# Keep adding to contents
self.content_len += chunk_len
read_data += self.client.recv(chunk_len)
chunk_data_buf = ''
# Fetch remaining CRLF
if self.client.recv(2) != "\r\n":
# Error in packet
return None
if not chunk_len:
# If last chunk
break
continue
chunk_data_buf += '\r'
# If not CRLF continue appending
# character to chunked data buffer
chunk_data_buf += read_ch
return read_data
def close(self):
self.client.close()
def verbose_print(verbosity, *args):
if (verbosity):
print ''.join(str(elems) for elems in args)
@ -133,7 +25,7 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "======== GET HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
uri = "/hello?query1=value1&query2=value2&query3=value3"
# GET hello response
@ -142,13 +34,14 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "Sending additional headers : ")
for k, v in test_headers.iteritems():
verbose_print(verbosity, "\t", k, ": ", v)
sess.send_get(uri, test_headers)
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("GET", url=uri, headers=test_headers)
resp = sess.getresponse()
resp_hdrs = resp.getheaders()
resp_data = resp.read()
try:
if resp_hdrs["Custom-Header-1"] != "Custom-Value-1":
if resp.getheader("Custom-Header-1") != "Custom-Value-1":
return False
if resp_hdrs["Custom-Header-2"] != "Custom-Value-2":
if resp.getheader("Custom-Header-2") != "Custom-Value-2":
return False
except:
return False
@ -156,7 +49,7 @@ def test_get_handler(ip, port, verbosity = False):
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, "Server response to GET /hello")
verbose_print(verbosity, "Response Headers : ")
for k, v in resp_hdrs.iteritems():
for k, v in resp_hdrs:
verbose_print(verbosity, "\t", k, ": ", v)
verbose_print(verbosity, "Response Data : " + resp_data)
verbose_print(verbosity, "========================================\n")
@ -169,12 +62,12 @@ def test_post_handler(ip, port, msg, verbosity = False):
verbose_print(verbosity, "======== POST HANDLER TEST ============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
# POST message to /echo and get back response
sess.send_post("/echo", msg)
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("POST", url="/echo", body=msg)
resp = sess.getresponse()
resp_data = resp.read()
verbose_print(verbosity, "Server response to POST /echo (" + msg + ")")
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, resp_data)
@ -188,30 +81,28 @@ def test_put_handler(ip, port, verbosity = False):
verbose_print(verbosity, "======== PUT HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
# PUT message to /ctrl to disable /hello URI handler
verbose_print(verbosity, "Disabling /hello handler")
sess.send_put("/ctrl", "0")
sess.read_resp_hdrs()
if sess.content_len:
sess.read_resp_data()
sess.request("PUT", url="/ctrl", body="0")
resp = sess.getresponse()
resp.read()
sess.send_get("/hello")
sess.read_resp_hdrs()
resp_data1 = sess.read_resp_data()
sess.request("GET", url="/hello")
resp = sess.getresponse()
resp_data1 = resp.read()
verbose_print(verbosity, "Response on GET /hello : " + resp_data1)
# PUT message to /ctrl to enable /hello URI handler
verbose_print(verbosity, "Enabling /hello handler")
sess.send_put("/ctrl", "1")
sess.read_resp_hdrs()
if sess.content_len:
sess.read_resp_data()
sess.request("PUT", url="/ctrl", body="1")
resp = sess.getresponse()
resp.read()
sess.send_get("/hello")
sess.read_resp_hdrs()
resp_data2 = sess.read_resp_data()
sess.request("GET", url="/hello")
resp = sess.getresponse()
resp_data2 = resp.read()
verbose_print(verbosity, "Response on GET /hello : " + resp_data2)
# Close HTTP connection
@ -222,14 +113,14 @@ def test_custom_uri_query(ip, port, query, verbosity = False):
verbose_print(verbosity, "======== GET HANDLER TEST =============")
# Establish HTTP connection
verbose_print(verbosity, "Connecting to => " + ip + ":" + port)
sess = Session(ip, port)
sess = httplib.HTTPConnection(ip + ":" + port)
uri = "/hello?" + query
# GET hello response
verbose_print(verbosity, "Sending GET to URI : ", uri)
sess.send_get(uri, {})
resp_hdrs = sess.read_resp_hdrs()
resp_data = sess.read_resp_data()
sess.request("GET", url=uri, headers={})
resp = sess.getresponse()
resp_data = resp.read()
verbose_print(verbosity, "vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv")
verbose_print(verbosity, "Server response to GET /hello")
@ -253,6 +144,9 @@ if __name__ == '__main__':
port = args['port']
msg = args['msg']
test_get_handler (ip, port, True)
test_post_handler(ip, port, msg, True)
test_put_handler (ip, port, True)
if not test_get_handler (ip, port, True):
print "Failed!"
if not test_post_handler(ip, port, msg, True):
print "Failed!"
if not test_put_handler (ip, port, True):
print "Failed!"

View file

@ -12,7 +12,7 @@ This example demonstrates how to use an SD card with ESP32. Example does the fol
4. Rename the file. Before renaming, check if destination file already exists using `stat` function, and remove it using `unlink` function.
5. Open renamed file for reading, read back the line, and print it to the terminal.
*Note:* despite the name, `sdmmc` component doesn't support MMC/eMMC cards yet. It is also possible to extend `sdmmc` component to support SPI mode with SD cards via SPI peripheral.
This example support SD (SDSC, SDHC, SDXC) cards and eMMC chips.
## Hardware
@ -64,13 +64,14 @@ This command will burn the `XPD_SDIO_TIEH`, `XPD_SDIO_FORCE`, and `XPD_SDIO_REG`
## 4-line and 1-line modes
By default, example code uses the following initializer for SDMMC host peripheral configuration:
By default, example code uses the following initializer for SDMMC slot configuration:
```c++
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
```
Among other things, this sets `host.flags` to `SDMMC_HOST_FLAG_4BIT`, which means that SD/MMC driver will switch to 4-line mode when initializing the card (initial communication always happens in 1-line mode). If some of the card's D1, D2, D3 pins are not connected to the ESP32, set `host.flags` to `SDMMC_HOST_FLAG_1BIT` — then the SD/MMC driver will not attempt to switch to 4-line mode.
Among other things, this sets `slot_config.width = 0`, which means that SD/MMC driver will use the maximum bus width supported by the slot. For slot 1, it will switch to 4-line mode when initializing the card (initial communication always happens in 1-line mode). If some of the card's D1, D2, D3 pins are not connected to the ESP32, set `slot_config.width = 1` — then the SD/MMC driver will not attempt to switch to 4-line mode.
Note that even if card's D3 line is not connected to the ESP32, it still has to be pulled up, otherwise the card will go into SPI protocol mode.
## SPI mode

View file

@ -47,13 +47,13 @@ void app_main(void)
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
// To use 1-line SD mode, uncomment the following line:
// host.flags = SDMMC_HOST_FLAG_1BIT;
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
// To use 1-line SD mode, uncomment the following line:
// slot_config.width = 1;
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
// does make a difference some boards, so we do that here.

View file

@ -59,3 +59,5 @@ tools/kconfig_new/confgen.py
tools/kconfig_new/confserver.py
tools/kconfig_new/test/test_confserver.py
tools/windows/tool_setup/build_installer.sh
tools/test_idf_size/test.sh

View file

@ -6,7 +6,7 @@
# Includes information which is not shown in "xtensa-esp32-elf-size",
# or easy to parse from "xtensa-esp32-elf-objdump" or raw map files.
#
# Copyright 2017 Espressif Systems (Shanghai) PTE LTD
# Copyright 2017-2018 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -20,6 +20,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
from __future__ import print_function
from __future__ import unicode_literals
from builtins import dict
import argparse, sys, subprocess, re
import os.path
import pprint
@ -48,12 +51,6 @@ def load_map_data(map_file):
sections = load_sections(map_file)
return memory_config, sections
def output_section_for_address(memory_config, address):
for m in memory_config.values():
if m["origin"] <= address and m["origin"] + m["length"] > address:
return m["name"]
return None
def load_memory_config(map_file):
""" Memory Configuration section is the total size of each output section """
result = {}
@ -183,7 +180,7 @@ def main():
print("Per-file contributions to ELF file:")
print_detailed_sizes(sections, "file", "Object File")
if args.archive_details:
print "Symbols within the archive:", args.archive_details, "(Not all symbols may be reported)"
print("Symbols within the archive:", args.archive_details, "(Not all symbols may be reported)")
print_archive_symbols(sections, args.archive_details)
def print_summary(memory_config, sections):
@ -199,7 +196,7 @@ def print_summary(memory_config, sections):
used_data = get_size(".dram0.data")
used_bss = get_size(".dram0.bss")
used_dram = used_data + used_bss
used_iram = sum( get_size(s) for s in sections.keys() if s.startswith(".iram0") )
used_iram = sum( get_size(s) for s in sections if s.startswith(".iram0") )
flash_code = get_size(".flash.text")
flash_rodata = get_size(".flash.rodata")
total_size = used_data + used_iram + flash_code + flash_rodata
@ -230,7 +227,7 @@ def print_detailed_sizes(sections, key, header):
"Total")
print("%24s %10s %6s %6s %10s %8s %7s" % headings)
result = {}
for k in (sizes.keys()):
for k in sizes:
v = sizes[k]
result[k] = {}
result[k]["data"] = v.get(".dram0.data", 0)
@ -243,7 +240,11 @@ def print_detailed_sizes(sections, key, header):
def return_total_size(elem):
val = elem[1]
return val["total"]
for k,v in sorted(result.items(), key=return_total_size, reverse=True):
def return_header(elem):
return elem[0]
s = sorted(list(result.items()), key=return_header)
# do a secondary sort in order to have consistent order (for diff-ing the output)
for k,v in sorted(s, key=return_total_size, reverse=True):
if ":" in k: # print subheadings for key of format archive:file
sh,k = k.split(":")
print("%24s %10d %6d %6d %10d %8d %7d" % (k[:24],
@ -269,12 +270,14 @@ def print_archive_symbols(sections, archive):
s["sym_name"] = re.sub("(.text.|.literal.|.data.|.bss.|.rodata.)", "", s["sym_name"]);
result[section_name][s["sym_name"]] = result[section_name].get(s["sym_name"], 0) + s["size"]
for t in interested_sections:
print "\nSymbols from section:", t
print("\nSymbols from section:", t)
section_total = 0
for key,val in sorted(result[t].items(), key=lambda (k,v): v, reverse=True):
print("%s(%d)"% (key.replace(t + ".", ""), val)),
s = sorted(list(result[t].items()), key=lambda k_v: k_v[0])
# do a secondary sort in order to have consistent order (for diff-ing the output)
for key,val in sorted(s, key=lambda k_v: k_v[1], reverse=True):
print(("%s(%d)"% (key.replace(t + ".", ""), val)), end=' ')
section_total += val
print "\nSection total:",section_total
print("\nSection total:",section_total)
if __name__ == "__main__":
main()

28901
tools/test_idf_size/app.map Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,383 @@
Total sizes:
DRAM .data size: 9324 bytes
DRAM .bss size: 8296 bytes
Used static DRAM: 17620 bytes ( 163116 available, 9.7% used)
Used static IRAM: 38932 bytes ( 92140 available, 29.7% used)
Flash code: 146944 bytes
Flash rodata: 39580 bytes
Total image size:~ 234780 bytes (.bin may be padded larger)
Total sizes:
DRAM .data size: 9324 bytes
DRAM .bss size: 8296 bytes
Used static DRAM: 17620 bytes ( 163116 available, 9.7% used)
Used static IRAM: 38932 bytes ( 92140 available, 29.7% used)
Flash code: 146944 bytes
Flash rodata: 39580 bytes
Total image size:~ 234780 bytes (.bin may be padded larger)
Per-archive contributions to ELF file:
Archive File DRAM .data & .bss IRAM Flash code & rodata Total
liblwip.a 14 3751 0 66978 13936 84679
libc.a 0 0 0 55583 3889 59472
libesp32.a 2635 2375 7758 4814 8133 25715
libfreertos.a 4156 832 12853 0 1545 19386
libspi_flash.a 36 359 7004 886 1624 9909
libsoc.a 660 8 3887 0 3456 8011
libheap.a 1331 4 4376 1218 980 7909
libgcc.a 4 20 104 5488 888 6504
libvfs.a 232 103 0 3770 403 4508
libunity.a 0 121 0 2316 830 3267
libstdc++.a 8 16 0 1827 1062 2913
libnewlib.a 152 272 853 803 86 2166
libpthread.a 16 12 174 774 638 1614
libdriver.a 40 20 0 961 537 1558
liblog.a 8 268 456 396 166 1294
libapp_update.a 0 0 0 123 717 840
libtcpip_adapter.a 0 81 0 180 359 620
libhal.a 0 0 515 0 32 547
libm.a 0 0 92 0 0 92
libmain.a 0 0 0 53 10 63
libcxx.a 0 0 0 11 0 11
libxtensa-debug-module.a 0 0 8 0 0 8
libbootloader_support.a 0 0 0 0 0 0
libcoexist.a 0 0 0 0 0 0
libcore.a 0 0 0 0 0 0
libethernet.a 0 0 0 0 0 0
libmbedtls.a 0 0 0 0 0 0
libmesh.a 0 0 0 0 0 0
libnet80211.a 0 0 0 0 0 0
libnvs_flash.a 0 0 0 0 0 0
libphy.a 0 0 0 0 0 0
libpp.a 0 0 0 0 0 0
librtc.a 0 0 0 0 0 0
libsmartconfig_ack.a 0 0 0 0 0 0
libwpa.a 0 0 0 0 0 0
libwpa2.a 0 0 0 0 0 0
libwpa_supplicant.a 0 0 0 0 0 0
libwps.a 0 0 0 0 0 0
Total sizes:
DRAM .data size: 9324 bytes
DRAM .bss size: 8296 bytes
Used static DRAM: 17620 bytes ( 163116 available, 9.7% used)
Used static IRAM: 38932 bytes ( 92140 available, 29.7% used)
Flash code: 146944 bytes
Flash rodata: 39580 bytes
Total image size:~ 234780 bytes (.bin may be padded larger)
Per-file contributions to ELF file:
Object File DRAM .data & .bss IRAM Flash code & rodata Total
lib_a-vfprintf.o 0 0 0 14193 756 14949
lib_a-svfprintf.o 0 0 0 13834 756 14590
lib_a-svfiprintf.o 0 0 0 9642 1210 10852
lib_a-vfiprintf.o 0 0 0 9933 738 10671
nd6.o 8 1027 0 8427 136 9598
tcp_in.o 0 54 0 8127 916 9097
tasks.o 20 700 5667 0 503 6890
tcp_out.o 0 0 0 5060 1124 6184
sockets.o 0 728 0 4627 824 6179
tcp.o 4 23 0 4290 1384 5701
api_msg.o 0 0 0 3763 1366 5129
dhcp.o 0 8 0 3456 1401 4865
panic.o 2579 5 2145 0 0 4729
esp_err_to_name.o 0 0 0 50 4091 4141
unwind-dw2-fde.o 4 20 0 3316 404 3744
pbuf.o 0 1 0 2453 1161 3615
portasm.o 3084 0 480 0 0 3564
lib_a-dtoa.o 0 0 0 3522 13 3535
etharp.o 0 241 0 2618 658 3517
ip6.o 0 0 0 3212 124 3336
dns.o 0 1292 0 1809 206 3307
spi_flash_rom_patch.o 0 0 2518 0 766 3284
udp.o 2 4 0 3020 216 3242
intr_alloc.o 8 22 726 1749 710 3215
multi_heap.o 857 0 2217 0 0 3074
queue.o 8 56 2569 0 369 3002
flash_ops.o 32 41 2352 99 0 2524
unwind-dw2-xtensa.o 0 0 0 2172 324 2496
rtc_clk.o 660 8 1794 0 0 2462
lib_a-mprec.o 0 0 0 2134 296 2430
vfs.o 192 40 0 1995 132 2359
ip6_frag.o 0 6 0 1905 442 2353
api_lib.o 0 0 0 1425 919 2344
igmp.o 0 12 0 1604 707 2323
dbg_stubs.o 0 2072 32 100 0 2204
vfs_uart.o 40 63 0 1775 271 2149
unity_platform.o 0 13 0 1511 600 2124
esp_timer_esp32.o 8 26 1295 254 526 2109
rtc_periph.o 0 0 0 0 2080 2080
flash_mmap.o 0 296 1298 124 327 2045
heap_caps.o 4 0 1195 188 593 1980
eh_personality.o 0 0 0 1561 384 1945
ip4.o 0 6 0 1664 139 1809
netif.o 0 241 0 1239 287 1767
xtensa_vectors.o 8 0 1697 0 36 1741
cpu_start.o 0 1 806 277 486 1570
clk.o 0 0 67 581 893 1541
timers.o 8 56 1149 0 233 1446
sys_arch.o 0 8 0 1216 222 1446
multi_heap_poisoning.o 470 0 964 0 0 1434
heap_caps_init.o 0 4 0 1030 387 1421
mld6.o 0 4 0 1334 0 1338
cache_utils.o 4 14 836 81 390 1325
raw.o 0 4 0 1087 223 1314
esp_timer.o 8 20 702 429 142 1301
log.o 8 268 456 396 166 1294
system_api.o 0 8 589 0 662 1259
soc_memory_layout.o 0 0 0 0 1239 1239
icmp.o 0 0 0 769 371 1140
xtensa_intr_asm.o 1024 0 51 0 0 1075
port.o 0 16 617 0 369 1002
pthread.o 8 8 174 298 512 1000
icmp6.o 0 0 0 863 127 990
rtc_init.o 0 0 980 0 0 980
unity.o 0 108 0 767 90 965
rtc_time.o 0 0 803 0 137 940
dport_access.o 8 40 539 189 129 905
lib_a-fseeko.o 0 0 0 862 0 862
time.o 0 32 139 691 0 862
tcpip.o 0 16 0 644 191 851
esp_ota_ops.o 0 0 0 123 717 840
periph_ctrl.o 8 0 0 520 256 784
timers.o 0 12 0 638 131 781
partition.o 0 8 0 582 141 731
locks.o 8 0 552 0 84 644
ipc.o 0 36 159 329 104 628
tcpip_adapter_lwip.o 0 81 0 180 359 620
pthread_local_storage.o 8 4 0 476 126 614
inet_chksum.o 0 0 0 580 0 580
crosscore_int.o 8 8 204 126 148 494
netbuf.o 0 0 0 154 326 480
vfs_lwip.o 0 0 0 307 155 462
syscall_table.o 144 240 0 67 0 451
timer.o 16 0 0 112 281 409
int_wdt.o 0 1 87 301 0 389
eh_globals.o 0 16 0 149 193 358
brownout.o 0 0 0 145 191 336
freertos_hooks.o 8 128 43 137 0 316
windowspill_asm.o 0 0 311 0 0 311
cpu_util.o 0 0 310 0 0 310
rtc_module.o 8 8 0 291 0 307
xtensa_context.o 0 0 299 0 0 299
eh_terminate.o 0 0 0 117 141 258
ethernet.o 0 0 0 244 12 256
lib_a-puts.o 0 0 0 182 60 242
dport_panic_highint_hdl. 8 0 234 0 0 242
lib_a-reent.o 0 0 0 232 0 232
lib_a-fopen.o 0 0 0 228 0 228
dhcpserver.o 0 4 0 203 0 207
test_utils.o 0 0 0 38 140 178
lib_a-sprintf.o 0 0 0 167 0 167
cache_err_int.o 0 0 56 98 0 154
list.o 0 0 142 0 0 142
xtensa_intr.o 0 0 104 0 35 139
syscalls.o 0 0 94 45 0 139
si_class_type_info.o 0 0 0 0 136 136
lib_a-assert.o 0 0 0 68 60 128
lib_a-flags.o 0 0 0 127 0 127
lib_a-printf.o 0 0 0 116 0 116
ip4_addr.o 0 0 0 72 40 112
class_type_info.o 0 0 0 0 112 112
lib_a-s_frexp.o 0 0 0 110 0 110
ip.o 0 60 0 50 0 110
memp.o 0 0 0 0 108 108
lib2funcs.o 0 0 104 0 0 104
lib_a-vprintf.o 0 0 0 94 0 94
lib_a-s_fpclassify.o 0 0 92 0 0 92
def.o 0 0 0 91 0 91
lib_a-fiprintf.o 0 0 0 84 0 84
hw_random.o 0 4 74 0 0 78
stack_check.o 0 4 0 32 42 78
clock.o 0 0 72 0 0 72
reent_init.o 0 0 68 0 2 70
app_main.o 0 0 0 53 10 63
state_asm--restore_extra 0 0 62 0 0 62
state_asm--save_extra_nw 0 0 62 0 0 62
uart.o 8 12 0 38 0 58
new_opv.o 0 0 0 0 56 56
xtensa_vector_defaults.o 0 0 46 0 0 46
lib_a-fseek.o 0 0 0 45 0 45
_divdi3.o 0 0 0 0 40 40
_moddi3.o 0 0 0 0 40 40
_udivdi3.o 0 0 0 0 40 40
_umoddi3.o 0 0 0 0 40 40
new_op.o 0 0 0 0 40 40
xtensa_init.o 0 4 32 0 0 36
interrupts--intlevel.o 0 0 0 0 32 32
init.o 0 0 0 27 0 27
wifi_init.o 0 0 0 17 9 26
ip6_addr.o 0 0 0 0 20 20
lib_a-errno.o 0 0 0 10 0 10
int_asm--set_intclear.o 0 0 8 0 0 8
eri.o 0 0 8 0 0 8
cxx_exception_stubs.o 0 0 0 6 0 6
cxx_guards.o 0 0 0 5 0 5
FreeRTOS-openocd.o 4 0 0 0 0 4
eh_term_handler.o 4 0 0 0 0 4
eh_unex_handler.o 4 0 0 0 0 4
bootloader_flash.o 0 0 0 0 0 0
bootloader_sha.o 0 0 0 0 0 0
esp_image_format.o 0 0 0 0 0 0
lib_a-fputs.o 0 0 0 0 0 0
lib_a-snprintf.o 0 0 0 0 0 0
lib_a-strerror.o 0 0 0 0 0 0
lib_a-sysgettod.o 0 0 0 0 0 0
lib_a-u_strerr.o 0 0 0 0 0 0
lib_a-vsnprintf.o 0 0 0 0 0 0
lib_a-xpg_strerror_r.o 0 0 0 0 0 0
coexist_api.o 0 0 0 0 0 0
coexist_arbit.o 0 0 0 0 0 0
coexist_core.o 0 0 0 0 0 0
coexist_dbg.o 0 0 0 0 0 0
coexist_hw.o 0 0 0 0 0 0
coexist_param.o 0 0 0 0 0 0
coexist_timer.o 0 0 0 0 0 0
misc_nvs.o 0 0 0 0 0 0
gpio.o 0 0 0 0 0 0
ets_timer_legacy.o 0 0 0 0 0 0
event_default_handlers.o 0 0 0 0 0 0
event_loop.o 0 0 0 0 0 0
lib_printf.o 0 0 0 0 0 0
phy_init.o 0 0 0 0 0 0
sha.o 0 0 0 0 0 0
wifi_os_adapter.o 0 0 0 0 0 0
emac_dev.o 0 0 0 0 0 0
emac_main.o 0 0 0 0 0 0
event_groups.o 0 0 0 0 0 0
ringbuf.o 0 0 0 0 0 0
_addsubdf3.o 0 0 0 0 0 0
_cmpdf2.o 0 0 0 0 0 0
_divdf3.o 0 0 0 0 0 0
_divsf3.o 0 0 0 0 0 0
_extendsfdf2.o 0 0 0 0 0 0
_fixdfsi.o 0 0 0 0 0 0
_floatdidf.o 0 0 0 0 0 0
_floatdisf.o 0 0 0 0 0 0
_floatsidf.o 0 0 0 0 0 0
_muldf3.o 0 0 0 0 0 0
_popcountsi2.o 0 0 0 0 0 0
ethernetif.o 0 0 0 0 0 0
ethip6.o 0 0 0 0 0 0
wlanif.o 0 0 0 0 0 0
esp_sha256.o 0 0 0 0 0 0
mesh.o 0 0 0 0 0 0
mesh_common.o 0 0 0 0 0 0
mesh_config.o 0 0 0 0 0 0
mesh_main.o 0 0 0 0 0 0
mesh_parent.o 0 0 0 0 0 0
mesh_route.o 0 0 0 0 0 0
mesh_schedule.o 0 0 0 0 0 0
mesh_timer.o 0 0 0 0 0 0
mesh_utilities.o 0 0 0 0 0 0
mesh_wifi.o 0 0 0 0 0 0
ieee80211.o 0 0 0 0 0 0
ieee80211_action.o 0 0 0 0 0 0
ieee80211_action_vendor. 0 0 0 0 0 0
ieee80211_api.o 0 0 0 0 0 0
ieee80211_crypto.o 0 0 0 0 0 0
ieee80211_crypto_ccmp.o 0 0 0 0 0 0
ieee80211_crypto_tkip.o 0 0 0 0 0 0
ieee80211_crypto_wep.o 0 0 0 0 0 0
ieee80211_debug.o 0 0 0 0 0 0
ieee80211_ets.o 0 0 0 0 0 0
ieee80211_hostap.o 0 0 0 0 0 0
ieee80211_ht.o 0 0 0 0 0 0
ieee80211_ie_vendor.o 0 0 0 0 0 0
ieee80211_input.o 0 0 0 0 0 0
ieee80211_ioctl.o 0 0 0 0 0 0
ieee80211_mesh_quick.o 0 0 0 0 0 0
ieee80211_misc.o 0 0 0 0 0 0
ieee80211_nvs.o 0 0 0 0 0 0
ieee80211_output.o 0 0 0 0 0 0
ieee80211_phy.o 0 0 0 0 0 0
ieee80211_power.o 0 0 0 0 0 0
ieee80211_proto.o 0 0 0 0 0 0
ieee80211_regdomain.o 0 0 0 0 0 0
ieee80211_rfid.o 0 0 0 0 0 0
ieee80211_scan.o 0 0 0 0 0 0
ieee80211_sta.o 0 0 0 0 0 0
ieee80211_timer.o 0 0 0 0 0 0
wl_chm.o 0 0 0 0 0 0
wl_cnx.o 0 0 0 0 0 0
nvs_api.o 0 0 0 0 0 0
nvs_item_hash_list.o 0 0 0 0 0 0
nvs_page.o 0 0 0 0 0 0
nvs_pagemanager.o 0 0 0 0 0 0
nvs_storage.o 0 0 0 0 0 0
nvs_types.o 0 0 0 0 0 0
phy.o 0 0 0 0 0 0
phy_chip_v7.o 0 0 0 0 0 0
phy_chip_v7_ana.o 0 0 0 0 0 0
phy_chip_v7_cal.o 0 0 0 0 0 0
esf_buf.o 0 0 0 0 0 0
if_hwctrl.o 0 0 0 0 0 0
lmac.o 0 0 0 0 0 0
pm.o 0 0 0 0 0 0
pm_for_bcn_only_mode.o 0 0 0 0 0 0
pp.o 0 0 0 0 0 0
pp_debug.o 0 0 0 0 0 0
pp_timer.o 0 0 0 0 0 0
rate_control.o 0 0 0 0 0 0
trc.o 0 0 0 0 0 0
wdev.o 0 0 0 0 0 0
bt_bb.o 0 0 0 0 0 0
pm.o 0 0 0 0 0 0
rtc.o 0 0 0 0 0 0
rtc_analog.o 0 0 0 0 0 0
smartconfig_ack.o 0 0 0 0 0 0
gpio_periph.o 0 0 0 0 0 0
rtc_sleep.o 0 0 0 0 0 0
bad_alloc.o 0 0 0 0 0 0
del_op.o 0 0 0 0 0 0
del_opv.o 0 0 0 0 0 0
eh_exception.o 0 0 0 0 0 0
new_handler.o 0 0 0 0 0 0
pure.o 0 0 0 0 0 0
tinfo.o 0 0 0 0 0 0
ap_config.o 0 0 0 0 0 0
common.o 0 0 0 0 0 0
wpa.o 0 0 0 0 0 0
wpa_auth.o 0 0 0 0 0 0
wpa_auth_ie.o 0 0 0 0 0 0
wpa_common.o 0 0 0 0 0 0
wpa_debug.o 0 0 0 0 0 0
wpa_ie.o 0 0 0 0 0 0
wpa_main.o 0 0 0 0 0 0
wpabuf.o 0 0 0 0 0 0
wpas_glue.o 0 0 0 0 0 0
wpa2_internal.o 0 0 0 0 0 0
os_xtensa.o 0 0 0 0 0 0
wps_internal.o 0 0 0 0 0 0
Total sizes:
DRAM .data size: 9324 bytes
DRAM .bss size: 8296 bytes
Used static DRAM: 17620 bytes ( 163116 available, 9.7% used)
Used static IRAM: 38932 bytes ( 92140 available, 29.7% used)
Flash code: 146944 bytes
Flash rodata: 39580 bytes
Total image size:~ 234780 bytes (.bin may be padded larger)
Symbols within the archive: libdriver.a (Not all symbols may be reported)
Symbols from section: .dram0.data
timer_spinlock(16) periph_spinlock(8) s_rtc_isr_handler_list_lock(8) uart_selectlock(8)
Section total: 40
Symbols from section: .dram0.bss
p_uart_obj(12) s_rtc_isr_handle(4) s_rtc_isr_handler_list(4)
Section total: 20
Symbols from section: .iram0.text
Section total: 0
Symbols from section: .iram0.vectors
Section total: 0
Symbols from section: .flash.text
get_clk_en_mask(211) get_rst_en_mask(157) timer_group_intr_enable(112) rtc_isr(86) periph_module_enable(78) rtc_isr_ensure_installed(75) rtc_gpio_force_hold_dis_all(65) rtc_isr_register(65) is_wifi_clk_peripheral(28) uart_set_select_notif_callback(26) get_rst_en_reg(25) get_clk_en_reg(21) uart_get_selectlock(12)
Section total: 961
Symbols from section: .flash.rodata
str1.4(249) get_clk_en_mask(128) get_rst_en_mask(128) __FUNCTION__$5441(24) TG(8)
Section total: 537
Total sizes:
DRAM .data size: 0 bytes
DRAM .bss size: 0 bytes

12
tools/test_idf_size/test.sh Executable file
View file

@ -0,0 +1,12 @@
#! /bin/bash
{ coverage debug sys \
&& coverage erase &> output \
&& coverage run -a $IDF_PATH/tools/idf_size.py app.map &>> output \
&& coverage run -a $IDF_PATH/tools/idf_size.py --archives app.map &>> output \
&& coverage run -a $IDF_PATH/tools/idf_size.py --files app.map &>> output \
&& coverage run -a $IDF_PATH/tools/idf_size.py --archive_details libdriver.a app.map &>> output \
&& coverage run -a $IDF_PATH/tools/test_idf_size/test_idf_size.py &>> output \
&& diff output expected_output \
&& coverage report \
; } || { echo 'The test for idf_size has failed. Please examine the artifacts.' ; exit 1; }

View file

@ -0,0 +1,37 @@
#!/usr/bin/env python
#
# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
sys.path.append('..')
import idf_size
if __name__ == "__main__":
try:
idf_size.scan_to_header([], 'test')
except RuntimeError:
pass
try:
idf_size.load_memory_config(["Memory Configuration"])
pass
except RuntimeError:
pass
try:
idf_size.print_summary({"iram0_0_seg": {"length":0}, "dram0_0_seg": {"length":0}}, {})
except ZeroDivisionError:
pass