Merge branch 'master' into feature/cmake
This commit is contained in:
commit
5fbfd20f10
35 changed files with 31787 additions and 1546 deletions
|
@ -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:
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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, \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
302
components/sdmmc/sdmmc_common.c
Normal file
302
components/sdmmc/sdmmc_common.c
Normal 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;
|
||||
}
|
135
components/sdmmc/sdmmc_common.h
Normal file
135
components/sdmmc/sdmmc_common.h
Normal 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);
|
117
components/sdmmc/sdmmc_init.c
Normal file
117
components/sdmmc/sdmmc_init.c
Normal 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
352
components/sdmmc/sdmmc_io.c
Normal 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);
|
||||
}
|
233
components/sdmmc/sdmmc_mmc.c
Normal file
233
components/sdmmc/sdmmc_mmc.c
Normal 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
346
components/sdmmc/sdmmc_sd.c
Normal 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;
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
--------
|
||||
|
||||
|
|
|
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
|
@ -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__':
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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!"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
28901
tools/test_idf_size/app.map
Normal file
File diff suppressed because it is too large
Load diff
383
tools/test_idf_size/expected_output
Normal file
383
tools/test_idf_size/expected_output
Normal 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
12
tools/test_idf_size/test.sh
Executable 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; }
|
37
tools/test_idf_size/test_idf_size.py
Normal file
37
tools/test_idf_size/test_idf_size.py
Normal 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
|
Loading…
Reference in a new issue