Esempio n. 1
0
// TODO: all called funcs should be in IRAM to work with disabled flash cache
static void * esp_dbg_stubs_data_alloc(uint32_t size)
{
    ESP_LOGV(TAG, "%s %d", __func__, size);
	void *p = malloc(size);
    ESP_LOGV(TAG, "%s EXIT %p", __func__, p);
    return p;
}
Esempio n. 2
0
esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
    if (card->host.command_timeout_ms != 0) {
        cmd->timeout_ms = card->host.command_timeout_ms;
    } else if (cmd->timeout_ms == 0) {
        cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS;
    }

    int slot = card->host.slot;
    ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d",
            slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms);
    esp_err_t err = (*card->host.do_transaction)(slot, cmd);
    if (err != 0) {
        ESP_LOGD(TAG, "cmd=%d, sdmmc_req_run returned 0x%x", cmd->opcode, err);
        return err;
    }
    int state = MMC_R1_CURRENT_STATE(cmd->response);
    ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
               cmd->response[0],
               cmd->response[1],
               cmd->response[2],
               cmd->response[3],
               cmd->error,
               state);
    return cmd->error;
}
Esempio n. 3
0
void Subscription::feed_data(MQTT_Packet data) {
	if(on_received == nullptr) return;
	if(data.topic.length() < topic.length()) return;

	std::string topicRest;

	int i=0;
	while(true) {
		if(topic.at(i) == '#') {
			topicRest = std::string(data.topic.data() + i, data.topic.length() - i);
			break;
		}
		if(topic.at(i) != data.topic.at(i)) return;

		i++;

		if(i == topic.length()) {
			if(i != data.topic.length())
				return;
			else
				break;
		}
	}

	ESP_LOGV(mqtt_tag, "Topic %s matched! (Topic-Rest:%s)", topic.data(), topicRest.data());
	on_received({topicRest, data.data});
}
Esempio n. 4
0
esp_err_t WL_Ext_Perf::erase_sector_fit(uint32_t start_sector, uint32_t count)
{
    ESP_LOGV(TAG, "%s begin, start_sector = 0x%08x, count = %i", __func__, start_sector, count);
    // This method works with one flash device sector and able to erase "count" of fatfs sectors from this sector
    esp_err_t result = ESP_OK;

    uint32_t pre_check_start = start_sector % this->size_factor;


    for (int i = 0; i < this->size_factor; i++) {
        if ((i < pre_check_start) || (i >= count + pre_check_start)) {
            result = this->read(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
            WL_EXT_RESULT_CHECK(result);
        }
    }

    result = WL_Flash::erase_sector(start_sector / this->size_factor); // erase comlete flash sector
    WL_EXT_RESULT_CHECK(result);
    // And write back only data that should not be erased...
    for (int i = 0; i < this->size_factor; i++) {
        if ((i < pre_check_start) || (i >= count + pre_check_start)) {
            result = this->write(start_sector / this->size_factor * this->flash_sector_size + i * this->fat_sector_size, &this->sector_buffer[i * this->fat_sector_size / sizeof(uint32_t)], this->fat_sector_size);
            WL_EXT_RESULT_CHECK(result);
        }
    }
    return ESP_OK;
}
Esempio n. 5
0
static void spiffs_api_check(spiffs *fs, spiffs_check_type type, 
                            spiffs_check_report report, uint32_t arg1, uint32_t arg2)
{
    static const char * spiffs_check_type_str[3] = {
        "LOOKUP",
        "INDEX",
        "PAGE"
    };

    static const char * spiffs_check_report_str[7] = {
        "PROGRESS",
        "ERROR",
        "FIX INDEX",
        "FIX LOOKUP",
        "DELETE ORPHANED INDEX",
        "DELETE PAGE",
        "DELETE BAD FILE"
    };

    if (report != SPIFFS_CHECK_PROGRESS) {
        ESP_LOGE(TAG, "CHECK: type:%s, report:%s, %x:%x", spiffs_check_type_str[type], 
                              spiffs_check_report_str[report], arg1, arg2);
    } else {
        ESP_LOGV(TAG, "CHECK PROGRESS: report:%s, %x:%x", 
                              spiffs_check_report_str[report], arg1, arg2);
    }
}
Esempio n. 6
0
/**
 * @brief Take a semaphore.
 * Take a semaphore but return if we haven't obtained it in the given period of milliseconds.
 * @param [in] timeoutMs Timeout in milliseconds.
 * @param [in] owner The new owner (for debugging)
 * @return True if we took the semaphore.
 */
bool FreeRTOS::Semaphore::take(uint32_t timeoutMs, std::string owner) {
	ESP_LOGV(LOG_TAG, "Semaphore taking: %s for %s", toString().c_str(), owner.c_str());
	bool rc = false;
	if (m_usePthreads) {
		assert(false);  // We apparently don't have a timed wait for pthreads.
	} else {
		rc = ::xSemaphoreTake(m_semaphore, timeoutMs / portTICK_PERIOD_MS) == pdTRUE;
	}
	m_owner = owner;
	if (rc) {
		ESP_LOGV(LOG_TAG, "Semaphore taken:  %s", toString().c_str());
	} else {
		ESP_LOGE(LOG_TAG, "Semaphore NOT taken:  %s", toString().c_str());
	}
	return rc;
} // Semaphore::take
Esempio n. 7
0
static int vfs_semihost_open(void* ctx, const char * path, int flags, int mode)
{
    int fd = -1, host_err = 0;
    char *host_path;
    vfs_semihost_ctx_t *semi_ctx = ctx;

    ESP_LOGV(TAG, "%s: %p '%s 0x%x 0x%x'", __func__, semi_ctx, path, flags, mode);
    if (ctx_uses_abspath(semi_ctx)) {
        flags |= ESP_O_SEMIHOST_ABSPATH;
        host_path = malloc(strlen(semi_ctx->host_path)+strlen(path)+1);
        if(host_path == NULL) {
            errno = ENOMEM;
            return -1;
        }
        strcpy(host_path, semi_ctx->host_path);
        strcat(host_path, path);
    } else {
        host_path = (char *)path;
    }
    fd = generic_syscall(SYS_OPEN, (int)host_path, strlen(host_path), flags, mode, &host_err);
    if (ctx_uses_abspath(semi_ctx)) {
        free(host_path);
    }
    if (fd == -1) {
        errno = host_err;
    }
    return fd;
}
Esempio n. 8
0
void esp_dbg_stubs_init()
{
    s_dbg_stubs_ctl_data.tramp_addr     = (uint32_t)s_stub_code_buf;
    s_dbg_stubs_ctl_data.min_stack_addr = (uint32_t)s_stub_min_stack;
    s_dbg_stubs_ctl_data.data_alloc     = (uint32_t)esp_dbg_stubs_data_alloc;
    s_dbg_stubs_ctl_data.data_free      = (uint32_t)esp_dbg_stubs_data_free;

    s_stub_entry[ESP_DBG_STUB_CONTROL_DATA] = (uint32_t)&s_dbg_stubs_ctl_data;
    eri_write(ESP_DBG_STUBS_TRAX_REG, (uint32_t)s_stub_entry);
    ESP_LOGV(TAG, "%s stubs %x", __func__, eri_read(ESP_DBG_STUBS_TRAX_REG));
}
Esempio n. 9
0
static off_t vfs_semihost_lseek(void* ctx, int fd, off_t size, int mode)
{
    int ret = -1, host_err = 0;

    ESP_LOGV(TAG, "%s: %d %ld %d", __func__, fd, size, mode);
    ret = generic_syscall(SYS_SEEK, fd, size, mode, 0, &host_err);
    if (ret == -1) {
        errno = host_err;
    }
    return (off_t)ret;
}
Esempio n. 10
0
static int vfs_semihost_close(void* ctx, int fd)
{
    int ret = -1, host_err = 0;

    ESP_LOGV(TAG, "%s: %d", __func__, fd);
    ret = generic_syscall(SYS_CLOSE, fd, 0, 0, 0, &host_err);
    if (ret == -1) {
        errno = host_err;
    }
    return ret;
}
Esempio n. 11
0
static esp_err_t sdmmc_send_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
{
    int slot = card->host.slot;
    ESP_LOGV(TAG, "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d",
            slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen);
    esp_err_t err = (*card->host.do_transaction)(slot, cmd);
    if (err != 0) {
        ESP_LOGD(TAG, "sdmmc_req_run returned 0x%x", err);
        return err;
    }
    int state = MMC_R1_CURRENT_STATE(cmd->response);
    ESP_LOGV(TAG, "cmd response %08x %08x %08x %08x err=0x%x state=%d",
               cmd->response[0],
               cmd->response[1],
               cmd->response[2],
               cmd->response[3],
               cmd->error,
               state);
    return cmd->error;
}
Esempio n. 12
0
/**
 * @brief Wait for a semaphore to be released by trying to take it and
 * then releasing it again.
 * @param [in] owner A debug tag.
 * @return The value associated with the semaphore.
 */
uint32_t FreeRTOS::Semaphore::wait(std::string owner) {
	ESP_LOGV(LOG_TAG, ">> wait: Semaphore waiting: %s for %s", toString().c_str(), owner.c_str());

	if (m_usePthreads) {
		pthread_mutex_lock(&m_pthread_mutex);
	} else {
		xSemaphoreTake(m_semaphore, portMAX_DELAY);
	}

	m_owner = owner;

	if (m_usePthreads) {
		pthread_mutex_unlock(&m_pthread_mutex);
	} else {
		xSemaphoreGive(m_semaphore);
	}

	ESP_LOGV(LOG_TAG, "<< wait: Semaphore released: %s", toString().c_str());
	m_owner = std::string("<N/A>");
	return m_value;
} // wait
Esempio n. 13
0
static ssize_t vfs_semihost_read(void* ctx, int fd, void* data, size_t size)
{
    int host_err = 0;
    size_t ret = -1;

    ESP_LOGV(TAG, "%s: %d %u bytes", __func__, fd, size);
    ret = generic_syscall(SYS_READ, fd, (int)data, size, 0, &host_err);
    if (ret == -1) {
        errno = host_err;
    }
    return (ssize_t)ret;
}
Esempio n. 14
0
/**
 * @brief Give a semaphore.
 * The Semaphore is given.
 */
void FreeRTOS::Semaphore::give() {
	ESP_LOGV(LOG_TAG, "Semaphore giving: %s", toString().c_str());
	if (m_usePthreads) {
		pthread_mutex_unlock(&m_pthread_mutex);
	} else {
		xSemaphoreGive(m_semaphore);
	}
// #ifdef ARDUINO_ARCH_ESP32
// 	FreeRTOS::sleep(10);
// #endif

	m_owner = std::string("<N/A>");
} // Semaphore::give
Esempio n. 15
0
esp_err_t esp_flash_encrypt_check_and_update(void)
{
    uint32_t efuse_blk0 = REG_READ(EFUSE_BLK0_RDATA0_REG);
    ESP_LOGV(TAG, "efuse_blk0 raw value %08x", efuse_blk0);
    uint32_t flash_crypt_cnt = (efuse_blk0 & EFUSE_RD_FLASH_CRYPT_CNT_M) >> EFUSE_RD_FLASH_CRYPT_CNT_S;
    bool flash_crypt_wr_dis = efuse_blk0 & EFUSE_WR_DIS_FLASH_CRYPT_CNT;
    ESP_LOGV(TAG, "efuse FLASH_CRYPT_CNT 0x%x WR_DIS_FLASH_CRYPT_CNT 0x%x", flash_crypt_cnt, flash_crypt_wr_dis);

    if (__builtin_parity(flash_crypt_cnt) == 1) {
        /* Flash is already encrypted */
        int left = (7 - __builtin_popcount(flash_crypt_cnt)) / 2;
        if (flash_crypt_wr_dis) {
            left = 0; /* can't update FLASH_CRYPT_CNT, no more flashes */
        }
        ESP_LOGI(TAG, "flash encryption is enabled (%d plaintext flashes left)", left);
        return ESP_OK;
    }
    else {
        /* Flash is not encrypted, so encrypt it! */
        return encrypt_flash_contents(flash_crypt_cnt, flash_crypt_wr_dis);
    }
}
Esempio n. 16
0
static esp_err_t load_cal_data_from_nvs_handle(nvs_handle handle,
        esp_phy_calibration_data_t* out_cal_data)
{
    esp_err_t err;
    uint32_t cal_data_version;
    err = nvs_get_u32(handle, PHY_CAL_VERSION_KEY, &cal_data_version);
    if (err != ESP_OK) {
        ESP_LOGD(TAG, "%s: failed to get cal_version (0x%x)", __func__, err);
        return err;
    }
    uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
    ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
    if (cal_data_version != cal_format_version) {
        ESP_LOGD(TAG, "%s: expected calibration data format %d, found %d",
                __func__, cal_format_version, cal_data_version);
        return ESP_FAIL;
    }
    uint8_t cal_data_mac[6];
    size_t length = sizeof(cal_data_mac);
    err = nvs_get_blob(handle, PHY_CAL_MAC_KEY, cal_data_mac, &length);
    if (err != ESP_OK) {
        ESP_LOGD(TAG, "%s: failed to get cal_mac (0x%x)", __func__, err);
        return err;
    }
    if (length != sizeof(cal_data_mac)) {
        ESP_LOGD(TAG, "%s: invalid length of cal_mac (%d)", __func__, length);
        return ESP_ERR_INVALID_SIZE;
    }
    uint8_t sta_mac[6];
    esp_efuse_mac_get_default(sta_mac);
    if (memcmp(sta_mac, cal_data_mac, sizeof(sta_mac)) != 0) {
        ESP_LOGE(TAG, "%s: calibration data MAC check failed: expected " \
                MACSTR ", found " MACSTR,
                __func__, MAC2STR(sta_mac), MAC2STR(cal_data_mac));
        return ESP_FAIL;
    }
    length = sizeof(*out_cal_data);
    err = nvs_get_blob(handle, PHY_CAL_DATA_KEY, out_cal_data, &length);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "%s: failed to get cal_data(0x%x)", __func__, err);
        return err;
    }
    if (length != sizeof(*out_cal_data)) {
        ESP_LOGD(TAG, "%s: invalid length of cal_data (%d)", __func__, length);
        return ESP_ERR_INVALID_SIZE;
    }
    return ESP_OK;
}
Esempio n. 17
0
static esp_err_t store_cal_data_to_nvs_handle(nvs_handle handle,
        const esp_phy_calibration_data_t* cal_data)
{
    esp_err_t err;
    uint32_t cal_format_version = phy_get_rf_cal_version() & (~BIT(16));
    ESP_LOGV(TAG, "phy_get_rf_cal_version: %d\n", cal_format_version);
    err = nvs_set_u32(handle, PHY_CAL_VERSION_KEY, cal_format_version);
    if (err != ESP_OK) {
        return err;
    }
    uint8_t sta_mac[6];
    esp_efuse_mac_get_default(sta_mac);
    err = nvs_set_blob(handle, PHY_CAL_MAC_KEY, sta_mac, sizeof(sta_mac));
    if (err != ESP_OK) {
        return err;
    }
    err = nvs_set_blob(handle, PHY_CAL_DATA_KEY, cal_data, sizeof(*cal_data));
    return err;
}
Esempio n. 18
0
static void set_cache_and_start_app(
    uint32_t drom_addr,
    uint32_t drom_load_addr,
    uint32_t drom_size,
    uint32_t irom_addr,
    uint32_t irom_load_addr,
    uint32_t irom_size,
    uint32_t entry_addr)
{
    ESP_LOGD(TAG, "configure drom and irom and start");
    Cache_Read_Disable( 0 );
    Cache_Flush( 0 );

    /* Clear the MMU entries that are already set up,
       so the new app only has the mappings it creates.
    */
    for (int i = 0; i < DPORT_FLASH_MMU_TABLE_SIZE; i++) {
        DPORT_PRO_FLASH_MMU_TABLE[i] = DPORT_FLASH_MMU_TABLE_INVALID_VAL;
    }

    uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k
    ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count );
    int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count );
    ESP_LOGV(TAG, "rc=%d", rc );
    rc = cache_flash_mmu_set( 1, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count );
    ESP_LOGV(TAG, "rc=%d", rc );
    uint32_t irom_page_count = (irom_size + 64*1024 - 1) / (64*1024); // round up to 64k
    ESP_LOGV(TAG, "i mmu set paddr=%08x vaddr=%08x size=%d n=%d", irom_addr & 0xffff0000, irom_load_addr & 0xffff0000, irom_size, irom_page_count );
    rc = cache_flash_mmu_set( 0, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count );
    ESP_LOGV(TAG, "rc=%d", rc );
    rc = cache_flash_mmu_set( 1, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count );
    ESP_LOGV(TAG, "rc=%d", rc );
    DPORT_REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 );
    DPORT_REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 );
    Cache_Read_Enable( 0 );

    // Application will need to do Cache_Flush(1) and Cache_Read_Enable(1)

    ESP_LOGD(TAG, "start: 0x%08x", entry_addr);
    typedef void (*entry_t)(void) __attribute__((noreturn));
    entry_t entry = ((entry_t) entry_addr);

    // TODO: we have used quite a bit of stack at this point.
    // use "movsp" instruction to reset stack back to where ROM stack starts.
    (*entry)();
}
Esempio n. 19
0
esp_err_t esp_phy_rf_init(const esp_phy_init_data_t* init_data,
        esp_phy_calibration_mode_t mode, esp_phy_calibration_data_t* calibration_data)
{
    assert((s_phy_rf_init_count <= 1) && (s_phy_rf_init_count >= 0));

    _lock_acquire(&s_phy_rf_init_lock);
    if (s_phy_rf_init_count == 0) {
        // Enable WiFi/BT common peripheral clock
        periph_module_enable(PERIPH_WIFI_BT_COMMON_MODULE);
        ESP_LOGV(TAG, "register_chipv7_phy, init_data=%p, cal_data=%p, mode=%d",
                init_data, calibration_data, mode);
        phy_set_wifi_mode_only(0);
        register_chipv7_phy(init_data, calibration_data, mode);
        coex_bt_high_prio();
    } else {
#if CONFIG_SW_COEXIST_ENABLE
        coex_init();
#endif
    }
    s_phy_rf_init_count++;
    _lock_release(&s_phy_rf_init_lock);
    return ESP_OK;
}
Esempio n. 20
0
static void esp_dbg_stubs_data_free(void *addr)
{
    ESP_LOGV(TAG, "%s %p", __func__, addr);
    free(addr);
    ESP_LOGV(TAG, "%s EXIT %p", __func__, addr);
}
Esempio n. 21
0
esp_err_t WL_Ext_Perf::erase_range(size_t start_address, size_t size)
{
    esp_err_t result = ESP_OK;
    if ((start_address % this->fat_sector_size) != 0) {
        result = ESP_ERR_INVALID_ARG;
    }
    if (((size % this->fat_sector_size) != 0) || (size == 0)) {
        result = ESP_ERR_INVALID_ARG;
    }
    WL_EXT_RESULT_CHECK(result);

    // The range to erase could be allocated in any possible way
    // ---------------------------------------------------------
    // |       |       |       |       |
    // |0|0|x|x|x|x|x|x|x|x|x|x|x|x|0|0|
    // | pre   | rest  | rest  | post  |  <- check ranges
    //
    // Pre check - the data that is not fit to the full sector at the begining of the erased block
    // Post check - the data that are not fit to the full sector at the end of the erased block
    // rest - data that are fit to the flash device sector at the middle of the erased block
    //
    // In case of pre and post check situations the data of the non erased area have to be readed first and then
    // stored back.
    // For the rest area this operation not needed because complete flash device sector will be erased.

    ESP_LOGV(TAG, "%s begin, addr = 0x%08x, size = %i", __func__, start_address, size);
    // Calculate pre check values
    uint32_t pre_check_start = (start_address / this->fat_sector_size) % this->size_factor;
    uint32_t sectors_count = size / this->fat_sector_size;
    uint32_t pre_check_count = (this->size_factor - pre_check_start);
    if (pre_check_count > sectors_count) {
        pre_check_count = sectors_count;
    }

    // Calculate post ckeck
    uint32_t post_check_count = (sectors_count - pre_check_count) % this->size_factor;
    uint32_t post_check_start = ((start_address + size - post_check_count * this->fat_sector_size) / this->fat_sector_size);

    // Calculate rest
    uint32_t rest_check_count = sectors_count - pre_check_count - post_check_count;
    if ((pre_check_count == this->size_factor) && (0 == pre_check_start)) {
        rest_check_count+=this->size_factor;
        pre_check_count = 0;
    }
    uint32_t rest_check_start = start_address + pre_check_count * this->fat_sector_size;

    // Here we will clear pre_check_count amount of sectors
    if (pre_check_count != 0) {
        result = this->erase_sector_fit(start_address / this->fat_sector_size, pre_check_count);
        WL_EXT_RESULT_CHECK(result);
    }
    ESP_LOGV(TAG, "%s rest_check_start = %i, pre_check_count=%i, rest_check_count=%i, post_check_count=%i\n", __func__, rest_check_start, pre_check_count, rest_check_count, post_check_count);
    if (rest_check_count > 0) {
        rest_check_count = rest_check_count / this->size_factor;
        size_t start_sector = rest_check_start / this->flash_sector_size;
        for (size_t i = 0; i < rest_check_count; i++) {
            result = WL_Flash::erase_sector(start_sector + i);
            WL_EXT_RESULT_CHECK(result);
        }
    }
    if (post_check_count != 0) {
        result = this->erase_sector_fit(post_check_start, post_check_count);
        WL_EXT_RESULT_CHECK(result);
    }
    return ESP_OK;
}
Esempio n. 22
0
esp_err_t sdmmc_card_init(const sdmmc_host_t* config,
        sdmmc_card_t* card)
{
    ESP_LOGD(TAG, "%s", __func__);
    memset(card, 0, sizeof(*card));
    memcpy(&card->host, config, sizeof(*config));
    esp_err_t err = sdmmc_send_cmd_go_idle_state(card);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "%s: go_idle_state (1) returned 0x%x", __func__, err);
        return err;
    }
    ets_delay_us(10000);
    uint32_t host_ocr = get_host_ocr(config->io_voltage);
    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 SDHC/SDXC card");
    } else {
        ESP_LOGE(TAG, "%s: send_if_cond (1) returned 0x%x", __func__, err);
        return err;
    }
    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;
    }
    host_ocr &= card->ocr;
    ESP_LOGD(TAG, "sdmmc_card_init: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr);
    err = sddmc_send_cmd_all_send_cid(card, &card->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;
    }
    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;
    }
    err = sdmmc_send_cmd_select_card(card);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "%s: select_card returned 0x%x", __func__, err);
        return err;
    }
    if ((card->ocr & SD_OCR_SDHC_CAP) == 0) {
        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;
        }
    }
    err = sdmmc_send_cmd_send_scr(card, &card->scr);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "%s: send_scr returned 0x%x", __func__, err);
        return err;
    }
    if ((config->flags & SDMMC_HOST_FLAG_4BIT) &&
        (card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT)) {
        ESP_LOGD(TAG, "switching to 4-bit bus mode");
        err = sdmmc_send_cmd_set_bus_width(card, 4);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "set_bus_width failed");
            return err;
        }
        err = (*config->set_bus_width)(config->slot, 4);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "slot->set_bus_width failed");
            return err;
        }
        uint32_t status;
        err = sdmmc_send_cmd_stop_transmission(card, &status);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "stop_transmission failed (0x%x)", err);
            return err;
        }
    }
    uint32_t status = 0;
    while (!(status & MMC_R1_READY_FOR_DATA)) {
        // TODO: add some timeout here
        uint32_t count = 0;
        err = sdmmc_send_cmd_send_status(card, &status);
        if (err != ESP_OK) {
            return err;
        }
        if (++count % 10 == 0) {
            ESP_LOGV(TAG, "waiting for card to become ready (%d)", count);
        }
    }
    if (config->max_freq_khz >= SDMMC_FREQ_HIGHSPEED &&
        card->csd.tr_speed / 1000 >= SDMMC_FREQ_HIGHSPEED) {
        ESP_LOGD(TAG, "switching to HS bus mode");
        err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_HIGHSPEED);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
            return err;
        }
    } else if (config->max_freq_khz >= SDMMC_FREQ_DEFAULT &&
        card->csd.tr_speed / 1000 >= SDMMC_FREQ_DEFAULT) {
        ESP_LOGD(TAG, "switching to DS bus mode");
        err = (*config->set_card_clk)(config->slot, SDMMC_FREQ_DEFAULT);
        if (err != ESP_OK) {
            ESP_LOGE(TAG, "failed to switch peripheral to HS bus mode");
            return err;
        }
    }
    sdmmc_scr_t scr_tmp;
    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, "data check fail!");
        return ESP_ERR_INVALID_RESPONSE;
    }
    return ESP_OK;
}