mirror of
https://github.com/jomjol/AI-on-the-edge-device.git
synced 2025-12-07 20:16:55 +03:00
Compare commits
1 Commits
Update-tfl
...
v15.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d6f731d5a |
3
.github/workflows/build.yml
vendored
3
.github/workflows/build.yml
vendored
@@ -416,6 +416,7 @@ jobs:
|
||||
echo "Updating index and manifest file..."
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/index.html
|
||||
sed -i 's/$VERSION/${{ steps.last_release.outputs.tag_name }}/g' docs/manifest.json
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v4
|
||||
|
||||
@@ -426,4 +427,4 @@ jobs:
|
||||
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
uses: actions/deploy-pages@v3 # Note: v4 does not work!
|
||||
|
||||
24
Changelog.md
24
Changelog.md
@@ -1,6 +1,12 @@
|
||||
## [15.5.0] - 2024-02-02
|
||||
## [15.6.0] - 2024-02-09
|
||||
|
||||
### Changes
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.5.0...v15.6.0)
|
||||
|
||||
#### Fixed
|
||||
|
||||
* Fixed issues with the SD-Card initialization
|
||||
|
||||
## [15.5.0] - 2024-02-02
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.4.0...v15.5.0)
|
||||
|
||||
@@ -20,8 +26,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [15.4.0] - 2023-12-22
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.4.0)
|
||||
|
||||
#### Changed
|
||||
@@ -47,13 +51,10 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
* Minor html response bugfix
|
||||
|
||||
- Memory leakage (MQTT)
|
||||
|
||||
|
||||
|
||||
## [15.3.0] - 2023-07-22
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.3.0...v15.2.4)
|
||||
|
||||
#### Changed
|
||||
@@ -63,13 +64,8 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
- ana-cont_1207_s2_q.tflite
|
||||
- dig-cont_0620_s3_q.tflite
|
||||
|
||||
|
||||
|
||||
|
||||
## [15.2.4] - 2023-05-02
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.2.1...v15.2.4)
|
||||
|
||||
#### Changed
|
||||
@@ -93,8 +89,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [15.2.0] - 2023-04-23
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.1...v15.2.0)
|
||||
|
||||
#### Added
|
||||
@@ -124,8 +118,6 @@ For a full list of changes see [Full list of changes](https://github.com/jomjol/
|
||||
|
||||
## [15.1.1] - 2023-03-23
|
||||
|
||||
### Changes
|
||||
|
||||
For a full list of changes see [Full list of changes](https://github.com/jomjol/AI-on-the-edge-device/compare/v15.1.0...v15.1.1)
|
||||
|
||||
#### Added
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
set(srcs "diskio/diskio_mh.c"
|
||||
"diskio/diskio_rawflash_mh.c"
|
||||
"diskio/diskio_sdmmc_mh.c"
|
||||
"diskio/diskio_wl_mh.c"
|
||||
"src/ff_mh.c"
|
||||
"port/freertos/ffsystem_mh.c"
|
||||
"src/ffunicode_mh.c"
|
||||
"vfs/vfs_fat_mh.c"
|
||||
"vfs/vfs_fat_sdmmc_mh.c"
|
||||
"vfs/vfs_fat_spiflash_mh.c")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
INCLUDE_DIRS "." diskio vfs src
|
||||
REQUIRES wear_levelling esp-sdmmc
|
||||
PRIV_REQUIRES vfs spi_flash
|
||||
)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
@@ -1,278 +0,0 @@
|
||||
# AIOTED related changes, see https://github.com/jomjol/AI-on-the-edge-device/pull/2781
|
||||
These files/folders were copied from `framework-espidf@3.50002.230601/components/` and adapted to our own needs.
|
||||
Since not every SD/MMC was recognized and this was due to the implementation of ATA trim support, this was revised.
|
||||
Furthermore, files that we don't need were deleted from it.
|
||||
|
||||
## The most relevant changes are:
|
||||
### fatfs/diskio/diskio_sdmmc.c
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff), at lines 106 to 110 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
|
||||
### fatfs/src/ff.c
|
||||
added:
|
||||
```c
|
||||
#include "sdmmc_cmd.h"
|
||||
```
|
||||
|
||||
static FRESULT remove_chain(FFOBJID* obj, DWORD clst, DWORD pclst), at lines 1437 to 1454 changed from:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
} else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
to:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if(FF_FS_EXFAT || FF_CAN_TRIM){
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
}
|
||||
else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
}
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 5946 to 5949 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 6175 to 6178 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### sdmmc/sdmmc_cmd.c
|
||||
added:
|
||||
```c
|
||||
int FF_CAN_TRIM = 0;
|
||||
```
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card), at lines 630 to 636 changed from:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
to:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
|
||||
### sdmmc/include/sdmmc_cmd.h
|
||||
|
||||
added:
|
||||
```c
|
||||
extern int FF_CAN_TRIM;
|
||||
```
|
||||
|
||||
# Espressif IoT Development Framework
|
||||
|
||||
* [中文版](./README_CN.md)
|
||||
|
||||
ESP-IDF is the development framework for Espressif SoCs supported on Windows, Linux and macOS.
|
||||
|
||||
# ESP-IDF Release Support Schedule
|
||||
|
||||

|
||||
|
||||
- Please read [the support policy](SUPPORT_POLICY.md) and [the documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) for more information about ESP-IDF versions.
|
||||
- Please see the [End-of-Life Advisories](https://www.espressif.com/en/support/documents/advisories?keys=&field_type_of_advisory_tid%5B%5D=817) for information about ESP-IDF releases with discontinued support.
|
||||
|
||||
# ESP-IDF Release and SoC Compatibility
|
||||
|
||||
The following table shows ESP-IDF support of Espressif SoCs where ![alt text][preview] and ![alt text][supported] denote preview status and support, respectively. The preview support is usually limited in time and intended for beta versions of chips. Please use an ESP-IDF release where the desired SoC is already supported.
|
||||
|
||||
|Chip | v4.1 | v4.2 | v4.3 | v4.4 | v5.0 | |
|
||||
|:----------- |:---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:------------------------------------------------------------------------------------ |
|
||||
|ESP32 |![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S2 | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-C3 | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S3 | | | | ![alt text][supported] | ![alt text][supported] | [Announcement](https://www.espressif.com/en/news/ESP32_S3) |
|
||||
|ESP32-C2 | | | | | ![alt text][supported] | [Announcement](https://blog.espressif.com/esp32-c2-and-why-it-matter-s-bcf4d7d0b2c6) |
|
||||
|ESP32-H2 | | | | ![alt text][preview] | ![alt text][preview] | [Announcement](https://www.espressif.com/en/news/ESP32_H2) |
|
||||
|
||||
[supported]: https://img.shields.io/badge/-supported-green "supported"
|
||||
[preview]: https://img.shields.io/badge/-preview-orange "preview"
|
||||
|
||||
Espressif SoCs released before 2016 (ESP8266 and ESP8285) are supported by [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead.
|
||||
|
||||
# Developing With ESP-IDF
|
||||
|
||||
## Setting Up ESP-IDF
|
||||
|
||||
See https://idf.espressif.com/ for links to detailed instructions on how to set up the ESP-IDF depending on chip you use.
|
||||
|
||||
**Note:** Each SoC series and each ESP-IDF release has its own documentation. Please see Section [Versions](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) on how to find documentation and how to checkout specific release of ESP-IDF.
|
||||
|
||||
### Non-GitHub forks
|
||||
|
||||
ESP-IDF uses relative locations as its submodules URLs ([.gitmodules](.gitmodules)). So they link to GitHub. If ESP-IDF is forked to a Git repository which is not on GitHub, you will need to run the script [tools/set-submodules-to-github.sh](tools/set-submodules-to-github.sh) after git clone.
|
||||
|
||||
The script sets absolute URLs for all submodules, allowing `git submodule update --init --recursive` to complete. If cloning ESP-IDF from GitHub, this step is not needed.
|
||||
|
||||
## Finding a Project
|
||||
|
||||
As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in Getting Started, ESP-IDF comes with some example projects in the [examples](examples) directory.
|
||||
|
||||
Once you've found the project you want to work with, change to its directory and you can configure and build it.
|
||||
|
||||
To start your own project based on an example, copy the example project directory outside of the ESP-IDF directory.
|
||||
|
||||
# Quick Reference
|
||||
|
||||
See the Getting Started guide links above for a detailed setup guide. This is a quick reference for common commands when working with ESP-IDF projects:
|
||||
|
||||
## Setup Build Environment
|
||||
|
||||
(See the Getting Started guide listed above for a full list of required steps with more details.)
|
||||
|
||||
* Install host build dependencies mentioned in the Getting Started guide.
|
||||
* Run the install script to set up the build environment. The options include `install.bat` or `install.ps1` for Windows, and `install.sh` or `install.fish` for Unix shells.
|
||||
* Run the export script on Windows (`export.bat`) or source it on Unix (`source export.sh`) in every shell environment before using ESP-IDF.
|
||||
|
||||
## Configuring the Project
|
||||
|
||||
* `idf.py set-target <chip_name>` sets the target of the project to `<chip_name>`. Run `idf.py set-target` without any arguments to see a list of supported targets.
|
||||
* `idf.py menuconfig` opens a text-based configuration menu where you can configure the project.
|
||||
|
||||
## Compiling the Project
|
||||
|
||||
`idf.py build`
|
||||
|
||||
... will compile app, bootloader and generate a partition table based on the config.
|
||||
|
||||
## Flashing the Project
|
||||
|
||||
When the build finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this automatically by running:
|
||||
|
||||
`idf.py -p PORT flash`
|
||||
|
||||
Replace PORT with the name of your serial port (like `COM3` on Windows, `/dev/ttyUSB0` on Linux, or `/dev/cu.usbserial-X` on MacOS. If the `-p` option is left out, `idf.py flash` will try to flash the first available serial port.
|
||||
|
||||
This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `idf.py menuconfig`.
|
||||
|
||||
You don't need to run `idf.py build` before running `idf.py flash`, `idf.py flash` will automatically rebuild anything which needs it.
|
||||
|
||||
## Viewing Serial Output
|
||||
|
||||
The `idf.py monitor` target uses the [idf_monitor tool](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html) to display serial output from Espressif SoCs. idf_monitor also has a range of features to decode crash output and interact with the device. [Check the documentation page for details](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html).
|
||||
|
||||
Exit the monitor by typing Ctrl-].
|
||||
|
||||
To build, flash and monitor output in one pass, you can run:
|
||||
|
||||
`idf.py flash monitor`
|
||||
|
||||
## Compiling & Flashing Only the App
|
||||
|
||||
After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table:
|
||||
|
||||
* `idf.py app` - build just the app.
|
||||
* `idf.py app-flash` - flash just the app.
|
||||
|
||||
`idf.py app-flash` will automatically rebuild the app if any source files have changed.
|
||||
|
||||
(In normal development there's no downside to reflashing the bootloader and partition table each time, if they haven't changed.)
|
||||
|
||||
## Erasing Flash
|
||||
|
||||
The `idf.py flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `idf.py erase-flash`.
|
||||
|
||||
This can be combined with other targets, ie `idf.py -p PORT erase-flash flash` will erase everything and then re-flash the new app, bootloader and partition table.
|
||||
|
||||
# Resources
|
||||
|
||||
* Documentation for the latest version: https://docs.espressif.com/projects/esp-idf/. This documentation is built from the [docs directory](docs) of this repository.
|
||||
|
||||
* The [esp32.com forum](https://esp32.com/) is a place to ask questions and find community resources.
|
||||
|
||||
* [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one.
|
||||
|
||||
* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html).
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2017-2019 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT;
|
||||
typedef unsigned char BYTE;
|
||||
typedef uint32_t DWORD;
|
||||
|
||||
#define FF_DRV_NOT_USED 0xFF
|
||||
|
||||
#include "diskio_mh.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
/**
|
||||
* Structure of pointers to disk IO driver functions.
|
||||
*
|
||||
* See FatFs documentation for details about these functions
|
||||
*/
|
||||
typedef struct {
|
||||
DSTATUS (*init) (unsigned char pdrv); /*!< disk initialization function */
|
||||
DSTATUS (*status) (unsigned char pdrv); /*!< disk status check function */
|
||||
DRESULT (*read) (unsigned char pdrv, unsigned char* buff, uint32_t sector, unsigned count); /*!< sector read function */
|
||||
DRESULT (*write) (unsigned char pdrv, const unsigned char* buff, uint32_t sector, unsigned count); /*!< sector write function */
|
||||
DRESULT (*ioctl) (unsigned char pdrv, unsigned char cmd, void* buff); /*!< function to get info about disk and do some misc operations */
|
||||
} ff_diskio_impl_t;
|
||||
|
||||
/**
|
||||
* Register or unregister diskio driver for given drive number.
|
||||
*
|
||||
* When FATFS library calls one of disk_xxx functions for driver number pdrv,
|
||||
* corresponding function in discio_impl for given pdrv will be called.
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param discio_impl pointer to ff_diskio_impl_t structure with diskio functions
|
||||
* or NULL to unregister and free previously registered drive
|
||||
*/
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl);
|
||||
|
||||
#define ff_diskio_unregister(pdrv_) ff_diskio_register(pdrv_, NULL)
|
||||
|
||||
|
||||
/**
|
||||
* Get next available drive number
|
||||
*
|
||||
* @param out_pdrv pointer to the byte to set if successful
|
||||
*
|
||||
* @return ESP_OK on success
|
||||
* ESP_ERR_NOT_FOUND if all drives are attached
|
||||
*/
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,117 +0,0 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2016 */
|
||||
/* ESP-IDF port Copyright 2016 Espressif Systems (Shanghai) PTE LTD */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/time.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
|
||||
static ff_diskio_impl_t * s_impls[FF_VOLUMES] = { NULL };
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
const PARTITION VolToPart[FF_VOLUMES] = {
|
||||
{0, 0}, /* Logical drive 0 ==> Physical drive 0, auto detection */
|
||||
{1, 0}, /* Logical drive 1 ==> Physical drive 1, auto detection */
|
||||
#if FF_VOLUMES > 2
|
||||
{2, 0}, /* Logical drive 2 ==> Physical drive 2, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 3
|
||||
{3, 0}, /* Logical drive 3 ==> Physical drive 3, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 4
|
||||
{4, 0}, /* Logical drive 4 ==> Physical drive 4, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 5
|
||||
{5, 0}, /* Logical drive 5 ==> Physical drive 5, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 6
|
||||
{6, 0}, /* Logical drive 6 ==> Physical drive 6, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 7
|
||||
{7, 0}, /* Logical drive 7 ==> Physical drive 7, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 8
|
||||
{8, 0}, /* Logical drive 8 ==> Physical drive 8, auto detection */
|
||||
#endif
|
||||
#if FF_VOLUMES > 9
|
||||
{9, 0}, /* Logical drive 9 ==> Physical drive 9, auto detection */
|
||||
#endif
|
||||
};
|
||||
#endif
|
||||
|
||||
esp_err_t ff_diskio_get_drive(BYTE* out_pdrv)
|
||||
{
|
||||
BYTE i;
|
||||
for(i=0; i<FF_VOLUMES; i++) {
|
||||
if (!s_impls[i]) {
|
||||
*out_pdrv = i;
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
void ff_diskio_register(BYTE pdrv, const ff_diskio_impl_t* discio_impl)
|
||||
{
|
||||
assert(pdrv < FF_VOLUMES);
|
||||
|
||||
if (s_impls[pdrv]) {
|
||||
ff_diskio_impl_t* im = s_impls[pdrv];
|
||||
s_impls[pdrv] = NULL;
|
||||
free(im);
|
||||
}
|
||||
|
||||
if (!discio_impl) {
|
||||
return;
|
||||
}
|
||||
|
||||
ff_diskio_impl_t * impl = (ff_diskio_impl_t *)malloc(sizeof(ff_diskio_impl_t));
|
||||
assert(impl != NULL);
|
||||
memcpy(impl, discio_impl, sizeof(ff_diskio_impl_t));
|
||||
s_impls[pdrv] = impl;
|
||||
}
|
||||
|
||||
DSTATUS ff_disk_initialize (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->init(pdrv);
|
||||
}
|
||||
DSTATUS ff_disk_status (BYTE pdrv)
|
||||
{
|
||||
return s_impls[pdrv]->status(pdrv);
|
||||
}
|
||||
DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->read(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count)
|
||||
{
|
||||
return s_impls[pdrv]->write(pdrv, buff, sector, count);
|
||||
}
|
||||
DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
return s_impls[pdrv]->ioctl(pdrv, cmd, buff);
|
||||
}
|
||||
|
||||
DWORD get_fattime(void)
|
||||
{
|
||||
time_t t = time(NULL);
|
||||
struct tm tmr;
|
||||
localtime_r(&t, &tmr);
|
||||
int year = tmr.tm_year < 80 ? 0 : tmr.tm_year - 80;
|
||||
return ((DWORD)(year) << 25)
|
||||
| ((DWORD)(tmr.tm_mon + 1) << 21)
|
||||
| ((DWORD)tmr.tm_mday << 16)
|
||||
| (WORD)(tmr.tm_hour << 11)
|
||||
| (WORD)(tmr.tm_min << 5)
|
||||
| (WORD)(tmr.tm_sec >> 1);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_rawflash_mh.h"
|
||||
#include "esp_compiler.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
|
||||
|
||||
static const char* TAG = "diskio_rawflash";
|
||||
|
||||
const esp_partition_t* ff_raw_handles[FF_VOLUMES];
|
||||
|
||||
|
||||
DSTATUS ff_raw_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_raw_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_raw_read - pdrv=%i, sector=%i, count=%in", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
assert(part);
|
||||
esp_err_t err = esp_partition_read(part, sector * SPI_FLASH_SEC_SIZE, buff, count * SPI_FLASH_SEC_SIZE);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "esp_partition_read failed (0x%x)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
|
||||
DRESULT ff_raw_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
DRESULT ff_raw_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
const esp_partition_t* part = ff_raw_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_raw_ioctl: cmd=%in", cmd);
|
||||
assert(part);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = part->size / SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = SPI_FLASH_SEC_SIZE;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_raw_partition(BYTE pdrv, const esp_partition_t* part_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t raw_impl = {
|
||||
.init = &ff_raw_initialize,
|
||||
.status = &ff_raw_status,
|
||||
.read = &ff_raw_read,
|
||||
.write = &ff_raw_write,
|
||||
.ioctl = &ff_raw_ioctl
|
||||
};
|
||||
ff_diskio_register(pdrv, &raw_impl);
|
||||
ff_raw_handles[pdrv] = part_handle;
|
||||
return ESP_OK;
|
||||
|
||||
}
|
||||
|
||||
|
||||
BYTE ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (part_handle == ff_raw_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// 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.
|
||||
// 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.
|
||||
|
||||
#ifndef _DISKIO_RAWFLASH_DEFINED
|
||||
#define _DISKIO_RAWFLASH_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_partition.h"
|
||||
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param part_handle pointer to raw flash partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_raw_partition(unsigned char pdrv, const esp_partition_t* part_handle);
|
||||
unsigned char ff_diskio_get_pdrv_raw(const esp_partition_t* part_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _DISKIO_RAWFLASH_DEFINED
|
||||
@@ -1,147 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||
|
||||
static const char* TAG = "diskio_sdmmc";
|
||||
|
||||
//Check if SD/MMC card is present
|
||||
static DSTATUS ff_sdmmc_card_available(BYTE pdrv)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_get_status(card);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||
* fails. This error value is checked throughout the FATFS code.
|
||||
* Both functions return 0 on success.
|
||||
*/
|
||||
DSTATUS ff_sdmmc_initialize (BYTE pdrv)
|
||||
{
|
||||
return ff_sdmmc_card_available(pdrv);
|
||||
}
|
||||
|
||||
DSTATUS ff_sdmmc_status(BYTE pdrv)
|
||||
{
|
||||
if (s_disk_status_check_en[pdrv]) {
|
||||
return ff_sdmmc_card_available(pdrv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
#if (FF_USE_TRIM)
|
||||
DRESULT ff_sdmmc_trim (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
sdmmc_erase_arg_t arg;
|
||||
|
||||
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = card->csd.capacity;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD*) buff) = card->csd.sector_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
void ff_sdmmc_set_disk_status_check(BYTE pdrv, bool enable)
|
||||
{
|
||||
s_disk_status_check_en[pdrv] = enable;
|
||||
}
|
||||
|
||||
void ff_diskio_register_sdmmc(BYTE pdrv, sdmmc_card_t* card)
|
||||
{
|
||||
static const ff_diskio_impl_t sdmmc_impl = {
|
||||
.init = &ff_sdmmc_initialize,
|
||||
.status = &ff_sdmmc_status,
|
||||
.read = &ff_sdmmc_read,
|
||||
.write = &ff_sdmmc_write,
|
||||
.ioctl = &ff_sdmmc_ioctl
|
||||
};
|
||||
s_cards[pdrv] = card;
|
||||
s_disk_status_check_en[pdrv] = false;
|
||||
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (card == s_cards[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Enable/disable SD card status checking
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param enable mock ff_sdmmc_status function (return 0)
|
||||
*/
|
||||
void ff_sdmmc_set_disk_status_check(BYTE pdrv, bool enable);
|
||||
|
||||
/**
|
||||
* Register SD/MMC diskio driver
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param card pointer to sdmmc_card_t structure describing a card; card should be initialized before calling f_mount.
|
||||
*/
|
||||
void ff_diskio_register_sdmmc(unsigned char pdrv, sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* @brief Get the driver number corresponding to a card
|
||||
*
|
||||
* @param card The card to get its driver
|
||||
* @return Driver number to the card
|
||||
*/
|
||||
BYTE ff_diskio_get_pdrv_card(const sdmmc_card_t* card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,118 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "ff_mh.h"
|
||||
#include "esp_log.h"
|
||||
#include "diskio_wl_mh.h"
|
||||
#include "wear_levelling.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
static const char* TAG = "ff_diskio_spiflash";
|
||||
|
||||
wl_handle_t ff_wl_handles[FF_VOLUMES] = {
|
||||
[0 ... FF_VOLUMES - 1] = WL_INVALID_HANDLE
|
||||
};
|
||||
|
||||
DSTATUS ff_wl_initialize (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DSTATUS ff_wl_status (BYTE pdrv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_read (BYTE pdrv, BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_read - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_read(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_read failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_write (BYTE pdrv, const BYTE *buff, DWORD sector, UINT count)
|
||||
{
|
||||
ESP_LOGV(TAG, "ff_wl_write - pdrv=%i, sector=%i, count=%i\n", (unsigned int)pdrv, (unsigned int)sector, (unsigned int)count);
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
assert(wl_handle + 1);
|
||||
esp_err_t err = wl_erase_range(wl_handle, sector * wl_sector_size(wl_handle), count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_erase_range failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
err = wl_write(wl_handle, sector * wl_sector_size(wl_handle), buff, count * wl_sector_size(wl_handle));
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "wl_write failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_wl_ioctl (BYTE pdrv, BYTE cmd, void *buff)
|
||||
{
|
||||
wl_handle_t wl_handle = ff_wl_handles[pdrv];
|
||||
ESP_LOGV(TAG, "ff_wl_ioctl: cmd=%i\n", cmd);
|
||||
assert(wl_handle + 1);
|
||||
switch (cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD *) buff) = wl_size(wl_handle) / wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD *) buff) = wl_sector_size(wl_handle);
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t ff_diskio_register_wl_partition(BYTE pdrv, wl_handle_t flash_handle)
|
||||
{
|
||||
if (pdrv >= FF_VOLUMES) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
static const ff_diskio_impl_t wl_impl = {
|
||||
.init = &ff_wl_initialize,
|
||||
.status = &ff_wl_status,
|
||||
.read = &ff_wl_read,
|
||||
.write = &ff_wl_write,
|
||||
.ioctl = &ff_wl_ioctl
|
||||
};
|
||||
ff_wl_handles[pdrv] = flash_handle;
|
||||
ff_diskio_register(pdrv, &wl_impl);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (flash_handle == ff_wl_handles[i]) {
|
||||
ff_wl_handles[i] = WL_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright 2015-2017 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.
|
||||
|
||||
#ifndef _DISKIO_WL_DEFINED
|
||||
#define _DISKIO_WL_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "wear_levelling.h"
|
||||
|
||||
|
||||
/**
|
||||
* Register spi flash partition
|
||||
*
|
||||
* @param pdrv drive number
|
||||
* @param flash_handle handle of the wear levelling partition.
|
||||
*/
|
||||
esp_err_t ff_diskio_register_wl_partition(unsigned char pdrv, wl_handle_t flash_handle);
|
||||
unsigned char ff_diskio_get_pdrv_wl(wl_handle_t flash_handle);
|
||||
void ff_diskio_clear_pdrv_wl(wl_handle_t flash_handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _DISKIO_WL_DEFINED
|
||||
@@ -1,108 +0,0 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2017 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "ff_mh.h"
|
||||
#include "sdkconfig.h"
|
||||
#ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM
|
||||
#include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null on not enough core) */
|
||||
unsigned msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
#ifdef CONFIG_FATFS_ALLOC_PREFER_EXTRAM
|
||||
return heap_caps_malloc_prefer(msize, 2, MALLOC_CAP_DEFAULT | MALLOC_CAP_SPIRAM,
|
||||
MALLOC_CAP_DEFAULT | MALLOC_CAP_INTERNAL);
|
||||
#else
|
||||
return malloc(msize);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do for null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t *sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
*sobj = xSemaphoreCreateMutex();
|
||||
return (*sobj != NULL) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
vSemaphoreDelete(sobj);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
return (xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
xSemaphoreGive(sobj);
|
||||
}
|
||||
|
||||
#endif // FF_FS_REENTRANT
|
||||
@@ -1,329 +0,0 @@
|
||||
----------------------------------------------------------------------------
|
||||
Revision history of FatFs module
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
R0.00 (February 26, 2006)
|
||||
|
||||
Prototype.
|
||||
|
||||
|
||||
|
||||
R0.01 (April 29, 2006)
|
||||
|
||||
The first release.
|
||||
|
||||
|
||||
|
||||
R0.02 (June 01, 2006)
|
||||
|
||||
Added FAT12 support.
|
||||
Removed unbuffered mode.
|
||||
Fixed a problem on small (<32M) partition.
|
||||
|
||||
|
||||
|
||||
R0.02a (June 10, 2006)
|
||||
|
||||
Added a configuration option (_FS_MINIMUM).
|
||||
|
||||
|
||||
|
||||
R0.03 (September 22, 2006)
|
||||
|
||||
Added f_rename().
|
||||
Changed option _FS_MINIMUM to _FS_MINIMIZE.
|
||||
|
||||
|
||||
|
||||
R0.03a (December 11, 2006)
|
||||
|
||||
Improved cluster scan algorithm to write files fast.
|
||||
Fixed f_mkdir() creates incorrect directory on FAT32.
|
||||
|
||||
|
||||
|
||||
R0.04 (February 04, 2007)
|
||||
|
||||
Added f_mkfs().
|
||||
Supported multiple drive system.
|
||||
Changed some interfaces for multiple drive system.
|
||||
Changed f_mountdrv() to f_mount().
|
||||
|
||||
|
||||
|
||||
R0.04a (April 01, 2007)
|
||||
|
||||
Supported multiple partitions on a physical drive.
|
||||
Added a capability of extending file size to f_lseek().
|
||||
Added minimization level 3.
|
||||
Fixed an endian sensitive code in f_mkfs().
|
||||
|
||||
|
||||
|
||||
R0.04b (May 05, 2007)
|
||||
|
||||
Added a configuration option _USE_NTFLAG.
|
||||
Added FSINFO support.
|
||||
Fixed DBCS name can result FR_INVALID_NAME.
|
||||
Fixed short seek (<= csize) collapses the file object.
|
||||
|
||||
|
||||
|
||||
R0.05 (August 25, 2007)
|
||||
|
||||
Changed arguments of f_read(), f_write() and f_mkfs().
|
||||
Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
|
||||
Fixed f_mkdir() on FAT32 creates incorrect directory.
|
||||
|
||||
|
||||
|
||||
R0.05a (February 03, 2008)
|
||||
|
||||
Added f_truncate() and f_utime().
|
||||
Fixed off by one error at FAT sub-type determination.
|
||||
Fixed btr in f_read() can be mistruncated.
|
||||
Fixed cached sector is not flushed when create and close without write.
|
||||
|
||||
|
||||
|
||||
R0.06 (April 01, 2008)
|
||||
|
||||
Added fputc(), fputs(), fprintf() and fgets().
|
||||
Improved performance of f_lseek() on moving to the same or following cluster.
|
||||
|
||||
|
||||
|
||||
R0.07 (April 01, 2009)
|
||||
|
||||
Merged Tiny-FatFs as a configuration option. (_FS_TINY)
|
||||
Added long file name feature. (_USE_LFN)
|
||||
Added multiple code page feature. (_CODE_PAGE)
|
||||
Added re-entrancy for multitask operation. (_FS_REENTRANT)
|
||||
Added auto cluster size selection to f_mkfs().
|
||||
Added rewind option to f_readdir().
|
||||
Changed result code of critical errors.
|
||||
Renamed string functions to avoid name collision.
|
||||
|
||||
|
||||
|
||||
R0.07a (April 14, 2009)
|
||||
|
||||
Septemberarated out OS dependent code on reentrant cfg.
|
||||
Added multiple sector size feature.
|
||||
|
||||
|
||||
|
||||
R0.07c (June 21, 2009)
|
||||
|
||||
Fixed f_unlink() can return FR_OK on error.
|
||||
Fixed wrong cache control in f_lseek().
|
||||
Added relative path feature.
|
||||
Added f_chdir() and f_chdrive().
|
||||
Added proper case conversion to extended character.
|
||||
|
||||
|
||||
|
||||
R0.07e (November 03, 2009)
|
||||
|
||||
Septemberarated out configuration options from ff.h to ffconf.h.
|
||||
Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
|
||||
Fixed name matching error on the 13 character boundary.
|
||||
Added a configuration option, _LFN_UNICODE.
|
||||
Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
|
||||
|
||||
|
||||
|
||||
R0.08 (May 15, 2010)
|
||||
|
||||
Added a memory configuration option. (_USE_LFN = 3)
|
||||
Added file lock feature. (_FS_SHARE)
|
||||
Added fast seek feature. (_USE_FASTSEEK)
|
||||
Changed some types on the API, XCHAR->TCHAR.
|
||||
Changed .fname in the FILINFO structure on Unicode cfg.
|
||||
String functions support UTF-8 encoding files on Unicode cfg.
|
||||
|
||||
|
||||
|
||||
R0.08a (August 16, 2010)
|
||||
|
||||
Added f_getcwd(). (_FS_RPATH = 2)
|
||||
Added sector erase feature. (_USE_ERASE)
|
||||
Moved file lock semaphore table from fs object to the bss.
|
||||
Fixed f_mkfs() creates wrong FAT32 volume.
|
||||
|
||||
|
||||
|
||||
R0.08b (January 15, 2011)
|
||||
|
||||
Fast seek feature is also applied to f_read() and f_write().
|
||||
f_lseek() reports required table size on creating CLMP.
|
||||
Extended format syntax of f_printf().
|
||||
Ignores duplicated directory separators in given path name.
|
||||
|
||||
|
||||
|
||||
R0.09 (September 06, 2011)
|
||||
|
||||
f_mkfs() supports multiple partition to complete the multiple partition feature.
|
||||
Added f_fdisk().
|
||||
|
||||
|
||||
|
||||
R0.09a (August 27, 2012)
|
||||
|
||||
Changed f_open() and f_opendir() reject null object pointer to avoid crash.
|
||||
Changed option name _FS_SHARE to _FS_LOCK.
|
||||
Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
|
||||
|
||||
|
||||
|
||||
R0.09b (January 24, 2013)
|
||||
|
||||
Added f_setlabel() and f_getlabel().
|
||||
|
||||
|
||||
|
||||
R0.10 (October 02, 2013)
|
||||
|
||||
Added selection of character encoding on the file. (_STRF_ENCODE)
|
||||
Added f_closedir().
|
||||
Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
|
||||
Added forced mount feature with changes of f_mount().
|
||||
Improved behavior of volume auto detection.
|
||||
Improved write throughput of f_puts() and f_printf().
|
||||
Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
|
||||
Fixed f_write() can be truncated when the file size is close to 4GB.
|
||||
Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
|
||||
|
||||
|
||||
|
||||
R0.10a (January 15, 2014)
|
||||
|
||||
Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
|
||||
Added a configuration option of minimum sector size. (_MIN_SS)
|
||||
2nd argument of f_rename() can have a drive number and it will be ignored.
|
||||
Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
|
||||
Fixed f_close() invalidates the file object without volume lock.
|
||||
Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
|
||||
Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
R0.10c (November 09, 2014)
|
||||
|
||||
Added a configuration option for the platforms without RTC. (_FS_NORTC)
|
||||
Changed option name _USE_ERASE to _USE_TRIM.
|
||||
Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
|
||||
Fixed a potential problem of FAT access that can appear on disk error.
|
||||
Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
|
||||
|
||||
|
||||
|
||||
R0.11 (February 09, 2015)
|
||||
|
||||
Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
|
||||
Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
|
||||
Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.11a (September 05, 2015)
|
||||
|
||||
Fixed wrong media change can lead a deadlock at thread-safe configuration.
|
||||
Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
|
||||
Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
|
||||
Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
|
||||
Fixed errors in the case conversion teble of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12 (April 12, 2016)
|
||||
|
||||
Added support for exFAT file system. (_FS_EXFAT)
|
||||
Added f_expand(). (_USE_EXPAND)
|
||||
Changed some members in FINFO structure and behavior of f_readdir().
|
||||
Added an option _USE_CHMOD.
|
||||
Removed an option _WORD_ACCESS.
|
||||
Fixed errors in the case conversion table of Unicode (cc*.c).
|
||||
|
||||
|
||||
|
||||
R0.12a (July 10, 2016)
|
||||
|
||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
||||
f_forward() is available regardless of _FS_TINY.
|
||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
||||
|
||||
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
||||
|
||||
R0.13 (May 21, 2017)
|
||||
|
||||
Changed heading character of configuration keywords "_" to "FF_".
|
||||
Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
|
||||
Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
|
||||
Improved cluster allocation time on stretch a deep buried cluster chain.
|
||||
Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
|
||||
Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
|
||||
Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
|
||||
Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
|
||||
Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
|
||||
|
||||
|
||||
|
||||
R0.13a (October 14, 2017)
|
||||
|
||||
Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
|
||||
Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
|
||||
Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
|
||||
Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
|
||||
Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
|
||||
Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.13b (April 07, 2018)
|
||||
|
||||
Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
|
||||
Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
|
||||
Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
|
||||
Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
|
||||
|
||||
|
||||
|
||||
R0.13c (October 14, 2018)
|
||||
Supported stdint.h for C99 and later. (integer.h was included in ff.h)
|
||||
Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
|
||||
Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12)
|
||||
Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
|
||||
@@ -1,20 +0,0 @@
|
||||
FatFs Module Source Files R0.13c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
ffunicode.c Optional Unicode utility functions.
|
||||
ffsystem.c An example of optional O/S related functions.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You need to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
@@ -1,228 +0,0 @@
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* If a working storage control module is available, it should be */
|
||||
/* attached to the FatFs via a glue function rather than modifying it. */
|
||||
/* This is an example of glue functions to attach various exsisting */
|
||||
/* storage control modules to the FatFs module with a defined API. */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#include "ff_mh.h" /* Obtains integer types */
|
||||
#include "diskio_mh.h" /* Declarations of disk functions */
|
||||
|
||||
/* Definitions of physical drive number for each drive */
|
||||
#define DEV_RAM 0 /* Example: Map Ramdisk to physical drive 0 */
|
||||
#define DEV_MMC 1 /* Example: Map MMC/SD card to physical drive 1 */
|
||||
#define DEV_USB 2 /* Example: Map USB MSD to physical drive 2 */
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Get Drive Status */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_status (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_status();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Inidialize a Drive */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DSTATUS disk_initialize (
|
||||
BYTE pdrv /* Physical drive nmuber to identify the drive */
|
||||
)
|
||||
{
|
||||
DSTATUS stat;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
result = RAM_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_MMC :
|
||||
result = MMC_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
|
||||
case DEV_USB :
|
||||
result = USB_disk_initialize();
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return stat;
|
||||
}
|
||||
return STA_NOINIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Read Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_read (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
BYTE *buff, /* Data buffer to store read data */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_read(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Write Sector(s) */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
#if FF_FS_READONLY == 0
|
||||
|
||||
DRESULT disk_write (
|
||||
BYTE pdrv, /* Physical drive nmuber to identify the drive */
|
||||
const BYTE *buff, /* Data to be written */
|
||||
LBA_t sector, /* Start sector in LBA */
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
// translate the arguments here
|
||||
|
||||
result = RAM_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
// translate the arguments here
|
||||
|
||||
result = MMC_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
// translate the arguments here
|
||||
|
||||
result = USB_disk_write(buff, sector, count);
|
||||
|
||||
// translate the reslut code here
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/*-----------------------------------------------------------------------*/
|
||||
/* Miscellaneous Functions */
|
||||
/*-----------------------------------------------------------------------*/
|
||||
|
||||
DRESULT disk_ioctl (
|
||||
BYTE pdrv, /* Physical drive nmuber (0..) */
|
||||
BYTE cmd, /* Control code */
|
||||
void *buff /* Buffer to send/receive control data */
|
||||
)
|
||||
{
|
||||
DRESULT res;
|
||||
int result;
|
||||
|
||||
switch (pdrv) {
|
||||
case DEV_RAM :
|
||||
|
||||
// Process of the command for the RAM drive
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_MMC :
|
||||
|
||||
// Process of the command for the MMC/SD card
|
||||
|
||||
return res;
|
||||
|
||||
case DEV_USB :
|
||||
|
||||
// Process of the command the USB drive
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
return RES_PARERR;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
/*-----------------------------------------------------------------------/
|
||||
/ Low level disk interface modlue include file (C)ChaN, 2019 /
|
||||
/-----------------------------------------------------------------------*/
|
||||
|
||||
#ifndef _DISKIO_DEFINED
|
||||
#define _DISKIO_DEFINED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ff_mh.h"
|
||||
|
||||
/* Status of Disk Functions */
|
||||
typedef BYTE DSTATUS;
|
||||
|
||||
/* Results of Disk Functions */
|
||||
typedef enum {
|
||||
RES_OK = 0, /* 0: Successful */
|
||||
RES_ERROR, /* 1: R/W Error */
|
||||
RES_WRPRT, /* 2: Write Protected */
|
||||
RES_NOTRDY, /* 3: Not Ready */
|
||||
RES_PARERR /* 4: Invalid Parameter */
|
||||
} DRESULT;
|
||||
|
||||
|
||||
/*---------------------------------------*/
|
||||
/* Prototypes for disk control functions */
|
||||
|
||||
|
||||
DSTATUS disk_initialize (BYTE pdrv);
|
||||
DSTATUS disk_status (BYTE pdrv);
|
||||
DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
|
||||
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
|
||||
|
||||
|
||||
/* Disk Status Bits (DSTATUS) */
|
||||
|
||||
#define STA_NOINIT 0x01 /* Drive not initialized */
|
||||
#define STA_NODISK 0x02 /* No medium in the drive */
|
||||
#define STA_PROTECT 0x04 /* Write protected */
|
||||
|
||||
|
||||
/* Command code for disk_ioctrl fucntion */
|
||||
|
||||
/* Generic command (Used by FatFs) */
|
||||
#define CTRL_SYNC 0 /* Complete pending write process (needed at FF_FS_READONLY == 0) */
|
||||
#define GET_SECTOR_COUNT 1 /* Get media size (needed at FF_USE_MKFS == 1) */
|
||||
#define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
|
||||
#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at FF_USE_MKFS == 1) */
|
||||
#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
|
||||
|
||||
/* Generic command (Not used by FatFs) */
|
||||
#define CTRL_POWER 5 /* Get/Set power status */
|
||||
#define CTRL_LOCK 6 /* Lock/Unlock media removal */
|
||||
#define CTRL_EJECT 7 /* Eject media */
|
||||
#define CTRL_FORMAT 8 /* Create physical format on the media */
|
||||
|
||||
/* MMC/SDC specific ioctl command */
|
||||
#define MMC_GET_TYPE 10 /* Get card type */
|
||||
#define MMC_GET_CSD 11 /* Get CSD */
|
||||
#define MMC_GET_CID 12 /* Get CID */
|
||||
#define MMC_GET_OCR 13 /* Get OCR */
|
||||
#define MMC_GET_SDSTAT 14 /* Get SD status */
|
||||
#define ISDIO_READ 55 /* Read data form SD iSDIO register */
|
||||
#define ISDIO_WRITE 56 /* Write data to SD iSDIO register */
|
||||
#define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */
|
||||
|
||||
/* ATA/CF specific ioctl command */
|
||||
#define ATA_GET_REV 20 /* Get F/W revision */
|
||||
#define ATA_GET_MODEL 21 /* Get model name */
|
||||
#define ATA_GET_SN 22 /* Get serial number */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,422 +0,0 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT Filesystem module R0.14b /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2021, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
/ that the following condition is met:
|
||||
|
||||
/ 1. Redistributions of source code must retain the above copyright notice,
|
||||
/ this condition and the following disclaimer.
|
||||
/
|
||||
/ This software is provided by the copyright holder and contributors "AS IS"
|
||||
/ and any warranties related to this software are DISCLAIMED.
|
||||
/ The copyright owner or contributors be NOT LIABLE for any damages caused
|
||||
/ by use of this software.
|
||||
/
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#ifndef FF_DEFINED
|
||||
#define FF_DEFINED 86631 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "ffconf_mh.h" /* FatFs configuration options */
|
||||
|
||||
#if FF_DEFINED != FFCONF_DEF
|
||||
#error Wrong configuration file (ffconf.h).
|
||||
#endif
|
||||
|
||||
|
||||
/* Integer types used for FatFs API */
|
||||
|
||||
#if defined(_WIN32) /* Windows VC++ (for development only) */
|
||||
#define FF_INTDEF 2
|
||||
#include <windows.h>
|
||||
typedef unsigned __int64 QWORD;
|
||||
#include <float.h>
|
||||
#define isnan(v) _isnan(v)
|
||||
#define isinf(v) (!_finite(v))
|
||||
|
||||
#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */
|
||||
#define FF_INTDEF 2
|
||||
#include <stdint.h>
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef uint16_t WORD; /* 16-bit unsigned integer */
|
||||
typedef uint32_t DWORD; /* 32-bit unsigned integer */
|
||||
typedef uint64_t QWORD; /* 64-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
|
||||
#else /* Earlier than C99 */
|
||||
#define FF_INTDEF 1
|
||||
typedef unsigned int UINT; /* int must be 16-bit or 32-bit */
|
||||
typedef unsigned char BYTE; /* char must be 8-bit */
|
||||
typedef unsigned short WORD; /* 16-bit unsigned integer */
|
||||
typedef unsigned long DWORD; /* 32-bit unsigned integer */
|
||||
typedef WORD WCHAR; /* UTF-16 character type */
|
||||
#endif
|
||||
|
||||
|
||||
/* Type of file size and LBA variables */
|
||||
|
||||
#if FF_FS_EXFAT
|
||||
#if FF_INTDEF != 2
|
||||
#error exFAT feature wants C99 or later
|
||||
#endif
|
||||
typedef QWORD FSIZE_t;
|
||||
#if FF_LBA64
|
||||
typedef QWORD LBA_t;
|
||||
#else
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
#else
|
||||
#if FF_LBA64
|
||||
#error exFAT needs to be enabled when enable 64-bit LBA
|
||||
#endif
|
||||
typedef DWORD FSIZE_t;
|
||||
typedef DWORD LBA_t;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Type of path name strings on FatFs API (TCHAR) */
|
||||
|
||||
#if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */
|
||||
typedef WCHAR TCHAR;
|
||||
#define _T(x) L ## x
|
||||
#define _TEXT(x) L ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) u8 ## x
|
||||
#define _TEXT(x) u8 ## x
|
||||
#elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */
|
||||
typedef DWORD TCHAR;
|
||||
#define _T(x) U ## x
|
||||
#define _TEXT(x) U ## x
|
||||
#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
|
||||
#error Wrong FF_LFN_UNICODE setting
|
||||
#else /* ANSI/OEM code in SBCS/DBCS */
|
||||
typedef char TCHAR;
|
||||
#define _T(x) x
|
||||
#define _TEXT(x) x
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Definitions of volume management */
|
||||
|
||||
#if FF_MULTI_PARTITION /* Multiple partition configuration */
|
||||
typedef struct {
|
||||
BYTE pd; /* Physical drive number */
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern const PARTITION VolToPart[]; /* Volume - Partition mapping table */
|
||||
#endif
|
||||
|
||||
#if FF_STR_VOLUME_ID
|
||||
#ifndef FF_VOLUME_STRS
|
||||
extern const char* VolumeStr[FF_VOLUMES]; /* User defied volume ID */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Filesystem object structure (FATFS) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fs_type; /* Filesystem type (0:not mounted) */
|
||||
BYTE pdrv; /* Associated physical drive */
|
||||
BYTE n_fats; /* Number of FATs (1 or 2) */
|
||||
BYTE wflag; /* win[] flag (b0:dirty) */
|
||||
BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */
|
||||
WORD id; /* Volume mount ID */
|
||||
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
|
||||
WORD csize; /* Cluster size [sectors] */
|
||||
#if FF_MAX_SS != FF_MIN_SS
|
||||
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
|
||||
#endif
|
||||
#if FF_USE_LFN
|
||||
WCHAR* lfnbuf; /* LFN working buffer */
|
||||
#endif
|
||||
#if FF_FS_EXFAT
|
||||
BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */
|
||||
#endif
|
||||
#if FF_FS_REENTRANT
|
||||
FF_SYNC_t sobj; /* Identifier of sync object */
|
||||
#endif
|
||||
#if !FF_FS_READONLY
|
||||
DWORD last_clst; /* Last allocated cluster */
|
||||
DWORD free_clst; /* Number of free clusters */
|
||||
#endif
|
||||
#if FF_FS_RPATH
|
||||
DWORD cdir; /* Current directory start cluster (0:root) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */
|
||||
DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */
|
||||
DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */
|
||||
#endif
|
||||
#endif
|
||||
DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */
|
||||
DWORD fsize; /* Size of an FAT [sectors] */
|
||||
LBA_t volbase; /* Volume base sector */
|
||||
LBA_t fatbase; /* FAT base sector */
|
||||
LBA_t dirbase; /* Root directory base sector/cluster */
|
||||
LBA_t database; /* Data base sector */
|
||||
#if FF_FS_EXFAT
|
||||
LBA_t bitbase; /* Allocation bitmap base sector */
|
||||
#endif
|
||||
LBA_t winsect; /* Current sector appearing in the win[] */
|
||||
BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
|
||||
} FATFS;
|
||||
|
||||
|
||||
|
||||
/* Object ID and allocation information (FFOBJID) */
|
||||
|
||||
typedef struct {
|
||||
FATFS* fs; /* Pointer to the hosting volume of this object */
|
||||
WORD id; /* Hosting volume mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if FF_FS_EXFAT
|
||||
DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */
|
||||
#endif
|
||||
#if FF_FS_LOCK
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
#endif
|
||||
} FFOBJID;
|
||||
|
||||
|
||||
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !FF_FS_READONLY
|
||||
LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */
|
||||
BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */
|
||||
#endif
|
||||
#if FF_USE_FASTSEEK
|
||||
DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */
|
||||
#endif
|
||||
#if !FF_FS_TINY
|
||||
BYTE buf[FF_MAX_SS]; /* File private data read/write window */
|
||||
#endif
|
||||
} FIL;
|
||||
|
||||
|
||||
|
||||
/* Directory object structure (FF_DIR) */
|
||||
|
||||
typedef struct {
|
||||
FFOBJID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
LBA_t sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if FF_USE_LFN
|
||||
DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
|
||||
#endif
|
||||
#if FF_USE_FIND
|
||||
const TCHAR* pat; /* Pointer to the name matching pattern */
|
||||
#endif
|
||||
} FF_DIR;
|
||||
|
||||
|
||||
|
||||
/* File information structure (FILINFO) */
|
||||
|
||||
typedef struct {
|
||||
FSIZE_t fsize; /* File size */
|
||||
WORD fdate; /* Modified date */
|
||||
WORD ftime; /* Modified time */
|
||||
BYTE fattrib; /* File attribute */
|
||||
#if FF_USE_LFN
|
||||
TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */
|
||||
TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */
|
||||
#else
|
||||
TCHAR fname[12 + 1]; /* File name */
|
||||
#endif
|
||||
} FILINFO;
|
||||
|
||||
|
||||
|
||||
/* Format parameter structure (MKFS_PARM) */
|
||||
|
||||
typedef struct {
|
||||
BYTE fmt; /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
|
||||
BYTE n_fat; /* Number of FATs */
|
||||
UINT align; /* Data area alignment (sector) */
|
||||
UINT n_root; /* Number of root directory entries */
|
||||
DWORD au_size; /* Cluster size (byte) */
|
||||
} MKFS_PARM;
|
||||
|
||||
|
||||
|
||||
/* File function return code (FRESULT) */
|
||||
|
||||
typedef enum {
|
||||
FR_OK = 0, /* (0) Succeeded */
|
||||
FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */
|
||||
FR_INT_ERR, /* (2) Assertion failed */
|
||||
FR_NOT_READY, /* (3) The physical drive cannot work */
|
||||
FR_NO_FILE, /* (4) Could not find the file */
|
||||
FR_NO_PATH, /* (5) Could not find the path */
|
||||
FR_INVALID_NAME, /* (6) The path name format is invalid */
|
||||
FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
|
||||
FR_EXIST, /* (8) Access denied due to prohibited access */
|
||||
FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */
|
||||
FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */
|
||||
FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */
|
||||
FR_NOT_ENABLED, /* (12) The volume has no work area */
|
||||
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
|
||||
FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */
|
||||
FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */
|
||||
FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */
|
||||
FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */
|
||||
FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
|
||||
FR_INVALID_PARAMETER /* (19) Given parameter is invalid */
|
||||
} FRESULT;
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* FatFs module application interface */
|
||||
|
||||
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
|
||||
FRESULT f_close (FIL* fp); /* Close an open file object */
|
||||
FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */
|
||||
FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */
|
||||
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
|
||||
FRESULT f_truncate (FIL* fp); /* Truncate the file */
|
||||
FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */
|
||||
FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */
|
||||
FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */
|
||||
FRESULT f_readdir (FF_DIR* dp, FILINFO* fno); /* Read a directory item */
|
||||
FRESULT f_findfirst (FF_DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */
|
||||
FRESULT f_findnext (FF_DIR* dp, FILINFO* fno); /* Find next file */
|
||||
FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */
|
||||
FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */
|
||||
FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */
|
||||
FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */
|
||||
FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */
|
||||
FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */
|
||||
FRESULT f_chdir (const TCHAR* path); /* Change current directory */
|
||||
FRESULT f_chdrive (const TCHAR* path); /* Change current drive */
|
||||
FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */
|
||||
FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */
|
||||
FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */
|
||||
FRESULT f_setlabel (const TCHAR* label); /* Set volume label */
|
||||
FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */
|
||||
FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */
|
||||
FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */
|
||||
FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */
|
||||
FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */
|
||||
FRESULT f_setcp (WORD cp); /* Set current code page */
|
||||
int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */
|
||||
int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */
|
||||
int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */
|
||||
TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */
|
||||
|
||||
#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
|
||||
#define f_error(fp) ((fp)->err)
|
||||
#define f_tell(fp) ((fp)->fptr)
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
#define f_unmount(path) f_mount(0, path, 0)
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Additional user defined functions */
|
||||
|
||||
/* RTC function */
|
||||
#if !FF_FS_READONLY && !FF_FS_NORTC
|
||||
DWORD get_fattime (void);
|
||||
#endif
|
||||
|
||||
/* LFN support functions */
|
||||
#if FF_USE_LFN >= 1 /* Code conversion (defined in unicode.c) */
|
||||
WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
|
||||
WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
|
||||
DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */
|
||||
#endif
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
void* ff_memalloc (UINT msize); /* Allocate memory block */
|
||||
void ff_memfree (void* mblock); /* Free memory block */
|
||||
#endif
|
||||
|
||||
/* Sync functions */
|
||||
#if FF_FS_REENTRANT
|
||||
int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj); /* Create a sync object */
|
||||
int ff_req_grant (FF_SYNC_t sobj); /* Lock sync object */
|
||||
void ff_rel_grant (FF_SYNC_t sobj); /* Unlock sync object */
|
||||
int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*--------------------------------------------------------------*/
|
||||
/* Flags and offset address */
|
||||
|
||||
|
||||
/* File access mode and open method flags (3rd argument of f_open) */
|
||||
#define FA_READ 0x01
|
||||
#define FA_WRITE 0x02
|
||||
#define FA_OPEN_EXISTING 0x00
|
||||
#define FA_CREATE_NEW 0x04
|
||||
#define FA_CREATE_ALWAYS 0x08
|
||||
#define FA_OPEN_ALWAYS 0x10
|
||||
#define FA_OPEN_APPEND 0x30
|
||||
|
||||
/* Fast seek controls (2nd argument of f_lseek) */
|
||||
#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
|
||||
|
||||
/* Format options (2nd argument of f_mkfs) */
|
||||
#define FM_FAT 0x01
|
||||
#define FM_FAT32 0x02
|
||||
#define FM_EXFAT 0x04
|
||||
#define FM_ANY 0x07
|
||||
#define FM_SFD 0x08
|
||||
|
||||
/* Filesystem type (FATFS.fs_type) */
|
||||
#define FS_FAT12 1
|
||||
#define FS_FAT16 2
|
||||
#define FS_FAT32 3
|
||||
#define FS_EXFAT 4
|
||||
|
||||
/* File attribute bits for directory entry (FILINFO.fattrib) */
|
||||
#define AM_RDO 0x01 /* Read only */
|
||||
#define AM_HID 0x02 /* Hidden */
|
||||
#define AM_SYS 0x04 /* System */
|
||||
#define AM_DIR 0x10 /* Directory */
|
||||
#define AM_ARC 0x20 /* Archive */
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* FF_DEFINED */
|
||||
@@ -1,309 +0,0 @@
|
||||
#include "sdkconfig.h"
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs Functional Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FFCONF_DEF 86631 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
#define FF_FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: Basic functions are fully enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
|
||||
/ are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
#define FF_USE_FIND 0
|
||||
/* This option switches filtered directory read functions, f_findfirst() and
|
||||
/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
|
||||
|
||||
#define FF_USE_MKFS 1
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_FASTSEEK CONFIG_FATFS_USE_FASTSEEK
|
||||
/* This option switches fast seek function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_EXPAND 0
|
||||
/* This option switches f_expand function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_CHMOD 1
|
||||
/* This option switches attribute manipulation functions, f_chmod() and f_utime().
|
||||
/ (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
|
||||
|
||||
#define FF_USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
|
||||
#define FF_USE_STRFUNC 0
|
||||
#define FF_PRINT_LLI 0
|
||||
#define FF_PRINT_FLOAT 0
|
||||
#define FF_STRF_ENCODE 3
|
||||
/* FF_USE_STRFUNC switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion.
|
||||
/
|
||||
/ FF_PRINT_LLI = 1 makes f_printf() support long long argument and FF_PRINT_FLOAT = 1/2
|
||||
makes f_printf() support floating point argument. These features want C99 or later.
|
||||
/ When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character
|
||||
/ encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE
|
||||
/ to be read/written via those functions.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP
|
||||
/ 1: Unicode in UTF-16LE
|
||||
/ 2: Unicode in UTF-16BE
|
||||
/ 3: Unicode in UTF-8
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_CODE_PAGE CONFIG_FATFS_CODEPAGE
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect code page setting can cause a file open failure.
|
||||
/
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
/ 0 - Include all code pages above and configured by f_setcp()
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_FATFS_LFN_STACK)
|
||||
#define FF_USE_LFN 2
|
||||
#elif defined(CONFIG_FATFS_LFN_HEAP)
|
||||
#define FF_USE_LFN 3
|
||||
#else /* CONFIG_FATFS_LFN_NONE */
|
||||
#define FF_USE_LFN 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FATFS_MAX_LFN
|
||||
#define FF_MAX_LFN CONFIG_FATFS_MAX_LFN
|
||||
#endif
|
||||
|
||||
/* The FF_USE_LFN switches the support for LFN (long file name).
|
||||
/
|
||||
/ 0: Disable LFN. FF_MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
|
||||
/ requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
|
||||
/ additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
|
||||
/ The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
|
||||
/ be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
|
||||
/ specification.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
|
||||
|
||||
#ifdef CONFIG_FATFS_API_ENCODING_UTF_8
|
||||
#define FF_LFN_UNICODE 2
|
||||
#else /* CONFIG_FATFS_API_ENCODING_ANSI_OEM */
|
||||
#define FF_LFN_UNICODE 0
|
||||
#endif
|
||||
/* This option switches the character encoding on the API when LFN is enabled.
|
||||
/
|
||||
/ 0: ANSI/OEM in current CP (TCHAR = char)
|
||||
/ 1: Unicode in UTF-16 (TCHAR = WCHAR)
|
||||
/ 2: Unicode in UTF-8 (TCHAR = char)
|
||||
/ 3: Unicode in UTF-32 (TCHAR = DWORD)
|
||||
/
|
||||
/ Also behavior of string I/O functions will be affected by this option.
|
||||
/ When LFN is not enabled, this option has no effect. */
|
||||
|
||||
#define FF_LFN_BUF 255
|
||||
#define FF_SFN_BUF 12
|
||||
/* This set of options defines size of file name members in the FILINFO structure
|
||||
/ which is used to read out directory items. These values should be suffcient for
|
||||
/ the file names to read. The maximum possible length of the read file name depends
|
||||
/ on character encoding. When LFN is not enabled, these options have no effect. */
|
||||
|
||||
#define FF_FS_RPATH 0
|
||||
/* This option configures support for relative path.
|
||||
/
|
||||
/ 0: Disable relative path and remove related functions.
|
||||
/ 1: Enable relative path. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
*/
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_VOLUMES CONFIG_FATFS_VOLUME_COUNT
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
#define FF_STR_VOLUME_ID 0
|
||||
#define FF_VOLUME_STRS "RAM", "NAND", "CF", "SD", "SD2", "USB", "USB2", "USB3"
|
||||
/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
|
||||
/ When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
|
||||
/ number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
|
||||
/ logical drives. Number of items must not be less than FF_VOLUMES. Valid
|
||||
/ characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
|
||||
/ compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
|
||||
/ not defined, a user defined volume string table needs to be defined as:
|
||||
/
|
||||
/ const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
|
||||
*/
|
||||
|
||||
#define FF_MULTI_PARTITION 1
|
||||
/* This option switches support for multiple volumes on the physical drive.
|
||||
/ By default (0), each logical drive number is bound to the same physical drive
|
||||
/ number and only an FAT volume found on the physical drive will be mounted.
|
||||
/ When this function is enabled (1), each logical drive number can be bound to
|
||||
/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
|
||||
/ funciton will be available. */
|
||||
|
||||
/* SD card sector size */
|
||||
#define FF_SS_SDCARD 512
|
||||
/* wear_levelling library sector size */
|
||||
#define FF_SS_WL CONFIG_WL_SECTOR_SIZE
|
||||
|
||||
#define FF_MIN_SS MIN(FF_SS_SDCARD, FF_SS_WL)
|
||||
#define FF_MAX_SS MAX(FF_SS_SDCARD, FF_SS_WL)
|
||||
/* This set of options configures the range of sector size to be supported. (512,
|
||||
/ 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk, but a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
|
||||
/ for variable sector size mode and disk_ioctl() function needs to implement
|
||||
/ GET_SECTOR_SIZE command. */
|
||||
|
||||
#define FF_LBA64 0
|
||||
/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
|
||||
/ To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
|
||||
|
||||
#define FF_MIN_GPT 0x10000000
|
||||
/* Minimum number of sectors to switch GPT as partitioning format in f_mkfs and
|
||||
/ f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
|
||||
|
||||
#define FF_USE_TRIM 1
|
||||
/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
|
||||
/ To enable Trim function, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define FF_FS_TINY (!CONFIG_FATFS_PER_FILE_CACHE)
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the filesystem object (FATFS) is used for the file data transfer. */
|
||||
|
||||
#define FF_FS_EXFAT 0
|
||||
/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
|
||||
/ To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
#define FF_FS_NORTC 0
|
||||
#define FF_NORTC_MON 1
|
||||
#define FF_NORTC_MDAY 1
|
||||
#define FF_NORTC_YEAR 2020
|
||||
/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
|
||||
/ the timestamp function. Every object modified by FatFs will have a fixed timestamp
|
||||
/ defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
|
||||
/ To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
|
||||
/ added to the project to read current time form real-time clock. FF_NORTC_MON,
|
||||
/ FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
|
||||
/ These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
|
||||
|
||||
#define FF_FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
#define FF_FS_LOCK CONFIG_FATFS_FS_LOCK
|
||||
/* The option FF_FS_LOCK switches file lock function to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock function. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock function. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock control is independent of re-entrancy. */
|
||||
|
||||
/* #include <somertos.h> // O/S definitions */
|
||||
#define FF_FS_REENTRANT 1
|
||||
#define FF_FS_TIMEOUT (CONFIG_FATFS_TIMEOUT_MS / portTICK_PERIOD_MS)
|
||||
#define FF_SYNC_t SemaphoreHandle_t
|
||||
/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this function.
|
||||
/
|
||||
/ 0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The FF_FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
/* Some memory allocation functions are declared here in addition to ff.h, so that
|
||||
they can be used also by external code when LFN feature is disabled.
|
||||
*/
|
||||
void *ff_memalloc(unsigned msize);
|
||||
void ff_memfree(void *);
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
/* Redefine names of disk IO functions to prevent name collisions */
|
||||
#define disk_initialize ff_disk_initialize
|
||||
#define disk_status ff_disk_status
|
||||
#define disk_read ff_disk_read
|
||||
#define disk_write ff_disk_write
|
||||
#define disk_ioctl ff_disk_ioctl
|
||||
@@ -1,169 +0,0 @@
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Sample Code of OS Dependent Functions for FatFs */
|
||||
/* (C)ChaN, 2018 */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
#include "ff_mh.h"
|
||||
|
||||
|
||||
#if FF_USE_LFN == 3 /* Dynamic memory allocation */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Allocate a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */
|
||||
UINT msize /* Number of bytes to allocate */
|
||||
)
|
||||
{
|
||||
return malloc(msize); /* Allocate a new memory block with POSIX API */
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Free a memory block */
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
void ff_memfree (
|
||||
void* mblock /* Pointer to the memory block to free (nothing to do if null) */
|
||||
)
|
||||
{
|
||||
free(mblock); /* Free the memory block with POSIX API */
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if FF_FS_REENTRANT /* Mutal exclusion */
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Create a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to create a new
|
||||
/ synchronization object for the volume, such as semaphore and mutex.
|
||||
/ When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
//const osMutexDef_t Mutex[FF_VOLUMES]; /* Table of CMSIS-RTOS mutex */
|
||||
|
||||
|
||||
int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */
|
||||
BYTE vol, /* Corresponding volume (logical drive number) */
|
||||
FF_SYNC_t* sobj /* Pointer to return the created sync object */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
*sobj = CreateMutex(NULL, FALSE, NULL);
|
||||
return (int)(*sobj != INVALID_HANDLE_VALUE);
|
||||
|
||||
/* uITRON */
|
||||
// T_CSEM csem = {TA_TPRI,1,1};
|
||||
// *sobj = acre_sem(&csem);
|
||||
// return (int)(*sobj > 0);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// *sobj = OSMutexCreate(0, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// *sobj = xSemaphoreCreateMutex();
|
||||
// return (int)(*sobj != NULL);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// *sobj = osMutexCreate(&Mutex[vol]);
|
||||
// return (int)(*sobj != NULL);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Delete a Synchronization Object */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called in f_mount() function to delete a synchronization
|
||||
/ object that created with ff_cre_syncobj() function. When a 0 is returned,
|
||||
/ the f_mount() function fails with FR_INT_ERR.
|
||||
*/
|
||||
|
||||
int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to an error */
|
||||
FF_SYNC_t sobj /* Sync object tied to the logical drive to be deleted */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)CloseHandle(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(del_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// vSemaphoreDelete(sobj);
|
||||
// return 1;
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexDelete(sobj) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Request Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on entering file functions to lock the volume.
|
||||
/ When a 0 is returned, the file function fails with FR_TIMEOUT.
|
||||
*/
|
||||
|
||||
int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */
|
||||
FF_SYNC_t sobj /* Sync object to wait */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
|
||||
|
||||
/* uITRON */
|
||||
// return (int)(wai_sem(sobj) == E_OK);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OS_ERR err;
|
||||
// OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
|
||||
// return (int)(err == OS_NO_ERR);
|
||||
|
||||
/* FreeRTOS */
|
||||
// return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
|
||||
}
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* Release Grant to Access the Volume */
|
||||
/*------------------------------------------------------------------------*/
|
||||
/* This function is called on leaving file functions to unlock the volume.
|
||||
*/
|
||||
|
||||
void ff_rel_grant (
|
||||
FF_SYNC_t sobj /* Sync object to be signaled */
|
||||
)
|
||||
{
|
||||
/* Win32 */
|
||||
ReleaseMutex(sobj);
|
||||
|
||||
/* uITRON */
|
||||
// sig_sem(sobj);
|
||||
|
||||
/* uC/OS-II */
|
||||
// OSMutexPost(sobj);
|
||||
|
||||
/* FreeRTOS */
|
||||
// xSemaphoreGive(sobj);
|
||||
|
||||
/* CMSIS-RTOS */
|
||||
// osMutexRelease(sobj);
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,321 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "ff_mh.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Register FATFS with VFS component
|
||||
*
|
||||
* This function registers given FAT drive in VFS, at the specified base path.
|
||||
* If only one drive is used, fat_drive argument can be an empty string.
|
||||
* Refer to FATFS library documentation on how to specify FAT drive.
|
||||
* This function also allocates FATFS structure which should be used for f_mount
|
||||
* call.
|
||||
*
|
||||
* @note This function doesn't mount the drive into FATFS, it just connects
|
||||
* POSIX and C standard library IO function with FATFS. You need to mount
|
||||
* desired drive into FATFS separately.
|
||||
*
|
||||
* @param base_path path prefix where FATFS should be registered
|
||||
* @param fat_drive FATFS drive specification; if only one drive is used, can be an empty string
|
||||
* @param max_files maximum number of files which can be open at the same time
|
||||
* @param[out] out_fs pointer to FATFS structure which can be used for FATFS f_mount call is returned via this argument.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_register was already called
|
||||
* - ESP_ERR_NO_MEM if not enough memory or too many VFSes already registered
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_register(const char* base_path, const char* fat_drive,
|
||||
size_t max_files, FATFS** out_fs);
|
||||
|
||||
/**
|
||||
* @brief Un-register FATFS from VFS
|
||||
*
|
||||
* @note FATFS structure returned by esp_vfs_fat_register is destroyed after
|
||||
* this call. Make sure to call f_mount function to unmount it before
|
||||
* calling esp_vfs_fat_unregister_ctx.
|
||||
* Difference between this function and the one above is that this one
|
||||
* will release the correct drive, while the one above will release
|
||||
* the last registered one
|
||||
*
|
||||
* @param base_path path prefix where FATFS is registered. This is the same
|
||||
* used when esp_vfs_fat_register was called
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if FATFS is not registered in VFS
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_unregister_path(const char* base_path);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Configuration arguments for esp_vfs_fat_sdmmc_mount and esp_vfs_fat_spiflash_mount_rw_wl functions
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* If FAT partition can not be mounted, and this parameter is true,
|
||||
* create partition table and format the filesystem.
|
||||
*/
|
||||
bool format_if_mount_failed;
|
||||
int max_files; ///< Max number of open files
|
||||
/**
|
||||
* If format_if_mount_failed is set, and mount fails, format the card
|
||||
* with given allocation unit size. Must be a power of 2, between sector
|
||||
* size and 128 * sector size.
|
||||
* For SD cards, sector size is always 512 bytes. For wear_levelling,
|
||||
* sector size is determined by CONFIG_WL_SECTOR_SIZE option.
|
||||
*
|
||||
* Using larger allocation unit size will result in higher read/write
|
||||
* performance and higher overhead when storing small files.
|
||||
*
|
||||
* Setting this field to 0 will result in allocation unit set to the
|
||||
* sector size.
|
||||
*/
|
||||
size_t allocation_unit_size;
|
||||
/**
|
||||
* Enables real ff_disk_status function implementation for SD cards
|
||||
* (ff_sdmmc_status). Possibly slows down IO performance.
|
||||
*
|
||||
* Try to enable if you need to handle situations when SD cards
|
||||
* are not unmounted properly before physical removal
|
||||
* or you are experiencing issues with SD cards.
|
||||
*
|
||||
* Doesn't do anything for other memory storage media.
|
||||
*/
|
||||
bool disk_status_check_enable;
|
||||
} esp_vfs_fat_mount_config_t;
|
||||
|
||||
// Compatibility definition
|
||||
typedef esp_vfs_fat_mount_config_t esp_vfs_fat_sdmmc_mount_config_t;
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||
* - initializes SD card with configuration in slot_config
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||
* SDMMC peripheral, this structure can be initialized using
|
||||
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||
* macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||
* slot_config, and attach it to an initialized SPI bus.
|
||||
* - initializes SD card with configuration in host_config_input
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||
* `spi_bus_initialize()` before.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||
* of them in your application.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config_input,
|
||||
const sdspi_device_config_t* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_sdmmc_mount
|
||||
*
|
||||
* @deprecated Use `esp_vfs_fat_sdcard_unmount()` instead.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void);
|
||||
|
||||
/**
|
||||
* @brief Unmount an SD card from the FAT filesystem and release resources acquired using
|
||||
* `esp_vfs_fat_sdmmc_mount()` or `esp_vfs_fat_sdspi_mount()`
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if the card argument is unregistered
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount(const char* base_path, sdmmc_card_t *card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize FAT filesystem in SPI flash and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - initializes flash wear levelling library on top of the given partition
|
||||
* - mounts FAT partition using FATFS library on top of flash wear levelling
|
||||
* library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] wl_handle wear levelling driver handle
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from wear levelling library, SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_rw_wl
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param wl_handle wear levelling driver handle returned by esp_vfs_fat_spiflash_mount_rw_wl
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_rw_wl(const char* base_path, wl_handle_t wl_handle);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to initialize read-only FAT filesystem and register it in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
*
|
||||
* - finds the partition with defined partition_label. Partition label should be
|
||||
* configured in the partition table.
|
||||
* - mounts FAT partition using FATFS library
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* @note Wear levelling is not used when FAT is mounted in read-only mode using this function.
|
||||
*
|
||||
* @param base_path path where FATFS partition should be mounted (e.g. "/spiflash")
|
||||
* @param partition_label label of the partition which should be used
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_FOUND if the partition table does not contain FATFS partition with given label
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_ro was already called for the same partition
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SPI flash driver, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config);
|
||||
|
||||
/**
|
||||
* @brief Unmount FAT filesystem and release resources acquired using esp_vfs_fat_spiflash_mount_ro
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/spiflash")
|
||||
* @param partition_label label of partition to be unmounted
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_spiflash_mount_rw_wl hasn't been called
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* partition_label);
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
__attribute__((deprecated("esp_vfs_fat_spiflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_rw_wl instead")));
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle)
|
||||
__attribute__((deprecated("esp_vfs_fat_spiflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_rw_wl instead")));
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
__attribute__((deprecated("esp_vfs_fat_rawflash_mount is deprecated, please use esp_vfs_fat_spiflash_mount_ro instead")));
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label)
|
||||
__attribute__((deprecated("esp_vfs_fat_rawflash_unmount is deprecated, please use esp_vfs_fat_spiflash_unmount_ro instead")));
|
||||
|
||||
/**
|
||||
* @brief Get information for FATFS partition
|
||||
*
|
||||
* @param base_path Path where partition should be registered (e.g. "/spiflash")
|
||||
* @param[out] out_total_bytes Size of the file system
|
||||
* @param[out] out_free_bytes Current used bytes in the file system
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if partition not found
|
||||
* - ESP_FAIL if another FRESULT error (saved in errno)
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_info(const char* base_path, uint64_t* out_total_bytes, uint64_t* out_free_bytes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,30 +0,0 @@
|
||||
// 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.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include <sys/param.h>
|
||||
#include <stddef.h>
|
||||
|
||||
static inline size_t esp_vfs_fat_get_allocation_unit_size(
|
||||
size_t sector_size, size_t requested_size)
|
||||
{
|
||||
size_t alloc_unit_size = requested_size;
|
||||
const size_t max_sectors_per_cylinder = 128;
|
||||
const size_t max_size = sector_size * max_sectors_per_cylinder;
|
||||
alloc_unit_size = MAX(alloc_unit_size, sector_size);
|
||||
alloc_unit_size = MIN(alloc_unit_size, max_size);
|
||||
return alloc_unit_size;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,375 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "vfs_fat_internal_mh.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
#include "diskio_impl_mh.h"
|
||||
#include "diskio_sdmmc_mh.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "driver/sdmmc_host.h"
|
||||
#endif
|
||||
|
||||
static const char* TAG = "vfs_fat_sdmmc";
|
||||
static sdmmc_card_t* s_card = NULL;
|
||||
static uint8_t s_pdrv = FF_DRV_NOT_USED;
|
||||
static char * s_base_path = NULL;
|
||||
|
||||
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||
if ((err) !=ESP_OK) { \
|
||||
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
static void call_host_deinit(const sdmmc_host_t *host_config);
|
||||
static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config,
|
||||
const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||
|
||||
static esp_err_t mount_prepare_mem(const char *base_path,
|
||||
BYTE *out_pdrv,
|
||||
char **out_dup_path,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
char* dup_path = NULL;
|
||||
sdmmc_card_t* card = NULL;
|
||||
|
||||
// connect SDMMC driver to FATFS
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
|
||||
}
|
||||
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||
if (card == NULL) {
|
||||
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dup_path = strdup(base_path);
|
||||
if(!dup_path){
|
||||
ESP_LOGD(TAG, "could not copy base_path");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_card = card;
|
||||
*out_pdrv = pdrv;
|
||||
*out_dup_path = dup_path;
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t mount_to_vfs_fat(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv,
|
||||
const char *base_path)
|
||||
{
|
||||
FATFS* fs = NULL;
|
||||
esp_err_t err;
|
||||
ff_diskio_register_sdmmc(pdrv, card);
|
||||
ff_sdmmc_set_disk_status_check(pdrv, mount_config->disk_status_check_enable);
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
if (!((res == FR_NO_FILESYSTEM || res == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = partition_card(mount_config, drv, card, pdrv);
|
||||
if (err != ESP_OK) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t partition_card(const esp_vfs_fat_mount_config_t *mount_config,
|
||||
const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||
{
|
||||
FRESULT res = FR_OK;
|
||||
esp_err_t err;
|
||||
const size_t workbuf_size = 4096;
|
||||
void* workbuf = NULL;
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
LBA_t plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(pdrv, plist, workbuf);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
card->csd.sector_size,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(workbuf);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
free(workbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
static esp_err_t init_sdmmc_host(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
*out_slot = slot;
|
||||
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config,
|
||||
const void* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err;
|
||||
int card_handle = -1; //uninitialized
|
||||
sdmmc_card_t* card = NULL;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
char* dup_path = NULL;
|
||||
bool host_inited = false;
|
||||
|
||||
err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
//deinit() needs to be called to revert the init
|
||||
host_inited = true;
|
||||
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||
err = init_sdmmc_host(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
if (s_card == NULL) {
|
||||
//store the ctx locally to be back-compatible
|
||||
s_card = card;
|
||||
s_pdrv = pdrv;
|
||||
s_base_path = dup_path;
|
||||
} else {
|
||||
free(dup_path);
|
||||
}
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit(host_config);
|
||||
}
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t init_sdspi_host(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||
bus first and check the device parameters."
|
||||
, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdspi_mount(const char* base_path,
|
||||
const sdmmc_host_t* host_config_input,
|
||||
const sdspi_device_config_t* slot_config,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
sdmmc_card_t** out_card)
|
||||
{
|
||||
const sdmmc_host_t* host_config = host_config_input;
|
||||
esp_err_t err;
|
||||
int card_handle = -1; //uninitialized
|
||||
bool host_inited = false;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
sdmmc_card_t* card = NULL;
|
||||
char* dup_path = NULL;
|
||||
|
||||
err = mount_prepare_mem(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
|
||||
err = init_sdspi_host(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||
//to be called to revert `init_sdspi_host`
|
||||
host_inited = true;
|
||||
|
||||
/*
|
||||
* The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||
* above. But the input pointer is const, so create a new variable.
|
||||
*/
|
||||
sdmmc_host_t new_config;
|
||||
if (card_handle != host_config->slot) {
|
||||
new_config = *host_config_input;
|
||||
host_config = &new_config;
|
||||
new_config.slot = card_handle;
|
||||
}
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat(mount_config, card, pdrv, dup_path);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
if (s_card == NULL) {
|
||||
//store the ctx locally to be back-compatible
|
||||
s_card = card;
|
||||
s_pdrv = pdrv;
|
||||
s_base_path = dup_path;
|
||||
} else {
|
||||
free(dup_path);
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit(host_config);
|
||||
}
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static void local_card_remove(void)
|
||||
{
|
||||
s_card = NULL;
|
||||
free(s_base_path);
|
||||
s_base_path = NULL;
|
||||
s_pdrv = FF_DRV_NOT_USED;
|
||||
}
|
||||
|
||||
static void call_host_deinit(const sdmmc_host_t *host_config)
|
||||
{
|
||||
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||
host_config->deinit_p(host_config->slot);
|
||||
} else {
|
||||
host_config->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t unmount_card_core(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card(card);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
ff_diskio_unregister(pdrv);
|
||||
|
||||
call_host_deinit(&card->host);
|
||||
free(card);
|
||||
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount(void)
|
||||
{
|
||||
sdmmc_card_t* card = s_card;
|
||||
esp_err_t err = unmount_card_core(s_base_path, card);
|
||||
local_card_remove();
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
esp_err_t err = unmount_card_core(base_path, card);
|
||||
if (s_card == card) {
|
||||
local_card_remove();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -1,218 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "vfs_fat_internal_mh.h"
|
||||
#include "diskio_impl_mh.h"
|
||||
|
||||
#include "diskio_rawflash_mh.h"
|
||||
|
||||
#include "wear_levelling.h"
|
||||
#include "diskio_wl_mh.h"
|
||||
|
||||
static const char* TAG = "vfs_fat_spiflash";
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_rw_wl(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = NULL;
|
||||
|
||||
esp_partition_subtype_t subtype = partition_label ?
|
||||
ESP_PARTITION_SUBTYPE_ANY : ESP_PARTITION_SUBTYPE_DATA_FAT;
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
subtype, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
result = wl_mount(data_partition, wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "failed to mount wear levelling layer. result = %i", result);
|
||||
return result;
|
||||
}
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_wl_partition(pdrv, *wl_handle);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_wl_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
if (!((fresult == FR_NO_FILESYSTEM || fresult == FR_INT_ERR)
|
||||
&& mount_config->format_if_mount_failed)) {
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
if (workbuf == NULL) {
|
||||
result = ESP_ERR_NO_MEM;
|
||||
goto fail;
|
||||
}
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(
|
||||
CONFIG_WL_SECTOR_SIZE,
|
||||
mount_config->allocation_unit_size);
|
||||
ESP_LOGI(TAG, "Formatting FATFS partition, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)(FM_ANY | FM_SFD), 0, 0, 0, alloc_unit_size};
|
||||
fresult = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mkfs failed (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
free(workbuf);
|
||||
workbuf = NULL;
|
||||
ESP_LOGI(TAG, "Mounting again");
|
||||
fresult = f_mount(fs, drv, 0);
|
||||
if (fresult != FR_OK) {
|
||||
result = ESP_FAIL;
|
||||
ESP_LOGE(TAG, "f_mount failed after formatting (%d)", fresult);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
free(workbuf);
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_rw_wl(const char* base_path, wl_handle_t wl_handle)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_wl(wl_handle);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
ff_diskio_clear_pdrv_wl(wl_handle);
|
||||
// release partition driver
|
||||
esp_err_t err_drv = wl_unmount(wl_handle);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
if (err == ESP_OK) err = err_drv;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount_ro(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
{
|
||||
esp_err_t result = ESP_OK;
|
||||
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
// connect driver to FATFS
|
||||
BYTE pdrv = 0xFF;
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
result = ff_diskio_register_raw_partition(pdrv, data_partition);
|
||||
if (result != ESP_OK) {
|
||||
ESP_LOGE(TAG, "ff_diskio_register_raw_partition failed pdrv=%i, error - 0x(%x)", pdrv, result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
FATFS *fs;
|
||||
result = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
if (result == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (result != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", result);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
FRESULT fresult = f_mount(fs, drv, 1);
|
||||
if (fresult != FR_OK) {
|
||||
ESP_LOGW(TAG, "f_mount failed (%d)", fresult);
|
||||
result = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return result;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount_ro(const char* base_path, const char* partition_label)
|
||||
{
|
||||
const esp_partition_t *data_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA,
|
||||
ESP_PARTITION_SUBTYPE_DATA_FAT, partition_label);
|
||||
|
||||
if (data_partition == NULL) {
|
||||
ESP_LOGE(TAG, "Failed to find FATFS partition (type='data', subtype='fat', partition_label='%s'). Check the partition table.", partition_label);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_raw(data_partition);
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
f_mount(0, drv, 0);
|
||||
ff_diskio_unregister(pdrv);
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_vfs_fat_spiflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config,
|
||||
wl_handle_t* wl_handle)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_mount_rw_wl")));
|
||||
esp_err_t esp_vfs_fat_spiflash_unmount(const char* base_path, wl_handle_t wl_handle)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_unmount_rw_wl")));
|
||||
esp_err_t esp_vfs_fat_rawflash_mount(const char* base_path,
|
||||
const char* partition_label,
|
||||
const esp_vfs_fat_mount_config_t* mount_config)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_mount_ro")));
|
||||
esp_err_t esp_vfs_fat_rawflash_unmount(const char* base_path, const char* partition_label)
|
||||
__attribute__((alias("esp_vfs_fat_spiflash_unmount_ro")));
|
||||
@@ -1,11 +0,0 @@
|
||||
idf_component_register(SRCS "sdmmc_cmd_mh.c"
|
||||
"sdmmc_common_mh.c"
|
||||
"sdmmc_init_mh.c"
|
||||
"sdmmc_io_mh.c"
|
||||
"sdmmc_mmc_mh.c"
|
||||
"sdmmc_sd_mh.c"
|
||||
INCLUDE_DIRS "." "include"
|
||||
REQUIRES driver esp-fatfs
|
||||
PRIV_REQUIRES soc)
|
||||
|
||||
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
|
||||
@@ -1,279 +0,0 @@
|
||||
# AIOTED related changes, see https://github.com/jomjol/AI-on-the-edge-device/pull/2781
|
||||
These files/folders were copied from `framework-espidf@3.50002.230601/components/` and adapted to our own needs.
|
||||
Since not every SD/MMC was recognized and this was due to the implementation of ATA trim support, this was revised.
|
||||
Furthermore, files that we don't need were deleted from it.
|
||||
|
||||
## The most relevant changes are:
|
||||
### fatfs/diskio/diskio_sdmmc.c
|
||||
DRESULT ff_sdmmc_ioctl (BYTE pdrv, BYTE cmd, void* buff), at lines 106 to 110 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if (FF_USE_TRIM)
|
||||
case CTRL_TRIM:
|
||||
if(FF_CAN_TRIM){
|
||||
return ff_sdmmc_trim (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
}
|
||||
else{
|
||||
return RES_ERROR;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
```
|
||||
|
||||
### fatfs/src/ff.c
|
||||
added:
|
||||
```c
|
||||
#include "sdmmc_cmd.h"
|
||||
```
|
||||
|
||||
static FRESULT remove_chain(FFOBJID* obj, DWORD clst, DWORD pclst), at lines 1437 to 1454 changed from:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
} else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
to:
|
||||
```c
|
||||
#if FF_FS_EXFAT || FF_USE_TRIM
|
||||
if(FF_FS_EXFAT || FF_CAN_TRIM){
|
||||
if (ecl + 1 == nxt) { /* Is next cluster contiguous? */
|
||||
ecl = nxt;
|
||||
}
|
||||
else { /* End of contiguous cluster block */
|
||||
#if FF_FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) {
|
||||
res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
|
||||
if (res != FR_OK) return res;
|
||||
}
|
||||
#endif
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
rt[0] = clst2sect(fs, scl); /* Start of data area to be freed */
|
||||
rt[1] = clst2sect(fs, ecl) + fs->csize - 1; /* End of data area to be freed */
|
||||
disk_ioctl(fs->pdrv, CTRL_TRIM, rt); /* Inform storage device that the data in the block may be erased */
|
||||
}
|
||||
#endif
|
||||
scl = ecl = nxt;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 5946 to 5949 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
FRESULT f_mkfs(const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len), at lines 6175 to 6178 changed from:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
#endif
|
||||
```
|
||||
to:
|
||||
```c
|
||||
#if FF_USE_TRIM
|
||||
if(FF_CAN_TRIM){
|
||||
lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1; /* Inform storage device that the volume area may be erased */
|
||||
disk_ioctl(pdrv, CTRL_TRIM, lba);
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### sdmmc/sdmmc_cmd.c
|
||||
added:
|
||||
```c
|
||||
int FF_CAN_TRIM = 0;
|
||||
```
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card), at lines 630 to 636 changed from:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
to:
|
||||
```c
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
```
|
||||
|
||||
### sdmmc/include/sdmmc_cmd.h
|
||||
|
||||
added:
|
||||
```c
|
||||
extern int FF_CAN_TRIM;
|
||||
```
|
||||
|
||||
|
||||
# Espressif IoT Development Framework
|
||||
|
||||
* [中文版](./README_CN.md)
|
||||
|
||||
ESP-IDF is the development framework for Espressif SoCs supported on Windows, Linux and macOS.
|
||||
|
||||
# ESP-IDF Release Support Schedule
|
||||
|
||||

|
||||
|
||||
- Please read [the support policy](SUPPORT_POLICY.md) and [the documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) for more information about ESP-IDF versions.
|
||||
- Please see the [End-of-Life Advisories](https://www.espressif.com/en/support/documents/advisories?keys=&field_type_of_advisory_tid%5B%5D=817) for information about ESP-IDF releases with discontinued support.
|
||||
|
||||
# ESP-IDF Release and SoC Compatibility
|
||||
|
||||
The following table shows ESP-IDF support of Espressif SoCs where ![alt text][preview] and ![alt text][supported] denote preview status and support, respectively. The preview support is usually limited in time and intended for beta versions of chips. Please use an ESP-IDF release where the desired SoC is already supported.
|
||||
|
||||
|Chip | v4.1 | v4.2 | v4.3 | v4.4 | v5.0 | |
|
||||
|:----------- |:---------------------:| :---------------------:| :---------------------:| :---------------------:| :---------------------:|:------------------------------------------------------------------------------------ |
|
||||
|ESP32 |![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S2 | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-C3 | | | ![alt text][supported] | ![alt text][supported] | ![alt text][supported] | |
|
||||
|ESP32-S3 | | | | ![alt text][supported] | ![alt text][supported] | [Announcement](https://www.espressif.com/en/news/ESP32_S3) |
|
||||
|ESP32-C2 | | | | | ![alt text][supported] | [Announcement](https://blog.espressif.com/esp32-c2-and-why-it-matter-s-bcf4d7d0b2c6) |
|
||||
|ESP32-H2 | | | | ![alt text][preview] | ![alt text][preview] | [Announcement](https://www.espressif.com/en/news/ESP32_H2) |
|
||||
|
||||
[supported]: https://img.shields.io/badge/-supported-green "supported"
|
||||
[preview]: https://img.shields.io/badge/-preview-orange "preview"
|
||||
|
||||
Espressif SoCs released before 2016 (ESP8266 and ESP8285) are supported by [RTOS SDK](https://github.com/espressif/ESP8266_RTOS_SDK) instead.
|
||||
|
||||
# Developing With ESP-IDF
|
||||
|
||||
## Setting Up ESP-IDF
|
||||
|
||||
See https://idf.espressif.com/ for links to detailed instructions on how to set up the ESP-IDF depending on chip you use.
|
||||
|
||||
**Note:** Each SoC series and each ESP-IDF release has its own documentation. Please see Section [Versions](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/versions.html) on how to find documentation and how to checkout specific release of ESP-IDF.
|
||||
|
||||
### Non-GitHub forks
|
||||
|
||||
ESP-IDF uses relative locations as its submodules URLs ([.gitmodules](.gitmodules)). So they link to GitHub. If ESP-IDF is forked to a Git repository which is not on GitHub, you will need to run the script [tools/set-submodules-to-github.sh](tools/set-submodules-to-github.sh) after git clone.
|
||||
|
||||
The script sets absolute URLs for all submodules, allowing `git submodule update --init --recursive` to complete. If cloning ESP-IDF from GitHub, this step is not needed.
|
||||
|
||||
## Finding a Project
|
||||
|
||||
As well as the [esp-idf-template](https://github.com/espressif/esp-idf-template) project mentioned in Getting Started, ESP-IDF comes with some example projects in the [examples](examples) directory.
|
||||
|
||||
Once you've found the project you want to work with, change to its directory and you can configure and build it.
|
||||
|
||||
To start your own project based on an example, copy the example project directory outside of the ESP-IDF directory.
|
||||
|
||||
# Quick Reference
|
||||
|
||||
See the Getting Started guide links above for a detailed setup guide. This is a quick reference for common commands when working with ESP-IDF projects:
|
||||
|
||||
## Setup Build Environment
|
||||
|
||||
(See the Getting Started guide listed above for a full list of required steps with more details.)
|
||||
|
||||
* Install host build dependencies mentioned in the Getting Started guide.
|
||||
* Run the install script to set up the build environment. The options include `install.bat` or `install.ps1` for Windows, and `install.sh` or `install.fish` for Unix shells.
|
||||
* Run the export script on Windows (`export.bat`) or source it on Unix (`source export.sh`) in every shell environment before using ESP-IDF.
|
||||
|
||||
## Configuring the Project
|
||||
|
||||
* `idf.py set-target <chip_name>` sets the target of the project to `<chip_name>`. Run `idf.py set-target` without any arguments to see a list of supported targets.
|
||||
* `idf.py menuconfig` opens a text-based configuration menu where you can configure the project.
|
||||
|
||||
## Compiling the Project
|
||||
|
||||
`idf.py build`
|
||||
|
||||
... will compile app, bootloader and generate a partition table based on the config.
|
||||
|
||||
## Flashing the Project
|
||||
|
||||
When the build finishes, it will print a command line to use esptool.py to flash the chip. However you can also do this automatically by running:
|
||||
|
||||
`idf.py -p PORT flash`
|
||||
|
||||
Replace PORT with the name of your serial port (like `COM3` on Windows, `/dev/ttyUSB0` on Linux, or `/dev/cu.usbserial-X` on MacOS. If the `-p` option is left out, `idf.py flash` will try to flash the first available serial port.
|
||||
|
||||
This will flash the entire project (app, bootloader and partition table) to a new chip. The settings for serial port flashing can be configured with `idf.py menuconfig`.
|
||||
|
||||
You don't need to run `idf.py build` before running `idf.py flash`, `idf.py flash` will automatically rebuild anything which needs it.
|
||||
|
||||
## Viewing Serial Output
|
||||
|
||||
The `idf.py monitor` target uses the [idf_monitor tool](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html) to display serial output from Espressif SoCs. idf_monitor also has a range of features to decode crash output and interact with the device. [Check the documentation page for details](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/idf-monitor.html).
|
||||
|
||||
Exit the monitor by typing Ctrl-].
|
||||
|
||||
To build, flash and monitor output in one pass, you can run:
|
||||
|
||||
`idf.py flash monitor`
|
||||
|
||||
## Compiling & Flashing Only the App
|
||||
|
||||
After the initial flash, you may just want to build and flash just your app, not the bootloader and partition table:
|
||||
|
||||
* `idf.py app` - build just the app.
|
||||
* `idf.py app-flash` - flash just the app.
|
||||
|
||||
`idf.py app-flash` will automatically rebuild the app if any source files have changed.
|
||||
|
||||
(In normal development there's no downside to reflashing the bootloader and partition table each time, if they haven't changed.)
|
||||
|
||||
## Erasing Flash
|
||||
|
||||
The `idf.py flash` target does not erase the entire flash contents. However it is sometimes useful to set the device back to a totally erased state, particularly when making partition table changes or OTA app updates. To erase the entire flash, run `idf.py erase-flash`.
|
||||
|
||||
This can be combined with other targets, ie `idf.py -p PORT erase-flash flash` will erase everything and then re-flash the new app, bootloader and partition table.
|
||||
|
||||
# Resources
|
||||
|
||||
* Documentation for the latest version: https://docs.espressif.com/projects/esp-idf/. This documentation is built from the [docs directory](docs) of this repository.
|
||||
|
||||
* The [esp32.com forum](https://esp32.com/) is a place to ask questions and find community resources.
|
||||
|
||||
* [Check the Issues section on github](https://github.com/espressif/esp-idf/issues) if you find a bug or have a feature request. Please check existing Issues before opening a new one.
|
||||
|
||||
* If you're interested in contributing to ESP-IDF, please check the [Contributions Guide](https://docs.espressif.com/projects/esp-idf/en/latest/contribute/index.html).
|
||||
@@ -1,357 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int FF_CAN_TRIM;
|
||||
|
||||
/**
|
||||
* Probe and initialize SD/MMC card using given host
|
||||
*
|
||||
* @note Only SD cards (SDSC and SDHC/SDXC) are supported now.
|
||||
* Support for MMC/eMMC cards will be added later.
|
||||
*
|
||||
* @param host pointer to structure defining host controller
|
||||
* @param out_card pointer to structure which will receive information
|
||||
* about the card when the function completes
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_card_init(const sdmmc_host_t* host,
|
||||
sdmmc_card_t* out_card);
|
||||
|
||||
/**
|
||||
* @brief Print information about the card to a stream
|
||||
* @param stream stream obtained using fopen or fdopen
|
||||
* @param card card information structure initialized using sdmmc_card_init
|
||||
*/
|
||||
void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Get status of SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_get_status(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Write given number of sectors to SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param src pointer to data buffer to read data from; data size must be
|
||||
* equal to sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start writing
|
||||
* @param sector_count number of sectors to write
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
/**
|
||||
* Read given number of sectors from the SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param dst pointer to data buffer to write into; buffer size must be
|
||||
* at least sector_count * card->csd.sector_size
|
||||
* @param start_sector sector where to start reading
|
||||
* @param sector_count number of sectors to read
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_sector, size_t sector_count);
|
||||
|
||||
/**
|
||||
* Erase given number of sectors from the SD/MMC card
|
||||
*
|
||||
* @note When sdmmc_erase_sectors used with cards in SDSPI mode, it was
|
||||
* observed that card requires re-init after erase operation.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param start_sector sector where to start erase
|
||||
* @param sector_count number of sectors to erase
|
||||
* @param arg erase command (CMD38) argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
|
||||
size_t sector_count, sdmmc_erase_arg_t arg);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports discard
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_can_discard(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports trim
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Check if SD/MMC card supports sanitize
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK if supported by the card/device
|
||||
* - ESP_FAIL if not supported by the card/device
|
||||
*/
|
||||
esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Sanitize the data that was unmapped by a Discard command
|
||||
*
|
||||
* @note Discard command has to precede sanitize operation. To discard, use
|
||||
* MMC_DICARD_ARG with sdmmc_erase_sectors argument
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param timeout_ms timeout value in milliseconds required to sanitize the
|
||||
* selected range of sectors.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* Erase complete SD/MMC card
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_full_erase(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Read one byte from an SDIO card using IO_RW_DIRECT (CMD52)
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param reg byte address within IO function
|
||||
* @param[out] out_byte output, receives the value read from the card
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t reg, uint8_t *out_byte);
|
||||
|
||||
/**
|
||||
* Write one byte to an SDIO card using IO_RW_DIRECT (CMD52)
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param reg byte address within IO function
|
||||
* @param in_byte value to be written
|
||||
* @param[out] out_byte if not NULL, receives new byte value read
|
||||
* from the card (read-after-write).
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_byte(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t reg, uint8_t in_byte, uint8_t* out_byte);
|
||||
|
||||
/**
|
||||
* Read multiple bytes from an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs read operation using CMD53 in byte mode.
|
||||
* For block mode, see sdmmc_io_read_blocks.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where reading starts
|
||||
* @param dst buffer which receives the data read from card
|
||||
* @param size number of bytes to read
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* Write multiple bytes to an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs write operation using CMD53 in byte mode.
|
||||
* For block mode, see sdmmc_io_write_blocks.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param src data to be written
|
||||
* @param size number of bytes to write
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size exceeds 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_bytes(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* Read blocks of data from an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs read operation using CMD53 in block mode.
|
||||
* For byte mode, see sdmmc_io_read_bytes.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param dst buffer which receives the data read from card
|
||||
* @param size number of bytes to read, must be divisible by the card block
|
||||
* size.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_read_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, void* dst, size_t size);
|
||||
|
||||
/**
|
||||
* Write blocks of data to an SDIO card using IO_RW_EXTENDED (CMD53)
|
||||
*
|
||||
* This function performs write operation using CMD53 in block mode.
|
||||
* For byte mode, see sdmmc_io_write_bytes.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param function IO function number
|
||||
* @param addr byte address within IO function where writing starts
|
||||
* @param src data to be written
|
||||
* @param size number of bytes to read, must be divisible by the card block
|
||||
* size.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_SIZE if size is not divisible by 512 bytes
|
||||
* - One of the error codes from SDMMC host controller
|
||||
*/
|
||||
esp_err_t sdmmc_io_write_blocks(sdmmc_card_t* card, uint32_t function,
|
||||
uint32_t addr, const void* src, size_t size);
|
||||
|
||||
/**
|
||||
* Enable SDIO interrupt in the SDMMC host
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NOT_SUPPORTED if the host controller does not support
|
||||
* IO interrupts
|
||||
*/
|
||||
esp_err_t sdmmc_io_enable_int(sdmmc_card_t* card);
|
||||
|
||||
/**
|
||||
* Block until an SDIO interrupt is received
|
||||
*
|
||||
* Slave uses D1 line to signal interrupt condition to the host.
|
||||
* This function can be used to wait for the interrupt.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param timeout_ticks time to wait for the interrupt, in RTOS ticks
|
||||
* @return
|
||||
* - ESP_OK if the interrupt is received
|
||||
* - ESP_ERR_NOT_SUPPORTED if the host controller does not support
|
||||
* IO interrupts
|
||||
* - ESP_ERR_TIMEOUT if the interrupt does not happen in timeout_ticks
|
||||
*/
|
||||
esp_err_t sdmmc_io_wait_int(sdmmc_card_t* card, TickType_t timeout_ticks);
|
||||
|
||||
/**
|
||||
* Get the data of CIS region of an SDIO card.
|
||||
*
|
||||
* You may provide a buffer not sufficient to store all the CIS data. In this
|
||||
* case, this function stores as much data into your buffer as possible. Also,
|
||||
* this function will try to get and return the size required for you.
|
||||
*
|
||||
* @param card pointer to card information structure previously initialized
|
||||
* using sdmmc_card_init
|
||||
* @param out_buffer Output buffer of the CIS data
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @param inout_cis_size Mandatory, pointer to a size, input and output.
|
||||
* - input: Limitation of maximum searching range, should be 0 or larger than
|
||||
* buffer_size. The function searches for CIS_CODE_END until this range. Set to
|
||||
* 0 to search infinitely.
|
||||
* - output: The size required to store all the CIS data, if CIS_CODE_END is found.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: on success
|
||||
* - ESP_ERR_INVALID_RESPONSE: if the card does not (correctly) support CIS.
|
||||
* - ESP_ERR_INVALID_SIZE: CIS_CODE_END found, but buffer_size is less than
|
||||
* required size, which is stored in the inout_cis_size then.
|
||||
* - ESP_ERR_NOT_FOUND: if the CIS_CODE_END not found. Increase input value of
|
||||
* inout_cis_size or set it to 0, if you still want to search for the end;
|
||||
* output value of inout_cis_size is invalid in this case.
|
||||
* - and other error code return from sdmmc_io_read_bytes
|
||||
*/
|
||||
esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size);
|
||||
|
||||
/**
|
||||
* Parse and print the CIS information of an SDIO card.
|
||||
*
|
||||
* @note Not all the CIS codes and all kinds of tuples are supported. If you
|
||||
* see some unresolved code, you can add the parsing of these code in
|
||||
* sdmmc_io.c and contribute to the IDF through the Github repository.
|
||||
*
|
||||
* using sdmmc_card_init
|
||||
* @param buffer Buffer to parse
|
||||
* @param buffer_size Size of the buffer.
|
||||
* @param fp File pointer to print to, set to NULL to print to stdout.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: on success
|
||||
* - ESP_ERR_NOT_SUPPORTED: if the value from the card is not supported to be parsed.
|
||||
* - ESP_ERR_INVALID_SIZE: if the CIS size fields are not correct.
|
||||
*/
|
||||
esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,701 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "sdmmc_common_mh.h"
|
||||
|
||||
int FF_CAN_TRIM = 0;
|
||||
|
||||
static const char* TAG = "sdmmc_cmd";
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_app_cmd(sdmmc_card_t* card, sdmmc_command_t* cmd)
|
||||
{
|
||||
sdmmc_command_t app_cmd = {
|
||||
.opcode = MMC_APP_CMD,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &app_cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
// Check APP_CMD status bit (only in SD mode)
|
||||
if (!host_is_spi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) {
|
||||
ESP_LOGW(TAG, "card doesn't support APP_CMD");
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
return sdmmc_send_cmd(card, cmd);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_go_idle_state(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_GO_IDLE_STATE,
|
||||
.flags = SCF_CMD_BC | SCF_RSP_R0,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (host_is_spi(card)) {
|
||||
/* To enter SPI mode, CMD0 needs to be sent twice (see figure 4-1 in
|
||||
* SD Simplified spec v4.10). Some cards enter SD mode on first CMD0,
|
||||
* so don't expect the above command to succeed.
|
||||
* SCF_RSP_R1 flag below tells the lower layer to expect correct R1
|
||||
* response (in SPI mode).
|
||||
*/
|
||||
(void) err;
|
||||
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
||||
|
||||
cmd.flags |= SCF_RSP_R1;
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
if (err == ESP_OK) {
|
||||
vTaskDelay(SDMMC_GO_IDLE_DELAY_MS / portTICK_PERIOD_MS);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_if_cond(sdmmc_card_t* card, uint32_t ocr)
|
||||
{
|
||||
const uint8_t pattern = 0xaa; /* any pattern will do here */
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_IF_COND,
|
||||
.arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R7,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
uint8_t response = cmd.response[0] & 0xff;
|
||||
if (response != pattern) {
|
||||
ESP_LOGD(TAG, "%s: received=0x%x expected=0x%x", __func__, response, pattern);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_op_cond(sdmmc_card_t* card, uint32_t ocr, uint32_t *ocrp)
|
||||
{
|
||||
esp_err_t err;
|
||||
|
||||
sdmmc_command_t cmd = {
|
||||
.arg = ocr,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R3,
|
||||
.opcode = SD_APP_OP_COND
|
||||
};
|
||||
int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES;
|
||||
int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS;
|
||||
for (; nretries != 0; --nretries) {
|
||||
bzero(&cmd, sizeof cmd);
|
||||
cmd.arg = ocr;
|
||||
cmd.flags = SCF_CMD_BCR | SCF_RSP_R3;
|
||||
if (!card->is_mmc) { /* SD mode */
|
||||
cmd.opcode = SD_APP_OP_COND;
|
||||
err = sdmmc_send_app_cmd(card, &cmd);
|
||||
} else { /* MMC mode */
|
||||
cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK;
|
||||
cmd.arg |= MMC_OCR_SECTOR_MODE;
|
||||
cmd.opcode = MMC_SEND_OP_COND;
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
if (err != ESP_OK) {
|
||||
if (--err_cnt == 0) {
|
||||
ESP_LOGD(TAG, "%s: sdmmc_send_app_cmd err=0x%x", __func__, err);
|
||||
return err;
|
||||
} else {
|
||||
ESP_LOGV(TAG, "%s: ignoring err=0x%x", __func__, err);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// In SD protocol, card sets MEM_READY bit in OCR when it is ready.
|
||||
// In SPI protocol, card clears IDLE_STATE bit in R1 response.
|
||||
if (!host_is_spi(card)) {
|
||||
if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) ||
|
||||
ocr == 0) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
vTaskDelay(10 / portTICK_PERIOD_MS);
|
||||
}
|
||||
if (nretries == 0) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
if (ocrp) {
|
||||
*ocrp = MMC_R3(cmd.response);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_read_ocr(sdmmc_card_t *card, uint32_t *ocrp)
|
||||
{
|
||||
assert(ocrp);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_READ_OCR,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*ocrp = SD_SPI_R3(cmd.response);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_all_send_cid(sdmmc_card_t* card, sdmmc_response_t* out_raw_cid)
|
||||
{
|
||||
assert(out_raw_cid);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_ALL_SEND_CID,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R2
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
memcpy(out_raw_cid, &cmd.response, sizeof(sdmmc_response_t));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_cid(sdmmc_card_t *card, sdmmc_cid_t *out_cid)
|
||||
{
|
||||
assert(out_cid);
|
||||
assert(host_is_spi(card) && "SEND_CID should only be used in SPI mode");
|
||||
assert(!card->is_mmc && "MMC cards are not supported in SPI mode");
|
||||
sdmmc_response_t buf;
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_CID,
|
||||
.flags = SCF_CMD_READ | SCF_CMD_ADTC,
|
||||
.arg = 0,
|
||||
.data = &buf[0],
|
||||
.datalen = sizeof(buf)
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
sdmmc_flip_byte_order(buf, sizeof(buf));
|
||||
return sdmmc_decode_cid(buf, out_cid);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_relative_addr(sdmmc_card_t* card, uint16_t* out_rca)
|
||||
{
|
||||
assert(out_rca);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_SEND_RELATIVE_ADDR,
|
||||
.flags = SCF_CMD_BCR | SCF_RSP_R6
|
||||
};
|
||||
|
||||
/* MMC cards expect us to set the RCA.
|
||||
* Set RCA to 1 since we don't support multiple cards on the same bus, for now.
|
||||
*/
|
||||
uint16_t mmc_rca = 1;
|
||||
if (card->is_mmc) {
|
||||
cmd.arg = MMC_ARG_RCA(mmc_rca);
|
||||
}
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_blocklen(sdmmc_card_t* card, sdmmc_csd_t* csd)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SET_BLOCKLEN,
|
||||
.arg = csd->sector_size,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_csd(sdmmc_card_t* card, sdmmc_csd_t* out_csd)
|
||||
{
|
||||
/* The trick with SEND_CSD is that in SPI mode, it acts as a data read
|
||||
* command, while in SD mode it is an AC command with R2 response.
|
||||
*/
|
||||
sdmmc_response_t spi_buf;
|
||||
const bool is_spi = host_is_spi(card);
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_CSD,
|
||||
.arg = is_spi ? 0 : MMC_ARG_RCA(card->rca),
|
||||
.flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) :
|
||||
(SCF_CMD_AC | SCF_RSP_R2),
|
||||
.data = is_spi ? &spi_buf[0] : 0,
|
||||
.datalen = is_spi ? sizeof(spi_buf) : 0,
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
uint32_t* ptr = cmd.response;
|
||||
if (is_spi) {
|
||||
sdmmc_flip_byte_order(spi_buf, sizeof(spi_buf));
|
||||
ptr = spi_buf;
|
||||
}
|
||||
if (card->is_mmc) {
|
||||
err = sdmmc_mmc_decode_csd(cmd.response, out_csd);
|
||||
} else {
|
||||
err = sdmmc_decode_csd(ptr, out_csd);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_select_card(sdmmc_card_t* card, uint32_t rca)
|
||||
{
|
||||
/* Don't expect to see a response when de-selecting a card */
|
||||
uint32_t response = (rca == 0) ? 0 : SCF_RSP_R1;
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SELECT_CARD,
|
||||
.arg = MMC_ARG_RCA(rca),
|
||||
.flags = SCF_CMD_AC | response
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_scr(sdmmc_card_t* card, sdmmc_scr_t *out_scr)
|
||||
{
|
||||
size_t datalen = 8;
|
||||
uint32_t* buf = (uint32_t*) heap_caps_malloc(datalen, MALLOC_CAP_DMA);
|
||||
if (buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
sdmmc_command_t cmd = {
|
||||
.data = buf,
|
||||
.datalen = datalen,
|
||||
.blklen = datalen,
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.opcode = SD_APP_SEND_SCR
|
||||
};
|
||||
esp_err_t err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err == ESP_OK) {
|
||||
err = sdmmc_decode_scr(buf, out_scr);
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_set_bus_width(sdmmc_card_t* card, int width)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_APP_SET_BUS_WIDTH,
|
||||
.flags = SCF_RSP_R1 | SCF_CMD_AC,
|
||||
.arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1,
|
||||
};
|
||||
|
||||
return sdmmc_send_app_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_crc_on_off(sdmmc_card_t* card, bool crc_enable)
|
||||
{
|
||||
assert(host_is_spi(card) && "CRC_ON_OFF can only be used in SPI mode");
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = SD_CRC_ON_OFF,
|
||||
.arg = crc_enable ? 1 : 0,
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
return sdmmc_send_cmd(card, &cmd);
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_send_cmd_send_status(sdmmc_card_t* card, uint32_t* out_status)
|
||||
{
|
||||
sdmmc_command_t cmd = {
|
||||
.opcode = MMC_SEND_STATUS,
|
||||
.arg = MMC_ARG_RCA(card->rca),
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1
|
||||
};
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (out_status) {
|
||||
if (host_is_spi(card)) {
|
||||
*out_status = SD_SPI_R2(cmd.response);
|
||||
} else {
|
||||
*out_status = MMC_R1(cmd.response);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_write_sectors(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t block_size = card->csd.sector_size;
|
||||
if (esp_ptr_dma_capable(src) && (intptr_t)src % 4 == 0) {
|
||||
err = sdmmc_write_sectors_dma(card, src, start_block, block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the write into
|
||||
// separate single block writes, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (tmp_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
const uint8_t* cur_src = (const uint8_t*) src;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
memcpy(tmp_buf, cur_src, block_size);
|
||||
cur_src += block_size;
|
||||
err = sdmmc_write_sectors_dma(card, tmp_buf, start_block + i, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(tmp_buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_write_sectors_dma(sdmmc_card_t* card, const void* src,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) src,
|
||||
.datalen = block_count * block_size,
|
||||
.timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
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;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
/* SD mode: wait for the card to become idle based on R1 status */
|
||||
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
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);
|
||||
}
|
||||
}
|
||||
/* SPI mode: although card busy indication is based on the busy token,
|
||||
* SD spec recommends that the host checks the results of programming by sending
|
||||
* SEND_STATUS command. Some of the conditions reported in SEND_STATUS are not
|
||||
* reported via a data error token.
|
||||
*/
|
||||
if (host_is_spi(card)) {
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (status & SD_SPI_R2_CARD_LOCKED) {
|
||||
ESP_LOGE(TAG, "%s: write failed, card is locked: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (status != 0) {
|
||||
ESP_LOGE(TAG, "%s: card status indicates an error after write operation: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
size_t block_size = card->csd.sector_size;
|
||||
if (esp_ptr_dma_capable(dst) && (intptr_t)dst % 4 == 0) {
|
||||
err = sdmmc_read_sectors_dma(card, dst, start_block, block_count);
|
||||
} else {
|
||||
// SDMMC peripheral needs DMA-capable buffers. Split the read into
|
||||
// separate single block reads, if needed, and allocate a temporary
|
||||
// DMA-capable buffer.
|
||||
void* tmp_buf = heap_caps_malloc(block_size, MALLOC_CAP_DMA);
|
||||
if (tmp_buf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
uint8_t* cur_dst = (uint8_t*) dst;
|
||||
for (size_t i = 0; i < block_count; ++i) {
|
||||
err = sdmmc_read_sectors_dma(card, tmp_buf, start_block + i, 1);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "%s: error 0x%x writing block %d+%d",
|
||||
__func__, err, start_block, i);
|
||||
break;
|
||||
}
|
||||
memcpy(cur_dst, tmp_buf, block_size);
|
||||
cur_dst += block_size;
|
||||
}
|
||||
free(tmp_buf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_read_sectors_dma(sdmmc_card_t* card, void* dst,
|
||||
size_t start_block, size_t block_count)
|
||||
{
|
||||
if (start_block + block_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
size_t block_size = card->csd.sector_size;
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1,
|
||||
.blklen = block_size,
|
||||
.data = (void*) dst,
|
||||
.datalen = block_count * block_size
|
||||
};
|
||||
if (block_count == 1) {
|
||||
cmd.opcode = MMC_READ_BLOCK_SINGLE;
|
||||
} else {
|
||||
cmd.opcode = MMC_READ_BLOCK_MULTIPLE;
|
||||
}
|
||||
if (card->ocr & SD_OCR_SDHC_CAP) {
|
||||
cmd.arg = start_block;
|
||||
} else {
|
||||
cmd.arg = start_block * block_size;
|
||||
}
|
||||
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;
|
||||
}
|
||||
uint32_t status = 0;
|
||||
size_t count = 0;
|
||||
while (!host_is_spi(card) && !(status & MMC_R1_READY_FOR_DATA)) {
|
||||
// TODO: add some timeout here
|
||||
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);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_erase_sectors(sdmmc_card_t* card, size_t start_sector,
|
||||
size_t sector_count, sdmmc_erase_arg_t arg)
|
||||
{
|
||||
if (start_sector + sector_count > card->csd.capacity) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uint32_t cmd38_arg;
|
||||
if (arg == SDMMC_ERASE_ARG) {
|
||||
cmd38_arg = card->is_mmc ? SDMMC_MMC_TRIM_ARG : SDMMC_SD_ERASE_ARG;
|
||||
} else {
|
||||
cmd38_arg = card->is_mmc ? SDMMC_MMC_DISCARD_ARG : SDMMC_SD_DISCARD_ARG;
|
||||
}
|
||||
|
||||
/* validate the CMD38 argument against card supported features */
|
||||
if (card->is_mmc) {
|
||||
if ((cmd38_arg == SDMMC_MMC_TRIM_ARG) && (sdmmc_can_trim(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
if ((cmd38_arg == SDMMC_MMC_DISCARD_ARG) && (sdmmc_can_discard(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
} else { // SD card
|
||||
if ((cmd38_arg == SDMMC_SD_DISCARD_ARG) && (sdmmc_can_discard(card) != ESP_OK)) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
}
|
||||
|
||||
/* default as block unit address */
|
||||
size_t addr_unit_mult = 1;
|
||||
|
||||
if (!(card->ocr & SD_OCR_SDHC_CAP)) {
|
||||
addr_unit_mult = card->csd.sector_size;
|
||||
}
|
||||
|
||||
/* prepare command to set the start address */
|
||||
sdmmc_command_t cmd = {
|
||||
.flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_WAIT_BUSY,
|
||||
.opcode = card->is_mmc ? MMC_ERASE_GROUP_START :
|
||||
SD_ERASE_GROUP_START,
|
||||
.arg = (start_sector * addr_unit_mult),
|
||||
};
|
||||
|
||||
esp_err_t err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_START) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* prepare command to set the end address */
|
||||
cmd.opcode = card->is_mmc ? MMC_ERASE_GROUP_END : SD_ERASE_GROUP_END;
|
||||
cmd.arg = ((start_sector + (sector_count - 1)) * addr_unit_mult);
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE_GROUP_END) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* issue erase command */
|
||||
memset((void *)&cmd, 0 , sizeof(sdmmc_command_t));
|
||||
cmd.flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_WAIT_BUSY;
|
||||
cmd.opcode = MMC_ERASE;
|
||||
cmd.arg = cmd38_arg;
|
||||
cmd.timeout_ms = sdmmc_get_erase_timeout_ms(card, cmd38_arg, sector_count * card->csd.sector_size / 1024);
|
||||
|
||||
err = sdmmc_send_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd (ERASE) returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (host_is_spi(card)) {
|
||||
uint32_t status;
|
||||
err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
if (status != 0) {
|
||||
ESP_LOGE(TAG, "%s: card status indicates an error after erase operation: r2=0x%04x",
|
||||
__func__, status);
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_can_discard(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.rev >= EXT_CSD_REV_1_6)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
// SD card
|
||||
if ((!card->is_mmc) && !host_is_spi(card) && (card->ssr.discard_support == 1)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_can_trim(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_GB_CL_EN)) {
|
||||
FF_CAN_TRIM = 1;
|
||||
return ESP_OK;
|
||||
}
|
||||
FF_CAN_TRIM = 0;
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_can_sanitize(sdmmc_card_t* card)
|
||||
{
|
||||
if ((card->is_mmc) && (card->ext_csd.sec_feature & EXT_CSD_SEC_SANITIZE)) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_mmc_sanitize(sdmmc_card_t* card, uint32_t timeout_ms)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t index = EXT_CSD_SANITIZE_START;
|
||||
uint8_t set = EXT_CSD_CMD_SET_NORMAL;
|
||||
uint8_t value = 0x01;
|
||||
|
||||
if (sdmmc_mmc_can_sanitize(card) != ESP_OK) {
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
/*
|
||||
* A Sanitize operation is initiated by writing a value to the extended
|
||||
* CSD[165] SANITIZE_START. While the device is performing the sanitize
|
||||
* operation, the busy line is asserted.
|
||||
* SWITCH command is used to write the EXT_CSD register.
|
||||
*/
|
||||
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,
|
||||
.timeout_ms = timeout_ms,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_full_erase(sdmmc_card_t* card)
|
||||
{
|
||||
sdmmc_erase_arg_t arg = SDMMC_SD_ERASE_ARG; // erase by default for SD card
|
||||
esp_err_t err;
|
||||
if (card->is_mmc) {
|
||||
arg = sdmmc_mmc_can_sanitize(card) == ESP_OK ? SDMMC_MMC_DISCARD_ARG: SDMMC_MMC_TRIM_ARG;
|
||||
}
|
||||
err = sdmmc_erase_sectors(card, 0, card->csd.capacity, arg);
|
||||
if ((err == ESP_OK) && (arg == SDMMC_MMC_DISCARD_ARG)) {
|
||||
uint32_t timeout_ms = sdmmc_get_erase_timeout_ms(card, SDMMC_MMC_DISCARD_ARG, card->csd.capacity * ((uint64_t) card->csd.sector_size) / 1024);
|
||||
return sdmmc_mmc_sanitize(card, timeout_ms);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_get_status(sdmmc_card_t* card)
|
||||
{
|
||||
uint32_t stat;
|
||||
return sdmmc_send_cmd_send_status(card, &stat);
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
/*
|
||||
* 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_mh.h"
|
||||
|
||||
bool card_is_mmc = 0;
|
||||
|
||||
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;
|
||||
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_response_t raw_cid;
|
||||
if (!host_is_spi(card)) {
|
||||
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;
|
||||
}
|
||||
if (!card->is_mmc) {
|
||||
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 {
|
||||
/* 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. We temporiliy store the raw cid and do the
|
||||
* decoding after the RCA is set and the card is in data transfer
|
||||
* mode.
|
||||
*/
|
||||
memcpy(card->raw_cid, raw_cid, sizeof(sdmmc_response_t));
|
||||
}
|
||||
} 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_rca(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t 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;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_mmc_decode_cid(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err;
|
||||
sdmmc_response_t raw_cid;
|
||||
memcpy(raw_cid, card->raw_cid, sizeof(raw_cid));
|
||||
err = sdmmc_mmc_decode_cid(card->csd.mmc_ver, raw_cid, &card->cid);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: decoding CID failed (0x%x)", __func__, err);
|
||||
return err;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_csd(sdmmc_card_t* card)
|
||||
{
|
||||
assert(card->is_mem == 1);
|
||||
/* 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
|
||||
//NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
|
||||
};
|
||||
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";
|
||||
print_csd = true;
|
||||
}
|
||||
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->is_mmc ? card->csd.csd_ver : card->csd.csd_ver + 1),
|
||||
card->csd.sector_size, card->csd.capacity, card->csd.read_block_len);
|
||||
if (card->is_mmc) {
|
||||
fprintf(stream, "EXT CSD: bus_width=%d\n", (1 << card->log_bus_width));
|
||||
} else if (!card->is_sdio){ // make sure card is SD
|
||||
fprintf(stream, "SSR: bus_width=%d\n", (card->ssr.cur_bus_width ? 4 : 1));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
if (card->is_mmc) {
|
||||
return sdmmc_mmc_get_erase_timeout_ms(card, arg, erase_size_kb);
|
||||
} else {
|
||||
return sdmmc_sd_get_erase_timeout_ms(card, arg, erase_size_kb);
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* 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_mh.h"
|
||||
#include "sys/param.h"
|
||||
#include "soc/soc_memory_layout.h"
|
||||
|
||||
extern bool card_is_mmc;
|
||||
|
||||
#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
|
||||
|
||||
|
||||
#define SDMMC_SD_DISCARD_TIMEOUT 250 // SD erase (discard) timeout
|
||||
|
||||
/* 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
|
||||
|
||||
/* supported arguments for erase command 38 */
|
||||
#define SDMMC_SD_ERASE_ARG 0
|
||||
#define SDMMC_SD_DISCARD_ARG 1
|
||||
#define SDMMC_MMC_TRIM_ARG 1
|
||||
#define SDMMC_MMC_DISCARD_ARG 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);
|
||||
uint32_t sdmmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* 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);
|
||||
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr);
|
||||
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* 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);
|
||||
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb);
|
||||
|
||||
/* 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_rca(sdmmc_card_t* card);
|
||||
esp_err_t sdmmc_init_mmc_decode_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_ssr(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);
|
||||
esp_err_t sdmmc_init_mmc_check_ext_csd(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);
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* 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_mh.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 the contents of CID register*/
|
||||
SDMMC_INIT_STEP(is_mem, sdmmc_init_cid);
|
||||
|
||||
/* Assign RCA */
|
||||
SDMMC_INIT_STEP(!is_spi, sdmmc_init_rca);
|
||||
|
||||
/* Read and decode the contents of CSD register */
|
||||
SDMMC_INIT_STEP(is_mem, sdmmc_init_csd);
|
||||
|
||||
/* Decode the contents of mmc CID register */
|
||||
SDMMC_INIT_STEP(is_mmc && !is_spi, sdmmc_init_mmc_decode_cid);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* SD card: read SD Status register */
|
||||
SDMMC_INIT_STEP(is_sdmem, sdmmc_init_sd_ssr);
|
||||
|
||||
/* 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);
|
||||
/* Sanity check after eMMC switch to HS mode */
|
||||
SDMMC_INIT_STEP(is_mmc, sdmmc_init_mmc_check_ext_csd);
|
||||
/* TODO: add similar checks for SDIO */
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -1,636 +0,0 @@
|
||||
/*
|
||||
* 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_mh.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_compiler.h"
|
||||
|
||||
|
||||
#define CIS_TUPLE(NAME) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&cis_tuple_func_default, }
|
||||
#define CIS_TUPLE_WITH_FUNC(NAME, FUNC) (cis_tuple_t) {.code=CISTPL_CODE_##NAME, .name=#NAME, .func=&(FUNC), }
|
||||
|
||||
#define CIS_CHECK_SIZE(SIZE, MINIMAL) do {int store_size = (SIZE); if((store_size) < (MINIMAL)) return ESP_ERR_INVALID_SIZE;} while(0)
|
||||
#define CIS_CHECK_UNSUPPORTED(COND) do {if(!(COND)) return ESP_ERR_NOT_SUPPORTED;} while(0)
|
||||
#define CIS_GET_MINIMAL_SIZE 32
|
||||
|
||||
typedef esp_err_t (*cis_tuple_info_func_t)(const void* tuple_info, uint8_t* data, FILE* fp);
|
||||
|
||||
typedef struct {
|
||||
int code;
|
||||
const char *name;
|
||||
cis_tuple_info_func_t func;
|
||||
} cis_tuple_t;
|
||||
|
||||
static const char* TAG = "sdmmc_io";
|
||||
|
||||
static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp);
|
||||
static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp);
|
||||
|
||||
static const cis_tuple_t cis_table[] = {
|
||||
CIS_TUPLE(NULL),
|
||||
CIS_TUPLE(DEVICE),
|
||||
CIS_TUPLE(CHKSUM),
|
||||
CIS_TUPLE(VERS1),
|
||||
CIS_TUPLE(ALTSTR),
|
||||
CIS_TUPLE(CONFIG),
|
||||
CIS_TUPLE_WITH_FUNC(CFTABLE_ENTRY, cis_tuple_func_cftable_entry),
|
||||
CIS_TUPLE_WITH_FUNC(MANFID, cis_tuple_func_manfid),
|
||||
CIS_TUPLE(FUNCID),
|
||||
CIS_TUPLE(FUNCE),
|
||||
CIS_TUPLE(VENDER_BEGIN),
|
||||
CIS_TUPLE(VENDER_END),
|
||||
CIS_TUPLE(SDIO_STD),
|
||||
CIS_TUPLE(SDIO_EXT),
|
||||
CIS_TUPLE_WITH_FUNC(END, cis_tuple_func_end),
|
||||
};
|
||||
|
||||
|
||||
esp_err_t sdmmc_io_reset(sdmmc_card_t* card)
|
||||
{
|
||||
uint8_t sdio_reset = CCCR_CTL_RES;
|
||||
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)
|
||||
{
|
||||
/* If the host is configured to use low frequency, don't attempt to switch */
|
||||
if (card->host.max_freq_khz < SDMMC_FREQ_DEFAULT) {
|
||||
card->max_freq_khz = card->host.max_freq_khz;
|
||||
return ESP_OK;
|
||||
} else if (card->host.max_freq_khz < SDMMC_FREQ_HIGHSPEED) {
|
||||
card->max_freq_khz = SDMMC_FREQ_DEFAULT;
|
||||
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 (unlikely(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 (unlikely(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 (unlikely(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 (unlikely(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 (unlikely(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 (unlikely(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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print the CIS information of a CIS card, currently only ESP slave supported.
|
||||
*/
|
||||
|
||||
static esp_err_t cis_tuple_func_default(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
uint8_t code = *(data++);
|
||||
int size = *(data++);
|
||||
if (tuple) {
|
||||
fprintf(fp, "TUPLE: %s, size: %d: ", tuple->name, size);
|
||||
} else {
|
||||
fprintf(fp, "TUPLE: unknown(%02X), size: %d: ", code, size);
|
||||
}
|
||||
for (int i = 0; i < size; i++) fprintf(fp, "%02X ", *(data++));
|
||||
fprintf(fp, "\n");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_manfid(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
int size = *(data++);
|
||||
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
|
||||
CIS_CHECK_SIZE(size, 4);
|
||||
fprintf(fp, " MANF: %04X, CARD: %04X\n", *(uint16_t*)(data), *(uint16_t*)(data+2));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_end(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
fprintf(fp, "TUPLE: %s\n", tuple->name);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t cis_tuple_func_cftable_entry(const void* p, uint8_t* data, FILE* fp)
|
||||
{
|
||||
const cis_tuple_t* tuple = (const cis_tuple_t*)p;
|
||||
data++;
|
||||
int size = *(data++);
|
||||
fprintf(fp, "TUPLE: %s, size: %d\n", tuple->name, size);
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool interface = data[0] & BIT(7);
|
||||
bool def = data[0] & BIT(6);
|
||||
int conf_ent_num = data[0] & 0x3F;
|
||||
fprintf(fp, " INDX: %02X, Intface: %d, Default: %d, Conf-Entry-Num: %d\n", *(data++), interface, def, conf_ent_num);
|
||||
|
||||
if (interface) {
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
fprintf(fp, " IF: %02X\n", *(data++));
|
||||
}
|
||||
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool misc = data[0] & BIT(7);
|
||||
int mem_space = (data[0] >> 5 )&(0x3);
|
||||
bool irq = data[0] & BIT(4);
|
||||
bool io_sp = data[0] & BIT(3);
|
||||
bool timing = data[0] & BIT(2);
|
||||
int power = data[0] & 3;
|
||||
fprintf(fp, " FS: %02X, misc: %d, mem_space: %d, irq: %d, io_space: %d, timing: %d, power: %d\n", *(data++), misc, mem_space, irq, io_sp, timing, power);
|
||||
|
||||
CIS_CHECK_UNSUPPORTED(power == 0); //power descriptor is not handled yet
|
||||
CIS_CHECK_UNSUPPORTED(!timing); //timing descriptor is not handled yet
|
||||
CIS_CHECK_UNSUPPORTED(!io_sp); //io space descriptor is not handled yet
|
||||
|
||||
if (irq) {
|
||||
CIS_CHECK_SIZE(size--, 1);
|
||||
bool mask = data[0] & BIT(4);
|
||||
fprintf(fp, " IR: %02X, mask: %d, ",*(data++), mask);
|
||||
if (mask) {
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
size-=2;
|
||||
fprintf(fp, " IRQ: %02X %02X\n", data[0], data[1]);
|
||||
data+=2;
|
||||
}
|
||||
}
|
||||
|
||||
if (mem_space) {
|
||||
CIS_CHECK_SIZE(size, 2);
|
||||
size-=2;
|
||||
CIS_CHECK_UNSUPPORTED(mem_space==1); //other cases not handled yet
|
||||
int len = *(uint16_t*)data;
|
||||
fprintf(fp, " LEN: %04X\n", len);
|
||||
data+=2;
|
||||
}
|
||||
|
||||
CIS_CHECK_UNSUPPORTED(misc==0); //misc descriptor is not handled yet
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const cis_tuple_t* get_tuple(uint8_t code)
|
||||
{
|
||||
for (int i = 0; i < sizeof(cis_table)/sizeof(cis_tuple_t); i++) {
|
||||
if (code == cis_table[i].code) return &cis_table[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_print_cis_info(uint8_t* buffer, size_t buffer_size, FILE* fp)
|
||||
{
|
||||
ESP_LOG_BUFFER_HEXDUMP("CIS", buffer, buffer_size, ESP_LOG_DEBUG);
|
||||
if (!fp) fp = stdout;
|
||||
|
||||
uint8_t* cis = buffer;
|
||||
do {
|
||||
const cis_tuple_t* tuple = get_tuple(cis[0]);
|
||||
int size = cis[1];
|
||||
esp_err_t ret = ESP_OK;
|
||||
if (tuple) {
|
||||
ret = tuple->func(tuple, cis, fp);
|
||||
} else {
|
||||
ret = cis_tuple_func_default(NULL, cis, fp);
|
||||
}
|
||||
if (ret != ESP_OK) return ret;
|
||||
cis += 2 + size;
|
||||
if (tuple && tuple->code == CISTPL_CODE_END) break;
|
||||
} while (cis < buffer + buffer_size) ;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check tuples in the buffer.
|
||||
*
|
||||
* @param buf Buffer to check
|
||||
* @param buffer_size Size of the buffer
|
||||
* @param inout_cis_offset
|
||||
* - input: the last cis_offset, relative to the beginning of the buf. -1 if
|
||||
* this buffer begin with the tuple length, otherwise should be no smaller than
|
||||
* zero.
|
||||
* - output: when the end tuple found, output offset of the CISTPL_CODE_END
|
||||
* byte + 1 (relative to the beginning of the buffer; when not found, output
|
||||
* the address of next tuple code.
|
||||
*
|
||||
* @return true if found, false if haven't.
|
||||
*/
|
||||
static bool check_tuples_in_buffer(uint8_t* buf, int buffer_size, int* inout_cis_offset)
|
||||
{
|
||||
int cis_offset = *inout_cis_offset;
|
||||
if (cis_offset == -1) {
|
||||
//the CIS code is checked in the last buffer, skip to next tuple
|
||||
cis_offset += buf[0] + 2;
|
||||
}
|
||||
assert(cis_offset >= 0);
|
||||
while (1) {
|
||||
if (cis_offset < buffer_size) {
|
||||
//A CIS code in the buffer, check it
|
||||
if (buf[cis_offset] == CISTPL_CODE_END) {
|
||||
*inout_cis_offset = cis_offset + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (cis_offset + 1 < buffer_size) {
|
||||
cis_offset += buf[cis_offset+1] + 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*inout_cis_offset = cis_offset;
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_io_get_cis_data(sdmmc_card_t* card, uint8_t* out_buffer, size_t buffer_size, size_t* inout_cis_size)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
WORD_ALIGNED_ATTR uint8_t buf[CIS_GET_MINIMAL_SIZE];
|
||||
|
||||
/* Pointer to size is a mandatory parameter */
|
||||
assert(inout_cis_size);
|
||||
|
||||
/*
|
||||
* CIS region exist in 0x1000~0x17FFF of FUNC 0, get the start address of it
|
||||
* from CCCR register.
|
||||
*/
|
||||
uint32_t addr;
|
||||
ret = sdmmc_io_read_bytes(card, 0, 9, &addr, 3);
|
||||
if (ret != ESP_OK) return ret;
|
||||
//the sdmmc_io driver reads 4 bytes, the most significant byte is not the address.
|
||||
addr &= 0xffffff;
|
||||
if (addr < 0x1000 || addr > 0x17FFF) {
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* To avoid reading too long, take the input value as limitation if
|
||||
* existing.
|
||||
*/
|
||||
size_t max_reading = UINT32_MAX;
|
||||
if (*inout_cis_size != 0) {
|
||||
max_reading = *inout_cis_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the length while reading. If find the end tuple, or reaches the
|
||||
* limitation, read no more and return both the data and the size already
|
||||
* read.
|
||||
*/
|
||||
int buffer_offset = 0;
|
||||
int cur_cis_offset = 0;
|
||||
bool end_tuple_found = false;
|
||||
do {
|
||||
ret = sdmmc_io_read_bytes(card, 0, addr + buffer_offset, &buf, CIS_GET_MINIMAL_SIZE);
|
||||
if (ret != ESP_OK) return ret;
|
||||
|
||||
//calculate relative to the beginning of the buffer
|
||||
int offset = cur_cis_offset - buffer_offset;
|
||||
bool finish = check_tuples_in_buffer(buf, CIS_GET_MINIMAL_SIZE, &offset);
|
||||
|
||||
int remain_size = buffer_size - buffer_offset;
|
||||
int copy_len;
|
||||
if (finish) {
|
||||
copy_len = MIN(offset, remain_size);
|
||||
end_tuple_found = true;
|
||||
} else {
|
||||
copy_len = MIN(CIS_GET_MINIMAL_SIZE, remain_size);
|
||||
}
|
||||
if (copy_len > 0) {
|
||||
memcpy(out_buffer + buffer_offset, buf, copy_len);
|
||||
}
|
||||
cur_cis_offset = buffer_offset + offset;
|
||||
buffer_offset += CIS_GET_MINIMAL_SIZE;
|
||||
} while (!end_tuple_found && buffer_offset < max_reading);
|
||||
|
||||
if (end_tuple_found) {
|
||||
*inout_cis_size = cur_cis_offset;
|
||||
if (cur_cis_offset > buffer_size) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
} else {
|
||||
return ESP_OK;
|
||||
}
|
||||
} else {
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
/*
|
||||
* 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_mh.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;
|
||||
}
|
||||
|
||||
/* erased state of a bit, if 1 byte value read is 0xFF else 0x00 */
|
||||
card->ext_csd.erase_mem_state = ext_csd[EXT_CSD_ERASED_MEM_CONT];
|
||||
card->ext_csd.rev = ext_csd[EXT_CSD_REV];
|
||||
card->ext_csd.sec_feature = ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t sdmmc_init_mmc_check_ext_csd(sdmmc_card_t* card)
|
||||
{
|
||||
assert(card->is_mem == 1 && card->rca != 0);
|
||||
|
||||
/*
|
||||
* Integrity check required if card switched to HS mode
|
||||
* card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz)
|
||||
* For 26MHz limit background see sdmmc_mmc_enable_hs_mode()
|
||||
*/
|
||||
if (card->max_freq_khz <= SDMMC_FREQ_26M) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ensure EXT_CSD buffer is available before starting any SD-card operation */
|
||||
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;
|
||||
}
|
||||
|
||||
/* ensure card is in transfer state before read ext_csd */
|
||||
uint32_t status;
|
||||
esp_err_t err = sdmmc_send_cmd_send_status(card, &status);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: send_status returned 0x%x", __func__, err);
|
||||
goto out;
|
||||
}
|
||||
status = ((status & MMC_R1_CURRENT_STATE_MASK) >> MMC_R1_CURRENT_STATE_POS);
|
||||
if (status != MMC_R1_CURRENT_STATE_TRAN) {
|
||||
ESP_LOGE(TAG, "%s: card not in transfer state", __func__);
|
||||
err = ESP_ERR_INVALID_STATE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read EXT_CSD to ensure device works fine in HS mode */
|
||||
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;
|
||||
}
|
||||
|
||||
/* EXT_CSD static fields should match the previous read values in sdmmc_card_init */
|
||||
if ((card->ext_csd.rev != ext_csd[EXT_CSD_REV]) ||
|
||||
(card->ext_csd.sec_feature != ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT])) {
|
||||
ESP_LOGE(TAG, "%s: Data integrity test fail in HS mode", __func__);
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
|
||||
out:
|
||||
free(ext_csd);
|
||||
return err;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_mmc_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
/* TODO: calculate erase timeout based on ext_csd (trim_timeout) */
|
||||
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
|
||||
timeout_ms = MAX(1000, timeout_ms);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
|
||||
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
|
||||
return timeout_ms;
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
/*
|
||||
* 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_mh.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_ssr(sdmmc_card_t* card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
/* Get the contents of SSR register: SD additional information
|
||||
* ACMD13 to read 512byte SD status information
|
||||
*/
|
||||
uint32_t* sd_ssr = heap_caps_calloc(1, SD_SSR_SIZE, MALLOC_CAP_DMA);
|
||||
if (!sd_ssr) {
|
||||
ESP_LOGE(TAG, "%s: could not allocate sd_ssr", __func__);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
sdmmc_command_t cmd = {
|
||||
.data = sd_ssr,
|
||||
.datalen = SD_SSR_SIZE,
|
||||
.blklen = SD_SSR_SIZE,
|
||||
.opcode = SD_APP_SD_STATUS,
|
||||
.arg = 0,
|
||||
.flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ
|
||||
};
|
||||
|
||||
// read SD status register
|
||||
err = sdmmc_send_app_cmd(card, &cmd);
|
||||
if (err != ESP_OK) {
|
||||
free(sd_ssr);
|
||||
ESP_LOGE(TAG, "%s: sdmmc_send_cmd returned 0x%x", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = sdmmc_decode_ssr(sd_ssr, &card->ssr);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "%s: error sdmmc_decode_scr returned 0x%x", __func__, err);
|
||||
}
|
||||
free(sd_ssr);
|
||||
return err;
|
||||
}
|
||||
|
||||
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
|
||||
* mode, deselect the card first, then get the CSD, then select the card again. This step is
|
||||
* not required in SPI mode, since CMD7 (select_card) is not supported.
|
||||
*/
|
||||
const bool is_spi = host_is_spi(card);
|
||||
if (!is_spi) {
|
||||
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;
|
||||
}
|
||||
if (!is_spi) {
|
||||
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 = { 0 };
|
||||
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->erase_mem_state = SCR_DATA_STAT_AFTER_ERASE(resp);
|
||||
out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static const uint32_t s_au_to_size_kb[] = {
|
||||
0, 16, 32, 64,
|
||||
128, 256, 512, 1024,
|
||||
2 * 1024, 4 * 1024,
|
||||
8 * 1024, 12 * 1024,
|
||||
16 * 1024, 24 * 1024,
|
||||
32 * 1024, 64 * 1024
|
||||
};
|
||||
_Static_assert(sizeof(s_au_to_size_kb)/sizeof(s_au_to_size_kb[0]) == 16, "invalid number of elements in s_au_to_size_kb");
|
||||
|
||||
esp_err_t sdmmc_decode_ssr(uint32_t *raw_ssr, sdmmc_ssr_t* out_ssr)
|
||||
{
|
||||
uint32_t ssr[(SD_SSR_SIZE/sizeof(uint32_t))] = { 0 };
|
||||
size_t j = (SD_SSR_SIZE/sizeof(uint32_t) - 1);
|
||||
|
||||
for(size_t i = 0; i < (SD_SSR_SIZE/sizeof(uint32_t)); i++) {
|
||||
ssr[j - i] = __builtin_bswap32(raw_ssr[i]);
|
||||
}
|
||||
|
||||
out_ssr->cur_bus_width = SSR_DAT_BUS_WIDTH(ssr);
|
||||
out_ssr->discard_support = SSR_DISCARD_SUPPORT(ssr);
|
||||
out_ssr->fule_support = SSR_FULE_SUPPORT(ssr);
|
||||
uint32_t au = SSR_AU_SIZE(ssr);
|
||||
out_ssr->alloc_unit_kb = s_au_to_size_kb[au];
|
||||
out_ssr->erase_timeout = SSR_ERASE_TIMEOUT(ssr);
|
||||
out_ssr->erase_size_au = SSR_ERASE_SIZE(ssr);
|
||||
out_ssr->erase_offset = SSR_ERASE_OFFSET(ssr);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint32_t sdmmc_sd_get_erase_timeout_ms(const sdmmc_card_t* card, int arg, size_t erase_size_kb)
|
||||
{
|
||||
if (arg == SDMMC_SD_DISCARD_ARG) {
|
||||
return SDMMC_SD_DISCARD_TIMEOUT;
|
||||
} else if (arg == SDMMC_SD_ERASE_ARG) {
|
||||
if (card->ssr.alloc_unit_kb != 0 &&
|
||||
card->ssr.erase_size_au != 0 &&
|
||||
card->ssr.erase_timeout != 0 &&
|
||||
card->ssr.erase_offset != 0) {
|
||||
/* Card supports erase timeout estimation. See the erase timeout equation in SD spec. */
|
||||
uint32_t timeout_sec = card->ssr.erase_offset +
|
||||
card->ssr.erase_timeout * (erase_size_kb + card->ssr.alloc_unit_kb - 1) /
|
||||
(card->ssr.erase_size_au * card->ssr.alloc_unit_kb);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, ES=%u, ET=%u, EO=%u, AU=%u kB)",
|
||||
__func__, timeout_sec, erase_size_kb, card->ssr.erase_size_au,
|
||||
card->ssr.erase_timeout, card->ssr.erase_offset, card->ssr.alloc_unit_kb);
|
||||
return timeout_sec * 1000;
|
||||
} else {
|
||||
uint32_t timeout_ms = SDMMC_SD_DISCARD_TIMEOUT * erase_size_kb / card->csd.sector_size;
|
||||
timeout_ms = MAX(1000, timeout_ms);
|
||||
ESP_LOGD(TAG, "%s: erase timeout %u s (erasing %u kB, %ums per sector)",
|
||||
__func__, timeout_ms / 1000, erase_size_kb, SDMMC_SD_DISCARD_TIMEOUT);
|
||||
return timeout_ms;
|
||||
}
|
||||
} else {
|
||||
assert(false && "unexpected SD erase argument");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,6 @@ FILE(GLOB_RECURSE app_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.*)
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_timer esp-tflite-micro jomjol_logfile esp-fatfs esp-sdmmc)
|
||||
REQUIRES esp_timer esp-tflite-micro jomjol_logfile fatfs sdmmc vfs)
|
||||
|
||||
|
||||
|
||||
@@ -27,12 +27,10 @@ extern "C" {
|
||||
#include <esp_timer.h>
|
||||
#include "../../include/defines.h"
|
||||
|
||||
|
||||
#include "ClassLogFile.h"
|
||||
|
||||
//#include "esp_vfs_fat.h"
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "sdmmc_common_mh.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "../sdmmc_common.h"
|
||||
|
||||
static const char* TAG = "HELPER";
|
||||
|
||||
@@ -42,7 +40,7 @@ unsigned int systemStatus = 0;
|
||||
|
||||
sdmmc_cid_t SDCardCid;
|
||||
sdmmc_csd_t SDCardCsd;
|
||||
|
||||
bool SDCardIsMMC;
|
||||
|
||||
// #define DEBUG_DETAIL_ON
|
||||
|
||||
@@ -141,6 +139,7 @@ string getSDCardPartitionAllocationSize(){
|
||||
void SaveSDCardInfo(sdmmc_card_t* card) {
|
||||
SDCardCid = card->cid;
|
||||
SDCardCsd = card->csd;
|
||||
SDCardIsMMC = card->is_mmc;
|
||||
}
|
||||
|
||||
|
||||
@@ -860,7 +859,7 @@ struct SDCard_Manufacturer_database mmc_database[] = {
|
||||
/* Parse SD Card Manufacturer Database */
|
||||
string SDCardParseManufacturerIDs(int id)
|
||||
{
|
||||
if (card_is_mmc)
|
||||
if (SDCardIsMMC)
|
||||
{
|
||||
unsigned int id_cnt = sizeof(mmc_database) / sizeof(struct SDCard_Manufacturer_database);
|
||||
string ret_val = "";
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
//#include "sdmmc_cmd.h"
|
||||
#include "sdmmc_cmd_mh.h"
|
||||
|
||||
#include "sdmmc_cmd.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
651
code/components/jomjol_helper/sdcard_init.c
Normal file
651
code/components/jomjol_helper/sdcard_init.c
Normal file
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "sdcard_init.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "ffconf.h"
|
||||
#include "esp_compiler.h"
|
||||
#include "esp_vfs.h"
|
||||
#include "vfs_fat_internal.h"
|
||||
#include "diskio_impl.h"
|
||||
#include "diskio_sdmmc.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "driver/sdmmc_defs.h"
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
#include "driver/sdmmc_host.h"
|
||||
#endif
|
||||
|
||||
static sdmmc_card_t* s_cards[FF_VOLUMES] = { NULL };
|
||||
static bool s_disk_status_check_en[FF_VOLUMES] = { };
|
||||
|
||||
static const char* TAG = "sdcard_init";
|
||||
|
||||
#define CHECK_EXECUTE_RESULT(err, str) do { \
|
||||
if ((err) !=ESP_OK) { \
|
||||
ESP_LOGE(TAG, str" (0x%x).", err); \
|
||||
goto cleanup; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
typedef struct vfs_fat_sd_ctx_t {
|
||||
BYTE pdrv; //Drive number that is mounted
|
||||
esp_vfs_fat_mount_config_t mount_config; //Mount configuration
|
||||
FATFS *fs; //FAT structure pointer that is registered
|
||||
sdmmc_card_t *card; //Card info
|
||||
char *base_path; //Path where partition is registered
|
||||
} vfs_fat_sd_ctx_t;
|
||||
|
||||
static vfs_fat_sd_ctx_t *s_ctx[FF_VOLUMES] = {};
|
||||
|
||||
/**
|
||||
* This `s_saved_ctx_id` is only used by `esp_vfs_fat_sdmmc_unmount`, which is deprecated.
|
||||
* This variable together with `esp_vfs_fat_sdmmc_unmount` should be removed in next major version
|
||||
*/
|
||||
static uint32_t s_saved_ctx_id = FF_VOLUMES;
|
||||
|
||||
|
||||
static void call_host_deinit_mh(const sdmmc_host_t *host_config);
|
||||
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv);
|
||||
|
||||
|
||||
//Check if SD/MMC card is present
|
||||
static DSTATUS ff_sdmmc_card_available_mh(BYTE pdrv)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_get_status(card);
|
||||
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "Check status failed (0x%x)", err);
|
||||
return STA_NOINIT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ff_sdmmc_status() and ff_sdmmc_initialize() return STA_NOINIT when sdmmc_get_status()
|
||||
* fails. This error value is checked throughout the FATFS code.
|
||||
* Both functions return 0 on success.
|
||||
*/
|
||||
DSTATUS ff_sdmmc_initialize_mh (BYTE pdrv)
|
||||
{
|
||||
return ff_sdmmc_card_available_mh(pdrv);
|
||||
}
|
||||
|
||||
DSTATUS ff_sdmmc_status_mh(BYTE pdrv)
|
||||
{
|
||||
if (s_disk_status_check_en[pdrv]) {
|
||||
return ff_sdmmc_card_available_mh(pdrv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_read_mh (BYTE pdrv, BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_read_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_read_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
DRESULT ff_sdmmc_write_mh (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
esp_err_t err = sdmmc_write_sectors(card, buff, sector, count);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_write_blocks failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
|
||||
#if FF_USE_TRIM
|
||||
DRESULT ff_sdmmc_trim_mh (BYTE pdrv, DWORD start_sector, DWORD sector_count)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
sdmmc_erase_arg_t arg;
|
||||
|
||||
arg = sdmmc_can_discard(card) == ESP_OK ? SDMMC_DISCARD_ARG : SDMMC_ERASE_ARG;
|
||||
esp_err_t err = sdmmc_erase_sectors(card, start_sector, sector_count, arg);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "sdmmc_erase_sectors failed (%d)", err);
|
||||
return RES_ERROR;
|
||||
}
|
||||
return RES_OK;
|
||||
}
|
||||
#endif //FF_USE_TRIM
|
||||
|
||||
DRESULT ff_sdmmc_ioctl_mh (BYTE pdrv, BYTE cmd, void* buff)
|
||||
{
|
||||
sdmmc_card_t* card = s_cards[pdrv];
|
||||
assert(card);
|
||||
switch(cmd) {
|
||||
case CTRL_SYNC:
|
||||
return RES_OK;
|
||||
case GET_SECTOR_COUNT:
|
||||
*((DWORD*) buff) = card->csd.capacity;
|
||||
return RES_OK;
|
||||
case GET_SECTOR_SIZE:
|
||||
*((WORD*) buff) = card->csd.sector_size;
|
||||
return RES_OK;
|
||||
case GET_BLOCK_SIZE:
|
||||
return RES_ERROR;
|
||||
#if FF_USE_TRIM
|
||||
case CTRL_TRIM:
|
||||
if (sdmmc_can_trim(card) != ESP_OK) {
|
||||
return RES_PARERR;
|
||||
}
|
||||
return ff_sdmmc_trim_mh (pdrv, *((DWORD*)buff), //start_sector
|
||||
(*((DWORD*)buff + 1) - *((DWORD*)buff) + 1)); //sector_count
|
||||
#endif //FF_USE_TRIM
|
||||
}
|
||||
return RES_ERROR;
|
||||
}
|
||||
|
||||
void ff_sdmmc_set_disk_status_check_mh(BYTE pdrv, bool enable)
|
||||
{
|
||||
s_disk_status_check_en[pdrv] = enable;
|
||||
}
|
||||
|
||||
void ff_diskio_register_sdmmc_mh(BYTE pdrv, sdmmc_card_t* card)
|
||||
{
|
||||
static const ff_diskio_impl_t sdmmc_impl = {
|
||||
.init = &ff_sdmmc_initialize_mh,
|
||||
.status = &ff_sdmmc_status_mh,
|
||||
.read = &ff_sdmmc_read_mh,
|
||||
.write = &ff_sdmmc_write_mh,
|
||||
.ioctl = &ff_sdmmc_ioctl_mh
|
||||
};
|
||||
s_cards[pdrv] = card;
|
||||
s_disk_status_check_en[pdrv] = false;
|
||||
ff_diskio_register(pdrv, &sdmmc_impl);
|
||||
}
|
||||
|
||||
BYTE ff_diskio_get_pdrv_card_mh(const sdmmc_card_t* card)
|
||||
{
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
if (card == s_cards[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static bool s_get_context_id_by_card_mh(const sdmmc_card_t *card, uint32_t *out_id)
|
||||
{
|
||||
vfs_fat_sd_ctx_t *p_ctx = NULL;
|
||||
for (int i = 0; i < FF_VOLUMES; i++) {
|
||||
p_ctx = s_ctx[i];
|
||||
if (p_ctx) {
|
||||
if (p_ctx->card == card) {
|
||||
*out_id = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t s_get_unused_context_id_mh(void)
|
||||
{
|
||||
for (uint32_t i = 0; i < FF_VOLUMES; i++) {
|
||||
if (!s_ctx[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return FF_VOLUMES;
|
||||
}
|
||||
|
||||
static esp_err_t mount_prepare_mem_mh(const char *base_path, BYTE *out_pdrv, char **out_dup_path, sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
char* dup_path = NULL;
|
||||
sdmmc_card_t* card = NULL;
|
||||
|
||||
// connect SDMMC driver to FATFS
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
|
||||
if (ff_diskio_get_drive(&pdrv) != ESP_OK || pdrv == FF_DRV_NOT_USED) {
|
||||
ESP_LOGD(TAG, "the maximum count of volumes is already mounted");
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
// not using ff_memalloc here, as allocation in internal RAM is preferred
|
||||
card = (sdmmc_card_t*)malloc(sizeof(sdmmc_card_t));
|
||||
|
||||
if (card == NULL) {
|
||||
ESP_LOGD(TAG, "could not locate new sdmmc_card_t");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
dup_path = strdup(base_path);
|
||||
|
||||
if(!dup_path){
|
||||
ESP_LOGD(TAG, "could not copy base_path");
|
||||
err = ESP_ERR_NO_MEM;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*out_card = card;
|
||||
*out_pdrv = pdrv;
|
||||
*out_dup_path = dup_path;
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t s_f_mount_mh(sdmmc_card_t *card, FATFS *fs, const char *drv, uint8_t pdrv, const esp_vfs_fat_mount_config_t *mount_config)
|
||||
{
|
||||
esp_err_t err = ESP_OK;
|
||||
FRESULT res = f_mount(fs, drv, 1);
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGW(TAG, "failed to mount card (%d)", res);
|
||||
|
||||
bool need_mount_again = (res == FR_NO_FILESYSTEM || res == FR_INT_ERR) && mount_config->format_if_mount_failed;
|
||||
|
||||
if (!need_mount_again) {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
err = partition_card_mh(mount_config, drv, card, pdrv);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "mounting again");
|
||||
res = f_mount(fs, drv, 0);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mount failed after formatting (%d)", res);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t mount_to_vfs_fat_mh(const esp_vfs_fat_mount_config_t *mount_config, sdmmc_card_t *card, uint8_t pdrv, const char *base_path, FATFS **out_fs)
|
||||
{
|
||||
FATFS *fs = NULL;
|
||||
esp_err_t err;
|
||||
ff_diskio_register_sdmmc_mh(pdrv, card);
|
||||
ff_sdmmc_set_disk_status_check_mh(pdrv, mount_config->disk_status_check_enable);
|
||||
ESP_LOGD(TAG, "using pdrv=%i", pdrv);
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
|
||||
// connect FATFS to VFS
|
||||
err = esp_vfs_fat_register(base_path, drv, mount_config->max_files, &fs);
|
||||
*out_fs = fs;
|
||||
|
||||
if (err == ESP_ERR_INVALID_STATE) {
|
||||
// it's okay, already registered with VFS
|
||||
} else if (err != ESP_OK) {
|
||||
ESP_LOGD(TAG, "esp_vfs_fat_register failed 0x(%x)", err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
// Try to mount partition
|
||||
err = s_f_mount_mh(card, fs, drv, pdrv, mount_config);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
goto fail;
|
||||
}
|
||||
return ESP_OK;
|
||||
|
||||
fail:
|
||||
if (fs) {
|
||||
f_mount(NULL, drv, 0);
|
||||
}
|
||||
esp_vfs_fat_unregister_path(base_path);
|
||||
ff_diskio_unregister(pdrv);
|
||||
return err;
|
||||
}
|
||||
|
||||
static esp_err_t partition_card_mh(const esp_vfs_fat_mount_config_t *mount_config, const char *drv, sdmmc_card_t *card, BYTE pdrv)
|
||||
{
|
||||
FRESULT res = FR_OK;
|
||||
esp_err_t err;
|
||||
const size_t workbuf_size = 4096;
|
||||
void* workbuf = NULL;
|
||||
ESP_LOGW(TAG, "partitioning card");
|
||||
|
||||
workbuf = ff_memalloc(workbuf_size);
|
||||
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
LBA_t plist[] = {100, 0, 0, 0};
|
||||
res = f_fdisk(pdrv, plist, workbuf);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_fdisk failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, mount_config->allocation_unit_size);
|
||||
|
||||
ESP_LOGW(TAG, "formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
|
||||
if (res != FR_OK) {
|
||||
err = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(workbuf);
|
||||
return ESP_OK;
|
||||
fail:
|
||||
free(workbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
#if SOC_SDMMC_HOST_SUPPORTED
|
||||
static esp_err_t init_sdmmc_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
*out_slot = slot;
|
||||
return sdmmc_host_init_slot(slot, (const sdmmc_slot_config_t*) slot_config);
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||
{
|
||||
esp_err_t err;
|
||||
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||
uint32_t ctx_id = FF_VOLUMES;
|
||||
FATFS *fs = NULL;
|
||||
int card_handle = -1; //uninitialized
|
||||
sdmmc_card_t* card = NULL;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
char* dup_path = NULL;
|
||||
bool host_inited = false;
|
||||
|
||||
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
//deinit() needs to be called to revert the init
|
||||
host_inited = true;
|
||||
//If this failed (indicated by card_handle != -1), slot deinit needs to called()
|
||||
//leave card_handle as is to indicate that (though slot deinit not implemented yet.
|
||||
err = init_sdmmc_host_mh(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
|
||||
//For deprecation backward compatibility
|
||||
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||
s_saved_ctx_id = 0;
|
||||
}
|
||||
|
||||
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||
|
||||
if (!ctx) {
|
||||
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||
}
|
||||
|
||||
ctx->pdrv = pdrv;
|
||||
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||
ctx->card = card;
|
||||
ctx->base_path = dup_path;
|
||||
ctx->fs = fs;
|
||||
ctx_id = s_get_unused_context_id_mh();
|
||||
assert(ctx_id != FF_VOLUMES);
|
||||
s_ctx[ctx_id] = ctx;
|
||||
|
||||
return ESP_OK;
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit_mh(host_config);
|
||||
}
|
||||
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t init_sdspi_host_mh(int slot, const void *slot_config, int *out_slot)
|
||||
{
|
||||
esp_err_t err = sdspi_host_init_device((const sdspi_device_config_t*)slot_config, out_slot);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Failed to attach sdspi device onto an SPI bus (rc=0x%x), please initialize the \
|
||||
bus first and check the device parameters."
|
||||
, err);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card)
|
||||
{
|
||||
const sdmmc_host_t* host_config = host_config_input;
|
||||
esp_err_t err;
|
||||
vfs_fat_sd_ctx_t *ctx = NULL;
|
||||
uint32_t ctx_id = FF_VOLUMES;
|
||||
FATFS *fs = NULL;
|
||||
int card_handle = -1; //uninitialized
|
||||
bool host_inited = false;
|
||||
BYTE pdrv = FF_DRV_NOT_USED;
|
||||
sdmmc_card_t* card = NULL;
|
||||
char* dup_path = NULL;
|
||||
|
||||
err = mount_prepare_mem_mh(base_path, &pdrv, &dup_path, &card);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "mount_prepare failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
//the init() function is usually empty, doesn't require any deinit to revert it
|
||||
err = (*host_config->init)();
|
||||
CHECK_EXECUTE_RESULT(err, "host init failed");
|
||||
|
||||
err = init_sdspi_host_mh(host_config->slot, slot_config, &card_handle);
|
||||
CHECK_EXECUTE_RESULT(err, "slot init failed");
|
||||
//Set `host_inited` to true to indicate that host_config->deinit() needs
|
||||
//to be called to revert `init_sdspi_host`
|
||||
host_inited = true;
|
||||
|
||||
//The `slot` argument inside host_config should be replaced by the SD SPI handled returned
|
||||
//above. But the input pointer is const, so create a new variable.
|
||||
|
||||
sdmmc_host_t new_config;
|
||||
|
||||
if (card_handle != host_config->slot) {
|
||||
new_config = *host_config_input;
|
||||
host_config = &new_config;
|
||||
new_config.slot = card_handle;
|
||||
}
|
||||
|
||||
// probe and initialize card
|
||||
err = sdmmc_card_init(host_config, card);
|
||||
CHECK_EXECUTE_RESULT(err, "sdmmc_card_init failed");
|
||||
|
||||
err = mount_to_vfs_fat_mh(mount_config, card, pdrv, dup_path, &fs);
|
||||
CHECK_EXECUTE_RESULT(err, "mount_to_vfs failed");
|
||||
|
||||
if (out_card != NULL) {
|
||||
*out_card = card;
|
||||
}
|
||||
|
||||
//For deprecation backward compatibility
|
||||
if (s_saved_ctx_id == FF_VOLUMES) {
|
||||
s_saved_ctx_id = 0;
|
||||
}
|
||||
|
||||
ctx = calloc(sizeof(vfs_fat_sd_ctx_t), 1);
|
||||
|
||||
if (!ctx) {
|
||||
CHECK_EXECUTE_RESULT(ESP_ERR_NO_MEM, "no mem");
|
||||
}
|
||||
|
||||
ctx->pdrv = pdrv;
|
||||
memcpy(&ctx->mount_config, mount_config, sizeof(esp_vfs_fat_mount_config_t));
|
||||
ctx->card = card;
|
||||
ctx->base_path = dup_path;
|
||||
ctx->fs = fs;
|
||||
ctx_id = s_get_unused_context_id_mh();
|
||||
assert(ctx_id != FF_VOLUMES);
|
||||
s_ctx[ctx_id] = ctx;
|
||||
|
||||
return ESP_OK;
|
||||
|
||||
cleanup:
|
||||
if (host_inited) {
|
||||
call_host_deinit_mh(host_config);
|
||||
}
|
||||
|
||||
free(card);
|
||||
free(dup_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void call_host_deinit_mh(const sdmmc_host_t *host_config)
|
||||
{
|
||||
if (host_config->flags & SDMMC_HOST_FLAG_DEINIT_ARG) {
|
||||
host_config->deinit_p(host_config->slot);
|
||||
} else {
|
||||
host_config->deinit();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t unmount_card_core_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||
|
||||
if (pdrv == 0xff) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
// release SD driver
|
||||
ff_diskio_unregister(pdrv);
|
||||
|
||||
call_host_deinit_mh(&card->host);
|
||||
free(card);
|
||||
|
||||
esp_err_t err = esp_vfs_fat_unregister_path(base_path);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdmmc_unmount_mh(void)
|
||||
{
|
||||
esp_err_t err = unmount_card_core_mh(s_ctx[s_saved_ctx_id]->base_path, s_ctx[s_saved_ctx_id]->card);
|
||||
free(s_ctx[s_saved_ctx_id]);
|
||||
s_ctx[s_saved_ctx_id] = NULL;
|
||||
s_saved_ctx_id = FF_VOLUMES;
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_unmount_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
uint32_t id = FF_VOLUMES;
|
||||
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||
|
||||
if (!found) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
free(s_ctx[id]);
|
||||
s_ctx[id] = NULL;
|
||||
|
||||
esp_err_t err = unmount_card_core_mh(base_path, card);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_vfs_fat_sdcard_format_mh(const char *base_path, sdmmc_card_t *card)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
if (!card) {
|
||||
ESP_LOGE(TAG, "card not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
BYTE pdrv = ff_diskio_get_pdrv_card_mh(card);
|
||||
|
||||
if (pdrv == 0xff) {
|
||||
ESP_LOGE(TAG, "card driver not registered");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
const size_t workbuf_size = 4096;
|
||||
void *workbuf = ff_memalloc(workbuf_size);
|
||||
|
||||
if (workbuf == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
//unmount
|
||||
char drv[3] = {(char)('0' + pdrv), ':', 0};
|
||||
f_mount(0, drv, 0);
|
||||
|
||||
//format
|
||||
uint32_t id = FF_VOLUMES;
|
||||
bool found = s_get_context_id_by_card_mh(card, &id);
|
||||
assert(found);
|
||||
size_t alloc_unit_size = esp_vfs_fat_get_allocation_unit_size(card->csd.sector_size, s_ctx[id]->mount_config.allocation_unit_size);
|
||||
ESP_LOGI(TAG, "Formatting card, allocation unit size=%d", alloc_unit_size);
|
||||
const MKFS_PARM opt = {(BYTE)FM_ANY, 0, 0, 0, alloc_unit_size};
|
||||
FRESULT res = f_mkfs(drv, &opt, workbuf, workbuf_size);
|
||||
free(workbuf);
|
||||
|
||||
if (res != FR_OK) {
|
||||
ret = ESP_FAIL;
|
||||
ESP_LOGD(TAG, "f_mkfs failed (%d)", res);
|
||||
}
|
||||
|
||||
//mount back
|
||||
esp_err_t err = s_f_mount_mh(card, s_ctx[id]->fs, drv, pdrv, &s_ctx[id]->mount_config);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
unmount_card_core_mh(base_path, card);
|
||||
ESP_LOGE(TAG, "failed to format, resources recycled, please mount again");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
111
code/components/jomjol_helper/sdcard_init.h
Normal file
111
code/components/jomjol_helper/sdcard_init.h
Normal file
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "esp_err.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "sdmmc_cmd.h"
|
||||
#include "driver/sdmmc_types.h"
|
||||
#include "driver/sdspi_host.h"
|
||||
#include "ff.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "wear_levelling.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes SDMMC driver or SPI driver with configuration in host_config
|
||||
* - initializes SD card with configuration in slot_config
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note Use this API to mount a card through SDSPI is deprecated. Please call
|
||||
* `esp_vfs_fat_sdspi_mount()` instead for that case.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config Pointer to structure describing SDMMC host. When using
|
||||
* SDMMC peripheral, this structure can be initialized using
|
||||
* SDMMC_HOST_DEFAULT() macro. When using SPI peripheral,
|
||||
* this structure can be initialized using SDSPI_HOST_DEFAULT()
|
||||
* macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SDMMC peripheral, pass a pointer to sdmmc_slot_config_t
|
||||
* structure initialized using SDMMC_SLOT_CONFIG_DEFAULT.
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card if not NULL, pointer to the card information structure will be returned via this argument
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdmmc_mount_mh(const char* base_path, const sdmmc_host_t* host_config, const void* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||
|
||||
/**
|
||||
* @brief Convenience function to get FAT filesystem on SD card registered in VFS
|
||||
*
|
||||
* This is an all-in-one function which does the following:
|
||||
* - initializes an SPI Master device based on the SPI Master driver with configuration in
|
||||
* slot_config, and attach it to an initialized SPI bus.
|
||||
* - initializes SD card with configuration in host_config_input
|
||||
* - mounts FAT partition on SD card using FATFS library, with configuration in mount_config
|
||||
* - registers FATFS library with VFS, with prefix given by base_prefix variable
|
||||
*
|
||||
* This function is intended to make example code more compact.
|
||||
* For real world applications, developers should implement the logic of
|
||||
* probing SD card, locating and mounting partition, and registering FATFS in VFS,
|
||||
* with proper error checking and handling of exceptional conditions.
|
||||
*
|
||||
* @note This function try to attach the new SD SPI device to the bus specified in host_config.
|
||||
* Make sure the SPI bus specified in `host_config->slot` have been initialized by
|
||||
* `spi_bus_initialize()` before.
|
||||
*
|
||||
* @param base_path path where partition should be registered (e.g. "/sdcard")
|
||||
* @param host_config_input Pointer to structure describing SDMMC host. This structure can be
|
||||
* initialized using SDSPI_HOST_DEFAULT() macro.
|
||||
* @param slot_config Pointer to structure with slot configuration.
|
||||
* For SPI peripheral, pass a pointer to sdspi_device_config_t
|
||||
* structure initialized using SDSPI_DEVICE_CONFIG_DEFAULT().
|
||||
* @param mount_config pointer to structure with extra parameters for mounting FATFS
|
||||
* @param[out] out_card If not NULL, pointer to the card information structure will be returned via
|
||||
* this argument. It is suggested to hold this handle and use it to unmount the card later if
|
||||
* needed. Otherwise it's not suggested to use more than one card at the same time and unmount one
|
||||
* of them in your application.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if esp_vfs_fat_sdmmc_mount was already called
|
||||
* - ESP_ERR_NO_MEM if memory can not be allocated
|
||||
* - ESP_FAIL if partition can not be mounted
|
||||
* - other error codes from SDMMC or SPI drivers, SDMMC protocol, or FATFS drivers
|
||||
*/
|
||||
esp_err_t esp_vfs_fat_sdspi_mount_mh(const char* base_path, const sdmmc_host_t* host_config_input, const sdspi_device_config_t* slot_config, const esp_vfs_fat_mount_config_t* mount_config, sdmmc_card_t** out_card);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
#include "esp_chip_info.h"
|
||||
|
||||
// SD-Card ////////////////////
|
||||
#include "esp_vfs_fat_mh.h"
|
||||
#include "ffconf_mh.h"
|
||||
#include "sdcard_init.h"
|
||||
#include "esp_vfs_fat.h"
|
||||
#include "ffconf.h"
|
||||
#include "driver/sdmmc_host.h"
|
||||
///////////////////////////////
|
||||
|
||||
@@ -101,6 +102,7 @@ bool Init_NVS_SDCard()
|
||||
|
||||
ESP_LOGD(TAG, "Using SDMMC peripheral");
|
||||
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
|
||||
host.max_freq_khz = SDMMC_FREQ_HIGHSPEED;
|
||||
|
||||
// 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.
|
||||
@@ -118,14 +120,21 @@ bool Init_NVS_SDCard()
|
||||
// connected on the bus. This is for debug / example purpose only.
|
||||
slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
|
||||
// Der PullUp des GPIO13 wird durch slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
|
||||
// nicht gesetzt, da er eigentlich nicht benötigt wird,
|
||||
// dies führt jedoch bei schlechten Kopien des AI_THINKER Boards
|
||||
// zu Problemen mit der SD Initialisierung und eventuell sogar zur reboot-loops.
|
||||
// Um diese Probleme zu kompensieren, wird der PullUp manuel gesetzt.
|
||||
gpio_set_pull_mode(GPIO_NUM_13, GPIO_PULLUP_ONLY); // HS2_D3
|
||||
|
||||
// Options for mounting the filesystem.
|
||||
// If format_if_mount_failed is set to true, SD card will be partitioned and
|
||||
// formatted in case when mounting fails.
|
||||
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
|
||||
.format_if_mount_failed = false,
|
||||
.max_files = 12, // previously -> 2022-09-21: 5, 2023-01-02: 7
|
||||
.allocation_unit_size = 0, // 0 = auto
|
||||
.disk_status_check_enable = 1
|
||||
.allocation_unit_size = 0, // 0 = auto
|
||||
.disk_status_check_enable = 0
|
||||
};
|
||||
|
||||
sdmmc_card_t* card;
|
||||
@@ -135,7 +144,7 @@ bool Init_NVS_SDCard()
|
||||
// Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
|
||||
// Please check its source code and implement error recovery when developing
|
||||
// production applications.
|
||||
ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
ret = esp_vfs_fat_sdmmc_mount_mh(mount_point, &host, &slot_config, &mount_config, &card);
|
||||
|
||||
if (ret != ESP_OK) {
|
||||
if (ret == ESP_FAIL) {
|
||||
|
||||
@@ -232,9 +232,10 @@
|
||||
ROIInfo,
|
||||
cofcat,
|
||||
param,
|
||||
enhanceCon = false;
|
||||
lockAspectRatio = true;
|
||||
lockSizes = false;
|
||||
_roialt = "ana",
|
||||
enhanceCon = false,
|
||||
lockAspectRatio = true,
|
||||
lockSizes = false,
|
||||
domainname = getDomainname();
|
||||
|
||||
function doReboot() {
|
||||
@@ -306,9 +307,16 @@ function newROI() {
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
var _number= sel.options[sel.selectedIndex].text;
|
||||
sel = document.getElementById("index");
|
||||
var _roialt= sel.options[sel.selectedIndex].text;
|
||||
|
||||
if (ROIInfo.length > 0) {
|
||||
_roialt = sel.options[sel.selectedIndex].text;
|
||||
}
|
||||
else {
|
||||
_roialt = "ana";
|
||||
}
|
||||
|
||||
var _roinew = prompt("Please enter a name for the new ROI", "name");
|
||||
var _roinew = prompt("Please enter a name for the new ROI", _roialt);
|
||||
|
||||
if (_roinew === null) {
|
||||
return; //break out of the function early because prompt was aborted
|
||||
}
|
||||
@@ -715,8 +723,6 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
if (typeof ROIInfo === 'undefined') { // During init, ROIInfo is not defined yet
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var canvas = document.getElementById('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
@@ -813,18 +819,6 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
}
|
||||
}
|
||||
|
||||
function getCoords(elem) { // crossbrowser version
|
||||
var box = elem.getBoundingClientRect();
|
||||
var body = document.body;
|
||||
var docEl = document.documentElement;
|
||||
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
||||
var clientTop = docEl.clientTop || body.clientTop || 0;
|
||||
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
||||
var top = box.top + scrollTop - clientTop;
|
||||
var left = box.left + scrollLeft - clientLeft;
|
||||
return { top: Math.round(top), left: Math.round(left) };
|
||||
}
|
||||
|
||||
function mouseDown(e) {
|
||||
zw = getCoords(this)
|
||||
@@ -924,7 +918,7 @@ function drawTextBG(context, txt, x, y, padding) {
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
var _number= sel.options[sel.selectedIndex].text;
|
||||
sel = document.getElementById("index");
|
||||
var _roialt= sel.options[sel.selectedIndex].text;
|
||||
_roialt= sel.options[sel.selectedIndex].text;
|
||||
|
||||
var _roinew = prompt("Please enter a new name for the selected ROI", _roialt);
|
||||
if (_roinew === null) {
|
||||
|
||||
@@ -223,11 +223,12 @@
|
||||
ROIInfo,
|
||||
cofcat,
|
||||
param,
|
||||
enhanceCon = false;
|
||||
lockAspectRatio = true;
|
||||
lockSizes = false;
|
||||
lockSpaceEquidistant = true;
|
||||
space = 3;
|
||||
_roialt = "dig",
|
||||
enhanceCon = false,
|
||||
lockAspectRatio = true,
|
||||
lockSizes = false,
|
||||
lockSpaceEquidistant = true,
|
||||
space = 3,
|
||||
domainname = getDomainname();
|
||||
|
||||
function doReboot() {
|
||||
@@ -300,9 +301,16 @@ function newROI() {
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
var _number= sel.options[sel.selectedIndex].text;
|
||||
sel = document.getElementById("index");
|
||||
var _roialt= sel.options[sel.selectedIndex].text;
|
||||
|
||||
if (ROIInfo.length > 0) {
|
||||
_roialt = sel.options[sel.selectedIndex].text;
|
||||
}
|
||||
else {
|
||||
_roialt = "dig";
|
||||
}
|
||||
|
||||
var _roinew = prompt("Please enter a name for the new ROI", "name");
|
||||
var _roinew = prompt("Please enter a name for the new ROI", _roialt);
|
||||
|
||||
if (_roinew === null) {
|
||||
return; //break out of the function early because prompt was aborted
|
||||
}
|
||||
@@ -821,19 +829,6 @@ function draw() {
|
||||
}
|
||||
}
|
||||
|
||||
function getCoords(elem) { // crossbrowser version
|
||||
var box = elem.getBoundingClientRect();
|
||||
var body = document.body;
|
||||
var docEl = document.documentElement;
|
||||
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
|
||||
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
|
||||
var clientTop = docEl.clientTop || body.clientTop || 0;
|
||||
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
|
||||
var top = box.top + scrollTop - clientTop;
|
||||
var left = box.left + scrollLeft - clientLeft;
|
||||
return { top: Math.round(top), left: Math.round(left) };
|
||||
}
|
||||
|
||||
function mouseDown(e) {
|
||||
zw = getCoords(this)
|
||||
rect.startX = e.pageX - zw.left;
|
||||
@@ -987,7 +982,7 @@ function draw() {
|
||||
var sel = document.getElementById("Numbers_value1");
|
||||
var _number= sel.options[sel.selectedIndex].text;
|
||||
sel = document.getElementById("index");
|
||||
var _roialt= sel.options[sel.selectedIndex].text;
|
||||
_roialt= sel.options[sel.selectedIndex].text;
|
||||
|
||||
var _roinew = prompt("Please enter a new name for the selected ROI", _roialt);
|
||||
if (_roinew === null) {
|
||||
@@ -1008,7 +1003,6 @@ function draw() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
init();
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user